3. DESIGN SMELLS
Needless Repetition (Duplication)
Same code appears over and over again in different
forms.
Rigidity (Interdependence)
Every change causes too many changes in other parts of
the system.
In order to make system robust, it has to be insensitive
to small changes, i.e, a small change to a problem should
lead to only a small change in the solution.
4. DESIGN SMELLS
Immobility (Cannot Disentangle)
It is hard to reuse in another application because it
cannot be disentangled from the current application.
Fragility (Easily Breakable)
A change affects unexpected parts of the system and
causes it to break.
5. DESIGN SMELLS
Opacity (Difficulty in Understanding)
Tendency of a module to be difficult to understand.
Needless Complexity (Over Engineering)
Results from speculative approach (gold-plating!).
UseYAGNI
6. DESIGN SMELLS
Viscosity (Toughness in preserving design)
High DesignViscosity
Design preserving methods are harder to employ than
hacks.
Easy to do the wrong thing, but hard to do the right thing.
EnvironmentViscosity
7. OO DESIGN GUIDELINES
Strong Commitment to Encapsulation
Strong Commitment to Loose Coupling
Law of Demeter
Single Responsibility Principle
Coding to Super-Types & LSP
EncapsulateVariations & apply OCP
Create Role-Based Behaviors
Dependency Inversion
Favor Composition over Inheritance
Dependency Injection
Dependency Elimination
8. STRONG COMMITMENT
TO ENCAPSULATION
Ask (or query) an Object to get something for you.
if (tier.getType() == SILVER) {
// calculate loyalty points
}
// Still Query, but encapsulated
if (tier.isSilver()) {
// calculate loyalty points
}
Tell (or command) an Object to do something for you.
tier.loyaltyPoints(amountSpent)
9. TELL, DON’T ASK!
Procedural code gets information, inspects it and then
makes decisions. Its all about how to do.
Object-oriented code tells objects to do things.
Declare or State (think declaratively) what you want
and do not process (not procedurally) what you want.
10. PRESERVE ENCAPSULATION
Getters and Setters generally break encapsulation.
Don’t introduce getters or setters for all fields of an
object...just because IDEs provide this facility.
Don’t introduce getters just because tests need it.
engine.start();
assertThat(engine.isRunning(), is(true));
Use Observable Behavior instead, this improves the
API
assertThat(engine.start(), is(true));
11. PRESERVE ENCAPSULATION
Avoid returning a private collection
public class Building {
private List<Floor> floors = new ArrayList<Floor>();
//...
public List<Integer> getFloors() { return floors; }
}
Instead return a copy or a read-only version of it.
public class Building {
private List<Floor> floors = new ArrayList<Floor>();
//...
public List<Integer> getFloors() {
return Collections.unmodifiableList(floors);
}
}
12. STRONG COMMITMENT TO
LOOSE COUPLING
A E
D
C
B
dog.getBody().getTail().wag()
dog.expressHappiness()
dog.getBody().getTail().setLength(50)
state altering
“train wrecks”
“train wrecks”
refactored
dog.getBody().getTail().getLength()
state querying
“train wrecks”
13. YET ANOTHER FORM
A E
D
C
B
// In one place, we do
Body body = dog.getBody();
// and in some other place
// in codebase we do
Tail tail = body.getTail();
tail.setLength(50);
14. LAW OF DEMETER
No “Train Wrecks”
Send messages only to
object’s immediate
neighbors.
Principle of least
knowledge
Don’t talk to more
objects than required.
The more objects you
talk to, more the risk of
getting broken when any
one of the object
changes.
Restrict class
interaction in order to
minimize coupling
among classes.
15. CONFORMING TO
LAW OF DEMETER
A method of an Object should invoke only the
methods of the following kinds of objects
itself
its parameters
any objects it creates or directly collaborates
any composite object
As a guidance-rule, more than two “dots” means the
Law of Demeter is being violated.
16. SINGLE RESPONSIBILITY
PRINCIPLE (SRP)
There should never be more than one reason to change
a class.
A responsibility is an axis of change.
Key is in identifying which is an axis of change and which is
not?
Many responsibilities means many reasons to change.
This is hard to see, as we think
responsibility in groups.
17. WHY SHOULD RESPONSIBILITIES BE
SEPARATED INTO SEPARATE
CLASSES?
Because each responsibility is an axis of change.
When the requirements change, that change will be
manifest through a change in responsibility amongst
the classes.
If a class assumes more than one responsibility, then
there will be more than one reason for it to change.
Source: Robert C. Martin
18. THE CHALLENGE IN SRP IS…
The SRP is one of the simplest of the principle, and one
of the hardest to get right.
Conjoining responsibilities is something that we do
naturally.
Finding and separating those responsibilities from one
another is much of what software design is really about.
Source: Robert C. Martin
19. BENEFITS OF SRP…
Removes the “Immobility Smell” from Design
Immobility is the inability to reuse software from
other projects or from parts of the same project.
Deodorizes the “Rigidity Smell”
Generates a highly cohesive design.
20. CODING TO WHAT?
Coding to an interface
class Rectangle implements Shape { … }
Shape s = new Rectangle();
s.draw();
Coding to an implementation
Rectangle r = new Rectangle();
r.draw();
Code to Interfaces, not to Implementation
21. SUBTYPING & INHERITANCE
Subtyping a.k.a Interface Inheritance
Behavioral Subtyping (Semantic Relationship - “is-a”)
Inheritance a.k.a Implementation Inheritance
Code Reuse (Syntactic Relationship - “re-use”)
Subclass re-uses code from base-class and may over-ride
operations, by replacing base-class implementation with
its own.
Inheritance is not Subtyping (William Cook)
24. Reshaper shaper = new Reshaper();
Rectangle r = new Rectangle(2, 3);
shaper.reshape(r, 5, 3);
r.area();
Square s = new Square(2);
shaper.reshape(s, 5, 3);
s.area();
Implementation inheritance does not provide
assurance of polymorphic substitutability.
Will this give
correct result?
INHERITANCE EXAMPLE - 1
25. class Point2D {
private int x, y;
Point2D(int x, int y) {
this.x = x;
this.y = y;
}
//Do 2D rotation
public Point2D translate(int by) { … }
//Do 2D scaling
public Point2D scale(int factor) { … }
//rotation about origin
public Point2D rotate(int by) { … }
public int getX() { return x; }
public int getY() { return y; }
}
class Point3D extends Point2D {
private int z;
Point3D(int x, int y, int z) {
super(x, y);
this.z = z;
}
@Override // Do 3D translation
public Point3D translate(int by) { … }
@Override // Do 3D scaling
public Point3D scale(int factor) { … }
// rotation about 3 axes
public Point3D rotateX(int by) { … }
public Point3D rotateY(int by) { … }
public Point3D rotateZ(int by) { … }
public int getZ() { return z; }
}
INHERITANCE EXAMPLE - 2
26. Point3D overrides translate and scale in
Point2D to make it appropriate for 3D and introduces
additional rotation methods, leaving the rotate in
Point2D intact.
When we find ourselves modifying almost all methods in
the sub-class then we are not really using inheritance, we
are trying to get generality offered by subtype
polymorphism.
Be vigilant of such a situation and use interface to clarify
sub-typing from inheritance. Source: A Little Book on Java
INHERITANCE EXAMPLE - 2
27. INHERITANCE
Shape
+ area() : float
+ perimeter() : float
RectangleRTriangle Pentagon
Rectangle
Square
SUBTYPING
INHERITANCE is about
Gaining attributes and
functionality of super-type
SUB TYPING is about
Compatibility of
Interfaces
28. INHERITANCE & SUBTYPING
In Java, C#, C++, Scala, Inheritance and Subtyping are
welded together, i.e. Subtyping occurs automatically when
you have inheritance.
Inheritance == Subtyping
However, Inheritance and Subtyping are different.
Inheritance != SubTyping
Java supports subtype polymorphism (using interfaces)
independently of inheritance (inherit from single class). As
there is no code in interface, there is nothing to inherit.
Source: A Little Book on Java
29. LISKOV’S SUBSTITUTION
PRINCIPLE (LSP)
Sub-types must be substitutable for their Base-types
Be able to operate upon derivatives of that base class
without knowing it.
It makes clear that in OO Design “IS-A” relationship is
about behavioral semantics.
Not intrinsic private behavior, but extrinsic public behavior.
E.g. You cannot pass a Stack where Queue is required.
Semantics that clients can depend upon.
31. LSPVIOLATION
Shape
+ area() : float
+ perimeter() : float
RectangleRTriangle Pentagon Circle
+ area ()
+ circumference ()
This is a
CLEAR
violation
of LSP!
Source: Robert C. Martin
32. LSPVIOLATION
Shape
+ area() : float
+ perimeter() : float
RectangleRTriangle Pentagon Circle
+ area ()
+ circumference ()
This is a
CLEAR
violation
of LSP!
Square
This is a
SUBTLE
violation
of LSP! Source: Robert C. Martin
33. HANDLING CHANGES
When a single change to a program
Results in a cascade of changes to dependent
modules.
The program becomes rigid, unpredictable and not
reusable.
This is sign of
a bad design
34. ENCAPSULATE VARIATIONS
Identify things that vary and separate them from the
concepts that stay the same.
Evolve an Abstraction representing the concept.
Introduce Interface or Abstract super-type.
Unify variations on this pivot of abstraction.
Encapsulate each variation using a Concrete Class that
implements/extends the above super-type.
35. APPLY OPEN/CLOSED
PRINCIPLE
It says that you should design modules that never
change.
When requirements change, extend the behavior of
modules
By adding new code.
Not by changing old code that already works.
37. OPEN FOR
EXTENSION
Behavior of the module
can be extended.
Make the module behave
in new and different ways
as the requirements of
the application change.
The source code of such
a module is inviolate.
Resist making source
code changes to it.
CLOSED FOR
MODIFICATION
38. HOW CAN THESE TWO
OPPOSING
ATTRIBUTES BE
RESOLVED?
Abstraction is the Key.
39. WHY ABSTRACTIONS?
Its about naming the concept.
We like to retain flexibility to never make up the mind
about anything until you are forced to do it. Abstraction
is one way of doing this.
They are a way to control complexity.
They help us contain the guts of the stuff while avoiding
spill-over to things we are building.
They are the truths that do not vary when the details
change. Source: Structure and Interpretation of Computer Programs
40. WHAT MAKES A GOOD
ABSTRACTION?
An Abstraction focuses on Client’s perspective and
suppresses implementation perspective.
A simple logical model that can be described in a
sentence or a two.
It does not mean class’s processing is simple!
It simply means that the class is focused on a small set of
activities.
Source: S/W Design Studio, by Dr. Jim Fawcett, Syracuse University
41. NATURE OF ABSTRACTIONS
They represent “hinge points” for the places where the
design can bend or be extended.
This flexibility comes with a cost of themselves being
rigid to change.
Resist the temptation to introduce an abstraction unless
absolutely essential, introducing a pre-mature abstraction
leads to pre-mature design optimisation.
42. CREATE ROLES-BASED
BEHAVIORS
Name a role around a metaphor or around the
concepts which you are abstracting out.
This helps a client understand and relate to an object’s
behavior.
Role should convey everything about the
implemented behavior.
43. OCP IN REALITY
100% closure is not attainable.
Closure must be strategic.
We design systems such that they are closed to the
most likely kinds of changes that we foresee.
Source: Robert C. Martin
45. DEPENDENCY INVERSION
PRINCIPLE (DIP)
High level modules should not depend on low level
modules.
Both should depend upon abstractions.
Abstractions should not depend upon specifics.
Specifics should depend upon abstractions.
Source: Robert C. Martin
47. POLICY AND MECHANISM
Source: Robert C. Martin
High level modules contain policy decisions and
business models.
When High level modules (Policies) depend upon low
level modules, changes to low level modules
(Mechanisms) most likely will force upon a change in
them.
Ideally, it should be the other way around, high level
modules should be forcing lower level ones to
change.
48. SEPARATE POLICY FROM
MECHANISM…WHY?
Source: The Art of Unix Programming
Hard-wiring policy and mechanism
Makes policy rigid and hard to change.
Change in policy has a strong tendency to destabilise
mechanisms
Policy changes frequently.
Separating the two
Makes it possible to experiment with new policy without
breaking mechanisms.
Easier to write tests for mechanisms.
50. EXTENDING OBJECT
CAPABILITIES
Composition promotes “dynamic reuse”
Is a weaker association
Vary object behavior dynamically by changing collaborators
Inheritance promotes “static reuse”
Is a stronger association
Vary object behavior at compile time.
“has-a” can be better than “is-a”, leads to supple design
51. SUB-CLASS PROLIFERATION
Evils of Inheritance
Deep hierarchies obscure understanding.
As a guidance, question yourself when you find creating
hierarchies that are more than 2 levels deep.
Contractual changes on super-class ripple through Wide
hierarchies.
In general, Favor Composition over Inheritance
52. INTERFACE SEGREGATION
PRINCIPLE (ISP)
Deals with avoiding “Fat”, Non-cohesive class
interfaces.
Groups of methods used by different clients.
Don’t force clients to depend on methods they don’t
use.
Else they are subject to changes to those methods.
Results in an inadvertent coupling between all the clients.
54. LOOK MA....NO COUPLING!
Too many abstractions make it hard to understand
the system.
Refactor to Closures/Lambdas wherever its sensible
and possible
Dependency Elimination
You don’t need abstract classes or interfaces to achieve
polymorphism.
Close your class against modifications.
55. YET ANOTHER GUIDELINE
Dependency Elimination can be better than
Dependency Inversion.
Requires Language Level support to achieve this
Scala, Groovy, Ruby, C# and now Java8 make this possible.
Not possible with Java7.
Many patterns like Strategy, Command etc... get
subsumed where Closures are available
56. DEGREES OF COUPLING
Creating Concrete
Collaborator from within a
client
Injecting Concrete
Collaborator into a client
(Dependency Injection)
Injecting Code Block
into a client
(Dependency Elimination)
Direct
Loose
No Coupling
57. OBJECT DESIGN
GUIDELINES SUMMARY
Design for preserving Encapsulation
Tell, Don’t Ask!
Extend Object Capabilities
By favoring Composition over Inheritance
Use Mixins, if available and possible
Use Inheritance when nothing of the above works.
Design for High Cohesion
SRP, ISP
58. OBJECT DESIGN
GUIDELINES SUMMARY
Design for Reduced Coupling
Law of Demeter, OCP, LSP.
Separate Policy from Mechanism (DIP)
Design for No Coupling
Dependency Elimination
59. OBJECT DESIGN
GUIDELINES SUMMARY
Uphold Good Housekeeping Policy.
Use and clean your own stuff…
Uphold Green Environment Policy.
Undo changes made by you to your environment.
60. REFERENCES
Agile Principles, Patterns and
Practices
Robert C. Martin and Micah
Martin
Object Design
Rebecca Wirfs-Brock and Alan
McKean
Alec Sharp
The Pragmatic Programmers
Mock Roles, not Objects
Paper by Steve Freeman, Nat
Pryce,Tim Mackinnon, Joe
Walnes
InfoQ presentation by
Steve Freeman and Nat Pryce
Head-First Design Patterns Book
Venkat Subramaniam’s Blog
SICP - Abelson & Sussman