定义
在编程语言中一般有两种方式给一个类或者对象增加额外的行为或者功能:
一种是继承:一个子类继承一个父类可以使得子类在拥有自身方法之外还能够同时拥有父类的方法。
还有一种是关联:即将一个类的对象嵌入到另一个对象中,并且由另一个对象决定是否调用嵌入对象的行为来扩展自己的功能,可见这种方式是动态的比继承更加灵活。而嵌入的对象就是这里要说的装饰器。
装饰器模式是继承关系的一个替代方案,在不必改变原类文件和原类使用的继承的情况 下,通过使用对象之间的关联关系来取代类之间的继承关系,从而能够动态的给一个对 象拓展一些额外的功能。它也是一种对象结构型模式。
组成部分
装饰器模式具有以下四个成员角色:
抽象构件角色(Project):给出一个接口,以规范准备接收附加责任的对象。
具体构件角色(Employe):定义一个将要接收附加责任的类。
装饰角色(Manager):持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰角色(ManagerA、ManagerB):负责给构件对象贴上附加的责任。
例子
这里以手机的功能为例:
声明一个抽象构件角色手机的公共接口,并且具有拍照的功能:
public interface Phone { //手机具有拍照的功能 void photo(); } 复制代码
再声明一个具体构件角色,
public class OldPhone implements Phone{ @Override public void photo() { System.out.println("手机的拍照功能"); } } 复制代码
抽象装饰角色:
public class NewPhone implements Phone{ public OldPhone oldPhone; public NewPhone(OldPhone oldPhone) { this.oldPhone = oldPhone; } @Override public void photo() { System.out.println("手机的拍照功能"); } } 复制代码
具体装饰角色:
public class ConcreteDecorator extends NewPhone { public ConcreteDecorator(OldPhone oldPhone) { super(oldPhone); } public void photo() { super.photo(); video(); } public void video() { System.out.println("为手机增加额外的拍视频的功能"); } } 复制代码
测试:
public class Test { public static void main(String[] args) { Phone p = new OldPhone(); p.photo(); Phone phone = new ConcreteDecorator(new OldPhone()); phone.photo(); } } 复制代码
结果:
通过例子可以看到,没有改变原来的OldPhone类,同时也没有定义他的子类而实现了Phone的扩展新增了拍视频的功能,这就是装饰器模式的作用。
装饰器模式优点:
1、装饰器模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
2、可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
装饰器模式缺点
1、使用装饰器模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类,增加了系统的复杂性
2、装饰器模式比继承更加灵活机动的特性,但同时也装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
装饰器模式和继承的区别:
前面说了装饰器模式和继承机制都可以给一个类或者对象增加额外的行为或者功能,那么它们有什么区别:
继承比较于装饰器模式的优点在于代码结构清晰,而且实现简单;但是对于每一个的需要增强的类都要创建具体的子类来帮助其增强,这样会导致继承体系过于庞大。
而装饰器模式则可以动态的对对多个需要增强的类进行增强,同理需要维护需要增强的类的实例。进而使得代码稍微复杂。
装饰器模式与适配器模式的区别:
装饰器模式和适配器模式都是类结构设计模式,它们又有什么区别:
1、适配器模式主要用来兼容那些不能在一起工作的类,将它们转化为可以兼容目标接口,虽然也可以实现和装饰器一样的增加新职责,但是它的目的并不是这个。而装饰器模式主要是给被装饰的增加新职责的。
2、适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。而装饰器模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。
3、适配器模式是知道被适配者的详细情况的;而装饰器模式只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。
使用场景
1、当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
2、当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
3、当对象的功能要求可以动态地添加,也可以再动态地撤销时。
4、装饰器模式在java中的最著名的应用就是I/O 标准库的设计了。比如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。