57
1 OO Design Principles Project Group eXtreme Programming Md. Abul Bashar 07/09/2004

1 OO Design Principles Project Group eXtreme Programming Md. Abul Bashar 07/09/2004

Embed Size (px)

Citation preview

1

OO Design Principles

Project Group

eXtreme Programming

Md. Abul Bashar

07/09/2004

2

Outline

Problems with software system

Basic OO PrinciplesOCP, LSP, DIP

Class Cohesion and Coupling Principles ISP, SRP, LoD

Package Cohesion Principles REP, CRP, CCP

Package Coupling Principles ADP, SDP, SAP

What goes wrong with S/W?

Changing requirements

You start with a clear picture in your mind what the SW should do.

Then something goes wrong, the software starts to rot:

Changes and Additions are harder to make.

Simplest changes terrify you because of rippling unexpected effects

You must redesign the whole !!

4

Bad Signs of Rotting Design

RigidityCode difficult to change

Management reluctance to change anything becomes policy

FragilityEven small changes can cause cascading effects

Code breaks in unexpected places

ImmobilityCode is so tangled that it’s impossible to reuse anything

– a module could be reused in another system, but the effort and risk of separating the module from original environment is too high

ViscosityMuch easier to hack than to preserve original design.

5

Open-Closed Principle (OCP)

• "Software Systems change during their life time"– both better designs and poor designs have to face the changes;

– good designs are stable

Software entities should be open for extension, but closed for modification

B. Meyer, 1988 / quoted by R. Martin, 1996

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

6

Really the Heart of OO Design

100% satisfaction to OCP is never possible

But we should attempt to minimize the number of modules that do not satisfy it

Conformance to this principle yields the greatest level of reusability and maintainability

Open-Closed Principle (OCP)

7

Consider the following method of some class:

public double totalPrice(Part[] parts) { double total = 0.0; for (int i=0; i<parts.length; i++) { total += parts[i].getPrice(); } return total;}

- The job of the above function is to total the price of each part in the specified array of parts

- If Part is a base class or an interface and polymorphism is being used, then this class can easily accommodate new types of parts without having to be modified!

- It conforms to the OCP ??

Open-Closed Principle (OCP)

8

But what if the Accounting Department decided some price increase on motherboard parts and memory parts ?

- How about the following code?

public double totalPrice(Part[] parts) { double total = 0.0; for (int i=0; i<parts.length; i++) { if (parts[i] instanceof Motherboard) total += (1.45 * parts[i].getPrice()); else if (parts[i] instanceof Memory) total += (1.27 * parts[i].getPrice()); else total += parts[i].getPrice(); } return total;}

Open-Closed Principle (OCP)

9

Does this conform to the OCP?

- Every time the Accounting Department comes out with a new pricing policy, we have to modify the totalPrice() method!

- It is not Closed For Modification.

- Obviously, policy changes such as that mean that we have to modify code somewhere, so what could we do?

- To use our first version of totalPrice(), we could incorporate pricing policy in the getPrice() method of a Part

Open-Closed Principle (OCP)

10

Here are example Part and ConcretePart classes:

// Class Part is the superclass for all parts.public class Part { private double price; public Part(double price) (this.price = price;} public void setPrice(double price) {this.price = price;} public double getPrice() {return price;}}

// Class ConcretePart implements a part for sale.// Pricing policy explicit here!public class ConcretePart extends Part { public double getPrice() { // return (1.45 * price); //Premium return (0.90 * price); //Labor Day Sale }}

Open-Closed Principle (OCP)Part

ConcretePart1 ConcretePart2

11

But now we must modify each subclass of Part whenever the pricing policy changes!

- A better idea is to have a PricePolicy class which can be used to provide different pricing policies:

// The Part class now has a contained PricePolicy object.public class Part {

private double price; private PricePolicy pricePolicy;

public void setPricePolicy(PricePolicy pricePolicy) { this.pricePolicy = pricePolicy; }

public void setPrice(double price) {this.price = price;}

public double getPrice() {return pricePolicy.getPrice(price);}}

Open-Closed Principle (OCP)

12

/** * Class PricePolicy implements a given price policy. */public class PricePolicy { private double factor;

public PricePolicy (double factor) { this.factor = factor; }

public double getPrice(double price) {return price * factor;}}

Open-Closed Principle (OCP)

Part

ConcretePart1 ConcretePart2

<< Policy >>

ConcretePolicy

13

Liskov Substitution Principle (LSP)

Functions That Use References To Base Classes Must Be Able To Use Objects Of Derived Classes Without Knowing it.

14

// A very nice Rectangle class.public class Rectangle { private double width; private double height; public Rectangle(double w, double h) { width = w; height = h; }

public double getWidth() {return width;} public double getHeight() {return height;} public void setWidth(double w) {width = w;} public void setHeight(double h) {height = h;} public double area() {return (width * height);}

