Software Craftsmanship
SOLID Design Principles
These are agile, class level design principles. They allow you to minimize coupling and maximize cohesion. The result is code that is easy to read and maintain.
Single Responsibility Principle (SRP)
Open/Closed Principle (OCP)
Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
Dependency Inversion Principle (DIP)
guidelines
less prescriptive than design patterns
not a replacement for thinking critically about code
Coupling is a measure of interdependence between two routines or modules.
Cohesion is a measure of how strongly related each piece of functionality is.
With tightly coupled systems:
a change in one module forces changes in the others.
modules are harder to reuse.
modules are harder to test.
With low cohesion systems:
modules are complex with more operations.
modules are less maintainable and harder to work with.
Module A
Module B
High Coupling, Low Cohesion
Module A
Module B
Low Coupling, Low Cohesion
Module A
Module B
High Coupling, High Cohesion
Module A
Module B
Low Coupling, High Cohesion
Two or more modules are orthogonal if
changes in one do not affect the other.
When components are highly interdependent, there is no such thing as a quick, local fix.
Orthogonal software provides increased productivity and decreased risk because developers never have to worry about side-effects of making changes.
Orthogonal components are easier to swap meaning less dependence on a specific library or vendor.
Since objects are less dependent on the internal structure of other objects, classes can be changed without reworking their callers.
Each class or module should have only one responsibility, and thus, one reason to change.
Why this principle? It provides guidance around how large to make classes/modules.
Large classes tend to be incohesive.
The single responsibility should be entirely encapsulated by its class.
Bottom line: make classes small enough to maximize cohesion, but large enough to minimize coupling.
Single Responsibility Principle (SRP)
public class BlenderToaster
{
String toasterModel, blenderModel;
int toastFactor, blendFactor;
public void blend()
{
System.out.println(blenderModel +
" blending with a factor of " + blendFactor);
}
public void toast()
{
System.out.println(toasterModel +
" toasting with a factor of " + toastFactor);
}
}This class is trying to do too much.
A Violation of the SRP
Open/Closed Principle (OCP)
Software entities should be open for extension,
but closed for modification.
UI components in iOS are an excellent implementation of this principle.
Routines that use objects of parent classes must be able to use objects of subclasses without altering the correctness of the program.
Liskov Substitution Principle (LSP)
public class Circle extends Rectangle
{
double radius;
public Square(double r, Point c)
{
radius = r;
super(r * [Math.pi/2], r * [Math.pi/2], p);
}
}The Circle class seems to be cleverly reusing some of Rectangle's functionality, but the calculateCorners method will return results that don't make sense.
public class Rectangle
{
double width, length;
Point center;
public Rectangle(double w, double l, Point c)
{
width = w;
length = l;
center = c;
}
public int calculateArea()
{
return width * length;
}
public Point[] calculateCorners()
{
Point[] edges;
// do some calculations here
return edges;
}
}A Violation of the LSP
No client should be forced to depend on methods it does not use.
Interface Segregation Principle (ISP)
Interface-based design:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
Dependency Inversion Principle (DIP)
Why this principle? It provides guidance around how to correctly use interfaces.
Bottom line: implementations should depend on interfaces, not the other way around.