Upload
xiaoyan-chen
View
393
Download
2
Tags:
Embed Size (px)
DESCRIPTION
Summary of good object-oriented design principles which should be followed.
Citation preview
Object Oriented Design Principles –– class level Xiao-Yan Chen Beijing/July 13, 2007
2
Agenda
> Introduction
> OO Design Principles
> Evil Stuff and Anti-Patterns
3
Introduction
> Where we are?
Methodology and Design Tech
Process
Tools
Methodology Design
technique
Heavyweight
process
Agile
process
Process
appraisal and improvement
For supporting
process
For supporting
methodology
For supporting
Design technique
4
Principles
> What is a bad design?
> The principles
• OCP open-closed principle
• SRP single responsibility principle
• ISP interface segregation principle
• LSP Liskov substitution principle
• DIP dependency inversion principle
> Principles reviewed
5
Bad designs
> Rigidity – hard to change
> Fragility – easy to break
> Immobility – hard to reuse
> Viscosity – hard to do the right thing
> Needless Complexity – over design
> Needless Repetition – error prone
> Opacity – hard to read and understand
6
Open Closed Principle
> Examples of OCP violation
public interface Shape extends Comparable{
public void draw();
}
public class Circle implements Shape {
public void draw() {
}
public int compareTo(Object o) {
if (o instanceof Rectangel) {
return -1;
}
else if (o instanceof Circle) {
return 0;
}
else {
return 1;
}
}
}
Shape
Circle Rectangle
New
Shape
7
Open Closed Principle
Software entities should be open for extension,
but closed for modification
B. Meyer, 1988
> Be open for extension
• module's behavior can be extended
> Be closed for modification
• source code for the module must not be changes
> Modules should be written so they can be extended without requiring them to be modified
8
Open Closed Principle
> How to:
• Encapsulate what varies.
• Abstraction is the KEY.
• Use “Data-Driven” approaches.
> This principle implies that:
• Make all member variables private.
• No global variables.
• RTTI (Run-Time Type Information) is dangerous.
> Also:
• No significant program can be 100% closed.
• OK to take the first bullet.
9
Single Responsibility Principle
> Examples of SRP violation
Computational
Geometry
Application
Graphical
Application
GUI
Rectangle
draw()
area()
> This violation is bad for that:
• We must include the GUI in the Computational Geometry application.
• If a change to the Graphical Application causes the Rectangle to change, that change may force us to rebuild, retest, and redeploy the Computational Geometry Application.
10
Single Responsibility Principle
> A Class should have one reason to change
• A Responsibility is a reason to change
> Single Responsibility = increased cohesion
> Not following results in needless dependencies
• More reasons to change.
• Rigidity, Immobility
11
Single Responsibility Principle
> Conform to SRP:
Computational
Geometry
Application
Graphical
Application
Rectangle
draw()
GUI
Geometric
Rectangle
area()
12
Interface Segregation Principle
13
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
GraphicalRect_I
draw()
Computational
Geometry
Application
Graphical
Application
Rectangle
draw()
area()
GUI
GeometricRect_I
Area()
14
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)
15
Liskov Substitution Principle
> Any subclass should always be usable instead of its parent class.
• Pre-conditions can only get weaker
• Post-conditions can only get stronger
> Derived classes should require no more and promise no less.
16
Liskov Substitution Principle
public interface Bird{
public void fly();
}
public class Parrot implements Bird {
public void fly() {
System.out.println(“OK, I can fly.”);
}
}
public class Penguin implements Bird {
public void fly() {
throw new IllegalStateExeption(“Sorry, I can not fly…”);
}
}
>Example of LSP violation:
public class BirdCustomer {
……………..
Bird bird = new Parrot();
bird.fly();
……………..
……………..
bird = new Penguin();
bird.fly(); // oops, the customer will be surprised!
…………….
…………….
}
17
Liskov Substitution Principle
public class Rectangle {
public void setWidth(double w) {
this.width = w;
}
public void setHeight(double h) {
this.height = h;
}
public double area() {
return this.width * this.height;
}
}
public class Square extends Rectangle {
public void setWidth(double w) {
super.setWidth(w);
super.setHeight(w);
}
public void setHeight(double h) {
super.setWidth(h);
super.setHeight(h);
}
}
>Example of LSP violation:
public class RectangleCustomer {
……………..
Rectangel rect = new Square();
rect.setWidth(4);
rect.setHeight(5);
assert rect.area()==20;// oops, the customer will be surprised!
…………….
…………….
}
18
Liskov Substitution Principle
> IS-A (inheritance) relationship refers to the BEHAVIOR of the class.
> BEHAVIOR = public members.
19
Dependency Inversion Principle
> Procedural layering: violation of DIP
Policy layer
Mechanism
layer
Utility layer
> Bad for:
• Transitive dependency
• Transitive change impacts
20
Dependency Inversion Principle
> A base class in an inheritance hierarchy should not know any of its subclasses
> Modules with detailed implementations are not depended upon, but depend themselves upon abstractions
> OCP states the goal; DIP states the mechanism;
> LSP is the insurance for DIP
I. High-level modules should not depend on low-level modules.
Both should depend on abstractions.
II. Abstractions should not depend on details.
Details should depend on abstractions
R. Martin, 1996
21
Dependency Inversion Principle
> OO layering: Conforming to DIP
Policy
Policy
Layer
Policy Service
Interface
Mechanism
Mechanism Layer Mechanism Service
Interface
Utility
Utility
Layer
22
Dependency Inversion Principle
> This principle implies:
• Programming to interfaces, not implementations.
• Both the naming and the physical location of interfaces should respect their customers, not their implementations.
• Dependency Injection.
Anyway, I need to depend on a concrete implementation object at runtime, how can I get it?
23
Dependency Inversion Principle
> Dependency Injection:
• Don’t use new operator to instantiate a concrete class where you need, instead inject it from outside.
> Dependency Injection options:
• Constructor Injection with PicoContainer
• Setter Injection with Spring
• Interface Injection
• Using a Service Locator
For details, refer to http://www.martinfowler.com/articles/injection.html
24
OO Principles Reviewed
> Encapsulate what varies.
> Favor composition over inheritance.
> Program to interfaces, not implementations.
> Strive for loosely coupled designs between objects
that interact.
> Classes should be open for extension but closed for
modification.
> Depend on abstraction. Do not depend on concrete
classes.
> Only talk to your friends.
> Don’t call us, we’ll call you.
> A class should have only one reason to change.
Oh, what is this?
25
Law of Demeter
> Only talk to your friends, also known as “Law of Demeter”.
> Only invoke methods that belong to:
• The object itself.
• Objects passes in as a parameter to the method.
• Any object the method creates or instantiates.
• Any components of the object. (objects directly referred to)
> Violating when you write a_object.m1().m2();
> Keep our circle of friends small clear responsibility decrease complexity
> Law of Demeter for Concerns (LoDC) is good for Aspect Oriented Software Development.
26
Agenda
> Introduction
> OO Design Principles
> Evil Stuff and Anti-Patterns
27
Evil Stuff
> Singletons / Global variables • Singletons are actually OO global variables.
> Getters, Setters • Evil for exposing information/implementation which should be hidden.
• Eliminate data movement. Data flow is procedure-oriented thinking.
• Don't ask for the information you need to do the work; ask the object that has the information to do the work for you.
• Exceptions: computational query, get/set an interface
• http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html?page=1
• http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html
> Helper Classes • Actually global procedures, hard to maintain.
• http://blogs.msdn.com/nickmalik/archive/2005/09/06/461404.aspx
• http://blogs.msdn.com/nickmalik/archive/2005/09/07/462054.aspx
28
Anti-Patterns
> Category • Design related: The Blob, Poltergeist, Swiss Army Knife, Dead End
• Development related: Golden Hammer, Input Kludge
• Architecture related: Reinvent the wheel, Vendor lock-in
> The category: • http://www.antipatterns.com/briefing/index.htm
• http://www.devx.com/Java/Article/29162
> Poltergeist: http://www.icmgworld.com/corp/news/Articles/RS/jan_0302.asp
> Dead End: http://www.icmgworld.com/corp/news/Articles/RS/jan_0402.asp
Much enough principles! Tired of this session? Here are some cookies
30
Cookie – Thread Safe Singleton
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile can make the double-check singleton thread safe, but only since Java5.
31
Cookie – The dilemma of Observer
private class Model extends Observable {
public void handleAttributeChange(int value) {
this.setChanged();
this.notifyObservers(value);
}
}
Observer observer = new Observer() {
public void update(Observable o, Object arg) {
System.out.println(arg);
}
};
model.addObserver(observer)
new Thread() {
public void run() {
model.handleAttributeChange(1);
}
}.start();
new Thread() {
public void run() {
model.handleAttributeChange(2);
}
}.start();
Not thread safe, notifications to observers may be lost!
Notification order not guaranteed, early notification, maybe late received by observers.
32
Cookie – The dilemma of Observer
private class Model extends Observable {
public synchronized void handleAttributeChange(int value) {
this.setChanged();
this.notifyObservers(value);
}
}
model.addObserver(observer);
final Object object = new Object();
Observer observer = new Observer() {
public void update(Observable o, Object arg) {
synchronized (object) {
System.out.println(arg);
}
}
};
model.addObserver(observer);
new Thread() {
public void run() {
synchronized (object) {
model.addObserver()
}
}
}.start();
model.handleAttributeChange(1);
Notifications will not be lost.
But, still not thread safe, dead-lock is permitted!
The End Thank You!