Decorator Explained. Intent Attach additional responsibilities to an object dynamically. Decorators...

Preview:

Citation preview

Decorator

Explained

Intent

• Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality

• Also Known As – Wrapper

2

Motivation

• Sometimes we want to add responsibilities to individual objects. A graphical user interface toolkit, for example,should let you add properties like borders or behaviors like scrolling to any user interface component

• One way to add responsibilities is with inheritance. Inheriting a border from another class puts a border around every subclass instance.

• This is inflexible, however, because the choice of border is made statically. A client can't control how and when to decorate the component with a border.

3

Motivation• A more flexible approach is to enclose the

component in another object that adds the border

• The enclosing object is called a decorator• The decorator conforms to the interface of the

component it decorates so that its presence is transparent to the component's clients.

• The decorator forwards requests to the component and may perform additional actions (such as drawing a border) before or after forwarding.

4

Motivation

• Transparency lets you nest decorators recursively, thereby allowing an unlimited number of added responsibilities

5

Motivation• Suppose we have a TextView object that displays

text in a window.• TextView has no scroll bars by default, because we

might not always need them• When we do, we can use a ScrollDecorator to add

them• Suppose we also want to add a thick black border

around the TextView. We can use a BorderDecorator to add this as well

• We simply compose the decorators with the TextView to produce the desired results.

6

Motivation

• The following object diagram shows how to compose a TextView object with BorderDecorator and ScrollDecorator objects to produce a bordered, scrollable text view

7

Motivation

• The ScrollDecorator and BorderDecorator classes are subclasses or concrete classes of Decorator

• Decorator is an abstract class for visual components that decorate other visual components

8

Motivation

9

Motivation

• VisualComponent is the abstract class for visual objects. It defines their drawing and event handling interface.

• Decorator class simply forwards draw requests to its component, and Decorator subclasses can extend this operation.

• Decorator subclasses are free to add operations for specific functionality

• For example, ScrollDecorator's ScrollTo operation lets other objects scroll the interface if they know there happens to be a ScrollDecorator object in the interface.

10

Motivation

• The important aspect of this pattern is that it lets decorators appear anywhere a VisualComponent can

• That way clients generally can't tell the difference between a decorated component and an undecorated one, and so they don't depend at all on the decoration

11

Applicability

• Use Decorator– To add responsibilities to individual objects

dynamically and transparently, that is, without affecting other objects.

– For responsibilities that can be withdrawn.– When extension by sub-classing is impractical.

Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for sub-classing

12

Structure

13

Participants

• Component (VisualComponent)– defines the interface for objects that can have

responsibilities added to them dynamically.• ConcreteComponent (TextView)– defines an object to which additional responsibilities can be

attached.• Decorator– maintains a reference to a Component object and defines an

interface that conforms to Component's interface• ConcreteDecorator (BorderDecorator, ScrollDecorator)– adds responsibilities to the component.

14

Collaborations

• Decorator forwards requests to its Component object.

• It may optionally perform additional operations before and after forwarding the request.

15

Consequences• More flexibility than static inheritance

– With decorators, responsibilities can be added and removed at run-time simply by attaching and detaching them. In contrast, inheritance requires creating a new class for each additional responsibility (e.g., BorderedScrollableTextView, BorderedTextView). This gives rise to many classes and increases the complexity of a system.

• Avoids feature-laden classes high up in the hierarchy– Decorator offers a pay-as-you-go approach to adding

responsibilities. Instead of trying to support all foreseeable features in a complex, customizable class, you can define a simple class and add functionality incrementally with Decorator objects. Functionality can be composed from simple pieces. As a result, an application needn't pay for features it doesn't use. It's also easy to define new kinds of Decorators independently from the classes of objects they extend, even for unforeseen extensions

16

Consequences

• A decorator and its component aren't identical– A decorator acts as a transparent enclosure. But from

an object identity point of view, a decorated component is not identical to the component itself

• Lots of little objects– A design that uses Decorator often results in systems

composed of lots of little objects that all look alike. Although these systems are easy to customize by those who understand them, they can be hard to learn and debug

17

Implementation Issues• Interface conformance - A decorator object's

interface must conform to the interface of the component it decorates. ConcreteDecorator classes must therefore inherit from a common class.

• Omitting the abstract Decorator class - There's no need to define an abstract Decorator class when you only need to add one responsibility. In that case, you can merge Decorator's responsibility for forwarding requests to the component into the ConcreteDecorator

18

Starbuzz Coffee

19

Open-Closed Design Principle

•Classes should be open for extension, but closed for modification

•Our goal is to allow classes to be easily extended to incorporate new behavior without modifying existing code.

•We can get designs that are resilient to change and flexible enough to take on new functionality to meet changing requirements

•Let’s apply decorator pattern on starbuzz case.

20

Applying Decorator to Starbuzz

• Okay, we’ve seen that representing our beverage plus condiment pricing scheme with inheritance has not worked out very well – we get class explosions, rigid designs, or we add functionality to the base class that isn’t appropriate for some of the subclasses

