装饰者模式
在引入装饰者模式之前思考一个问题,父类进行添加新特性继承形成子类,但如果一直继承,像a->b->c......没完没了,只要继承链路中有个bug,或者基类添加新特性,那么就要维护整条链路代码。除此之外,还有类数量会变得很庞大。通过装饰者模式,可以避免继承滥用,不用修改底层代码,给对象添加新特性。
装饰者模式
含义:动态将责任附加到对象上,要扩展功能,装饰着提供比继承更加弹性的替代方案。属于结构型模式,它是作为现有的类的一个包装。
设定一个场景:
张师傅在地铁站旁开了一个煎饼铺子,社畜们早上都排队买不同种类的煎饼果子,但买的人实在太多了,需要建立价格系统,因为一张张摊煎饼在计算配料价格太麻烦了,张师傅打算设计自动报价机器人,顾客输入煎饼种类和配料生成配料单和价格。
如果我们单用继承,建立一个基本煎饼类有描述字段和cost()方法,那么一个煎饼通过继承可以分成,油条煎饼果子,油条煎饼果子加鸡蛋,油条煎饼果子加鸡蛋加鸡柳,油条煎饼果子加鸡蛋加鸡柳加鸡排,油条煎饼果子加鸡蛋加鸡柳加鸡排加黄瓜丝。。。。。这会写了好多子类煎饼,要计算好多模板,这时小王小青小李来了,他们不要油条煎饼果子,他们要薄脆煎饼果子为底加料,啊,那机器人又要倒回去继续计算一堆以薄脆煎饼为底类的子类煎饼果子的价格,形成好长的链路,那么,张师傅如果想在油条煎饼果子的油条换成自己炸的油条,或者相加蛋的价格,机器人就很难维护了,这就违反了少用继承和不针对实现编程双原则。
我们的目的,要用组合的思想代替继承,而不是通过修改。
为此,我们引入装饰者模式。
- 装饰者和被装饰者拥有相同超类
- 用多个装饰者包装一个对象
- 装饰者可以在所委托被装饰着的行为之前或之后加上自己的特性
- 被装饰者可以在任何时候被装饰
- 将具体功能职责划分,同时继承装饰者模式。
- Component 类充当抽象角色,不应该具体实现。
- 修饰类引用和继承 Component 类,具体扩展类重写父类方法。
- 运用继承达到类型匹配,而新行为是通过组合对象达成,行为来自装饰者和其他装饰者之间的组合关系。

在本案例中,顾客需要一个油条煎饼果子加鸡蛋加鸡柳,装饰者模式机器人是这么做的:
- 煎一个油条煎饼
- 用鸡蛋(egg)装饰者装饰煎饼
- 用鸡柳(ChickenFillet)装饰者装饰包装鸡蛋油条煎饼果子,注意 还是组合关系,虽然是包装 但是还是组合非继承。
- 调用cost(),依赖委托,将材料价格层级加上并返回。
抽象组件:Pancake,具体组件为FiredBreadStickPancake,CrispPancake,我们定义CondimentDecorator为抽象装饰者,接下来就是实例化CondimentDecorator。

Egg加蛋,鸡柳ChickenFillet,Sausage火腿肠是具体装饰者,装饰煎饼,装饰者实现类扩展煎饼,我们用实例变量记录煎饼(被装饰者),我们通过将煎饼当具体装饰者的构造器参数记录到实例变量。
为了将煎饼和加料完整描述出来,用委托的做法将描述得到在进行附加描述。价钱同理。
代码如下:
抽象组件类,装饰者继承他
//原始煎饼果子 public abstract class Pancake { public String description = "unknnown Pancake"; public String getDescription(){ return description; } public abstract double cost(); }
具体组件类:
//薄脆煎饼果子 public class CrispPancake extends Pancake { public CrispPancake() { description = "薄脆煎饼果子"; } @Override public double cost() { return 1; } } //油条煎饼果子 public class FiredBreadStickPancake extends Pancake { public FiredBreadStickPancake() { description = "油条煎饼果子"; } @Override public double cost() { return 1; } }
抽象装饰者:
//调料 public abstract class CondimentDecorator extends Pancake { @Override //父类方法抽象化 public abstract String getDescription(); }
装饰者类:
public class ChickenFillet extends CondimentDecorator { Pancake pancake; public ChickenFillet(Pancake pancake){ this.pancake = pancake; } @Override public double cost() { return 3 + pancake.cost(); } @Override public String getDescription() { return pancake.getDescription() + ", 加鸡柳"; } } public class Egg extends CondimentDecorator { Pancake pancake; public Egg(Pancake beverage){ this.pancake = beverage; } @Override public double cost() { return 1.5 + pancake.cost(); } @Override public String getDescription() { return pancake.getDescription() + ", 加蛋"; } } public class Sausage extends CondimentDecorator { Pancake pancake; public Sausage(Pancake pancake){ this.pancake = pancake; } @Override public double cost() { return 2.5 + pancake.cost(); } @Override public String getDescription() { return pancake.getDescription() + ", 加火腿肠"; } }
测试类:
public class PancakeStore { public static void main(String[] args) { //装饰者模式测试 System.out.println("煎饼果子铺开张"); System.out.println("------------油条煎饼----------------"); Pancake firedStickPancake = new FiredBreadStickPancake(); System.out.println(firedStickPancake.getDescription() + " ¥"+firedStickPancake.cost()); System.out.println("------------薄脆煎饼----------------"); Pancake crispPancke = new CrispPancake(); System.out.println(crispPancke.getDescription() + " ¥"+crispPancke.cost()); System.out.println("------------油条煎饼加双蛋----------------"); Pancake doubleEggPancke = new FiredBreadStickPancake(); doubleEggPancke = new Egg(doubleEggPancke); doubleEggPancke = new Egg(doubleEggPancke); System.out.println(doubleEggPancke.getDescription() + " ¥"+doubleEggPancke.cost()); System.out.println("------------薄脆煎饼加蛋加鸡柳-------------"); Pancake eggSausagePancake = new CrispPancake(); eggSausagePancake = new Egg(eggSausagePancake); eggSausagePancake = new ChickenFillet(eggSausagePancake); System.out.println(eggSausagePancake.getDescription() + " ¥"+eggSausagePancake.cost()); System.out.println("------------油条煎饼加双蛋加鸡柳加烤肠-------------"); Pancake eggChickenSausagePancake = new FiredBreadStickPancake(); eggChickenSausagePancake = new Egg(eggChickenSausagePancake); eggChickenSausagePancake = new Egg(eggChickenSausagePancake); eggChickenSausagePancake = new ChickenFillet(eggChickenSausagePancake); eggChickenSausagePancake = new Sausage(eggChickenSausagePancake); System.out.println(eggChickenSausagePancake.getDescription() + " ¥"+eggChickenSausagePancake.cost()); } }
结果:

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。

浙公网安备 33010602011771号