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()

Rectangle Renderer Draw()

Client Application

Algorithm

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.

Client Application

Algorithm

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
cd Diagram Examples Rectangle + + SetHight() : void SetWidth() : void

Why is that an LSP violation?

Square + + SetHight() : void SetWidth() : void

Real-Life Example
cd Diagram Examples MarshalByRefObject
+ cd Diagram Examples «interface» IContributeObjectSink {abstract} GetObjectSink (MarshalByRefObject, IMasshageSink ) : IMessageSink {abstract}

ContextBoundObject

.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

ServicedComponents violates LSP: You cannot add your own sinks (it only uses its own)

ContextBoundObject

ServicedComponent

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

:Module

:Module

:Function

:Function

:Function

:Function

DIP
cd Diagram Examples :Program

«interface» :Interface1

«interface» :Interface2

«interface» :Interface3

«realize»

«realize»

«realize»

«realize»

:Class1

:Class2

:Class3

: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(); }

PS O O

// 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 arnon@rgoarchitects.com

Sign up to vote on this title
UsefulNot useful