For details, see chapter 3 of HeadFirst Design Pattern decoration objects
The modifier mode dynamically attaches the responsibility to the object. To expand the function, the modifier provides an alternative solution that is more flexible than inheritance.
Take a coffee shop as an example.
(1) The decorator and the decorator object have the same super type.
(2) one or more decorators can wrap an object.
(3) Since the decorator and the object to be decorated have the same super-type, you can replace it with the decorated object in any case where you need the original object (wrapped.
(4) The decorator Can add his/her own behavior before and after the act of the entrusted decorator to achieve a specific purpose.
(5) objects can be decorated at any time, so you can use your favorite decoration objects dynamically and without limitation during running.
0. Design Principles
Class should be open to extensions and closed to modifications.
1. Abstract coffee base class.
// Beverage is an abstract class with two methods: getDescription () and cost (). Public abstract class Beverage {String description = "Unknown Beverage"; // getDescription () has been implemented here, but cost () must be implemented in the subclass. Public String getDescription () {return description;} public abstract double cost ();}
2. Four types of coffee inherit from the Berverage base class.
// First, let Espresso be extended from the Beverage class, because Espresso is a kind of Beverage. Public class Espresso extends Beverage {// to set the Beverage description, we wrote a constructor. Remember, the description instance variable inherits from the Beverage class. Public Espresso () {description = "Espresso" ;}// Finally, the price of Espresso needs to be calculated. Now, the price of Espresso can be returned directly at $1.99 without worrying about the seasoning price. Public double cost () {return 1.99 ;}}
public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } public double cost() { return .89; }}
public class DarkRoast extends Beverage { public DarkRoast() { description = "Dark Roast Coffee"; } public double cost() { return .99; }}
public class Decaf extends Beverage { public Decaf() { description = "Decaf Coffee"; } public double cost() { return 1.05; }}
3. the abstract base class of the seasoning, inherited from the Beverage base class.
// First, the CondimentDecorator must replace the Beverage. Therefore, the CondimentDecorator must be extended from the Beverage class. Public abstract class CondimentDecorator extends Beverage {// all spices and decorative classes must implement the getDescription () method again. Public abstract String getDescription ();}
In section 4, the modifier class inherits from the CondimentDecorator base class. Note that apart from cost (), they must also implement getDescription ().
// Moka is a modifier, so it extends the purple CondimentDecorator. Public class Mocha extends CondimentDecorator {// do not forget that CondimentDecorator is extended from Beverage. // To enable Mocha to reference a Beverage, follow these steps: // (1) use an instance variable to record the Beverage, that is, the decorator. // (2) Try to record the decorator (beverage) in the instance variable. The practice here is to take the drink as a parameter of the constructor and then record the drink in the instance variable. Beverage beverage; public Mocha (Beverage beverage) {this. beverage = beverage;} public String getDescription () {// we want to describe not just about drinks (for example, "DarkRoast"), but completely about spices (for example, "DarkRoast, mocha "). // First use the delegate method to get a description, and then add an additional description (such as "Mocha") to it "). Return beverage. getDescription () + ", Mocha";} public double cost () {// calculate the price of a Mocha beverage. First, delegate the call to the decorated object to calculate the price, and then add the Mocha price to get the final result. Return. 20 + beverage. cost ();}}
public class Milk extends CondimentDecorator { Beverage beverage; public Milk(Beverage beverage) { this.beverage = beverage; } public String getDescription() { return beverage.getDescription() + ", Milk"; } public double cost() { return .10 + beverage.cost(); }}
public class Soy extends CondimentDecorator { Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; } public String getDescription() { return beverage.getDescription() + ", Soy"; } public double cost() { return .15 + beverage.cost(); }}
public class Whip extends CondimentDecorator { Beverage beverage; public Whip(Beverage beverage) { this.beverage = beverage; } public String getDescription() { return beverage.getDescription() + ", Whip"; } public double cost() { return .10 + beverage.cost(); }}
5. The relational architecture of all classes.
6. Test the code.
Public class StarbuzzCoffee {public static void main (String args []) {// order a cup of Espresso. Print out its description and price without dressing. Beverage beverage = new Espresso (); System. out. println (beverage. getDescription () + "$" + beverage. cost (); // create a DarkRoast object. Beverage beverage2 = new DarkRoast (); // use Mocha to decorate it. Beverage2 = new Mocha (beverage2); // use the second Mocha to decorate it. Beverage2 = new Mocha (beverage2); // use Whip to decorate it. Beverage2 = new Whip (beverage2); System. out. println (beverage2.getDescription () + "$" + beverage2.cost (); // finally, a cup of HouseBlend coffee with soy milk, Moka, and milk. Beverage beverage3 = new HouseBlend (); beverage3 = new Soy (beverage3); beverage3 = new Mocha (beverage3); beverage3 = new Whip (beverage3); System. out. println (beverage3.getDescription () + "$" + beverage3.cost ());}}
The printed result.
Espresso $1.99Dark Roast Coffee, Mocha, Mocha, Whip $1.49House Blend Coffee, Soy, Mocha, Whip $1.34
7. the java io package adopts the decorator mode.
The custom FilterInputStream subclass.
public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream in) { super(in); } public int read() throws IOException { 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; }}Test code.
public class InputTest { public static void main(String[] args) throws IOException { int c; try { InputStream in = 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(); } }}