Transcript Title

Object Oriented
Design Principles
Arnon Rotem-Gal-Oz
Product Line Architect
Preface
Nothing really new here
Just a summary of other people’s work:
D.L. Parnas
M. Fowler
R.C. Martin, B. Meyer, B. Liskov
W.J. Brown, R.C. Malveau, H.W. McCormick,
T.J. Mowbray
GoF
N. Malik
A. Holub
And probably others I forgot
Agenda
7 Deadly Sins of Design
The Rules
Basic Stuff
Evil Stuff
More Stuff
7 Deadly Sins of Design
Rigidity – make it hard to change
Fragility – make it easy to break
Immobility – make it hard to reuse
Viscosity – make it hard to do the right
thing
Needless Complexity – over design
Needless Repetition – error prone
Not doing any
The Rules
Apply Common Sense
Don’t get too dogmatic / religious
Every decision is a tradeoff
All other principles are just that
Guidelines
“best practices”
Consider carefully if you should violate
them - but, know you can.
Basic Stuff
OCP open-closed principle
SRP single responsibility principle
ISP interface segregation principle
LSP Liskov substitution principle
DIP dependency inversion principle
Dependency Injection
Open Closed Principle
Software entities ( Classes, Modules,
Methods, etc. ) should be open for
extension, but closed for modification.
Also Known As
Protected Variation
What Parnas meant when he coined
“Information hiding”
Why OCP
If not followed a single change to a
program results in a cascade of changes
to dependent modules.
The program becomes fragile, rigid,
unpredictable and un-reusable.
OCP Example
Template
Method
OCP implications (examples)
Use private modifier for class members
Refrain from adding Getters
automatically
Use abstractions
Extend by inheritance, delegation
Inversion of Control (IoC)
Single Responsibility
Principle
A Class should have one reason to
change
A Responsibility is a reasons to change
Can be tricky to get granularity right
Why SRP
Single Responsibility = increased
cohesion
Not following results in needless
dependencies
More reasons to change.
Rigidity, Immobility
SRP Example (1/2)
The Rectangle has 2 responsibilities
Algorithm
Rectangle
Draw()
Area()
Client
Application
DirectX
Dependency
Suddenly the Algorithm needs DirectX
SRP Example (2/2)
Rectangle
Area()
Algorithm
Rectangle
Renderer
Draw()
Client
Application
DirectX
Dependency
Interface Segregation
Principle
Many client specific interfaces are
better than one general purpose
interface
Create an interface per client type not
per client
Avoid needless coupling to clients
Why ISP
Otherwise – increased coupling
between different clients
Basically a variation on SRP
ISP example
IRectangle
Area()
IRectangle
Renderer
Draw()
Rectangle
Impl.
Algorithm
Client
Application
DirectX
Realization
Dependency
Liskov Substitution Principle
“What is wanted here is something like the
following substitution property: 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.” (Barbara Liskov,
1988)
Or in English
Any subclass should always be usable
instead of its parent class.
Corollary - All derived classes must
honour the contracts of their base
classes
IS A = same public behavior
Pre-conditions can only get weaker
Post-conditions can only get stronger
Why LSP
Failing to follow LSP result in a mess
OCP violations (if/then to identify types)
Superclass unit test will fail
Strange behavior
Trivial Example
Why is that an LSP violation?
cd Diagram Examples
Rectangle
+
+
SetHight() : void
SetWidth() : void
Square
+
+
SetHight() : void
SetWidth() : void
Real-Life Example
cd Diagram Examples
cd Diagram Examples
«interface»
IContributeObjectSink
MarshalByRefObject
{abstract}
+
ContextBoundObject
GetObjectSink (MarshalByRefObject, IMasshageSink ) : IMessageSink {abstract}
.NET creates a transparent proxy and
intercepts all calls to ContextBoundObjects
Implementing IContextObjectSink On a
class derived from ContrextAttribute
lets you add a sink in the chain
Real-Life Example
cd Diagram Examples
MarshalByRefObject
ContextBoundObject
ServicedComponent
ServicedComponents
violates LSP:
You cannot add your own sinks
(it only uses its own)
Dependency Inversion
Principle
Higher level modules should not
depend on lower level modules
Both should depend on abstractions
Interfaces or Abstract classes
Abstractions should not depend on
details
(Not to be confused with Dependency
Injection and Inversion of Control)
Why DIP
Increase loose coupling
Abstract interfaces don't change
Concrete classes implement interfaces
Concrete classes easy to throw away and replace
Increase mobility
Increase isolation
decrease rigidity
Increase testability
Increase maintainablity
Closely related to LSP
Reminder - Procedural Design
cd Diagram Examples
:Program
:Module
:Function
:Module
:Function
:Module
:Function
:Function
DIP
cd Diagram Examples
:Program
«interface»
:Interface1
«realize»
:Class1
«interface»
:Interface2
«realize»
:Class2
«interface»
:Interface3
«realize»
:Class3
«realize»
:Class4
DIP implications
Layers
Interface based programming
Separated Interface
put interface in separate package than
implementation
Dependency Injection
Old Way
public class MyApp
{
public MyApp()
{
authenticator = new Authenticator();
database = new Database();
logger = new Logger();
errorHandler = new ErrorHandler();
}
// More code here...
}
Dependency Injection
DIP says we should depend on an
interface
how do we get the concrete instance
New Way?
public class MyApp
{
public MyApp()
{
authenticator = new IAuthenticator();
database = new IDatabase();
logger = new ILogger();
errorHandler = new IErrorHandler();
}
// More code here...
}
Dependency Injection
Option 1 – Factory
User depends on factory
Factory depends on destination
Option 2 – Locator/Registry/Directory
The component still controls the wiring
Instantiation Sequence
Dependency on the Locator
Option 3 – Dependency Injection
An assembler controls the wiring
DI Options
Setter Injection
The component is passive
Someone injects the
dependency
DI Options
Setter Injection
Constructor Injection
“Always” initialized
Better dependency visibility
Other DI Options
Interface Injection
Variation on setter injection
Getter Injection
Needs AOP (not clean)
Evil Stuff
Switch statements
If (type())
Singletons / Global variables
Getters
Helper Classes
More Stuff
Package principles
Not the core of this presentation
Smells
Anti-Patterns
Package Principles
Reuse-Release Equivalency Principle
Common Closure Principle
Common Reuse Principle
Acyclic Dependencies Principle
Stable Dependencies Principle
Stable Abstractions Principle
CodeSmells
Something that's quick to spot
Indication for a possible problem
Not always the problem it self
May not be a problem at all
CodeSmell example – Long Method
DesignSmell (1)
Many CodeSmells can also apply to
design
Long Parameter List
Large Class (Swiss Army knife / Blob)
Type Embedded in Name
Uncommunicative Name
Data Class
Refused Bequest (LSP violations)
DesignSmell (2)
Inappropriate Intimacy
Lazy Class
Feature Envy (Managers)
Shotgun Surgery
Parallel Inheritance Hierarchies
Message Chains
Component Without Interface
Singletons
Design Related Anti-Patterns
The Blob
Functional Decomposition
Poltergeist
Golden Hammer
Swiss Army Knife
Kevorkian Component (Dead End)
(Other Anti-Patterns deal with
Architecture, Management, Code)
Thank you…
Arnon Rotem-Gal-Oz
[email protected]