允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
Component(抽象构件):给出一个抽象接口,装饰器模式中公共方法的类,在装饰器模式结构图的顶层,以规范准备接收附加责任的对象。
ConcreteComponent(具体构件):是要动态扩展的对象,转换器模式中具体的被装饰的类,它继承自Component。
Decorator(装饰器):持有一个构件(Component)对象的实例,它是装饰器模式中的核心对象,所有具体装饰器对象的父类,完成装饰器的部分职能。可以只对被装饰的对象进行一些简单的包裹,也可包含对Component中方法的实现。
ConcreteDecorator(具体装饰):完成具体的装饰功能。装饰功能的实现是通过调用被装饰对象对应的方法,加上装饰对象自身的方法。这是装饰器模式动机中的添加额外功能的关键。
优点
缺点
共同点:都拥有一个目标对象。装饰器通过包装一个装饰对象来扩展其功能,而又不改变其接口,这实际上是基于对象的适配器模式的一种变种。
不同点:适配器模式需要实现另外一个接口,而装饰器模式必须实现该对象的接口。适配器模式主要是为了接口的转换,而装饰者模式关注的是通过组合来动态的为被装饰者注入新的功能或行为(即所谓的责任)。
假设我去买咖啡,首先服务员给我冲了一杯原味咖啡,我希望服务员给我加些牛奶和白糖混合入原味咖啡中。使用装饰器模式就可以解决这个问题。
定义了获取花费和配料的接口。
/** * 咖啡接口 */ public interface Coffee { /** * 获取价格 * @return */ public float getPrice(); /** * 获取咖啡 * @return */ public String getCoffee(); }
实现Coffe接口,花费1元,配料中,只有咖啡
/** * 原味咖啡类 */ public class OriginalCoffee implements Coffee { @Override public float getPrice() { return 1; } @Override public String getCoffee() { return "原味咖啡"; } }
咖啡对象的装饰器类,同样实现Coffee接口,定义一个Coffe对象的引用,在构造器中进行初始化。并且将getPrice()和getCoffee()方法转发给被装饰对象。
/** * 咖啡的"装饰器",可以给咖啡添加各种"配料" * 该类是一个抽象类需要具体子类来实现 */ public class DecoratorAbstractCoffee implements Coffee { /** * 具体咖啡的接口 */ protected final Coffee coffee; /** * 构造方法,初始化咖啡对象的引用 * @param coffee */ public DecoratorAbstractCoffee(Coffee coffee) { this.coffee = coffee; } /** * 获取价格,装饰器父类中直接转发"请求"至引用对象 * @return */ @Override public float getPrice() { return coffee.getPrice(); } /** * 获取咖啡,装饰器父类中直接转发"请求"至引用对象 * @return */ @Override public String getCoffee() { return coffee.getCoffee(); } }
具体的装饰器类,负责往咖啡中“添加”牛奶,注意看getPrice()方法和getCoffee()方法,可以在转发请求之前或者之后,增加功能。如果是代理模式,这里的结构就有所不同,通常代理模式根据运行时的条件来判断是否转发请求。
/** * 混合牛奶到蜂蜜中 */ public class CreamCoffee extends DecoratorAbstractCoffee { private float price = (float) 0.5; /** * 调用父类的构造方法 * @param coffee */ public CreamCoffee(Coffee coffee) { super(coffee); } /** * 增加配料需要加钱 * @return */ @Override public float getPrice() { return coffee.getPrice()+price; } /** * 对咖啡进行加工 * @return */ @Override public String getCoffee() { return coffee.getCoffee()+";添加牛奶"; } }
另一个具体装饰器类,用来给咖啡加蜂蜜,一样的逻辑。
class HoneyCoffee extends DecoratorAbstractCoffee { private float price = (float) 1.4; public HoneyCoffee(Coffee coffee) { super(coffee); } @Override public float getPrice() { return coffee.getPrice()+price; } @Override public String getCoffee() { return coffee.getCoffee()+";添加蜂蜜"; } }
public class DecoratorMain { public static void main(String[] args) { //是不是很像 javaIO中的 stream流 Coffee coffee = new CreamCoffee(new HoneyCoffee(new OriginalCoffee())); System.out.println(coffee.getCoffee()); System.out.println(coffee.getPrice()); } }
装饰器模式是代替增加子类的一种解决方案,体现了聚合/合成复用原则的思想,尽量使用组合的方式来扩展功能,这样就把基本功能和扩展功能解耦了,使得代码可复用,可维护,灵活。关键点在于装饰器模式可以动态地为对象增加扩展功能。