599 lines
9.2 KiB
Plaintext
599 lines
9.2 KiB
Plaintext
|
Application
|
|||
|
Programming
|
|||
|
Hend Alkittawi
|
|||
|
|
|||
|
SOLID Principles
|
|||
|
Applying SOLID Principles In Object
|
|||
|
Oriented Design
|
|||
|
|
|||
|
WHY SOLID PRINCIPLES?
|
|||
|
-
|
|||
|
|
|||
|
Watch this video from the Coursera course Software Development
|
|||
|
Processes and Methodologies
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The content for this lecture is based on a series of
|
|||
|
papers/book chapters by Robert Martin
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
You might find these references useful
|
|||
|
-
|
|||
|
|
|||
|
Uncle Bob SOLID principles
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
SOLID Design Principles with Java Examples | Clean Code and
|
|||
|
Best Practices | Geekific
|
|||
|
|
|||
|
SYMPTOMS OF ROTTING DESIGNS
|
|||
|
-
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Rigidity
|
|||
|
-
|
|||
|
|
|||
|
Difficult to change in even simple ways
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Changes cause a cascade of issues in dependent modules
|
|||
|
|
|||
|
Immobility
|
|||
|
-
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Inability to reuse modules
|
|||
|
|
|||
|
Fragility
|
|||
|
-
|
|||
|
|
|||
|
Tendency for changes to cause problems in many areas
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
New problems often in areas with no conceptual relationship to the original
|
|||
|
change
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Viscosity
|
|||
|
-
|
|||
|
|
|||
|
Viscosity of design → It is harder to make changes that preserve the original
|
|||
|
design
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Viscosity of environment → Development environment is slow and inefficient
|
|||
|
|
|||
|
CHANGING REQUIREMENTS
|
|||
|
-
|
|||
|
|
|||
|
Requirements change in a way that was not anticipated by the
|
|||
|
original design
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The requirements document is the most volatile document in a
|
|||
|
project
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
We need to make our designs resilient to changes and protect
|
|||
|
them from rotting
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Tree Comic
|
|||
|
|
|||
|
DEPENDENCY MANAGEMENT
|
|||
|
-
|
|||
|
|
|||
|
Rot is caused by changes that introduce new and unplanned for
|
|||
|
dependencies
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Rigidity, Fragility, Immobility, and Viscosity are directly or
|
|||
|
indirectly caused by improper dependencies between software
|
|||
|
modules
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
To avoid rot, dependencies between modules in an application
|
|||
|
must be managed
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
This management consists of dependency firewalls across which
|
|||
|
dependencies do not propagate
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
SOLID is a way to manage dependencies
|
|||
|
-
|
|||
|
|
|||
|
The Single Responsibility Principle (SRP)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The Open Closed Principle (OCP)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The Liskov Substitution Principle (LSP)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The Interface Segregation Principle (ISP)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
The Dependency Inversion Principle (DIP)
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Single Responsibility Principle (SRP)
|
|||
|
-
|
|||
|
|
|||
|
There should never be more than one reason for a class to
|
|||
|
change
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
If a class has more than one responsibility, then there will
|
|||
|
be more than one reason for it to change
|
|||
|
-
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
A class should have one responsibility
|
|||
|
|
|||
|
Multiple responsibilities can become coupled
|
|||
|
-
|
|||
|
|
|||
|
Changes to one responsibility can impair the class' ability to
|
|||
|
meet its other responsibilities
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Single Responsibility Principle (SRP)
|
|||
|
-
|
|||
|
|
|||
|
An example of how a class that has too many responsibilities
|
|||
|
can be split into multiple classes
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
If needed, a class can have objects from the other classes
|
|||
|
|
|||
|
Employee
|
|||
|
|
|||
|
Employee
|
|||
|
|
|||
|
EmployeeDB
|
|||
|
|
|||
|
EmployeeReport
|
|||
|
|
|||
|
name
|
|||
|
salary
|
|||
|
|
|||
|
name
|
|||
|
salary
|
|||
|
|
|||
|
name
|
|||
|
salary
|
|||
|
|
|||
|
name
|
|||
|
salary
|
|||
|
|
|||
|
// payroll methods
|
|||
|
// hr methods
|
|||
|
// DB methods
|
|||
|
|
|||
|
// pay methods
|
|||
|
|
|||
|
// DB methods
|
|||
|
|
|||
|
// hr methods
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Open Closed Principle (OCP)
|
|||
|
-
|
|||
|
|
|||
|
Software entities should be open for extension but closed for
|
|||
|
modification
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Design modules that never change
|
|||
|
-
|
|||
|
|
|||
|
When requirements change, extend the behavior of a module by
|
|||
|
adding new code not by changing code that already works
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Modules that conform to OCP
|
|||
|
-
|
|||
|
|
|||
|
Have behaviors that can be extended
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Have source code that does not change
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Open Closed Principle (OCP)
|
|||
|
-
|
|||
|
|
|||
|
Example for a design that is not closed for modification
|
|||
|
|
|||
|
public abstract class Shape {
|
|||
|
public abstract String getShapeType();
|
|||
|
}
|
|||
|
public class Circle extends Shape {
|
|||
|
private String shapeType = “Circle”;
|
|||
|
private double radius;
|
|||
|
private Point center;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public String getShapeType(){
|
|||
|
return shapeType;
|
|||
|
}
|
|||
|
|
|||
|
public class Square extends Shape {
|
|||
|
private String shapeType = “Square”;
|
|||
|
private double side;
|
|||
|
private Point topLeftCorner;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public String getShapeType(){
|
|||
|
return shapeType;
|
|||
|
}
|
|||
|
|
|||
|
public void drawAllShapes(Shape[] shapes){
|
|||
|
for(int i = 0; i < shapes.length; i++){
|
|||
|
Shape shape = shape[i];
|
|||
|
switch (shape.getShapeType()){
|
|||
|
case "Circle":
|
|||
|
drawCircle(shape);
|
|||
|
break;
|
|||
|
case "Square":
|
|||
|
drawSquare(shape);
|
|||
|
break;
|
|||
|
default:
|
|||
|
System.out.println("Unknown shape: "
|
|||
|
+ shape.getShapeType());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
What if we
|
|||
|
add more
|
|||
|
shapes?
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Open Closed Principle (OCP)
|
|||
|
-
|
|||
|
|
|||
|
Example how the previous design can be modified to be closed
|
|||
|
for modifications
|
|||
|
public abstract class Shape {
|
|||
|
public abstract void draw();
|
|||
|
}
|
|||
|
public class Circle extends Shape {
|
|||
|
private double radius;
|
|||
|
private Point center;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void draw(){
|
|||
|
// code to draw here;
|
|||
|
}
|
|||
|
|
|||
|
public class Square extends Shape {
|
|||
|
private double side;
|
|||
|
private Point topLeftCorner;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void drawAllShapes(Shape[] shapes){
|
|||
|
for(int i = 0; i < shapes.length; i++){
|
|||
|
Shape shape = shape[i];
|
|||
|
shape.draw();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void draw(){
|
|||
|
// code to draw here;
|
|||
|
}
|
|||
|
|
|||
|
What if we
|
|||
|
add more
|
|||
|
shapes?
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Liskov Substitution Principle (LSP)
|
|||
|
-
|
|||
|
|
|||
|
Subclasses should be substitutable for their base classes
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Example for a subclass that is not substitutable for its base
|
|||
|
class
|
|||
|
|
|||
|
public class Rectangle {
|
|||
|
private double width;
|
|||
|
private double height;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
public void setWidth(double width) { this.width = width; }
|
|||
|
public void setHeight(double height) { this.height = height; }
|
|||
|
public double getWidth() { return width; }
|
|||
|
public double getHeight() { return height; }
|
|||
|
|
|||
|
public class Square extends Rectangle {
|
|||
|
public void setWidth(double width){
|
|||
|
super.setWidth(width);
|
|||
|
super.setHeight(width);
|
|||
|
}
|
|||
|
public void setHeight(double height){
|
|||
|
super.setWidth(height);
|
|||
|
super.setHeight(height);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void calArea(Rectangle r) {
|
|||
|
r.setWidth(5);
|
|||
|
r.setHeight(4);
|
|||
|
double area = r.getWidth() * r.getHeight();
|
|||
|
if (area != 20)
|
|||
|
System.out.println("Unexpected area: " + area);
|
|||
|
}
|
|||
|
|
|||
|
What if
|
|||
|
calArea()
|
|||
|
is passed a
|
|||
|
square?
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Liskov Substitution Principle (LSP)
|
|||
|
-
|
|||
|
|
|||
|
LSP is an important feature of programs that conform to the Open Closed
|
|||
|
Principle
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
When subclasses are completely substitutable for their base class, then
|
|||
|
methods that use the base class can be substituted with impunity and the
|
|||
|
subclasses can be changed with impunity
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
In our example
|
|||
|
-
|
|||
|
|
|||
|
Geometrically, a square is a rectangle, but a square object is not a rectangle
|
|||
|
object.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Behaviorally, a square is not a rectangle. The behavior of a square object is
|
|||
|
not consistent with the behavior of a rectangle object.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
For LSP to hold, subclasses must conform to the behavior that clients expect of
|
|||
|
the base classes that they use.
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Interface Segregation Principle (ISP)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Using many client specific interfaces is better than using one general
|
|||
|
purpose interface.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
If you have a class that has several clients, rather than loading the
|
|||
|
class with all the methods that the clients need, create specific
|
|||
|
interfaces for each client.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
It is okay for a method to appear in more than one interface so that
|
|||
|
separate clients can use the same method.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
When interfaces between classes and existing clients change, consider
|
|||
|
adding new interfaces for existing objects which can reduce
|
|||
|
recompilation and redeployment.
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Interface Segregation Principle
|
|||
|
(ISP)
|
|||
|
-
|
|||
|
|
|||
|
Example for a design that does not
|
|||
|
follow ISP
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Bloated interfaces can lead to
|
|||
|
inadvertent couplings between clients
|
|||
|
|
|||
|
Client A
|
|||
|
|
|||
|
Service
|
|||
|
Client A methods
|
|||
|
|
|||
|
Client B
|
|||
|
…
|
|||
|
|
|||
|
Client B methods
|
|||
|
…
|
|||
|
|
|||
|
Client N
|
|||
|
|
|||
|
Client N methods
|
|||
|
|
|||
|
Video
|
|||
|
|
|||
|
<<interface>>
|
|||
|
VideoActions
|
|||
|
|
|||
|
that ought to be isolated otherwise
|
|||
|
-
|
|||
|
|
|||
|
Bloated interfaces can be segregated
|
|||
|
to prevent this coupling
|
|||
|
|
|||
|
Prime Video
|
|||
|
|
|||
|
playVideo()
|
|||
|
getVideoPlayTime()
|
|||
|
playRandomAd()
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Dependency Inversion Principle (DIP)
|
|||
|
-
|
|||
|
|
|||
|
Depend on abstractions. Do not depend on concretions
|
|||
|
(implementations)
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
High level modules should not depend on low level modules.
|
|||
|
Both should depend upon abstractions.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Abstractions should not depend upon details. Details should
|
|||
|
depend upon abstractions
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
Every dependency in a design should target an interface or an
|
|||
|
abstract class. No dependency should target a concrete class
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Dependency Inversion Principle (DIP)
|
|||
|
-
|
|||
|
|
|||
|
the idea …
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Dependency Inversion Principle (DIP)
|
|||
|
-
|
|||
|
|
|||
|
Example for how a copy program can work with any reader and
|
|||
|
writer that implement the Reader and Writer interfaces. It is
|
|||
|
no longer dependent on particular lower level modules!
|
|||
|
|
|||
|
public void copy() throws Exception {
|
|||
|
Scanner scnr = new Scanner(System.in);
|
|||
|
PrintStream ps = new PrintStream(new File("myFile"));
|
|||
|
while (scnr.hasNext()) {
|
|||
|
String line = scnr.nextLine();
|
|||
|
ps.println(line);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public interface Reader {
|
|||
|
public boolean hasLine();
|
|||
|
public String getLine() throws Exception;
|
|||
|
}
|
|||
|
public interface Writer {
|
|||
|
public void putLine (String s) throws Exception;
|
|||
|
}
|
|||
|
public void copy( Reader input, Writer output) throws Exception{
|
|||
|
while (input.hasLine()) {
|
|||
|
output.putLine(input.getLine()));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
SOLID PRINCIPLES
|
|||
|
-
|
|||
|
|
|||
|
The Dependency Inversion Principle (DIP)
|
|||
|
-
|
|||
|
|
|||
|
Proper application of the Dependency Inversion Principle is
|
|||
|
necessary for the creation of reusable frameworks.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
It is important for the construction of code that is resilient
|
|||
|
to change.
|
|||
|
|
|||
|
-
|
|||
|
|
|||
|
When abstraction is isolated from details, code is easier to
|
|||
|
maintain.
|
|||
|
|
|||
|
IMPORTANT
|
|||
|
|
|||
|
In the industry, problem
|
|||
|
solving often requires
|
|||
|
interaction among many
|
|||
|
colleagues. Rarely will you
|
|||
|
be able to get everyone on a
|
|||
|
project to agree on the
|
|||
|
right approach to a
|
|||
|
solution. Also, rarely will
|
|||
|
any particular approach be
|
|||
|
perfect. You'll often
|
|||
|
compare the relative merits
|
|||
|
of different approaches.
|
|||
|
|
|||
|
DO YOU HAVE ANY
|
|||
|
QUESTIONS?
|
|||
|
|
|||
|
THANK
|
|||
|
YOU!
|
|||
|
|
|||
|
@
|
|||
|
|
|||
|
hend.alkittawi@utsa.edu
|
|||
|
|
|||
|
By Appointment
|
|||
|
Online
|
|||
|
|
|||
|
|