SOLID Priniciples - for code refactor and smells code means first identify wrost code, then apply SOLID principles to refactor.
S - Single Responsibility
O - Open for extension / Closed for Modification (Strategy Pattern )
L - Liskov Substutions
I - Interface Segression
D - Dependency Inversion ( Template Method Pattern)
Here S-0 principle based on responsibilities and L - I based on split responsiblities.
S - Single Responsibility
"A class should have only one reason to change"
Usecase: we need an object to keep an email message.
interface IEmail {
public void setSender(String sender);
public void setReceiver(String receiver);
public void setContent(String content);
}
Email Responsibilites: setSender, setReceiver
Content Responsibilites: setContent (Currently supporting String content only)
Now we got new feature: we need to support html, pdf feature content. But our current Email supports String content type
Solution: Then we need to take more care String content convert it into interface like Below.
interface IContent {
public String getAsString(); //used for serialize
}
Now our modified IEmail Interface:
interface IEmail {
public void setSender(String sender);
public void setReceiver(String receiver);
public void setContent(IContent content);
}
class Email implments IEmail {
public void setSender(String sender) { //implment }
public void setReceiver(String receiver) { //implment }
public void setContent(IContent content) { //implment }
}
Open Close Principle : Open for extension but closed for modifications
Through bad coding to good coding we will demonstrate this example.
abstract class Shape { class Rectangle extends Shape { class Circle extends Shape {
int m_type; Rectangle() { Circle() {
} super.m_type = 1; super.m_type = 2;
} } }}
//Open-close priniciple bad example -
class GraphicEditor {
public void drawShape(Shape s) {
if(s.m_type==1) { drawRectangle() ; }
else if(s.m_type==2) { drawCircle(); }
}
//circle responsible behaviour
public void drawCircle(Circle c) { }
//rectangle responsible behaviour
public void drawRectangle(Rectangle r) { }
}
In the bad code having rectangle and circle behaviours.
circle - behaviour - drawCircle();
rectangle - behaviour - drawRectangle();
Issue: if we add new shape then we need to change drawShape() and also add the new drawMehtod() into GraphicEditor.
Solution: In the new design we use abstract draw() method in GraphicEditor for drawing objects. draw() implmentation into the concrete shape objects.
abstract class Shape { class Rectangle extends Shape { class Circle extends Shape {
abstract void draw(); draw() { draw() {
} //implment //implment
} } }}
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
Now no changes required when a new shape is added (Good !!)
Please refer Strategy pattern which will suitable for this principle.
D - Dependency Inversion principle "Depend on Abstract/Interface not concrete classes"
I will explain 2 ways this principle
Class Manager { Class Worker {
Worker worker; public void work() {
public void setWorker(Worker worker) { System.out.println("working");
this.worker = worker; }
} }
public void manager() {
worker.work();
}
}
Now company introduced new specialized worker then we need to change Manager class also
Class SuperWorker {
public void work() {
System.out.println("super working");
}
}
Solution: Now Design a new Abstraction Layer is added through the IWorker interface.
interface IWorker { public void work() ; }
class Worker implements IWorker { public void work() { System.out.println("working"); } }
class SuperWorker implements IWorker { public void work() { System.out.println("super working"); } }
Class Manager {
IWorker worker;
public void setWorker(IWorker worker) {
this.worker = worker;
}
public void manager() {
worker.work();
}
}
S - Single Responsibility
O - Open for extension / Closed for Modification (Strategy Pattern )
L - Liskov Substutions
I - Interface Segression
D - Dependency Inversion ( Template Method Pattern)
Here S-0 principle based on responsibilities and L - I based on split responsiblities.
S - Single Responsibility
"A class should have only one reason to change"
Usecase: we need an object to keep an email message.
interface IEmail {
public void setSender(String sender);
public void setReceiver(String receiver);
public void setContent(String content);
}
Email Responsibilites: setSender, setReceiver
Content Responsibilites: setContent (Currently supporting String content only)
Now we got new feature: we need to support html, pdf feature content. But our current Email supports String content type
Solution: Then we need to take more care String content convert it into interface like Below.
interface IContent {
public String getAsString(); //used for serialize
}
Now our modified IEmail Interface:
interface IEmail {
public void setSender(String sender);
public void setReceiver(String receiver);
public void setContent(IContent content);
}
class Email implments IEmail {
public void setSender(String sender) { //implment }
public void setReceiver(String receiver) { //implment }
public void setContent(IContent content) { //implment }
}
Open Close Principle : Open for extension but closed for modifications
Through bad coding to good coding we will demonstrate this example.
abstract class Shape { class Rectangle extends Shape { class Circle extends Shape {
int m_type; Rectangle() { Circle() {
} super.m_type = 1; super.m_type = 2;
} } }}
//Open-close priniciple bad example -
class GraphicEditor {
public void drawShape(Shape s) {
if(s.m_type==1) { drawRectangle() ; }
else if(s.m_type==2) { drawCircle(); }
}
//circle responsible behaviour
public void drawCircle(Circle c) { }
//rectangle responsible behaviour
public void drawRectangle(Rectangle r) { }
}
In the bad code having rectangle and circle behaviours.
circle - behaviour - drawCircle();
rectangle - behaviour - drawRectangle();
Issue: if we add new shape then we need to change drawShape() and also add the new drawMehtod() into GraphicEditor.
Solution: In the new design we use abstract draw() method in GraphicEditor for drawing objects. draw() implmentation into the concrete shape objects.
abstract class Shape { class Rectangle extends Shape { class Circle extends Shape {
abstract void draw(); draw() { draw() {
} //implment //implment
} } }}
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
Now no changes required when a new shape is added (Good !!)
Please refer Strategy pattern which will suitable for this principle.
D - Dependency Inversion principle "Depend on Abstract/Interface not concrete classes"
I will explain 2 ways this principle
- "High-level modules should not depend on low-level modules, both should depend on abstractions" style.
Class Manager { Class Worker {
Worker worker; public void work() {
public void setWorker(Worker worker) { System.out.println("working");
this.worker = worker; }
} }
public void manager() {
worker.work();
}
}
Now company introduced new specialized worker then we need to change Manager class also
Class SuperWorker {
public void work() {
System.out.println("super working");
}
}
Solution: Now Design a new Abstraction Layer is added through the IWorker interface.
interface IWorker { public void work() ; }
class Worker implements IWorker { public void work() { System.out.println("working"); } }
class SuperWorker implements IWorker { public void work() { System.out.println("super working"); } }
Class Manager {
IWorker worker;
public void setWorker(IWorker worker) {
this.worker = worker;
}
public void manager() {
worker.work();
}
}
- "Concrete classes should depend on interfaces and abstract classes because they change less often and more higher level modules" style.
please refer my blog: https://rameshvanka.blogspot.com/2020/02/templatemethod-design.html
In this template method design pattern concrete classes depend on parent class House method buildHouse(), this parent method changes very less often.