package decorator; /** * @author jiq * 类型:Structural * 定义:动态地将责任附加到对象上 * 若要拓展功能,装饰器提供了比继承更加有弹性的替代方案。 * OO原则: 类应该 对修改关闭,对拓展开放!!! * 1 作用: 对拓展开放,对修改关闭。 避免继承滥用,学会使用组合,在运行时装饰类。 2 为什么要这么做呢? 可以在不修改任何底层代码的情况下(<免于改变>),给你的(或者别人的)对象赋予新的职责(<易于拓展>)。 这就是著名的“开放-关闭”原则: 类应当对拓展开放,对修改关闭。 3 实际用法: 可见除了继承,装饰者模式也可以让我们拓展行为。 装饰者模式意味着一群装饰者类,这些类用来包装具体的组件。 你可以用无数装饰者来包装(组合)一个组件,而不需要更改任何原有的组件。 4 已有案例: Java IO流中的各种包装类就是典型的装饰者。 * ******************************************************* * 类之间的关系: * 组件抽象类:Beverage * 装饰器抽象基类: CondimentDecorator 也继承自该抽象组件(为了迭代装饰) * * 实际的Beverage(家用咖啡,特浓咖啡等)继承自该抽象组件 Beverage * 各种装饰器(调料,大小等)继承自装饰器抽象基类CondimentDecorator * * 用各种装饰器装饰实际的Beverage * * 如果只是用继承,每一种饮料都继承自Beverage,各种调料以及饮料的组合都需要一个类,这简直就是类爆炸!!! */ /** * our company make beverage (组件基类) */ abstract class Beverage{ protected String description = "unKown Beverage"; public String getDescription(){ return description; } /** calculate price of this beverage */ public abstract double cost(); } /** * Decorator(调料 - 装饰器基类) * 问题: 为什么装饰者还要从Beverage继承呢? * 为了能够使得具体的装饰器能够被其他装饰器装饰(拓展)。 * 而装饰器只能装饰(拓展)Beverage。 * */ abstract class CondimentDecorator extends Beverage{ public abstract String getDescription(); } /////////////////////////////////////////////////////////// //特浓咖啡(组件) class Espresso extends Beverage{ public Espresso(){description = "Espresso";} public double cost() {return 0.9;} } //家常咖啡(组件) class HouseBlend extends Beverage{ public HouseBlend(){description = "HouseBlend"; } public double cost() {return 1.0;} } /////////////////////////////////////////////////////////////// //用Mocha(装饰器)来装饰一杯beverage class Mocha extends CondimentDecorator{ Beverage beverage; //wrap a beverage(组合) public Mocha(Beverage beverage){this.beverage = beverage;} public String getDescription() { return beverage.getDescription() + ", add Mocha"; } public double cost() { return 0.20 + beverage.cost(); } } //用豆浆(装饰器)来装饰一杯beverage class Soy extends CondimentDecorator{ Beverage beverage; //wrap a beverage(组合) public Soy(Beverage beverage){this.beverage = beverage;} public String getDescription() { return beverage.getDescription() + ", add Soy"; } public double cost() { return 0.30 + beverage.cost(); } } public class Starbuzz { public static void main(String[] args) { /** * 制造一杯Espresso咖啡,什么调料也不放 * */ Beverage b1 = new Espresso(); System.out.println(b1.getDescription() + " $" + b1.cost()); /** * 制造一杯HoseBlend咖啡,放入豆浆和摩卡 * */ Beverage b2 = new HouseBlend(); Beverage newBeverage = new Mocha(new Soy(b2)); System.out.println(newBeverage.getDescription() + " $" + newBeverage.cost()); /////////////////////////////////////////////////////////////// /** * 思考:如果生成的饮料决定从今天开始加上容量大小。 * 供顾客挑选大杯,中杯以及小杯,然后根据饮料容量 * 来收费,我们应该怎么改变装饰者来应对这样的需求??? * 很显然,我们不能更改现有组件,因为我们的原则就是对修改关闭。 * 那怎么拓展呢??? 答案是增加一种装饰器。 * 装饰大小的装饰器,可以设置大小,更新价格。 * */ /** * 制造一杯HoseBlend咖啡,放入豆浆,设置为中杯 * */ Beverage b3 = new HouseBlend(); Beverage b4 = new SizeDecorator(new Soy(b3), 1); //设置大小 System.out.println(b4.getDescription() + " $" + b4.cost()); //是不是很爽啊?对修改关闭,对拓展任意开放 } } /** 拓展一个设置饮料大小,并且根据大小计算价格的装饰器 */ class SizeDecorator extends CondimentDecorator{ Beverage beverage; //wrap a beverage int size; //拓展的属性 public SizeDecorator(Beverage beverage, int size){ this.beverage = beverage; this.size = size; } //拓展的方法 public void setSize(int size){ this.size = size; } //拓展的方法 public int getSize(){ return size; } public String getDescription() { return beverage.getDescription() + ", set size:" + size; } public double cost() { return beverage.cost() * (0.5 + size/10); //根据大小计算价格 } }