In software development, principles differ from patterns in the sense that where patterns represent complete, identifiable, repeatable solutions to common problems, principles are objective, factual statements that can be made about code and the manner in which it is constructed and the overall design of an implementation. In other words, patterns refer to code scenarios while principles refer to qualities of code and these qualities are useful in identifying the value of the code.
In this post, we are going to be introduced to Bob Martin’s SOLID design principles. These principles have been around for a long time, and it is immeasurably important for every object-oriented developer to understand them and use them in making day-to-day design decisions. It is not that uncommon to see developers dive into development tasks by writing code first and considering architecture and design second. A good developer though inverts this scenario and considers a sound design before writing the first line of code! This prevents undue code maintenance pain later and allows for the development of better applications through and through.
Consideration of design principles is extremely important throughout a development effort, and failure to make the proper considerations can have a devastating effect on the development of the application and the application’s usefulness, performance, and maintainability.
What are the SOLID Design Principles?
In this section, we are going to outline and briefly discuss each of the five design principles. In subsequent posts we will dive into each principle in more detail. The five SOLID design principles are listed below:
- The Single Responsibility Principle (SRP) – this principle states that there should never be more than one reason to change a class. This means also that a given class should exist for one and only one purpose.
- The Open-Closed Principle – this principle states that modules should be open for extension but closed for modification. This seems a bit unclear at first until you realize that you can change an object’s behavior by either using abstractions, implementing common interfaces, and from inheritance from common base classes or extending abstract base classes.
- The Liskov Substitution Principle – this principle, introduced by Barbara Liskov, simply-stated means that derivative classes have to be substitutable for their base classes. If we think about this for a minute, we can see that this also means that any class that implements a specific interface can be replaced by any other class that implements that interface. In other words, it can be substituted for the original class. Furthermore, a derived class must honor the ‘contract’ set by its super class.
- The Interface Segregation Principle (ISP) – this principle states that objects should not be forced to implement interfaces that they do not use. Though this may sound obvious based on the wording, in practice it really means that interfaces should be finely-grained and not specific to the classes for which their implementation is intended.
- The Dependency Inversion Principle (DIP) – this principle states that higher-level classes should not depend upon lower-level classes but that both should depend on abstractions. Furthermore, these abstractions should not depend on concrete details but rather on abstractions as well.
Though each principle can be discussed individually, we should understand that no single principle should exist or be applied by itself. They should all be considered as part of the design process. In the following posts, we will dive into each principle in detail and take a look at some simplified code examples that illustrate each one.