Solid Principles in java with examples
SOLID is one of the most popular sets of
design principles when developing object-oriented software. It is a mnemonic
acronym for the five design principles which follow:
- Single
Responsibility Principle
- Open- Close
Principle
- Liskov
Substitution Principle
- Interface
Segregation Principle
- Dependency
Inversion Principle
1) Single Responsibility Principle (SRP)
The Single Responsibility Principle (SRP)
states that a class should only have one responsibility. Additionally, it
should have just one reason to change.
A lot of members can exist as long as they
relate to the single responsibility. It could be that multiple class members
may need modification when the one reason for change occurs. Multiple classes
may need to be updated, too.
Example:
class Movie{
public
String author(){...}
public void displayDetails(){...}
public void updateMovie(){...}
}
Here
we have logic of the database and logic of reporting all mixed up within one
class. If you have multiple responsibilities that are combined into one
class, it may be hard to change one part without breaking another. Mixing
responsibilities also makes the class more difficult to understand, and more
difficult to test, diminishing cohesion. The best way to fix this is to
split the class into two different classes, each of which has one
responsibility: access to the database and reporting, all separate.
2) Open- Close Principle (OCP)
The
Open-Closed Principle (OCP) states classes should be open to extension, but
closed to change. "Open to Extension" means you should design your
classes to be able to add new functionality as new requirements are generated.
"Closed for modification" means that you should change existing code
and trigger possible new bugs.
Those two
parts of the principle seem contradictory. However, you can add functionality
without editing existing source code, if you structure your classes and their
dependencies correctly.
Example:
In this example we implemented a class
called movieTickets.
public class movieTickets {
private
String type;
private
String movieName;
private
double price;
//Constructors,
getters & setters
}
public class
upcomingMovies extends movieTickets {
private
String upcomingMovieName;
//constructor,
getters + setters
}
3) Liskov Substitution Principle (LSP)
The
Liskov Substitution Principle (LSP) applies to inheritance hierarchies,
specifying that you should design your classes so that subclasses can be
substituted for client dependencies without the client being aware of the
change.
Therefore
all subclasses have to operate in the same way as their base classes. The
subclass's particular features may be different but must adhere to the base
class's expected behaviour. The subclass must not only follow the methods and
properties of the base class but also adhere to its implied behaviour, in order
to be a true behavioral subtype.
Example:
public class Rectangle{
private double height;
private double width;
public double area();
public void setHeight(double height);
public void setWidth(double width);
}
public class
Square extends Rectangle{
public void setHeight(double height){
super.setHeight(height);
super.setWidth(width);
}
public void setWidth(double width){
setHeight(width);
}
}
4) Interface Segregation Principle (ISP)
The Interface
Segregation Principle (ISP) states that Consumers should not be forced to rely
on interfaces they do not use. When we have non-cohesive interfaces, we are
guided by the ISP to create multiple cohesive, smaller interfaces.
Using
tightly focused interfaces, classes and their dependencies communicate when you
apply ISP, minimizing dependencies on unused members and decreasing coupling
accordingly. Smaller interfaces are easier to implement, enhancing the
flexibility and reuse options. As fewer classes share these interfaces,
the number of changes required in response to a modification of the interface
is reduced, which increases robustness.
Example:
public interface Messenger{
askForCard();
tellInvalidCard();
askForPin();
tellInvalidPin();
tellCardWasSiezed();
askForAccount();
tellNotEnoughInAccount();
tellAmountDeposited();
tellBalance();
}
Instead, split the
Messenger interface up so that different ATM functionality depend on separate
Messengers.
public interface LoginMessenger{
askForCard();
tellInvalidCard();
askForPin();
tellInvalidPin();
}
public interface WithdrawMessenger{
tellNotEnoughInAccount();
askForPaymentConfirmation();
}
public class EnglishMessenger implements LoginMessenger,WithdrawMessenger{...}
}
5) Dependency Inversion Principle (DIP)
The
Dependency Inversion Principle (DIP) states that high-level modules should not
depend on low-level modules; abstractions should rely upon them. Furthermore,
abstraction should not depend on details; abstraction should focus on facts.
The
aim is to isolate our class behind a boundary formed by the abstraction on
which it depends. If all the details behind those abstractions change, our
class will remain safe. This helps to keep the coupling down and makes it
easier to change our design. In addition, DIP helps us to test things in
isolation, information such as database are plugins for our program.
Example:
public interface Reader {char getchar();}
public interface Writer { void putchar(char c)}
class CharCopier{
void copy(Reader
reader,Writer writer){
int c;
while ((c=reader.getchar())!=EOF){
wrter.putchar();
}
}
}
public Keyboard implements Reader
{...}
public Printer implements Writer
{...}
public Keyboard implements Reader {...}
public Printer implements Writer {...}
Comments
Post a Comment