设计模式 - 装饰者模式
装饰者模式:在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者模式中的角色
抽象组件:是一个接口或抽象类,就是定义最核心的对象,也是最原始的对象,这个类是需要装饰类的基类。
具体组件:是被装饰者的一个实现类,要装饰的就是这个具体的实现类。
抽象装饰者:一般是一个抽象类,实现接口或者抽象方法,它里面有个指向被装饰者的引用,而且它还继承了抽象被装饰者。
具体装饰者:具体的装饰者类。
类图
应用场景
假设有一家甜点店,店里甜点的价格是根据原材料的价格计算的。例如:蓝莓口味的面包是由面包的基本价格加上配料蓝莓的价格;蓝莓草莓混合口味的是由蛋糕的基本价格加上配料蓝莓和草莓的价格。
代码实现
抽象组件:
package com.huey.pattern.decorator; /** * 甜点类,被装饰者的抽象 */ public abstract class Dessert { protected String description = "Unknown Dessert"; /** * 对甜点的描述,需要被装饰的方法 * @return */ public String getDescription() { return description; } public Dessert() { } /** * 甜点的价格,需要被装饰的方法 * @return */ public abstract double cost(); public void display() { System.out.println(this.getDescription() + " ¥" + this.cost()); } }
具体组件 A:
package com.huey.pattern.decorator; /** * 面包类,被装饰者的具体实现 */ public class Bread extends Dessert { public Bread() { description = "Bread"; } @Override public double cost() { return 5.0; } }
具体组件 B:
package com.huey.pattern.decorator; /** * 蛋糕类,被装饰者的具体实现 */ public class Cake extends Dessert { public Cake() { description = "Cake"; } @Override public double cost() { return 9.90; } }
抽象装饰者:
package com.huey.pattern.decorator; /** * 佐料类,装饰者的抽象 */ public abstract class CondimentDecorator extends Dessert { /** * 被装饰者的引用 */ protected Dessert dessert; public CondimentDecorator(Dessert dessert) { this.dessert = dessert; } public abstract String getDescription(); public abstract double cost(); }
具体装饰者 A:
package com.huey.pattern.decorator; /** * 蓝莓类,装饰者的具体实现 */ public class Blueberry extends CondimentDecorator { public Blueberry(Dessert dessert) { super(dessert); } /** * 通过被装饰者的引用,扩展其方法的功能 */ @Override public double cost() { return dessert.cost() + 1.5; } /** * 通过被装饰者的引用,扩展其方法的功能 */ @Override public String getDescription() { return dessert.getDescription() + ", Blueberry"; } }
具体装饰者 B:
package com.huey.pattern.decorator; /** * 巧克力类,装饰者的具体实现 */ public class Chocolate extends CondimentDecorator { public Chocolate(Dessert dessert) { super(dessert); } /** * 通过被装饰者的引用,扩展其方法的功能 */ @Override public double cost() { return dessert.cost() + 1.0; } /** * 通过被装饰者的引用,扩展其方法的功能 */ @Override public String getDescription() { return dessert.getDescription() + ", Chocolate"; } }
单元测试:
package com.huey.pattern.decorator; public class DecoratorPatternTest { public static void main(String[] args) { /** * 未被装饰的蛋糕 */ Dessert cake = new Cake(); cake.display(); /** * 被巧克力装饰的蛋糕 */ Dessert chocolateCake = new Cake(); chocolateCake = new Chocolate(chocolateCake); chocolateCake.display(); /** * 被巧克力和蓝莓装饰的面包 * 被装饰者可以被多个装饰者装饰 */ Dessert blueberryChocolateBread = new Bread(); blueberryChocolateBread = new Chocolate(blueberryChocolateBread); blueberryChocolateBread = new Blueberry(blueberryChocolateBread); blueberryChocolateBread.display(); } }
结果输出:
Cake ¥9.9 Cake, Chocolate ¥10.9 Bread, Chocolate, Blueberry ¥7.5
装饰者模式的适用场合
1) 当需要为某个现有的对象动态地增加一个新的功能或职责时,可以考虑使用装饰者模式;
2) 当某个对象的职责经常发生变化或者经常需要动态地增加职责,避免为了适应这样的变化而增加继承子类扩展的方式。
Java SDK 中的装饰器模式
在 JDK 中,java.io 部分运用了大量的装饰者模式。下图是 java.io.InputStream 的装饰者模式类图。除了 java.io.OutputStream、java.io.Reader、java.io.Writer 等也大量运用了装饰者模式。