The document discusses the SOLID principles of object-oriented design:
- Single Responsibility Principle (SRP): A class should have one, and only one, responsibility.
- Open/Closed Principle (OCP): Software entities should be open for extension but closed for modification.
- Liskov Substitution Principle (LSP): Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
- Interface Segregation Principle (ISP): No client should be forced to depend on methods it does not use - have many client-specific interfaces rather than one general purpose interface.
- Dependency Inversion Principle (DIP): High
4. SOLID
• SRP The Single Responsibility
• OCP Principle
• LSP The Open Closed Principle
• ISP The Liskov Substitution Principle
• DIP The Interface Segregation
Principle
Robert Martin
The Dependency Inversion
Principle
6. Definition
Class should have only one
responsibility and only one reason to
change
Robert Martin
http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfO
od
7. Rationale
Just because you can,
does not mean you
should.
We want systems of many small, simple,
cohesive classes with clear purpose.
9. In quest of responsibility
Ask yourself:
• What concept does the class represent?
• What does the class do?
• What causes the class to change?
• For existing classes: is class X responsible for doing
Z?
Answer must be one-liner.
10. Violation symptoms
• Managers, Services, Facades…
• Long lists of variables, dependencies, methods,
arguments
• Problematic unit-testing (e.g. when you need Spies)
• Different levels of abstractions inside a class
• A lot of details to tackle, implicitness
11. Overloaded responsibilities
interface JsonService { public class TaxCalculator {
Json marshall(Object object); @Autowired
Object unmarshall(Json json); private EmployeeRepository employeeRepository;
void prettyPrint();
void validate(Json json, Wadl wadl); Tax calculate(Id employeeId) {
} Employee employee =
employeeRepository.findBy(employeeId);
return calculate(employee);
}
}
class FooValidator { class User {
void validate(Foo foo) { public User(Registration registration) {
if (foo.isSuitableForValidation()) { this.email = registration.getEmail();
// actual validation this.firstName = registration.getFirstName();
} this.lastName = registration.getLastName();
} }
} public User(Invitation invitation) {
…
}
}
13. Definition
Software entities (classes, modules,
functions etc.) should be open for
extension, but closed for modification.
Robert Martin
http://www.objectmentor.com/resources/articles/ocp.pdf
22. Definitions
The LSP specifies that functions that use
pointers of references to base classes
must be able to use objects of derived
classes without knowing it.
Robert Martin
http://www.objectmentor.com/resources/articles/lsp.pdf
23. Syntactical conformance is not enough! It’s all
about semantics:
• Pre-conditions (can’t strengthen, can weaken)
• Post-conditions (can’t weaken, can strengthen)
• Preserved supertype’s invariants
30. Violation indicators
• Modification of existing code on new
subtype
• Unnatural class hierarchies
• Funny restrictions (do not use this class
if…)
• instanceof, if, switch …
• InvalidOperationException() or similar
32. Definitions
No client should be forced to depend
on methods it does not use.
Many client specific interfaces are
better than one general purpose
interface.
Robert Martin
Agile Software Development, 2002
33. Less FAT
FAT
interfaces
• Are not cohesive
• Violate SRP
• Force implementations to violate SRP
• Suffer from high afferent/efferent coupling
34. Flexible layering
interface RegistrationSettings {
com.4finance.cola.client int getMaxAttempts();
}
interface BonusSettings {
com.4finance.cola.product com.4finance.cola.client.bonus BonusAmount getBonusAmount();
}
interface SolvencySettings {
interface ProductSettings { com.4finance.cola.client.solvency Income getMinIncomeForSubmission();
int getMaxRegistrationAttempts();
}
BonusAmount getBonusAmount();
Income getMinIncomeForSubmission();
}
Consider
• Impact of change
• Dependency management
36. Lower coupling
interface JsonService { class JsonServiceImpl implements JsonService {
Json marshall(Object json); …
Object unmarshall(Json json); }
ValidationResult validate(Json json, Wadl wadl);
void prettyPrint(Json json);
}
Pretty printing Validation (Un)marshalling
Consider
• High afferent coupling: all clients relying on
particular Json functionality are coupled with
Wadl.
• High efferent coupling: JsonService
implementation will violate SRP as it relies on all
dependencies required for fulfilling the contract.
37. Understandability
interface EventPublisher { interface EventPublisher {
void publish(); void publish();
void setJsonService(JsonService void setJsonMarshaller(JsonMarshaller
jsonService); jsonMarshaller);
} }
Consider
• When JsonService is set, you need to dive into
EventPublisher implementor’s details in order to
understand what parts of it are in use.
38. Example – PageRouter
interface PageRouter { interface PageRouter{
Route getRouteToProfile(); Route getRoute();
Route getRouteToRegistration(); }
}
interface ProfilePageRouter extends PageRouter {
class DefaultPageRouter implements PageRouter { }
…
} interface RegistrationPageRouter extends PageRounter {
}
class RuPageRouter {
// RU overrides both routes class DefaultProfilePageRouter implements ProfilePageRounter
} {
}
…
Consider
• What if alternative implementation appeared that
overrides only one route?
40. Definition
High-level modules should not depend
on low-level modules. Both should
depend on abstractions.
Robert Martin
http://www.objectmentor.com/resources/articles/dip.pdf
41. << interface >>
AS ervice
Component A Component A
<< interface >>
BS vice
er
Component B Component B
Component C Component C
A directly depends on B in order to realize its
potential.
A defines external needs with interfaces it owns.
B indirectly satisfies needs of A by implementing
them.
42. Increasing reuse
1 2 3
Widgets Widgets Widgets
DatePicker DatePicker DatePicker
DateFormatter
Loans System Loans System
DateTimeUtils DateFormatter
Loans System
DateTimeUtils DateTimeUtils
43. DwA != DI
In true Dependency Inversion, high-level
module owns the abstraction.
48. But… Why care?
SOLID contribute to clean code. Writing clean
code is what you must do in order to call
yourself a professional. There is no
reasonable excuse for doing anything less
than your best.
It’s not only about classes. Size matters, but it has supporting functions – signals of bloated responsibility.
Better insights of the system – many clear concepts. Divide & conqueror – mini problems.No one likes systems of GOD objects, GOD methodsLess things in mindTestabilityComposability
Spend time. Every line – ask yourself about responsibilityConscious design & better micro-architectures
Side-effects require JavaDocs! JavaDocs are evil.
- How important it is to define contract at the early stages
EmptyAbstractClass – pig with a lipstick
Goal: increasing reuse & decoupling.Indirection can be solved by DI or Service Locator (Service Registry a-la OSGi)
High-level module: “reusable” or “decoupled from details”!= Separate interface
You have to be careful about dependency direction as it is part of your design.