• So, here’s what we’ll do instead: we’ll start with a beverage and decorate it with the condiments at runtime. For example, if the customer wants a Dark Roast with Mocha and Whip then,

21

Applying Decorator to Starbuzz

• We will perform the following steps1. Take a DarkRoast object2. Decorate it with a Mocha object3. Decorate it with a Whip object4. Call the cost() method and rely on delegation to

add on the condiment costs

22

Applying Decorator to Starbuzz

23

Remember that DarkRoast inherits from Beverage and hasa cost() method that computes the cost of the drink

So, Mocha has a cost() method too, and through polymorphism we can treat any Beverage wrapped in Mocha as a Beverage, too (because Mocha is a subtype of Beverage)

So, a DarkRoast wrapped in Mocha and Whip is still a Beverage and we can do anything with it we can do with a DarkRoast, including call its cost() method.

Calculating Cost of Starbuzz Coffee

• Now it’s time to compute the cost for the customer. We do this by calling cost() on the outermost decorator, Whip, and Whip is going to delegate computing the cost to the objects it decorates. Once it gets a cost, it will add on the cost of the Whip.

24

Class Structure for Starbuzz

25

Some Important Facts

• So here we’re using inheritance to achieve the type matching , but we aren’t using inheritance to get behavior

• When we compose a decorator with a component, we are adding new behavior. We are acquiring new behavior not by inheriting it from a superclass, but by composing objects together.

26

Code for Starbuzz…Let’s define the abstract class Beverage

public abstract class Beverage{

protected String m_description;

// get a description of the beverage public virtual String Description { get { return m_description; } }

// calculate cost of the beverage public abstract double Cost();

}

27

Concrete Beverage Classes// HouseBlend coffee implements

Beverage

public class HouseBlend : Beverage{ // Constructor public HouseBlend() {

m_description = "House Blend"; }

// calculate base cost of House Blend public override double Cost()

{ return 0.89; }}

// DarkRoast coffee implements Beverage

public class DarkRoast : Beverage{ // Constructor public DarkRoast() {

m_description = "Dark Roast"; }

// calculate base cost of Dark Roast public override double Cost()

{ return 1.00; }}

28

Decorators (Condiments)// abstract base class CondimentDecorator is-a Beveragepublic abstract class CondimentDecorator : Beverage {}

public class Mocha : CondimentDecorator{

private Beverage m_beverage;public Mocha(Beverage beverage) {

this.m_beverage = beverage; } // getter implements abstract class Description public override String Description{ get{return m_beverage.Description + ", Mocha“; } } // get the Cost of the condiment plus the base-cost of the original beverage public override double Cost() { return 0.20 + m_beverage.Cost(); }} 29

Decorators (Condiments)public class Milk : CondimentDecorator{

private Beverage m_beverage;public Milk(Beverage beverage)

{ this.m_beverage = beverage; }// getter implements abstract class public override String Description{Get{return m_beverage.Description + ", Milk"; } }// get the Cost of the condiment plus the

base-//cost of the original beverage public override double Cost() { return 0.10 + m_beverage.Cost(); }}

public class Whip : CondimentDecorator{private Beverage m_beverage;public Whip(Beverage beverage) { this.m_beverage = beverage; } // getter implements abstract class Description public override String Description { get{return m_beverage.Description + ", Whip"; } } // get the Cost of the condiment plus the base-//

cost of the original beverage public override double Cost() { return 0.15 + m_beverage.Cost(); }}

30

Calling Programclass Program{ static void Main(string[] args) { //Ordering an Espresso Beverage beverage = new Espresso(); Console.WriteLine("Your Order is: " + beverage.Description + " & would Cost: " + beverage.Cost()); Console.ReadLine(); //Ordering a Darkroast with Double Mocha and Whip Beverage beveragetwo = new DarkRoast(); beveragetwo = new Mocha(beveragetwo); beveragetwo = new Mocha(beveragetwo); beveragetwo = new Whip(beveragetwo); Console.WriteLine("Your Order is: " + beveragetwo.Description + " & would Cost: " +

beveragetwo.Cost()); Console.ReadLine(); //Ordering a Houseblend with Soye Mocha and Whip Beverage beveragethree = new HouseBlend(); beveragethree = new Soye(beveragethree); beveragethree = new Mocha(beveragethree); beveragethree = new Whip(beveragethree); Console.WriteLine("Your Order is: " + beveragethree.Description + " & would Cost: " +

beveragethree.Cost()); Console.ReadLine(); }} 31

Outputs

32

Quiz # 2

Looking at the Diagram, please answer the two questions. 1. What’s wrong with the design. How can we make this design more

flexible?2. Please make a diagram of the changed design after applying the

appropriate design principle?33

PizzaStore

NyStyleCheezePizza

NyStyleCheezePizza

NyStyleCheezePizza

NyStyleCheesePizza

ChicagoCheezePizza

ChicagoCheezePizza

ChicagoCheezePizza

ChicagoCheesePizza

Recommended