One of the personal practices I have when writing C# code is that my classes are either
abstract. This really started with MicroObjects, but I've had the notion of it before. Sealed classes were hard to test... so I stopped, before.
Why I've changed is largely the same reason I originally started... at some point I was told/taught/read that classes should be designed to be inherited, or it wasn't meant to be inherited. Anything inbetween was a breeding ground for bugs.
This is the source of my "sealed or abstract". For the java folx, final or abstract. :)
This is a stricter version of the O in solid. Open for extension, closed for modification.
Non-sealed/abstract classes are exactly that. You can extend it, but don't go modifying it.
I don't think that works well. Classes should either be designed for inheritance, or prevent inheritance.
This works fantastic in an emergent design capacity. Write the class you need, when you need slightly different, make two versions with a common base. The ruthless refactoring sets you up to only ever have sealed or abstract classes.
I can hear some of you calling foul on me because I'm a strong advocate of
Composition, NOT inheritance - Quinn Gil
So why am I telling you to use abstract classes? Common behavior. Once you identify what's common and what's provided by the derived class... then you switch to composition.
My process to continue to avoid inheritance is to use inheritance.
Concrete -> Base & Derived -> Base w/Composition & Derived -> Base & Derived w/Composition -> Concrete w/Composition and interface.
Inheritance is quick, easy and ensures things work. Composition comes next when you've identified the interactions and relationships. Then you make the derived classes do the composition and the base class is almost empty. Then you don't need the base class.
This is the idealized flow. I do use these steps. Sometimes I'll shortcut the base & derived w/composition; only because my objects are small and it's easy to see. Following these steps allows your code to continue to compile at every step. You're able to migrate a single concrete class at a time through each step. It prevents having to change a dozen file to get compiling code. Generally only a file or two. That's important so you can get quick feedback.
Anyway... that's a quick bit on some thoughts about being either sealed or abstract. Never neither.
I'm sure I have more thoughtss... but it's Tuesday and I need to get a post up. :|
I'm running a bit low. haven't sat down and focused. I have some ideas... anyway. working on it. :)