SOLID Design Principles
SOLID Design Principles
• These are not RULES and so they don’t guarantee that the result will always satisfy above requirements. However, by following
• In practical world, it’s not always possible to adhere to these guidelines 100%. Reasons could be performance, legacy code,
integrations. But as designer/programmer we should try to follow them where it’s practical.
• Like design patterns, these principles teach you what has worked for others and you’re free to choose, modify and mix them
together to come up with a design that is best suited for your problem.
• Remember there is no silver bullet for designing software. All we have are some guidelines based on others experience. But this is
what makes software design exciting. We can draw on these principles & patterns and come up with our own unique designs
Copyright (c) 2022 Coffee Powered Crew
SOLID Principles
• SOLID is an acronym are a group of 5 principles. If we take first letter of names of these 5 principles we word SOLID.
• O – Open/Closed principle
• D – Dependency inversion
• These are NOT the only principles for software design but they are few of the most important ones.
• Their order is not important when applying these guidelines and they are not dependant on each other.
Copyright (c) 2022 Coffee Powered Crew
Single Responsibility Principle
• This principle can be summarized as: every class that we write should have only one responsibility/purpose.
• This principle is based on cohesion principle. Remember? High Cohesion & Low Coupling!
• A single responsibility here means that class provides a very focused functionality or it addresses a specific concern
of our desired functionality. In other words a class should have one & only one reason to change.
• Let’s say we’ve a code that creates & sends a message to a remote service listening on a port. Now this code can
change for few reasons: the communication protocol/parameters may change or the message format changes or if
• The Single Responsibility principle says that we have three separate responsibilities here, and should be in three
separate classes or modules. This makes our classes more easy to write, test, change & review.
Copyright (c) 2022 Coffee Powered Crew
Open/Closed Principle
• Open/closed principle states that any software entity (method, class, interface, module etc.) should be open for
extension but closed for modification. Sounds intentionally cryptic, isn’t it?
• The principle is fairly simple: We should be able to modify behavior of software entity easily without needing to
• Open => Open for extension (change/modify behavior); Closed => original entity’s source code is sealed from changes.
• So in Java it means using inheritance to modify the “behavior”. We can then substitute/swap implementation used by
• Abstraction is the key! If client code is using interface/abstract class references it’s easy to swap out implementations
expected and this should NOT alter desired properties of the program.
• The principle appears quite simple on surface. But note that, we are not only talking about simple type based
substitution where we need to follow some rules for overriding a method. This is also about behavioral subtyping.
• To put simply, this principle ensures that child class method does not change what was expected from a base class
method. Child class can use different logic/algorithm but it should not deviate from the overall expectation.
• For example, if a base class has print method which accepts a string and prints a string to console, then a child class
method can override it and print to a file. We’re changing where the printing occurs but not changing the
places for this: a method name itself, method level comments or an external document (design document) can
• For existing code where none of the above are present then the base class method code and its unit tests will set
expectation.
• For example in the print example on last page, if the method was called printToConsole then the method name is
setting expectation which is: print a string to console and so a child class method printing to file will violate the
method in child class also taking some action based on the message in addition to printing it.
• So overall takeaway is: Other people/code expect certain behavior from a method, when overriding such method
do not deviate from this behavior as then our child class is not truly a drop-in replacement of base class.
• This principle is important because it forces us to write child classes that are truly portable. If we come up with a
more efficient way to do something, all existing code which was using old base class would be able to take
• This principles suggests breaking down large interfaces into smaller & more specific interfaces. These smaller
• Although it’s an easy principle to understand, it’s also one which gets broken fairly often. Interfaces for UI,
interfaces for web services often have methods that do lots of different things, breaking the interface segregation
principle.
• In worst case scenario you end up with implementations that provide a “dummy” or empty implementation or
methods that throw exceptions like UnsupportedOperationException. These are clear indicators that you need to
• High-level modules should not depend directly on low-level modules. Both should depend on abstractions.
• Abstractions should not depend upon details. Details should depend upon abstractions.
• In essence this principle is about trying to avoid direct, tight coupling between modules/classes. This allows us to
compose the system using different implementations without requiring changes to modules.
• Word module here can be applied to our regular Java modules and even to classes.
• High level and low level modules here are referring to type of functionality the modules provide.
• A high level module will implement “business logic” and will map to use cases for the application.
Copyright (c) 2022 Coffee Powered Crew
Dependency Inversion Principle
• A low level module provides functionality that is basic and not tied to any one use case. For example a database
connection code, a sorting and searching code or file & network handling code etc.
• As per this principle, our business logic classes should not directly be tied to concrete classes providing database
handling or network code etc. Instead these classes should use abstractions to talk to low level classes e.g. Using
• And our low level classes also must confirm to the interfaces they implement. For example if a new low level class
for handling networking with UDP is written it must implement the methods of interface already used by high level
classes. It should not require high level classes to use methods specific to this new class. (Abstractions should not depend
or how I’m given these dependencies. It is a principle which states what should happen but NOT how it happens.
• Dependency Injection is a specific pattern with which we provide dependencies. Injection is HOW we implement
• So to summarize, Dependency inversion is NOT same as dependency injection! Dependency injection is about
control over wiring of dependencies, who creates necessary dependency objects, who supplies them, how & when.