Upload
millicent-hines
View
218
Download
0
Tags:
Embed Size (px)
Citation preview
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.
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)
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
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)
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)
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
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