java设计模式--装饰器模式
装饰器模式
装饰器模式属于设计模式中的结构型模式,又名包装(Wrapper)模式,装饰器模式以对客户端透明的方式来扩展对象的功能,是通过继承的方式扩展功能的替代方案。
装饰器模式的适用性
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
-
装饰器模式可以处理那些可以撤销的职责。
-
当不能采用生成子类的方法进行扩充时,一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
装饰器模式的结构图

由上图所示,装饰器模式总共包含了四类角色:
-
抽象构件角色(Component):定义一个对象接口,可以给这些对象动态地添加职责。
-
具体构件角色(ConcreteComponent):定义一个对象,可以给这个对象添加一些职责。
-
装饰角色(Decorator):维持一个纸箱Component对象的指针,并定义一个与Component接口一直的接口。
-
具体装饰角色(ConcreteDecorator):向组件添加职责。
装饰器模式的例子
现在有一个场景:
1、有一批厨师,分别为中国人和英国人厨师,他们共同的特点就是做晚饭。
2、这些厨师呢有些会在做晚饭之前洗手、有些会在做晚饭之前洗头。
那么要实现这些功能可以通过继承的方式来实现洗手和实现洗头的功能,实现洗手功能需要增加两个类,如果再要实现洗头这个功能又要增加两个类,如果增加洗头和洗手两个功能还得增加两个类,这样每增加一个新功能都要通过继承去实现,这样类的增长速度会大大增大。所以我们通过装饰器模式来实现这个功能。
先抽象出抽象构件角色,Cook接口:
public interface Cook { public void cookDinner(); }
具体构件角色中国人厨师和英国人厨师如下:
public class ChineseCook implements Cook { @Override public void cookDinner() { System.out.println("做中国式晚饭"); } }
public class EnglishCook implements Cook { @Override public void cookDinner() { System.out.println("做英国式晚餐"); } }
定义一个装饰器角色,具体的功能扩展动作由装饰器来做,装饰器需要持有Cook的引用:
public abstract class FilterCook implements Cook { protected Cook cook; }
接下来实现具体装饰器角色来为构件角色来添加先洗手和先洗头发的功能:
public class WashHandCook extends FilterCook { public WashHandCook(Cook cook) { this.cook = cook; } @Override public void cookDinner() { System.out.println("先洗手"); cook.cookDinner(); } }
public class WashHearCook extends FilterCook { public WashHearCook(Cook cook) { this.cook = cook; } @Override public void cookDinner() { System.out.println("先洗头"); cook.cookDinner(); } }
客户端分别通过装饰器来实现功能的扩展:
public class Client { public static void main(String[] args) { System.out.println("---------1----------"); Cook c1 = new WashHandCook(new ChineseCook()); c1.cookDinner(); System.out.println("---------2----------"); Cook c2 = new WashHearCook(new ChineseCook()); c2.cookDinner(); System.out.println("---------3----------"); Cook c3 = new WashHandCook(new EnglishCook()); c3.cookDinner(); System.out.println("---------4----------"); Cook c4 = new WashHearCook(new EnglishCook()); c4.cookDinner(); System.out.println("--------------------"); } } 执行结果: ---------1---------- 先洗手 做中国式晚饭 ---------2---------- 先洗头 做中国式晚饭 ---------3---------- 先洗手 做英国式晚餐 ---------4---------- 先洗头 做英国式晚餐 --------------------
通过装饰器模式实现了功能:客户端只定义了Cook接口,而不关心具体的实现,新增了两个具体装饰器分别可以给中国人厨师的类和英国人厨师的类使用,还可以给其他实现了Cook接口的类使用,装饰器模式实现了装饰器扩展功能的复用。
装饰器模式的全透明和半透明模式
1、全透明模式是具体装饰器角色与抽象构件角色有着完全相同的接口方法。(上面的例子就是一个全透明模式,具体装饰器角色只拥有cookDiner这一个方法)
2、半透明模式和全透明模式不一样,半透明模式是是具体装饰器角色不一定只拥有与抽象构件相同的接口方法,为了自己的精细化还可以拥有别的扩展方法。
而在我们实际开发中更多的使用的是半透明模式,因为全透明模式是一个比较理想化的模式方法,如果要是用半透明的装饰器模式,在上述客户端我们应该将代码改成:
public class Client { public static void main(String[] args) { System.out.println("---------1----------"); WashHandCook c1 = new WashHandCook(new ChineseCook()); c1.cookDinner(); System.out.println("---------2----------"); WashHearCook c2 = new WashHearCook(new ChineseCook()); c2.cookDinner(); System.out.println("---------3----------"); WashHandCook c3 = new WashHandCook(new EnglishCook()); c3.cookDinner(); System.out.println("---------4----------"); WashHearCook c4 = new WashHearCook(new EnglishCook()); c4.cookDinner(); System.out.println("--------------------"); } }
上面的这种写法更具有现实的意义。
装饰器模式的优缺点
优点:
1、装饰器模式比静态继承更灵活,通过为一个Component类提供多个对应的Decorator类,这样使得可以对多个扩展功能的混合使用,例如上述例子,可以为中国厨师既添加先洗手又添加先洗头发的功能。
2、装饰器模式可以避免层次结构高层的类有太多的的特性,Decorator类可以动态地为Component类添加许多扩展功能,或者删除许多扩展功能,这种即用即付的模式避免了Component类逐渐的复杂化。
缺点:
使用装饰器模式会产生许多看上去类似的小对象,这些对象看上去类似仅仅是在互相连接的方式上有所不同,这样会导致程序一旦出现错误,会使得查错变得非常困难,而且这么多相似的类会使得会学习这些系统更加的困难。
浙公网安备 33010602011771号