The SOLID Principles of Object-Oriented Programming Explained in Plain English
The SOLID Principles of Object-Oriented Programming Explained in Plain English
These five principles help us understand the need for certain design
patterns and software architecture in general. So I believe that it is a topic
that every developer should learn.
This article will teach you everything you need to know to apply SOLID
principles to your projects.
We will start by taking a look into the history of this term. Then we are
going to get into the nitty-gritty details – the why's and how's of each
principle – by creating a class design and improving it step by step.
Background
The SOLID principles were first introduced by the famous Computer
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 1 sur 21
:
The SOLID principles were first introduced by the famous Computer
Scientist Robert J. Martin (a.k.a Uncle Bob) in his paper in 2000. But the
SOLID acronym was introduced later by Michael Feathers.
Uncle Bob is also the author of bestselling books Clean Code and Clean
Architecture, and is one of the participants of the "Agile Alliance".
Let's look at each principle one by one. Following the SOLID acronym, they
are:
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 2 sur 21
:
and therefore it should have only a single reason to change.
This means that if a class is a data container, like a Book class or a Student
class, and it has some fields regarding that entity, it should change only
when we change the data model.
Merge conflicts are another example. They appear when different teams
change the same file. But if the SRP is followed, fewer conflicts will appear
– files will have a single reason to change, and conflicts that do exist will be
easier to resolve.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 3 sur 21
:
We will look at the code for a simple bookstore invoice program as an
example. Let's start by defining a book class to use in our invoice.
class Book {
String name;
String authorName;
int year;
int price;
String isbn;
public Book(String name, String authorName, int year, int price, String
this.name = name;
this.authorName = authorName;
this.year = year;
this.price = price;
this.isbn = isbn;
}
}
This is a simple book class with some fields. Nothing fancy. I am not making
fields private so that we don't need to deal with getters and setters and
can focus on the logic instead.
Now let's create the invoice class which will contain the logic for creating
the invoice and calculating the total price. For now, assume that our
bookstore only sells books and nothing else.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 4 sur 21
:
private double discountRate;
return priceWithTaxes;
}
Here is our invoice class. It also contains some fields about invoicing and 3
methods:
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 5 sur 21
:
printInvoice method, that should print the invoice to console, and
You should give yourself a second to think about what is wrong with this
class design before reading the next paragraph.
The first violation is the printInvoice method, which contains our printing
logic. The SRP states that our class should only have a single reason to
change, and that reason should be a change in the invoice calculation for
our class.
There is another method that violates the SRP in our class: the saveToFile
method. It is also an extremely common mistake to mix persistence logic
with business logic.
We can create new classes for our printing and persistence logic so we will
no longer need to modify the invoice class for those purposes.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 6 sur 21
:
We create 2 classes, InvoicePrinter and InvoicePersistence, and move the
methods.
Now our class structure obeys the Single Responsibility Principle and
every class is responsible for one aspect of our application. Great!
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 7 sur 21
:
every class is responsible for one aspect of our application. Great!
Open-Closed Principle
The Open-Closed Principle requires that classes should be open for
extension and closed to modification.
So what this principle wants to say is: We should be able to add new
functionality without touching the existing code for the class. This is
because whenever we modify the existing code, we are taking the risk of
creating potential bugs. So we should avoid touching the tested and
reliable (mostly) production code if possible.
But how are we going to add new functionality without touching the class,
you may ask. It is usually done with the help of interfaces and abstract
classes.
Now that we have covered the basics of the principle, let's apply it to our
Invoice application.
Let's say our boss came to us and said that they want invoices to be saved
to a database so that we can search them easily. We think okay, this is easy
peasy boss, just give me a second!
We create the database, connect to it, and we add a save method to our
InvoicePersistence class:
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 8 sur 21
:
public class InvoicePersistence {
Invoice invoice;
Unfortunately we, as the lazy developer for the book store, did not design
the classes to be easily extendable in the future. So in order to add this
feature, we have modified the InvoicePersistence class.
If our class design obeyed the Open-Closed principle we would not need
to change this class.
So, as the lazy but clever developer for the book store, we see the design
problem and decide to refactor the code to obey the principle.
interface InvoicePersistence {
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 9 sur 21
:
We change the type of InvoicePersistence to Interface and add a save
method. Each persistence class will implement this save method.
@Override
public void save(Invoice invoice) {
// Save to DB
}
}
@Override
public void save(Invoice invoice) {
// Save to file
}
}
Now our persistence logic is easily extendable. If our boss asks us to add
another database and have 2 different types of databases like MySQL and
MongoDB, we can easily do that.
You may think that we could just create multiple classes without an
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 10 sur 21
:
interface and add a save method to all of them.
But let's say that we extend our app and have multiple persistence classes
like InvoicePersistence, BookPersistence and we create a
PersistenceManager class that manages all persistence classes:
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 11 sur 21
:
This is the expected behavior, because when we use inheritance we
assume that the child class inherits everything that the superclass has. The
child class extends the behavior but never narrows it down.
Therefore, when a class does not obey this principle, it leads to some nasty
bugs that are hard to detect.
class Rectangle {
protected int width, height;
public Rectangle() {
}
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 12 sur 21
:
}
Now we decide to create another class for Squares. As you might know, a
square is just a special type of rectangle where the width is equal to the
height.
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) {
super.setHeight(height);
super.setWidth(height);
}
}
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 13 sur 21
:
Our Square class extends the Rectangle class. We set height and width to
the same value in the constructor, but we do not want any client (someone
who uses our class in their code) to change height or weight in a way that
can violate the square property.
class Test {
Your team's tester just came up with the testing function getAreaTest and
tells you that your getArea function fails to pass the test for square
objects.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 14 sur 21
:
objects.
In the first test, we create a rectangle where the width is 2 and the height
is 3 and call getAreaTest. The output is 20 as expected, but things go
wrong when we pass in the square. This is because the call to setHeight
function in the test is setting the width as well and results in an
unexpected output.
The principle states that many client-specific interfaces are better than
one general-purpose interface. Clients should not be forced to implement
a function they do no need.
class Car {
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 15 sur 21
:
We modeled a very simplified parking lot. It is the type of parking lot
where you pay an hourly fee. Now consider that we want to implement a
parking lot that is free.
@Override
public void parkCar() {
@Override
public void unparkCar() {
@Override
public void getCapacity() {
@Override
public double calculateFee(Car car) {
return 0;
}
@Override
public void doPayment(Car car) {
throw new Exception("Parking lot is free");
}
}
Our parking lot interface was composed of 2 things: Parking related logic
(park car, unpark car, get capacity) and payment related logic.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 16 sur 21
:
But it is too specific. Because of that, our FreeParking class was forced to
implement payment-related methods that are irrelevant. Let's separate or
segregate the interfaces.
We've now separated the parking lot. With this new model, we can even
go further and split the PaidParkingLot to support different types of
payment.
Now our model is much more flexible, extendable, and the clients do not
need to implement any irrelevant logic because we provide only parking-
related functionality in the parking lot interface.
"If the OCP states the goal of OO architecture, the DIP states the
primary mechanism".
These two principles are indeed related and we have applied this pattern
before while we were discussing the Open-Closed Principle.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 17 sur 21
:
We want our classes to be open to extension, so we have reorganized our
dependencies to depend on interfaces instead of concrete classes. Our
PersistenceManager class depends on InvoicePersistence instead of the
classes that implement that interface.
Conclusion
In this article, we started with the history of SOLID principles, and then we
tried to acquire a clear understanding of the why's and how's of each
principle. We even refactored a simple Invoice application to obey SOLID
principles.
I want to thank you for taking the time to read the whole article and I hope
that the above concepts are clear.
If you are interested in reading more articles like this, you can subscribe to
my blog's mailing list to get notified when I publish a new article.
If you read this far, tweet to the author to show them you care.
Tweet a thanks
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 18 sur 21
:
Learn to code for free. freeCodeCamp's open source curriculum has
helped more than 40,000 people get jobs as developers. Get started
ADVERTISEMENT
Our mission: to help people learn to code for free. We accomplish this by creating
thousands of videos, articles, and interactive coding lessons - all freely available to
the public. We also have thousands of freeCodeCamp study groups around the
world.
Donations to freeCodeCamp go toward our education initiatives, and help pay for
servers, services, and staff.
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 19 sur 21
:
Trending Guides
Exponents in Python
sizeof Operator in C
Dijkstra’s Algorithm
enumerate() in Python
What is an Algorithm?
Parse a Boolean in JS
Python Datetime.now()
Escape a String in JS
What is SQL?
Python Slicing
JavaScript Range
Merge Arrays in JS
JS Modulo Operator
JavaScript Encoding
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 20 sur 21
:
JavaScript Get Request
JS .reverse() Function
Our Charity
Copyright Policy
https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/ 16.09.23 11 12
Page 21 sur 21
: