37
Lecture 16 Lecture 16 Composition vs Composition vs Inheritance: Inheritance: The Final Chapter The Final Chapter

Lecture 16 Composition vs Inheritance: The Final Chapter

Embed Size (px)

Citation preview

Lecture 16Lecture 16

Composition vs Inheritance:Composition vs Inheritance:

The Final ChapterThe Final Chapter

Inheritance* DrawbacksInheritance* Drawbacks• Inheritance imposes a tight coupling between parent Inheritance imposes a tight coupling between parent

class and its subclasses.class and its subclasses.• The parent class / child class relationship is brittle. A The parent class / child class relationship is brittle. A

change in the parent class could break its child classes change in the parent class could break its child classes even though there are no changes in the child classes.even though there are no changes in the child classes.

• Inheritance exposes implementation details thus Inheritance exposes implementation details thus violating encapsulation.violating encapsulation.

* * This presentation assumes implementation inheritance, not This presentation assumes implementation inheritance, not interface inheritance.interface inheritance.

Inheritance GuidelinesInheritance Guidelines• The decision to use inheritance should not be The decision to use inheritance should not be

made lightly. The decision to use inheritance made lightly. The decision to use inheritance should be make on sound OO principles:should be make on sound OO principles: “IS A” – Is the subclass indeed a type of parent class?

In many cases “IS A” criteria isn’t good enough to make this determination.

Will the type hierarchy violate the Liskov Substitution Principle?

Per “Design by Contract”…Are the preconditions of an overridden method the same or weaker than that of the parent type?Are the postconditions the same or stronger?

Is the base type designed and documented for inheritance? How volatile is the base type?

Liskov Substitution PrincipleLiskov Substitution Principle• If for each object oIf for each object o11 of type S there is an object o of type S there is an object o22 of type T such that for all of type T such that for all

programs P defined in terms of T, the behavior of P is unchanged when oprograms P defined in terms of T, the behavior of P is unchanged when o11 is is substituted for osubstituted for o22 then S is a subtype of T. then S is a subtype of T. oo11 is an instance of a is an instance of a SSubtype ubtype oo22 is an instance of a is an instance of a TType ype PP is the is the TType that defines the behavior the object it realizes. ype that defines the behavior the object it realizes. PP should behave the same regardless of whether it realizes a should behave the same regardless of whether it realizes a TType (ype (oo22) or a ) or a SSubtype (ubtype (oo11 ). ).

• Programmatically, a function or method using object of a base Programmatically, a function or method using object of a base class should behave the same when it is made to use object of class should behave the same when it is made to use object of a class derived from the base class.a class derived from the base class. LSP is an OOD guideline stressing the importance of behavior over LSP is an OOD guideline stressing the importance of behavior over

data.data. Method overriding in derived classes is probably the biggest cause of Method overriding in derived classes is probably the biggest cause of

LSP violations. Method overriding should be done with great impunity LSP violations. Method overriding should be done with great impunity to avoid these violations. to avoid these violations.

Overriding an implemented method violates the “Dependency Inversion Overriding an implemented method violates the “Dependency Inversion Principle” Principle” (depend on abstractions, not implementations).(depend on abstractions, not implementations).

Design By ContractDesign By Contract• To help with designing classes that conform to the To help with designing classes that conform to the

Liskov Substitution Principle, Design by Contract (DbC) Liskov Substitution Principle, Design by Contract (DbC) concepts can be employed to help facilitate proper class concepts can be employed to help facilitate proper class inheritance.inheritance. Preconditions define the minimum requirements that must be Preconditions define the minimum requirements that must be

met before a method can execute.met before a method can execute. Postconditions define what conditions the method is obligated to Postconditions define what conditions the method is obligated to

satisfy. satisfy. When applying Design By Contract to subclasses, DbC says…When applying Design By Contract to subclasses, DbC says…

When redefining a routine [in a derivative], you may When redefining a routine [in a derivative], you may only replace its precondition by a weaker one, and its only replace its precondition by a weaker one, and its postcondition by a stronger one.postcondition by a stronger one.

Design By ContractDesign By Contract• When using an object (derived or base) When using an object (derived or base)

through its base class interface…through its base class interface… The client must not require stronger The client must not require stronger

preconditions on a method than the base preconditions on a method than the base class (i.e. there cannot be more preconditions class (i.e. there cannot be more preconditions than that required by the base class).than that required by the base class).

A method of a derived class must satisfy at A method of a derived class must satisfy at least the same post conditions as the base least the same post conditions as the base class (i.e. the derived class cannot support class (i.e. the derived class cannot support less postconditions than the base).less postconditions than the base).

Favor Composition Over Favor Composition Over InheritanceInheritance

• Composition is often the better choice for Composition is often the better choice for extending and adding functionality to a structure.extending and adding functionality to a structure.

• The “Open Closed Principle” can arguably be The “Open Closed Principle” can arguably be more effectively applied through composition more effectively applied through composition than through inheritance.than through inheritance. ““Open Closed Principle” - A class should be open for Open Closed Principle” - A class should be open for

extension, but closed for modification.extension, but closed for modification. Ideally, code should never have to be changed. New Ideally, code should never have to be changed. New

functionality can be added through…functionality can be added through… Inheritance or in many cases…Inheritance or in many cases…

Composition and Delegation!Composition and Delegation!

Favor Composition over Favor Composition over InheritanceInheritance

• Composition is the act of composing an object from other Composition is the act of composing an object from other objects.objects.

• Composition is expressed as a “has-a” or “knows-a” Composition is expressed as a “has-a” or “knows-a” relationship.relationship.

• Types of composition:Types of composition: Association – One object uses another to perform some type of Association – One object uses another to perform some type of

service (e.g. utility class). service (e.g. utility class). Aggregation – Represents a whole/part relation. Aggregation Aggregation – Represents a whole/part relation. Aggregation

implies that a complex object is composed of other objects (e.g. implies that a complex object is composed of other objects (e.g. Account is composed of one or more policies).Account is composed of one or more policies).

• Notice that you could get composability by replacing Notice that you could get composability by replacing inheritance with aggregation.inheritance with aggregation.

AggregationAggregation

JavaJava UMLUML

Aggregation is a form of association representing a whole/part relationship between two classes. Aggregation implies that the whole is at a conceptually higher level than the part, whereas an association implies both classes are at the same conceptual level. An instance variable in the whole holds a reference to the part(s). The difference between association and aggregation is entirely conceptual and is focused strictly on semantics.

In an aggregation relationship, the part class instance (tire) can outlive the whole class (car). The relationship is represented with a hollow diamond attached at the whole and an arrow pointingto the part.

CompositionComposition

JavaJava UMLUML

Composition is a special form of aggregation, which implies the whole is responsible for the lifecycle of the part. Composition is also non-shared. So while the part doesn't necessarily need to be destroyed when the whole is destroyed, the whole is responsible for either keeping alive or destroying the part. The part cannot be shared with other wholes. The whole, however, can transfer ownership to another object, which then assumes lifetime responsibility.

The above trivial example shows that water is composed of hydrogen and oxygen. The composition relationship is represented with a solid diamond attached at the whole and an arrow pointingto the part.

Composition over Inheritance: Composition over Inheritance: The Decorator PatternThe Decorator Pattern

• Starbutts Coffee, a startup business, Starbutts Coffee, a startup business, decides to offer some standard blends decides to offer some standard blends according to the following scheme:according to the following scheme:

Beverage

description

cost()getDescr()

Houseblend DarkRoast Decaf Espresso

cost() cost() cost() cost()

The Price of SuccessThe Price of Success

Managing ComplexityManaging Complexity

• What happens when the price of milk goes What happens when the price of milk goes up? How to update cost-calculationup? How to update cost-calculation

• Try thisTry this Use a Beverage base class and add instance Use a Beverage base class and add instance

vaiables to represent the milk contributionvaiables to represent the milk contribution How does this stand up to change??How does this stand up to change??

We Get ThisWe Get This

descriptionmilksoy mocha whip

Beverage

getDescription()cost()hasMilk()setMilk()hasSoy()...

Houseblend DarkRoast Decaf Espresso

cost() cost() cost() cost()

Meet the Decorator PatternMeet the Decorator Pattern

• The preceding is an open-closed nightmareThe preceding is an open-closed nightmare• Combine the power of inheritance with extending Combine the power of inheritance with extending

the object’s behavior at runtimethe object’s behavior at runtime• Start with a Beverage and extend it at run-timeStart with a Beverage and extend it at run-time

Take a DarkRoast objectTake a DarkRoast object Decorate it with a Mocha objectDecorate it with a Mocha object Decorate that with a Whip objectDecorate that with a Whip object Call cost() and use delegation to sum up the milk costCall cost() and use delegation to sum up the milk cost

We ObtainWe Obtain

DarkRoastMochaWhip

cost()cost()

cost()

RemarksRemarks

• Inheritance: Mocha is_a Beverage, etc.Inheritance: Mocha is_a Beverage, etc.• To calculate cost start with the cost on Whip, To calculate cost start with the cost on Whip,