LSP Example

15

Now, What about a Square class? Clearly, a square is a rectangle, so the Square class should be derived from the Rectangle class, right? Let's see!

Observations:

• A square does not need both a width and a height as attributes but it will inherit them from Rectangle anyway. • The inherited setWidth() and setHeight() methods are not really appropriate for a Square, since the width and height of a square are identical.

• So we'll need to override setWidth() and setHeight().

• Having to override these simple methods is a clue that this might not be an appropriate use of inheritance!

LSP Example

16

Here's the Square class:

// A Square class.public class Square extends Rectangle { public Square(double s) {super(s, s);}

public void setWidth(double w) { super.setWidth(w); super.setHeight(w); }

public void setHeight(double h) { super.setHeight(h); super.setWidth(h); }

}

LSP Example

Rectangle

setWidth(w)

setHeight(h)

CirclesetWidth(w)setHeight(h)

17

Everything looks good. But check this out!

public class TestRectangle {// Define a method that takes a Rectangle reference.

public static void testLSP(Rectangle r) { r.setWidth(4.0); r.setHeight(5.0); System.out.println( "Width is 4.0 and Height is 5.0, so Area is " + r.area() );

if (r.area() == 20.0) System.out.println("Looking good!\n");

else System.out.println("Huh?? What kind of rectangle is this??\n");}

LSP Example

18

public static void main(String args[]) { //Create a Rectangle and a Square

Rectangle r = new Rectangle(1.0, 1.0);

Square s = new Square(1.0);

// Now call the method above. According to the // LSP, it should work for either Rectangles or // Squares. Does it?? testLSP(r);

testLSP(s);

}

}

LSP Example

19

Test program output:

Width is 4.0 and Height is 5.0, so Area is 20.0 Looking good!

Width is 4.0 and Height is 5.0, so Area is 25.0 Huh?? What kind of rectangle is this??

Looks like we violated the LSP!

LSP Example

20

What’s the problem here?

Implicit assumptionsThe programmer made the assumption that changing the width of a Rectangle leaves its height unchanged.

Didn’t consider that, the base class can write a method that causes the design model to break down.

Behaviorally, a Square is not a Rectangle! A Square object is not polymorphic with a Rectangle object.

Solutions can not be viewed in isolation.

21

Liskov Substitution Principle (LSP)

IS-A relationship is all about behavior

All subclasses must conform to the behavior as of base class

Subclass must NOT have more constraints than it’s base class

The guarantee of the LSP is – a subclass can always be used whenever it’s base class is used.

22

Dependency Inversion Principle (DIP)

Depend upon Abstraction,

Do NOT Depend upon Concretions.

23

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

OCP states the goal; DIP states the mechanism

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

Dependency Inversion Principle (DIP)

24

DIP Related Heuristic

• Use inheritance to avoid direct bindings to classes:

Design to an interface, not an implementation!

Client

Interface(abstract class)

Implementation(concrete class)

25

Design to an Interface

• Abstract classes/interfaces:– tend to change much less frequently– abstractions are ‘hinge points’ where it is easier to extend/modify– shouldn’t have to modify classes/interfaces that represent the

abstraction (OCP)

• Exceptions– Some classes are very unlikely to change;

• therefore little benefit to inserting abstraction layer

• Example: String class

– In cases like this can use concrete class directly• as in Java or C++

26

• Avoid structures in which higher-level layers depend on lower-level abstractions:– In example below, Policy layer is ultimately dependant on

Utility layer.

Avoid Transitive Dependencies

Policy Layer

MechanismLayer

UtilityLayer

Depends on Depends on

DIP Related Heuristic

27

• Use inheritance and abstract ancestor classes to effectively eliminate transitive dependencies:

Policy Layer

MechanismLayer

UtilityLayer

depends on

depends on UtilityInterface

MechanismInterface

Solution to Transitive Dependencies

28

• If you cannot find a satisfactory solution for the class you are designing, try delegating responsibility to one or more classes:

When in doubt, add a level of indirection

Problem Holder

ProblemSolver

DIP Related Heuristic

29

• It is generally easier to remove or by-pass existing levels of indirection than it is to add them later:

XSo, Blue class re-implements some or all of green class’s responsibilities for efficiency and calls red object directly

Blue class’s indirect message calls to red class fail to meet some criteria (e.g. real-time constraints, etc.)

When in doubt …

30

• Many client-specific interfaces are better than one general purpose interface

• Consequence: – impact of changes to one interface aren’t as big if interface

is smaller– interface pollution

Clients should not be forced to depend upon interfaces that they do not use.

R. Martin, 1996

Interface Segregation Principle (ISP)

31

Interface Segregation Principle (ISP)

32

Single Responsibility Principle (SRP)

There Should NEVER be More Than One Reason for a Class to Change.

33

