Object-oriented design is a topic about which dozens of books have been written. I do not wish to replace those lengthy tomes with this blog entry. However, this blog entry does intend to provide a basic means to write better code. The provided means are the rules below. There are some exceptions to those rules, so they are not ironclad. However, following them generally will lead you to write better code. In addition, the code that implements these rules is available for download.
- All public elements, excluding constants, should be included in a class’s interface. Those public elements can exist anywhere in the interface’s inheritance structure. Fastidiously following this principle allows a developer to use the Dependency Inversion Principle (DIP), which seeks to make all class references point to either interfaces or abstract classes. Replacing concrete class references with references to abstract classes or interfaces allows a coder to use multiple implementations of an interface or an abstract class without necessarily having to change the code blocks where that object is being utilized. As a result, the code is much more maintainable and extensible.
- Any class that should not be instantiated should be declared abstract. Abstract classes cannot be instantiated. Any code that attempts to do so will generate a compile-time error. Such errors tell the programmer that he, wittingly or unwittingly, is using a given class incorrectly. Warning the programmer of incorrect usage can avoid unwanted behavior, which can result in bugs. If those bugs result from seemingly correct code, then they can be difficult to find. Such a mess can easily be obviated by declaring any class that should not be instantiated abstract.
- All classes that should not be extended should prevent coders from doing so. This rule addresses the same problem as the rule above. Unwanted behavior has the potential to introduce bugs that are difficult to root out. In the same manner that incorrect instantiation can lead to unwanted behavior, incorrect class extension can lead to unwanted behavior as well. As a result, a developer should prevent coders from extending classes that should not be extended.
- Any method that should not be overridden should also prevent coders from doing so. This rule is another variation on the same theme: unwanted behavior. Overriding a method that should not be overridden can introduce a raft of problems, especially if the overriding coder is unaware of the behavior of the method in the parent class. A coder should not be allowed to cause that much damage to the code base. Consequently, a coder should only be allowed to override certain methods and all other methods should block overrides.
- A class hierarchy can be built from a collection of small interfaces. The first rule discussed using interfaces to represent the public face of entities. However, those entity interfaces can be constructed from smaller more focused interfaces. This concept is called the Interface Segregation Principle (ISP). ISP allows entity interfaces to be expanded in a plug-and-play manner. An interface need only extend/implement a given interface to have that interface’s functionality. In addition, all interfaces that inherit from that interface can be updated by modifying that interface. As a result, code that makes use of ISP is more maintainable and extensible.
- Your code base should be constructed from a hierarchy of base classes. Interfaces represent similar outward facing functionality, but a class hierarchy has similarities beyond public functionality. They also have similar implementations. Those similarities of implementation should be represented through a hierarchy of base classes. Where possible, any class that can be instantiated should inherit from one of those base classes. Like ISP, a hierarchy of base classes allows a coder to change a class by simply changing its base class. Similarly, any class that has a given base class can be updated by updating that base class. Because of that capability, a code base that utilizes a collection of common base classes is more flexible and extensible.