There are many people who will say there are 5 design principles. Well, I believed that too for some time. There are a lot more design principles that you will be considering while designing an application/framework. But these are the few principles that you MUST adhere to in any application/framework class design.
These are the Principles of Object Oriented Class Design popularly known as the SOLID Principles.
- The Single Responsibility Principle [SRP]
- The Open/Closed Principle [OCP]
- The Liskov Substitution Principle [LSP]
- The Interface Segregation Principle [ISP]
- The Dependency Inversion Principle [DIP]
Uniform Access Principle
The other important principles, less popularly known (and not asked in interviews) are related to packaging
. Not to be confused with Java packages, Package here refers to a deployable component, or simply something you would 'release' for others to use - like a jar file. These are the Principles of Package Architecture aslo knows as Package Cohesion Principles.
- The Release Reuse Equivalency Principle [REP]
- The Common Closure Principle [CCP]
- The Common Reuse Principle [CRP]
- The Acyclic Dependencies Principle [ADP]
- The Stable Dependencies Principle [SDP]
- The Stable Abstractions Principle [SAP]
In this post I will try to give introduction to each of the above principles. Even though they all seem easy to grasp, it takes a lot of effort and practice to get them right.
I am planning on writing a series of posts explaining each principle in detail. I will come back and provide a link to the post when it is ready.
A Class should have only one reason to change
According to this principle, a class should have a single responsibility. There should be only one reason for the class to change.
This means, in simple terms, every other class depending on this class uses it in the same way. As a result any change to the class doesn't break the code in unexpected places.
Contrast this with a class that has two (or more) responsibilities. When one of the responsibilities change, it may have an affect on classes that doesn't use (or depend on) that responsibility and they themselves need to be changed (unnecessarily).
A module should be open for extension but closed for modification
The premise of open-closed principle is that one should not be changing the existing (working) code to incorporate the changes in requirement. Instead, the behavior of the class (or module) should be extended only by adding new code.
That means, classes and modules should be designed such that it is easy to extend the behavior and are closed for modification.
Subclasses should be substitutable for their base classes
This may seem obvious, but there are subtleties that need to be considered. Subclassing may inherit the type of the base class but there is no guarentee that the subclass's behavior is consistant with what the application needs.
For example, it is common for sorting utilities to accept Comparators
. In your application you can write custom comparator by implementing the Comparator interface. This is all fine, but application starts to behave weirdly if the custom comparator orders the elements in reverse order. This custom comparator is violating the LSP.
Clients should not be forced to depend upon interfaces that they do not use
I have spent considerable amount of time understanding how this principle is different from the SRP.
Some of my reasonings:
- Fat interfaces are sometimes inevitable (SRP does allow for carefully considered fat interaces) and ISP helps to keep things tidy under those circumstances.
Depend upon Abstractions. Do not depend upon concretions
The aim of this principle is to make a class (or module) as independent as possible. An independent component is both easy to replace and reuse somewhere else.
In simple terms -
- High level modules should not depend upon low level modules. Both should depend upon abstractions
- Abstractions should not depend upon details. Details should depend upon abstractions
_