Object-oriented design principles

Post on 10-May-2015

383 views 2 download

Tags:

description

Summary of good object-oriented design principles which should be followed.

transcript

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!