Why not Make Every Method Virtual?
In an effort to make my blog suck less, I figured I’d post my response to Ward Bells Do not Make Every Method Virtual. He carries on the discussion of whether or not .NET methods should be made virtual by default, as they are in Java. You should read his post before mine, as this will establish the context of everything that follows (and if you don’t already subscribe to his blog, be sure to add it – you’ve been missing out on a lot of good stuff).
All done? Ok, I’ll continue.
Reviewing case against default virtual methods
Unwanted Extension Points
Wards "elevator" example demonstrates that we should not make all methods virtual, since it exposes extension points where we do not want them. Let’s have a look at the reason this particular extension point is not wanted.
The elevator.Up() method has responsibilities for moving the elevator up and closing doors etc. These responsibilities need to be chained in the right order to function safely; we do not wish client code to break the desired behaviour by inadvertently calling Up() at the wrong time.
This seems to highlight a smell that the single responsibility principle is not being adhered to in the elevator class. Since this issue is cause by incorrect ordering of multiple responsibilities. By adhering to SRP, separating out the different behaviours from the responsibility of coordinating them, we can avoid this conflict. We can then keep this method as virtual, and benefit from an additional extension hook safely.
Paraphrasing another point made (I hope Ward will correct me if this is a misrepresentation) is that we may want to be very explicit about the points in which an application can be extended. Marking a method as virtual is a way of explicitly showing your "approved" extension hook.
Arguably, I prefer to see my "extension hooks" more explicitly than a virtual method. As an application developer looking for a "seam" to extend, I would favour seeing an interface that I can implement, over inheriting from a class and overriding it’s virtual methods. This is mainly because to interfaces tend to be more discoverable than virtual methods.
Lastly, I have a different perspective on the OCP. I believe "closed for modification" to be in reference to the actual LOC contained in the class rather than "closing off" modifications to behaviour. I agree however, that making all methods virtual does open up a greater susceptibility to the violation of LSP.
The arguments made against methods being virtual as default mostly seem to fit under the umbrella of protecting yourself against unwitting developers. I think there are safer ways to do this whilst still obtaining the flexibility provided when all methods are virtual by default.