The SOLID Principles
The SOLID Principles
D Principles
S – Single-Responsibility Principle.
O – Open-Closed Principle.
1. Single-Responsibility Principle
2. Open-Closed Principle
Example
In this simple example, you can see a rectangle class that has two
methods; Area () and Draw ().
It’s important to keep in mind that this principle leads to many other
nuances and concepts, and not only those that fall within SOLID, but
others that should be known and able to apply to get an application
with a solid design.
Open/closed principle
The open/closed principle is the second principle in the row regarding
the solid principles acronym.
Currently our system has only one kind of discount which applies to
all adults.
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
import java.math.RoundingMode;
And the discount service shall apply this discount to the price given.
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
return discount.apply(price);
}
}
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
import java.math.RoundingMode;
This makes things a little more complicated for the discount service
since the service has to apply both the discount for adult and both the
discount for seniors.
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
return discount.apply(price);
}
By doing so we modified the discount service sourcecode to extend its
behaviour. Also for every different discount that the sales department
might come up with, the discount service will get extra methods.
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Override
public BigDecimal apply(BigDecimal price) {
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Override
public BigDecimal apply(BigDecimal price) {
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
for(Discount discount:discounts) {
discountPrice = discount.apply(discountPrice);
}
return discountPrice;
}
}
package com.gkatzioura.solid.ocp;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Override
public BigDecimal apply(BigDecimal price) {
BigDecimal percent = new BigDecimal("0.01");
BigDecimal discount = price.multiply(percent);
return price.subtract(discount.setScale(2,
RoundingMode.HALF_UP));
}
}
You can find the source code on github. The next principle is
the liskov substitution principle.
Liskov substitution principle
Previously we took a dive into solid principles including the single
responsibility and the open/closed principle.
The Liskov substitution principle (LSP) is a particular definition of a
subtyping relation, called (strong) behavioral subtyping,
package com.gkatzioura.solid.liskov;
System.out.println("Employee is working");
}
package com.gkatzioura.solid.liskov;
@Override
public void work() {
throw new IllegalArgumentException("Employees on vacation should
not work");
}
}
import java.util.List;
for(Employee employee:employees) {
employee.work();
}
}
}
package com.gkatzioura.solid.liskov;
public class Employee {
package com.gkatzioura.solid.liskov;
package com.gkatzioura.solid.liskov;
void relax();
}
Then the project will use only employees who are implementations of
the WorkingEmployee interface and extend the employee class.
package com.gkatzioura.solid.liskov;
@Override
public void work() {
}
}
package com.gkatzioura.solid.liskov;
import java.util.List;
for(WorkingEmployee workingEmployee:workingEmployees) {
workingEmployee.work();
}
}
}
package com.gkatzioura.solid.segragation;
void compete();
void swim();
void highJump();
void longJump();
We have added the method compete but also there some extra
methods like swim highJump and longJump.
Suppose that JohnDoe is a swimming athlete. By implementing the
Athlete interface we have to implement methods like highJump and
longJump which JohnDoe will never use.
package com.gkatzioura.solid.segragation;
@Override
public void compete() {
System.out.println("John Doe started competing");
}
@Override
public void swim() {
System.out.println("John Doe started swimming");
}
@Override
public void highJump() {
}
@Override
public void longJump() {
}
}
The same problem will occur for another athlete who might be a field
Athlete competing on high jump and long jump.
We will follow the interface segregation principle and we will refactor
the original interface
package com.gkatzioura.solid.segragation;
void compete();
}
Then we will create two other interfaces one for Jumping athletes and
one for Swimming athletes.
package com.gkatzioura.solid.segragation;
void swim();
package com.gkatzioura.solid.segragation;
void longJump();
And therefore John Doe will not have to implement actions that he is
not capable of performing.
package com.gkatzioura.solid.segragation;
@Override
public void compete() {
System.out.println("John Doe started competing");
}
@Override
public void swim() {
System.out.println("John Doe started swimming");
}
}
1. DRY (Don't repeat yourself)
Our first object-oriented design principle is DRY, as the name suggests DRY
(don't repeat yourself) means don't write duplicate code, instead
use Abstraction to abstract common things in one place. If you have a block of
code in more than two places consider making it a separate method, or if you
use a hard-coded value more than one time make them public final constant.
By using common code for two different functionality or thing you closely
couple them forever and when your OrderId changes its format, your SSN
validation code will break.
So beware of such coupling and just don’t combine anything which uses
similar code but are not related
If you are coding in Java then follow the principle of making variable and
methods private by default and increasing access step by step e.g. from
private to protected and not public.
Ideally, if you are adding new functionality only than your code should be
tested and that's the goal of Open Closed Design principle. By the way, the
Open-Closed principle is "O" from the SOLID acronym.
In Simple language Open closed design principles says that new functionality
should be added by introducing new classes, methods or fields instead of
modifying already tried and tested code. One of the way to achieve this is
Inheritance where class is extended to introduce new functionality on top of
inherited basic features.
Btw, you would need a Pluralsight membership to get access this course,
which cost around $29 per month or $299 annually (14% discount).
If you don't have Pluralsight membership, I encourage you to get one because
it allows you to access their 5000+ online courses on all the latest topics like
front-end and back-end development, machine learning, etc. It also includes
interactive quizzes, exercises, and latest certification material.
It's more like Netflix for Software Developers and Since learning is an
important part of our job, Plurlasight membership is a great way to stay ahead
of your competition.
They also provide a 10-day free trial without any commitment, which is a great
way to not just access this course for free but also to check the quality of
courses before joining Pluralsight
Another benefit of this design principle in Java is, the interface has the
disadvantage of implementing all method before any class can use it so having
single functionality means less method to implement. If you don't the get the
benefit of the interface in coding then I suggest you read my blog post, the real
usage of an interface in Java to learn more.