This post originated from an RSS feed registered with Agile Buzz
by Keith Ray.
Original Post: Designing for Inheritance
Feed Title: MemoRanda
Feed URL: http://homepage.mac.com/1/homepage404ErrorPage.html
Feed Description: Keith Ray's notes to be remembered on agile software development, project management, oo programming, and other topics.
What's so hard about Designing for Inheritance? Not being able to predict the future. It may be impossible to know what every possible subclass is going to want to do with your base class, but it isn't hard to make a class that handles the majority of subclassing needs. (And if you make the source code available, the person writing the subclass has all the information that they need, including being able to refactor your class.)
But the real need isn't subclassing per se, but substitutability. Your class doesn't do exactly what we want, and we want to substitute our own class that does exactly what we want. In languages that require subclassing (or interface implementation) to get substitutability/polymorphism, that means we need to subclass or implement an interface. In more dynamic languages like Smalltalk, Ruby, Python, etc., we can just implement the methods in a new class, and pass in an object of that class where the other class object was expected. As long as the program isn't explicitly checking the name/identifier of the class of the object ("o.getClass().equals(expectedClass)" or "o instanceof expectedClass") we can usually get what we want.
One simple way to implement substitutability in languages like Java or C++ is to declare interfaces or abstract base classes that reflect the public API of your concrete classes. Always declare variables and parameters using the interface/ABC type everywhere - never use the concrete type. There's a rule of (less-dynamic) OO design: abstract types should only depend on other abstract types, and concrete types should depend only on abstract types. Of course, no one follows that rule to the letter. It's more of a guideline. But something to think about, perhaps with some exceptions for "common" types like String.
Here are some guidelines for writing a class that is easy to subclass: Always declare all public methods "virtual" or non-final. Declare member variables private, but also declare protected accessor methods for all the member variables and use those everywhere. Every place that you create an new object, put that "new" statement in a protected factory method that could be overridden. Break down large methods into ones that call a sequence of smaller, protected methods. If you have private methods, consider that a sign that those methods belong to another class (probably one that hasn't been written yet.) It's a little easier in Java than C++, because method-calls-on-this in a constructor in Java are "virtual" and are not virtual in C++.