TL;DR • Single Responsability Principle - SRP - is related with the semantics and API design of functional units of a software systems • The best functional units - functions, classes or even modules - are those ones that are built in order to accomplish just one well-defined responsability inside the system
Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( ) How many responsability does this class has? Answer : 3 responsabilities Salary Calculation Information Persistence Reporting
Employee - calculatePayoff( ) - saveInfo( ) - describeEmployee( ) And we if we add another method ? - findById( ) - reportWorkedHours( ) And we if we add another method ?
• FANOUT = 3 • Employee is a FAT class • Responsability co-location means actor coupling • Fragility symptom Employee calculatePayoff( ) save( ) describeEmployee( ) ORM API Payoff API Reporter API CLIENT CLASS CLIENT CLASS
TIPS TO CONFORM WITH SRP • Identity actors • More small classes and more colaborators per class • Small functions / methods per class / module • Avoid statics as hell • Design Patterns
TL;DR • Dependency Inversion Principle - DIP - is related with the impact of API design into the flow of control of program • Dependency Inversion is achieved when the API design enable decoupling of source-code dependencies from runtime dependencies
Engine.class Car.class JVM (Runtime) • A runtime dependency • In order to execute this program, the JVM must correlate instances with classes scanned into classpath, and Car instance will require a Engine instance
public interface Engine { public void start(boolean flag); } public class GasolineEngine implements Engine { @Override public void start(boolean flag) { !" TODO } } public class Car { final Engine engine; public Car(Engine e) { engine = e; } public void turnOn() { engine.start(false); } }
public class App { public static void main(String[] args) { Engine gasolinePowered = new GasolineEngine(); Car mercedes = new Car(gasolinePowered); mercedes.turnOn(); } }
public class App { public static void main(String[] args) { Engine gasolinePowered = new GasolineEngine(); Car mercedes = new Car(gasolinePowered); mercedes.turnOn(); } }
TL;DR • The Interface Segregation Principle - ISP - is a practical consideration for your interfaces design • Your interfaces must not leak abstractions : ie, clients of some behaviour must not know things they don't need
Sometimes when leveraging on interfaces, it is easy to get implementations bloated with "un-needed" methods, and suffer the same issues from FAT classes
I.S.P. Leverage class composition and/or interface inheritance + default methods in order to let the clients of some abstraction know only exactly what they need to know, nothing more
TL;DR • Liskov Substitution Principle - LSP - is a practical consideration to effective subtypes construction • A good OO software must ensure that all open types are perfectably substitutable : i.e. you may expect the exact same behaviour from a supertype or a subtype
"If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
THE REFUSED BEQUEST • Design smell related to LSP violation. • Proposed by Martin Fowler (Refactoring) • Happens when the subtype "does not care" about supertype methods and overwrite them improperly • Typical scenarios : side-effects, throwed exceptions, etc
public class Rectangle { int width, height; public int perimeter() { return 2 * (width + height); } public int area() { return width * height; } public void setHeight(int height) { this.height = height; } public void setWidth(int width) { this.width = width; } public int getWidth() { return width; } public int getHeight() { return height; } }
public class Rectangle { int width, height; public int perimeter() { return 2 * (width + height); } public int area() { return width * height; } public void setHeight(int height) { this.height = height; } public void setWidth(int width) { this.width = width; } public int getWidth() { return width; } public int getHeight() { return height; } }
Can this be solved ??? But with a terrible side-effect, because the client of Rectangle abstraction now knows about low level details (Square) … YES : instanceOf
TL;DR • The Open-closed Principle - OCP - is the moral center of a software system • Ideally, a software system should be designed in a way that it is open for extensions but closed for modifications
Open to extensions : means that we can add new features to the system easily Closed to modification : means that we can achieve this without source-code changes
D.I.P. and L.S.P. are keys to design a system in a way that high level policies (behaviours) do not depend directly on low level details (implementations)
BIG DESIGN UP-FRONT AGILE DESING • Effective to small scopes • Not scalable to great projects • Over-engineering • No scope defined • Retrospective learning • Incremental explorations over domain • Adaptation and re-working
O.C.P. Cannot be 100% achieved in practice, but must be seeked All other 4 principles violations + bad code are factors that compromise the OCP The moral center of software Reflects the entropy control