On SOLID code: OPosted: June 15, 2011
The O in SOLID stands for “Open/Closed Principle”, and in practice encompasses a number of programming practices that enable the construction of useful, stable lass hierarchies. When building a class, every part of the class should be as closed as possible for modification, and as open as needed for extension. In practice, this means that all methods should be private unless absolutely necessary. All members should be both private and final, again, unless absolutely necessary.
Classes should be closed for modification in order to enable a number of things. First and foremost, members should be private so that operations on them are handled by the root class, which can then be modified and versioned in ways that can be guaranteed to be both backwards compatible and stable. Once encapsulation is broken, base classes become both unstable and difficult to version, since there is no clear contract as to how and when their state can be modified. The most stable, versionable class has immutable state and no public members. Such classes are often not particularly useful or interesting, although they do exist (for example, one could imagine a class responsible for doing periodic healthchecks against a cluster of machines and recording the results.) When opening a class up to extension, such as declaring methods protected, there should be a clear, explicit contract as to the expected behavior of extensions. When opening methods up for public or package consumption, again, there should be both a clear, well understood behavioral contract, and subclasses should not be able to alter this contract, since that would violate the ability for those subclasses to be reliably substituted for their base class.
The use of interfaces and abstract base classes where appropriate also enables the closing of classes for modification, since modifications that break the interface’s contract, or that of the abstract class will be caught by the compiler. The intelligent use of interfaces and abstract classes is incredibly important: Successful framework design, which enables high degrees of reuse, often relies on dependencies on abstraction (interfaces, abstract classes,) rather than concretions. We put a great deal of thoughts into what things in the system should be interfaces, and where abstract classes can be deployed. Such tools provide wonderfully useful constraints as well, ensuring that failure to write extensions to a framework in the correct way are caught as early and quickly as possible.