delegate to Mocha and then delegate to delegate to Mocha and then delegate to DarkRoast. Which returns its cost to Mocha, which DarkRoast. Which returns its cost to Mocha, which add this to its cost and returns that to Whip, which add this to its cost and returns that to Whip, which takes that and calculates the entire costtakes that and calculates the entire cost

• The Decorator adds its own behavior either before The Decorator adds its own behavior either before or after delegating to the object it decorates to or after delegating to the object it decorates to finish up the work.finish up the work.

The Decorator PatternThe Decorator PatternComponent

ConcreteComponent Decorator

ConcreteDecorator A

ConcreteDecorator B

methodA()methodB()

methodA()methodB()

methodA()methodB()

methodA()methodB()

methodA()methodB()

ConcreteWrappedObject

ConcreteWrappedObject

ExplanationExplanation

• Components can be used on their own or Components can be used on their own or decorateddecorated

• Each decorator has_a ComponentEach decorator has_a Component• Decorators implement the same interface as Decorators implement the same interface as

the component they decoratethe component they decorate• Concrete Decorator has an instance Concrete Decorator has an instance

variable for the Component it wrapsvariable for the Component it wraps

Back to StarbuttsBack to Starbutts

Beverage

CondimentDecorator

Milk Mocha Whip

HouseBlend DarkRoast

Decaf Espresso

cost()cost()

cost()cost()

cost() cost() cost()

cost()

getDescr()

getDescr()

getDescr() getDescr() getDescr()

component

Write the Code: ComponentWrite the Code: Component

public abstract class Beverage{ String description = “unknown beverage”; public String getDescription(){ return description;} public abstract double cost();}

Write the Code: Write the Code: CondimentDecoratorCondimentDecorator

public abstract class CondimentDecorator extends Beverage{ public abstract double cost(); //force implementation}

Write the Code: Write the Code: ConcreteComponentConcreteComponent

public class Espresso extends Beverage{ public Espresso(){ description = “Espresso”;} public double cost(){ return 1.99;}}

public class DarkRoast{ ...//etc}

Write the Code: Write the Code: ConcreteDecoratorConcreteDecorator

public class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage){ this.beverage = beverage;} public String getDescription(){ return beverage.getDescription() + “, Mocha”;} public double cost(){ return .20 + beverage.cost();}}etc

Build and TestBuild and Test

public class Starbutts{ public static void main(String args[]){ Beverage beverage1 = new Espresso(); System.out.println(beverage1 .getDescription() + “$” + beverage.cost()); Beverage beverage2 = new DarkRoast(); beverage2 = new Mocha(beverage2); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); System.out.println(beverage2.getDescription() + “$” + beverage2.cost());

Build and Test (Ctd)Build and Test (Ctd)

Beverage beverage3 = new HouseBlend(); beverage3 = new Soy(beverage3); beverage3 = new Mocha(beverage3); beverage3 = new Whip(beverage3); System.out.println(beverage3.getDescription () + “$” + beverage3.cost()); }}

Now Use ThisNow Use This

The Decorator Pattern Combines The Decorator Pattern Combines Composition with InheritanceComposition with Inheritance

Favor Composition over Favor Composition over Inheritance – Decorator PatternInheritance – Decorator Pattern

• The Decorator pattern provides a common alternative to The Decorator pattern provides a common alternative to inheritance. Wrapper classes use the Decorator pattern.inheritance. Wrapper classes use the Decorator pattern.

• Decorator classes mirror the type of components they Decorator classes mirror the type of components they decorate. decorate. They are the same type as the components they decorate either They are the same type as the components they decorate either

through inheritance or implementation.through inheritance or implementation.• The decorator holds an instance of the component it The decorator holds an instance of the component it

decorates. In other words, the decorator is composed of the decorates. In other words, the decorator is composed of the object it decorates.object it decorates. Decorators are prevalent in many core Java packages e.g. Decorators are prevalent in many core Java packages e.g.

Java.io.FilterInputStream (wraps InputStream)Java.io.FilterInputStream (wraps InputStream) HttpServletResponseWrapper (wraps ServletResponse)HttpServletResponseWrapper (wraps ServletResponse)

Favor Composition over Favor Composition over Inheritance – Decorator PatternInheritance – Decorator Pattern

• Decorator classes mirror the type of components Decorator classes mirror the type of components they decorate. they decorate. They are the same type as the components they They are the same type as the components they

decorate either through inheritance or decorate either through inheritance or implementation.implementation.

• Decorators are transparent to the clientDecorators are transparent to the client• The decorator holds an instance of the The decorator holds an instance of the

