SOLID Principle
Just consequences of doing OOB “properly”.
Static: a bad idea
I have been thinking if static fields are an awful idea for a while, and it is good to get some confirmation from Ewan’s view.
Bad for alterability
Having a static field means we assume the field should be the same across all instances - it might be true for the current scenario, but the situation may change any time.
If this assumption is broken, we will have to modify the class and modify ALL of its clients.
Bad for comprehensibility
The reader has to keep track of the static field over the life cycles of all objects of the class - it may be changed any time.
- This is not efficient because it requires reading more code from external presentation.
- This is not effective because missing any changes to the static field will lead to very different understanding of the programme.
C/R Alterability heuristic revisited
“Change Ratio” C/N varies by change cases, then how is the change ratio used to assess the overall alterability of the design?
Look for the design that minimises C
“For likely and non-trivial impact change cases”
Avoid dependency for change case
Avoid designs where which design has lower CR highly depends on the change case
Single Responsibility Principle (SRP)
v.s. Responsibility-Driven Design
Responsibility = an obligation to perform a task or know information
With this principle, you’ll have classes with multiple “responsibility”, because here the definition of “responsibility” is different to Martin’s SRP.
SRP and Alterability
Consider two designs:
- Design A: A1, A2, A3, …. , AN
- Design B: B1, A3, … , AN. For Design B, B1 is the combo of A1 and A2.
Consider possible change cases:
- Change case c1 requires Ai to change (i = 1…N)
- Design A: CR = 1/N
- Design B: CR = 1/(N-1)
- For c1, A is more changeable than B
- Change case c2 requires Ai and Ak to change (i + k > 3)
- Design A: CR = 2/N
- Design B: CR = 2/(N-1)
- For c2, A is more changeable than B
- Change case c3 requires Ai and Ak to change (i + k = 3)
- Design A: CR = 2/N
- Design B: CR = 1/(N-1)
- For c3, B is more changeable than A, when N > 2.
Conclusion
- For most change cases, the C/N of Design A will be smaller than Design B.
- Therefore, Design A has a better overall alterability than Design B.
SRP and Design Patterns
Many of the elements of a design pattern are there to do just one thing.
Examples:
- Decorator: each decorator
- Composite: each leaf and composite
- Command: each concrete command
- Dependency Injection: each service, and the client
- Strategy: each concrete strategy
SRP Conclusion
- “One responsibility”: not actionable
- The “one reason to change” or “group things that change for the same reason together” definitions are perhaps easier to action, because the reasoning can be done in terms of change cases.
Open Closed Principle (OCP)
Fundamentally, we create classes and we do not intend them to change. If a new behaviour is needed, we extend instead of modifying.
The real power comes from judicious use of polymorphism, that is identifying the method to execute via dynamic dispatch.
Consequence
Typically leads to more abstract entities that are made concrete either through:
- extension, or
- supplying the concrete implementations as parameters (e.g. with dependency injection)
What is open, what is closed?
- Implementation of method is closed for modification, but open to being overriden/extended by subclasses.
- Contexts of method (where the method is being called) is closed, yet by polymophism the behaviour is open to be varied.
OCP and Alterability
Reducing C.
Actionable
- When we change something, anything that needs to change is not closed to change.
- When designing something, think about what variations are needed for the context schema.
Polymorphism!
The core of change.
Proper use of polymorphism lies at the core of OOP.
Liskov Substitution Principle (LSP)
Child classes must be substitutable for their Parent classes.
- Code reuse is not very interesting.
- Context reuse is more interesting: the context can remain unchanged, but program behaviour changes.
Any design pattern that relies on inheritance (pretty much all of them) is based on LSP.
Interface Segregation Principle
Make fine-grained interfaces that are client-specific.
Because we are limiting between the knowledge exposed to the client, we are reducing the possibility that the client needs to change according to the class.
ISP and Alterability
Reduce the impact of change -> Reduce C.
ISP and Design Patterns
e.g. Observer
Command typically only have execute()
The Dependency Inversion Principle (DIP)
Actionable
Look for opportunities to make fields abstract instead of concrete.