Dmitry Melnikov
Single Responsibility Principle
Open Closed Principle
Liskov Substitution Principle
Interface Sergregation Principle
Dependency Inversion Principle
First mentioned in Robert "Uncle Bob" Martin's book.

Wrote several popular books
Popularized the adjective "clean"
One of the authors of the Manifesto for Agile Software Development
Started Software Craftsmanship Movement
In 2004 or thereabouts, Michael Feathers sent me an email saying that if I rearranged the principles, their first words would spell the word SOLID — and thus the SOLID principles were born.
It's decades of academic research and industry experience.

Each class should have only one responsibility.
Each class should have only one responsibility.
But what is responsibility?
No precise definition
Impossible to reason about
It's not a practical definition
Code is satisfied to SRP if you are able to describe what each class does without saying "and".
public class AuthManager { public void login(String username, String password) { // … } public void logout() { // … }}
⛔ Logs users into the system and logs users out of the system.
But
✔ Handles users's authentification.
Each class should have only one reason to change.
Uncle Bob's blog post
public class Employee { public Money calculatePay(); public void save(); public String reportHours();}+-------------------------------+| class Feature1 || +------------+ +------------+ || | doA() | | doB() | || +------------+ +------------+ |+-------------------------------++-------------------------------+| class Feature1 || +------------+ +------------+ || | doA() | | doB() | || +------------+ +------------+ |+-------------------------------++-------------------------------+| class Feature2 || +------------+ +------------+ || | doA() | | doC() | || +------------+ +------------+ |+-------------------------------++-------------------------------+| class Feature3 || +------------+ +------------+ || | doB() | | doC() | || +------------+ +------------+ |+-------------------------------+Practical definition: each class should have only one reason to change.
How to follow SRP:
Pros:
High cohesion and low coupling
Abstraction is an alternative representation of a concept that hides irrelevant details and exposes only the properties you are interested in.
"If you can't explain it to a six year old, you don't understand it yourself." ― Albert Einstein
Coffee machine hides all the details how to boil cappucino.

CPU -> Firmware -> OS -> ApplicationApplication abstraction example
Statement -> Method (expose operations) -> Object (expose functional behavior) -> Module (expose domain concept) -> ApplicationInterface and Implementation
Essential abstractions:
Software entities (classes, modules, functions) should be open for extension, but closed for modification.
Open Closed Principl: Modules should be open and closed — Bertrand Meyer, 1988

A module is said to be open if it is still available for extention. For example, it should be possible to expand its set of operations or add fields to its data structures
A module is said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (its interface in the sense of information hiding). At the implementation level, closure for a module also implies that you may compile it, perhaps store it in a library, and make it available for others (its clients) to use.
No changes after modules released to clients
It's possible to extend all modules
Design and document for inheritance or else prohibit it. — Joshua Bloch, Effective Java
Robert "Uncle Bob" Martin, Agile Software Developments: Principles, Patterns, and Practices.
How is it possible that the behaviors of a module can be modified without changing its source code? Without changing the module, how can we change what a module does?
The answer is abstraction. In C# or any other object-oriented programming language (OOPL), it is possible to create abstractions that are fixed and yet represent an unbounded group of possible behaviors. The abstractions are abstract base classes, and the unbounded group of possible behaviors are represented by all the possible derivative classes.
It is possible for a module to manipulate an abstraction. Such a module can be closed for modification, since it depends on an abstraction that is fixed. Yet the behavior of that module can be extended by creating new derivatives of the abstraction.
Software entities (classes, modules, functions) should be open for extension, but closed for modification.
≈
Depend on stable abstractions and change system's behaviour by providing different implementations.
OCP is polymorphism principle.
Further reading: Protected Variation: The importance of being Closed
public class CoffeeMachine { public void brew(Order order) { if (order.getType() == Type.ESPRESSO) { // brew espresso } else if (order.getType() == Type.CAPPUCCINO) { // brew cappuccino } else { // brew other } // … }}public class CoffeeMachineOCP { private final BrewerFactory brewerFactory; public CoffeeMachineOCP(BrewerFactory brewerFactory) { this.brewerFactory = brewerFactory; } public void brew(Order order) { Brewer brewer = brewerFactory.getBrewer(order.getType()); brewer.brew(); }}OCP minifies challenges in predictable changes
Limitations
You need some experience to extract right abstractions
If done right it's easy to provide additional business value
If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.)
Simple example:
class Rectnagle and class Square
If square is a subtype of Rectangle, everywhere I use Reactangle I can substitute it with Square without any failures.
What is subtype? How can I determine LSP violations ahead of time?
Barbara Liskov, Turing Award laureate 2008.
For contributions to practical and theoretical foundations of programming language and system design, especially related to data abstraction, fault tolerance, and distributed computing.
Data Abstraction and Hierarchy, 1987.
Abstractions can be used to encapsulate potential modifications. For example, suppose we want a program to run on different machines. We can accomplish this by inventing abstractions that hide the differences between machines so that to move the program to a different machine only those abstractions need be reimplemented. A good design principle is to think about expected modifications and organize the design by using abstractions that encapsulate the changes.
≈ OCP
Behavioral Subtyping Using Invariants and Constraints, 1999.
class ServiceSubclass extends Service {}
ServiceSubclass could be not a subtype of Service

If subclass implements a method from its superclass, then the number of arguments should be the same.
The type of each argument in subclass method should be the supertype of the type of the respective arguments in superclass method.
class Service { doSmth(Argument arg) { /*…*/ }}class ServiceSubclass extends Service { doSmth(SubclassArgument arg) { /*…*/ }}
Don't make subclass method arguments more specific.
Either both superclass and sublass methods return result, or neigher does.
If there is a result, then the type of the result in the subclass is a subtype of the type of the result in the superclass.
class Service { Result doSmth() { /*…*/ }}class ServiceSublass extends Service { ResultSubclass doSmth() { /*…*/ }}
Overridden method returns a result if and only if the parent method does too.
Don't make subclass methods return more general types.
Exceptions thrown by a method in the subclass should be contained in the set of exceptions thrown by the respective method in the superclass.
Checked exceptions checked by compiler, but unchecked not.
class HttpClientException extends IOException
class HttpsClientException extends IOException
Don't throw new types of exceptions from subclasses.
Pre-condition is an assertion about the state of the system before the method is called.
Pre-conditions required by methods of a subclass mustn't be stronger than pre-conditions required by methods of a superclass.
class Service { doSmth(Argument arg) { /*…*/ } // non null or null}class ServiceSubclass extends Service { doSmth(SubclassArgument arg) { /*…*/ } // non null}
ServiceSubclass isn't a proper subtype because of stronger pre-conditions.
A subclass should be able to operate with all states that a superclass can operate in.
Post-condition is an assertion about the state of the system after method execution completes.
Post-conditions guaranteed by methods of a subclass mustn't be weaker than post-conditions guaranteed by methods of a superclass.
class Service { Result doSmth() { /*…*/ } // non null}class ServiceSublass extends Service { ResultSubclass doSmth() { /*…*/ } // nullable}
ServiceSubclass isn't a proper subtype bacause it guarantees weaker post-conditions.
Clients shouldn't be surprised by the results of invocation of methods of a subclass.
Invariant is an assertion about a specific class property which is always true.
Invariants guaranteed by a subclass must include all invariants guaranteed by a superclass.
Ex: Number of elements in the queue <= capacity
Constraint is an assertion about how class property evolves over time.
Invariants can't express dynamic properties, so constraints were added.
class MessageErrorDetector { void processMessage(Message message) { /*…*/ } boolean isErrorDetected() { /*…*/ }}
Constraints enforced by a subclass must include all constraints enforced by a superclass.
class MessageErrorDetectorSubclass { void processMessage(Message message) { /*…*/ } boolean isErrorDetected() { /*…*/ } void resetError() { /*…*/ }}class Rectangle { void setWidth(int width) { /*…*/ } void setHeight(int height) { /*…*/ } }class Square extends Rectangle { void setWidth(int width) { /*…*/ } void setHeight(int height) { /*…*/ } }
Subtype is a subclass or an implementation which can be substituted for the type it extends or implements.
Design and document for inheritance or else prohibit it. — Joshua Bloch, Effective Java
Android example:
Application, Activity and Service all extends Context and violite LSP.
https://stackoverflow.com/questions/3572463/what-is-context-on-android
Clients shouldn't be forced to depend on methods they don't use.

RxJava: Observable, Subject are separated according to ISP.
AppVisibility / ScreenOn.
Segregated interfaces allow to segregate functionality if/when needed.
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
+-----------------------+ | TopLevelClass | +-----------------------+ | +-----------|-----------+ | MidLevelClass | +----------/-\----------+ /-- --\ /-- --\ +----------+ +----------+| F1 |------| F2 |+----------+ +----------+ +-----------------------+ | TopLevelClass | +-----------------------+ | +-----------|-----------+ | MidLevelAbstraction | +-----------|-----------+ | +-----------|-----------+ | MidLevelClass | +----------/-\----------+ /-- --\ /-- --\ +----------+ +----------+| F1 |------| F2 |+----------+ +----------+Inverted flow of control - IOC.
Service and Service Callback.
Works even for modules and different teams.
Protection from future changes in requirements
Reusability
Break dependency on external modules
Easier integration between modules
Better visibility and accountability
Cons:
More efforts to implement
More efforts to change
Increased code complexity
Follow the principles 😳
SRP: small classes with narrow functionality
OCP: make abstractions to minify impact of changes
LSP: proper subtyping
ISP: depend only on functionality classes actually use
DIP: inversion of flow control
Managing complexity is the most important technical topic in software development. In my view, it's so important that Software's Primary Technical Imperative has to be managing complexity. Complexity is not a new feature of software development. — Steve McConnel, Code Complete.
+----------------------------------------------+| +---------------------+ || | | || | | || | +--------+ 5000 LOC | || | |500 LOC | | || | +--------+ | || +---------------------+ || 50000 LOC || || || |+----------------------------------------------+Managing complexity = deal with small parts of the app while safely ignoring other parts
No difference between 40K LOC and 50K LOC
Separation of Concerns
The code is read much more often than it's written, especially interfaces
Code simplicity (container ships)


Single Responsibility Principle
Open Closed Principle
Liskov Substitution Principle
Interface Sergregation Principle
Dependency Inversion Principle
Keyboard shortcuts
| ↑, ←, Pg Up, k | Go to previous slide |
| ↓, →, Pg Dn, Space, j | Go to next slide |
| Home | Go to first slide |
| End | Go to last slide |
| Number + Return | Go to specific slide |
| b / m / f | Toggle blackout / mirrored / fullscreen mode |
| c | Clone slideshow |
| p | Toggle presenter mode |
| t | Restart the presentation timer |
| ?, h | Toggle this help |
| Esc | Back to slideshow |