History of SOLID Principles in Java: Object-Oriented Programming
History of SOLID Principles in Java: Object-Oriented Programming
When you use the principle of S.O.L.I.D in your coding, you start writing the code
that is both efficient and effective.
In this blog, we will discuss all the five SOLID principles of Java in details.
Single Responsibility Principle in Java
What does it say?
Robert C. Martin describes it as one class should have only one and only
responsibility.
According to the single responsibility principle, there should be only one reason due
to which a class has to be changed. It means that a class should have one task to
do. This principle is often termed as subjective.
The principle can be well understood with an example. Imagine there is a class
which performs following operations.
Connected to a database
Read some data from database tables
Finally, write it to a file.
Have you imagined the scenario? Here the class has multiple reasons to change,
and few of them are the modification of file output, new data base adoption. When
we are talking about single principle responsibility, we would say, there are too many
reasons for the class to change; hence, it doesn’t fit properly in the single
responsibility principle.
For example, an Automobile class can start or stop itself but the task of washing it
belongs to the CarWash class. In another example, a Book class has properties to
store its own name and text. But the task of printing the book must belong to the
Book Printer class. The Book Printer class might print to console or another medium
but such dependencies are removed from the Book class
Why is that this Principle is Required?
When the Single Responsibility Principle is followed, testing is easier. With a single
responsibility, the class will have fewer test cases. Less functionality also means
fewer dependencies to other classes. It leads to better code organization since
smaller and well-purposed classes are easier to search.
Suppose you are asked to implement a UserSetting service wherein the user can
change the settings but before that the user has to be authenticated. One way to
implement this would be:
Explore Curriculum
Basic function of the chrome browser is to surf different sites. Do you want to check
grammar when you are writing an email using chrome browser?If yes, you can
simply use Grammarly extension, it provides you grammar check on the content.
This mechanism where you are adding things for increasing the functionality of the
browser is an extension. Hence, the browser is a perfect example of functionality
that is open for extension but is closed for modification. In simple words, you can
enhance the functionality by adding/installing plugins on your browser, but cannot
build anything new.
Lets say we need to calculate areas of various shapes. We start with creating a class
for our first shape Rectangle which has 2 attributes length & width.
1
2 public class AreaCalculator
3 public double calculateRectangleArea(Rectangle rectangle)
4 return rectangle.length *rectangle.width;
5 }
6 }
7
So far so good. Now let’s say we get our second shape circle. So
we promptly create a new class Circle with a single attribute radius.
1
public class Circle
2 {public double radius;
3 }
4
Then we modify Areacalculator class to add circle calculations
through a new method calculateCircleaArea()
1
2
3
4 public class AreaCalculator
5 { public double calculateRectangleArea(Rectangle rectangle)
{ return rectangle.length *rectangle.width;
6
}
7 public double calculateCircleArea(Circle circle)
8 {return (22/7)*circle.radius*circle.radius;
9 }
10 }
11
12
13
However, note that there were flaws in the way we designed our solution above.
Lets say we have a new shape pentagon. In that case, we will again end up
modifying the AreaCalculator class. As the types of shapes grows this becomes
messier as AreaCalculator keeps on changing and any consumers of this class will
have to keep on updating their libraries which contain AreaCalculator. As a result,
AreaCalculator class will not be baselined(finalized) with surety as every time a new
shape comes it will be modified. So, this design is not closed for modification.
Let us now see a more elegant design which solves the flaws in the above design by
adhering to the Open/Closed Principle. We will first of all make the design extensible.
For this we need to first define a base type Shape and have Circle & Rectangle
implement Shape interface.
1
2 public class AreaCalculator
3 {public double calculateShapeArea(Shape shape)
4 {return shape.calculateArea();
5 }
6 }
7
This AreaCalculator class now fully removes our design flaws noted above and gives
a clean solution which adheres to the Open-Closed Principle. Let’s move on with
other SOLID Principles in Java
You can say that it is a unique object-oriented principle. The principle can further be
simplified by ; a child type of a particular parent type without making any
complication or blowing things up should have the ability to stand in for that
parent.This principle is closely related to Liskov Substitution principle.
The LSP is popularly explained using the square and rectangle example. if we
assume an ISA relationship between Square and Rectangle. Thus, we call “Square
is a Rectangle.” The code below represents the relationship.
1
2 public class Rectangle
3 {private int length;
4 private int breadth;
5
6 public int getLength()
7 {return length;
8 }
9 public void setLength(int length)
10
11
{this.length = length;
12 }
13 public int getBreadth()
14 {return breadth;
15
}
16
17 public void setBreadth(int breadth)
18 {this.breadth = breadth;
19 }
20 public int getArea()
21
22 {return this.length * this.breadth;
23 }
24 }
25
Below is the code for Square. Note that Square extends Rectangle.
Thus, in the example shown below, the function calculateArea which uses the
reference of “Rectangle” should be able to use the objects of derived class such as
Square and fulfill the requirement posed by Rectangle definition. One should note
that as per the definition of Rectangle, following must always hold true given the data
below:
1. Length must always be equal to the length passed as the input to method,
setLength
2. Breadth must always be equal to the breadth passed as input to method,
setBreadth
3. Area must always be equal to product of length and breadth
In case, we try to establish ISA relationship between Square and Rectangle such
that we call “Square is a Rectangle”, above code would start behaving unexpectedly
if an instance of Square is passed Assertion error will be thrown in case of check for
area and check for breadth, although the program will terminate as the assertion
error is thrown due to failure of Area check.
The Square class does not need methods like setBreadth or setLength. The
LSPDemo class would need to know the details of derived classes of Rectangle
(such as Square) to code appropriately to avoid throwing error. The change in the
existing code breaks the open-closed principle in the first place.
For example, a single logging interface for writing and reading logs is useful for a
database but not for a console. Reading logs make no sense for a console logger.
Moving on with this SOLID Principles in Java article.
Let us say that there is a Restaurant interface which contains methods for accepting
orders from online customers, dial-in or telephone customers and walk-in customers.
It also contains methods for handling online payments (for online customers) and in-
person payments (for walk-in customers as well as telephone customers when their
order is delivered at home).
1
2 public class OnlineClientImpl implements RestaurantInterface
3 {public void acceptOnlineOrder()
4 {//logic for placing online order
5
}
6
7 public void takeTelephoneOrder()
8 {//Not Applicable for Online Order
9 throw new UnsupportedOperationException();
10 }
11 public void payOnline()
12 {//logic for paying online
13
14
}
15 public void walkInCustomerOrder()
16 {//Not Applicable for Online Order
17 throw new UnsupportedOperationException();
18 }
19 public void payInPerson() {
20 //Not Applicable for Online Order
21
22 throw new UnsupportedOperationException();
23 }
24 }
25
Since the above code(OnlineClientImpl.java) is for online orders, throw
UnsupportedOperationException.
Online, telephonic and walk-in clients use the RestaurantInterface
implementation specific to each of them.
The implementation classes for Telephonic client and Walk-in client will have
unsupported methods.
Since the 5 methods are part of the RestaurantInterface, the implementation
classes have to implement all 5 of them.
The methods that each of the implementation classes throw
UnsupportedOperationException. As you can clearly see – implementing all
methods is inefficient.
Any change in any of the methods of the RestaurantInterface will be
propagated to all implementation classes. Maintenance of code then starts
becoming really cumbersome and regression effects of changes will keep
increasing.
RestaurantInterface.java breaks Single Responsibility Principle because the
logic for payments as well as that for order placement is grouped together in a
single interface.
To overcome the above mentioned problems , we apply Interface Segregation
Principle to refactor the above design.
1. Separate out payment and order placement functionalities into two separate
lean interfaces,PaymentInterface.java and OrderInterface.java.
2. Each of the clients use one implementation each of PaymentInterface and
OrderInterface. For example – OnlineClient.java uses OnlinePaymentImpl and
OnlineOrderImpl and so on.
3. Single Responsibility Principle is now attached as Payment
interface(PaymentInterface.java) and Ordering interface(OrderInterface).
4. Change in any one of the order or payment interfaces does not affect the
other. They are independent now.There will be no need to do any dummy
implementation or throw an UnsupportedOperationException as each
interface has only methods it will always use.
You go to a local store to buy something, and you decide to pay for it by using your
debit card. So, when you give your card to the clerk for making the payment, the
clerk doesn’t bother to check what kind of card you have given.
Even if you have given a Visa card, he will not put out a Visa machine for swiping
your card. The type of credit card or debit card that you have for paying does not
even matter; they will simply swipe it. So, in this example, you can see that both you
and the clerk are dependent on the credit card abstraction and you are not worried
about the specifics of the card. This is what a dependency inversion principle is.
Comprehensive Java Course Certification Training