Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

15 decorator pattern

1,062 views

Published on

  • Be the first to comment

15 decorator pattern

  1. 1. Decorator PatternApplied to I/O stream classes
  2. 2. Design Principle• Classes should be open for extension, but closed for modification – Apply the principle to the areas that are most likely to change
  3. 3. Decorator Pattern• Attaches additional responsibilities to an object dynamically. Decorator provides a flexible alternative to subclassing for extending functionality.• Example: StarBuzz Coffee – Several blends • HouseBlend, DarkRoast, Decaf, Espresso – Condiments • Steamed milk, soy, mocha, whipped milk – Extra charge for each – How do we charge all combinations? • First attempt: inheritance
  4. 4. See Decorator0
  5. 5. <<abstract>> The description instance variable is set in each Beverage subclass and holds a description of the description beverage, like “Java Dark Roast” getDescription() Cost() method is cost() getDescription() method abstract. Subclasses returns the description need to implement their own implementationHouseBlend DarkRoast Decaf Espressocost() cost() cost() cost() Each subclass implements cost() to return the cost of the beverage
  6. 6. Problem with first attempt:• How do we add the condiments? – Apply subclass and inheritance • Add subclasses and inheritance for condiments • But there can be combinations of condiments• Class explosion when all combinations are considered – There must be a better way – Can we treat condiments as instance variables and apply inheritance?
  7. 7. <<abstract>> Beverage description: String milk: boolean New boolean values for each condiment soy: boolean mocha: boolean whip: boolean getDescription() We implement cost() inBeverage’s cost() Beverage (instead of cost()calculates the cost of keeping it abstract), so that itcondiments. can calculate the costs associated with theThe subclasses hasMilk() condiments for a particularcalculate the cost of setMilk() beverage instance.the beverage and add Subclasses will still overridethe cost of condiments hasSoy() cost(), but they also invoke setSoy() the super version to calculate the total cost() of hasMocha() the basic beverage plus the setMocha() costs of the added condiments. hasWhip() setWhip()
  8. 8. Decorator1• Run example Decorator1
  9. 9. Decorator1: Any problem?• Our goal is to simplify maintenance. – Prices can change – code change – New condiments – add new methods and alter cost method in superclass – New beverages e.g. tea – Double mocha?• Code changes in the superclass when the above happens or in using combinations
  10. 10. Design Principle• Classes should be open for extension, but closed for modification – Apply the principle to the areas that are most likely to change• We want our designs that are resilient to change and flexible enough to take on new functionality to meet changing requirements
  11. 11. How?• We want techniques to allow code to be extended without direct modification
  12. 12. Start with a beverage and decorate it with condiments at run time• Example: – Take a DarkRoast object – Decorate it with a Mocha object – Decorate it with a Whip object – Call the cost() method and rely on delegation to add the condiment costs
  13. 13. cost() DarkRoastcost() cost() DarkRoast Mocha cost() cost() cost() 0.10 .20 .99 DarkRoast 1.29 Mocha Whip
  14. 14. Decorator Pattern• The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality• See class diagram on the next slide
  15. 15. Each component can be used on Component its own, or wrapped by a decorator methodA() component methodB() Each decorator HAS_A (wraps) a component i.e. the decorator has an instance variable that holds a ConcreteComponent Decorator reference to a methodA() component methodA() methodB() methodB()The ConcreteDecorator ConcreteDecoratorA ConcreteDecoratorBhas an instance variable Component wrappedObj Component wrappedObjfor the thing it decorates(the component the methodA() Object newStateDecorator wraps) methodB() methodA()Decorator can add new newBehavior() methodB()methods Can extend state of the component newBehavior()
  16. 16. Beverage acts asabstract component componentclass 4 concrete components, 1 per coffee type The condiment decorators. They need to implement not only cost() but also getDescription()
  17. 17. Discussion• It appears that the condimentDecorator is a subclass of Beverage – Really we are subclassing to get the correct type, not to inherit its behavior – When we compose a decorator with a component, we are adding new behavior – Inheritance: the behavior is determined statically at compile time – Composition: we can mix and match decorators any way we like at runtime.
  18. 18. More: Inheritance vs compositionInheritance Composition• Need to change • Can implement new existing code any decorators at any time we want new time to add new behavior behavior
  19. 19. One more question:• We started with Beverage as an abstract class in Java. Can it be an interface in Java?• Discussion: yes or no? Why?
  20. 20. Code Study:Decorator project - Beveragepublic abstract class Beverage { String description = "Unknown Beverage"; public String getDescription() { // already implemented return description; } public abstract double cost(); // Need to implement cost()}
  21. 21. Condiments (Decorator) class We need to be interchangeable with a Beverage, so extend the Beverage class – not to get its behavior but its typepublic abstract class CondimentDecorator extends Beverage { public abstract String getDescription();} Here we require all the condiment decorators reimplement the getDescription() method. Explanations coming
  22. 22. Coding beveragespublic class HouseBlend extends Beverage { Note the description public HouseBlend() { variable is inherited description = "House Blend Coffee"; from Beverage. To } take care of description, put this public double cost() { in the constructor for the class return .89; }} Compute the cost of a HouseBlend. Need not worry about condiments
  23. 23. Coding condiments Mocha is decorator, so extend CondimentDecorator, which extends Beveragepublic class Mocha extends CondimentDecorator { Instantiate Mocha with Beverage beverage; a reference to a Beverage using public Mocha(Beverage beverage) { 1. An instance variable this.beverage = beverage; to hold the } beverage we are wrapping public String getDescription() { 2. A way to set this return beverage.getDescription() + ", Mocha"; instance to the } object we are We want the description to wrapping – we pass include the beverage – the beverage we public double cost() { say Houseblend – and the are wrapping to the return .20 + beverage.cost(); condiments } decorator’s Cost of condiment + cost of constructor} beverage
  24. 24. public class StarbuzzCoffee { public static void main(String args[]) { Beverage beverage = new Espresso(); // espresso order, no condiments System.out.println(beverage.getDescription() + " $" + beverage.cost()); Beverage beverage2 = new DarkRoast(); // get a DarkRoast beverage2 = new Mocha(beverage2); // wrap it with Mocha beverage2 = new Mocha(beverage2); // warp it with Mocha beverage2 = new Whip(beverage2); // Wrap it with a Whip System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); Beverage beverage3 = new HouseBlend(); // get a Houseblend beverage3 = new Soy(beverage3); // wrap with Soy beverage3 = new Mocha(beverage3); // wrap with Mocha beverage3 = new Whip(beverage3); // wrap with Whip System.out.println(beverage3.getDescription() + " $" + beverage3.cost()); }}
  25. 25. Executing StarBuzzCoffee
  26. 26. Summary1. CondimentDecorator extends Beverage class – Purpose: the decorators have the same type as the objects they are decorating – Inheritance is used to achieve type matching – Inheritance is NOT used to get behavior1. Where does the behavior come in? – We acquire behavior not by inheriting from a superclass, but by composing objects together
  27. 27. Summary3. We subclass the abstract class Beverage to have the correct type not to inherit its behavior4. The behavior comes in through the composition of decorators with the base components as well as other decorators5. Because we are using object composition, we get a more flexibility about how to mix and match condiments and beverages.6. Inheritance determines the behavior at compile time. With composition, we can mix and match decorators any way at will at runtime.
  28. 28. Summary7. We started with Beverage as an abstract class in Java. Could it be an interface in Java?Since all we need to inherit is the type of component, we could use an interface in Java.
  29. 29. DecoratorDecorating the java.io classes
  30. 30. Abstract InputStream component FilterInputStream is anFileInputStream ‘abstract’ decorator StringBufferInputStream FilterInputStream ByteArrayInputStream These InputStreams act as concrete PushBackInputStream components, to be wrapped with decorators Concrete DataInputStream decorators LineNumberInputStream BufferedInputStream Ablity to count line •Buffers input for numbers as it reads data performance •Methods readLine(), mark(), reset()
  31. 31. Writing your own Java I/O decorator• We have learned the decorator pattern• And I/O class diagram• Write a decorator that converts all uppercase characters to lowercase characters in the input stream
  32. 32. Extend the FilterInputStream, the abstract decorator for allimport java.io.*; inputStreampublic class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream in) { super(in); Implement 2 read() methods, taking a } byte (or an array of bytes) and convert each byte to lowercase if it is an public int read() throws IOException { uppercase character int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char)c)); } public int read(byte[] b, int offset, int len) throws IOException { int result = super.read(b, offset, len); for (int i = offset; i < offset+result; i++) { b[i] = (byte)Character.toLowerCase((char)b[i]); } return result; }}
  33. 33. import java.io.*;public class InputTest { public static void main(String[] args) throws IOException { int c; Set up fileInputStream and decorate it, first with try { BufferedInputSteam and then InputStream in = LowercaseInputStream filter. new LowerCaseInputStream( new BufferedInputStream( new FileInputStream("test.txt"))); while((c = in.read()) >= 0) { System.out.print((char)c); } in.close(); } catch (IOException e) { e.printStackTrace(); } }}
  34. 34. Executing project IO• Test.txt contains “I now know the DECORATOR PATTERN.”

×