component it decorates. In other words, the component it decorates. In other words, the decorator is decorator is composedcomposed of the object it of the object it decorates.decorates.

Favor Composition over Favor Composition over Inheritance – Decorator PatternInheritance – Decorator Pattern

Decorator Class DiagramDecorator Class Diagram

Favor Composition over Favor Composition over InheritanceInheritance

• InheritanceInheritance ProsPros

Code sharingCode sharing

ConsCons BrittleBrittle Tight coupling between Tight coupling between

parent/child classesparent/child classes

• CompositionComposition ProsPros

Evaluated at runtime Evaluated at runtime (dynamic binding)(dynamic binding)

LightweightLightweight FlexibleFlexible

ConsCons Increased system Increased system

complexitycomplexity

Composition increases system flexibility. If we use composition, we can extend an object’s behavior dynamically, at runtime, adding new responsibilities to objects that we may not have thought about during design time. Another benefit of this technique is that we do not need to change existing code, and so the chances of introducing new bugs or unwanted effects are reduced.

Questions?Questions?

Object Oriented Design Object Oriented Design PrinciplesPrinciples

• Encapsulate what variesEncapsulate what varies• Favor composition over inheritanceFavor composition over inheritance• Program to interfaces, not implementationsProgram to interfaces, not implementations• Strive for loosely coupled designs between objects that interactStrive for loosely coupled designs between objects that interact• Classes should be open for extension but closed for modificationClasses should be open for extension but closed for modification

The open/closed principle is the single most important guide for an object The open/closed principle is the single most important guide for an object oriented designer. Designing modules whose behavior can be modified oriented designer. Designing modules whose behavior can be modified without making source code modifications to that module is what yields without making source code modifications to that module is what yields the benefits of OOD.the benefits of OOD.

• Abstractions should not depend on details. Details should Abstractions should not depend on details. Details should depend on abstractions (Dependency Inversion Principle)depend on abstractions (Dependency Inversion Principle)

Encapsulate What VariesEncapsulate What Varies• Take what varies and “encapsulate” it so it won’t Take what varies and “encapsulate” it so it won’t

affect the rest of your code. affect the rest of your code. In other words, identify the aspects that vary and In other words, identify the aspects that vary and

separate them from what stays the same.separate them from what stays the same. Results in fewer unintended consequences from code Results in fewer unintended consequences from code

changes.changes. Decouples components that change frequently from those Decouples components that change frequently from those

that are relatively stable.that are relatively stable. Changes become much less limited in scope and aren’t as Changes become much less limited in scope and aren’t as

invasive.invasive. Engenders modularity and flexibility.Engenders modularity and flexibility.

Encapsulate What VariesEncapsulate What VariesData EncapsulationData Encapsulation

• Typical Example – The JavaBeanTypical Example – The JavaBean Define private membersDefine private members Encapsulate access to private members by Encapsulate access to private members by

creating accessors and mutators (getters and creating accessors and mutators (getters and setters) .setters) .

Eclipse provide a refactoring function to easily Eclipse provide a refactoring function to easily encapsulate class fields.encapsulate class fields.

Encapsulate What VariesEncapsulate What VariesProcedural EncapsulationProcedural Encapsulation

• Identify what varies and hide it behind an interface Identify what varies and hide it behind an interface – Implementations of the interface can vary – Implementations of the interface can vary without violating the contract of the interface.without violating the contract of the interface. This is the essence of the OO concept - “This is the essence of the OO concept - “AbstractionAbstraction”. ”.

Abstraction focuses on the outside view of an object Abstraction focuses on the outside view of an object and separates an object’s behavior from its and separates an object’s behavior from its implementation.implementation.

Dependency Inversion Principle - Abstractions should Dependency Inversion Principle - Abstractions should not depend on details. Details should depend on not depend on details. Details should depend on Abstractions.Abstractions.

Encapsulate What VariesEncapsulate What Varies• The “Encapsulate What Varies” principle forms the basis The “Encapsulate What Varies” principle forms the basis

for most design patterns. For example…for most design patterns. For example… Factory - Code that creates objects is generally subject to Factory - Code that creates objects is generally subject to

frequent change. Use the Factory pattern to frequent change. Use the Factory pattern to encapsulateencapsulate object creation.object creation.

State - The State pattern allows an object to vary its behavior State - The State pattern allows an object to vary its behavior when its internal state changes. Each state is generally when its internal state changes. Each state is generally encapsulatedencapsulated in its own class. in its own class.

Strategy - Strategy - EncapsulatesEncapsulates each algorithm in a family of each algorithm in a family of algorithms so that they can vary for each client that uses algorithms so that they can vary for each client that uses them.them.