There are many resources available on the internet that cover in detail what the SOLID design principles are in relation to object oriented programming. My goal of this article is to provide a clear and concise summary that condenses this topic in a way that can be read within a few minutes and used as a reference point for your future coding endeavors. For code examples, I felt it better to just google “Solid Code Examples” and leverage the many great resources already available for this, rather recreate it myself.
The Object-Oriented Principles
Degree to which a module, class, or other construct, is tied directly to others. For example, the degree of coupling between two classes can be seen as how dependent one class is on the other. By abstracting many of the implementation needs into various interfaces and introducing the concepts of OCP and DIP, you can create a system that has very low coupling.
The extent to which two or more parts of a system are related and how they work together to create something more valuable than the individual parts. Think of the old adage, “The whole is greater than the sum of the parts.” With low coupling and SRP-you can stack a lot of small pieces together like building blocks to create something larger and more complex. Think of a puzzle and the many pieces that make up the whole.
Strong encapsulation is evidenced by the ability for a developer to use a given class or module by its interface, alone. The developer does not, and should not, need to know the implementation specifics of the class or module. LSP, DIP, and SRP all work hand in hand to create encapsulation. You can encapsulate the behavioral implementations in many individual objects, preventing them from leaking into each other, while ensuring that you aren’t violating any of the individual abstraction’s semantics or purpose, according to LSP
S: Single Responsibility Principle (SRP)
Says that classes, modules, etc., should have one and only one reason to change. This helps to drive cohesion into a system and can be used as a measure of coupling as well. When the business perception and context has changed, then you have a reason to change the class. The lesson to remember in this release of the application is that the Single Responsibility Principle is driven by the needs of the business to allow change. “A single reason to change” helps you understand which logically separate concepts should be grouped together by considering the business concept and context, instead of the technical concept alone. ISP can be considered a sub-set, or more specific form of the Single Responsibility Principle.
O: Open-Closed Principle (OCP)
How a system can be extended by modifying the behavior of individual classes or modules, without having to modify the class or module itself. This helps you create well-encapsulated, highly cohesive systems. Two saw blades that attach to the same type of saw, will have one thing in common: how they attach to the saw-the interface between the saw and the blade. The wide variety of blades, combined with the common method of attaching them to the saw, allows you to change the behavior of the saw without having to modify the mechanical portion of the saw. The ubiquitous nature of explicit interfaces in .NET, as well as abstract base classes, delegates, and other forms of abstraction, all provide different ways of allowing custom behavior to be supplied to existing classes and modules. You can use design patterns such as Strategy, Template, State, and others to facilitate the behavioral changes through the use of such abstractions.
L: Liskov Substitution Principle (LSP)
Helps with encapsulation and cohesion. This principle says that you should not violate the intent or semantics of the abstraction that you are inheriting from or implementing. An object inheriting from a base class, interface, or other abstraction must be semantically substitutable for the original abstraction. The square-rectangle issue illustrates a violation of the Liskov Substitution Principle. You clearly have the wrong abstraction to represent both a square and a rectangle for this scenario. This is evidenced by the square overriding the height and width properties of the rectangle, and changing the expected behavior of a rectangle. By limiting the abstraction to only what is common among all of the shapes, and ensuring that no shape has a different meaning for “area” you can help prevent LSP violations.
I: Interface Segregation Principle (ISP)
Helps to make your system easy to understand and use. It says that you should not force a client to depend on an interface (API) that the client does not need. This helps you develop a well-encapsulated, cohesive set of parts. If there are multiple concerns represented by an interface, or the methods and properties are unclear, then it becomes difficult to know which methods should be called when. Provide a separated set of interfaces that encapsulate the processes in question, independently.
D: Dependency Inversion Principle (DIP)
Helps you to understand how to correctly bind your system together. It tells you to have your implementation detail depend on the higher-level policy abstractions, and not the other way around. This helps you to move toward a system that is coupled correctly, and directly influences that system’s encapsulation and cohesion. You need to ensure that you are only working with the interface and never with a concrete implementation. That is, the code that relies on the interface should only ever know about the interface. Another way to think about DIP is to say that policy (high level) should not depend on detail (implementation), but detail should depend on policy. The higher-level policy should define an abstraction that it will call out to, where some detailed implementation executes the requested action. You want to decouple your system so that you can change individual pieces without having to change anything more than the individual piece