Single Responsibility Principle (SRP)

• More than one reason means More than one Responsibilities.

• Responsibilities become coupled

34

Single Responsibility Principle (SRP)

35

Single Responsibility Principle (SRP)

36

• Cohesion Principles – Reuse/Release Equivalency Principle (REP)

– Common Reuse Principle (CRP)

– Common Closure Principle (CCP)

• Coupling Principles – Acyclic Dependencies Principle (ADP)

– Stable Dependencies Principle (SDP)

– Stable Abstractions Principle (SAP)

Principles of OO High Level Design

37

• What means this ? – A reusable software element cannot really be reused in practice unless it

is managed by a release system of some kind• e.g. release numbers or names

– All related classes must be released together

– Release granule Reuse granule• no reuse without release • must integrate the entire module (can’t reuse less)

– Classes are to small • we need larger scale entities, i.e. package

The granule of reuse is the granule of release.Only components that are released through a tracking

system can be efficiently reused.R. Martin, 1996

Reuse/Release Equivalency Principle

38

All classes in a package [library] should be reused together. If you reuse one of the classes in the package,

you reuse them all.R. Martin, Granularity 1996

Packages of reusable components should be grouped by expected usage, Not: common functionality, nor another arbitrary categorization.

Classes are usually reused in groups based on collaborations between library classes

Common Reuse Principle (CRP)

39

The Façade Pattern becomes critical when the level of reuse becomes a targeted goal:

If you started with…

(e.g. a mess!)

Common Reuse Principle (CRP)

40

The Façade Solution

“Façade” says you should clean it up like this:

Common Reuse Principle (CRP)

41

• What means this ? – Classes that change together belong together– Goal: limit the dispersion of changes among released packages

• changes must affect the smallest number of released packages

– Classes within a package must be cohesive– Given a particular kind of change, either all classes or no class

in a component needs to be modified

The classes in a package should be closed against the same kinds of changes.

A change that affects a package affects all the classes in that package

R. Martin, 1996

Common Closure Principle (CCP)

42

• REP and CRP makes life easier for re-users– packages very small

• CCP makes life easier for maintainer– large packages

• Packages are not fixed in stone– early lifetime dominated by CCP– later you want to reuse: focus on REP CRP

Reuse vs. Maintenance

43

The dependency structure for released component must be a Directed Acyclic Graph (DAG)

There can be no cycles.R. Martin, 1996

Acyclic Dependencies Principles (ADP)

44

Acyclic Dependencies Principles (ADP)

45

A Cycle Creeps In !

Acyclic Dependencies Principles (ADP)

46

Breaking a cycle

Acyclic Dependencies Principles (ADP)

47

The dependencies between components in a design should be in the direction of stability.

A component should only depend upon components that are more stable than it is.

R. Martin, 1996

Depend only upon components whose I metric is lower than yoursR. Martin, 1996

SDP Violation

Stable Dependencies Principles (SDP)

48

Where to Put High-Level Design?

• High-level architecture and design decisions don't change often– shouldn't be volatile place them in stable packages

– design becomes hard to change inflexible design

• How can a totally stable package (I = 0) be flexible enough to withstand change? – improve it without modifying it...

Answer: The Open-Closed Principle classes that can be extended without modifying them

Abstract Classes

Stable Dependencies Principles (SDP)

49

• Ideal Architecture– Instable (changeable) packages on the top

• must be concrete

– Stable (hard to change) package on the bottom • hard to change, but easy to extend• highly abstract (easily extended)• Interfaces have more intrinsic stability than executable code

• SAP is a restatement of DIP

The abstraction of a package should be proportional to its stability!Packages that are maximally stable should be maximally abstract.

Instable packages should be concrete.R. Martin, 1996

Stable Abstractions Principles (SAP)

50

The Main Sequence

Instability (I) should increase as abstraction (A) decreases.R. Martin, 2000

Stable Abstractions Principles (SAP)

51

Temperature

Sensor

Temperature

Display

Application

Instable

Abs

trac

tStable Abstractions Principles (SAP)

52

Temperature

Sensor

Temperature

Display

Application

Instable

Abs

trac

t

Observer

Subject

Stable Abstractions Principles (SAP)

53

OO Principles and Design Patterns

• We introduce dependency when we create object

• OO Principles favor abstraction. – How to use abstraction?– Abstract classes can’t be instantiated.

Part

ConcretePart1 ConcretePart2

<< Policy >>

ConcretePolicy

54

Abstract Factory Patterns

• DIP and Abstract Factory

55

Composite Patterns

56

Summery

OO Principles lead to a software which conforms with changing requirements.

Basic OO PrinciplesOCP, LSP, DIP

Class Cohesion and Coupling Principles ISP, SRP, LoD

Package Cohesion Principles REP, CRP, CCP

Package Coupling Principles ADP, SDP, SAP

57

Questions ?!