概念
装饰器(Decorator)模式属于设计模式中的结构模式,通过装饰里动态的给一些对象增加职责。装饰器模式通常也会被称作包装(Wrapper)模式。装饰器模式包括一下四种角色:
Component:组件对象的接口,可以给这些对象动态的添加职责;
ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;
Decorator:所有装饰器的父类,需要定义一个与组件接口一致的接口(主要是为了实现装饰器功能的复用,即具体的装饰器A可以装饰另外一个具体的装饰器B,因为装饰器类也是一个Component),并持有一个Component对象,该对象其实就是被装饰的对象。如果不继承组件接口类,则只能为某个组件添加单一的功能,即装饰器对象不能在装饰其他的装饰器对象。
ConcreteDecorator:具体的装饰器类,实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。
结构图:
Demo示例:
下面假设我们在一家咖啡店:咖啡品种多样,但区别只是在于加奶和糖的份量不同,下面我们提供一个添加调料的add()组件接口,组件对象咖啡Coffee实现了add()接口同时也作为装饰器装饰的原始对象用来添加不同的调料。所有装饰器父类Contact(调制)实现了组件接口add()方法,同时拥有一个被装饰对象Coffee。Demo中提供两个装饰类分别为咖啡加奶AddMilk和加糖AddSugar;
(1)组件接口
//组件对象的接口
public interface Drink {
void add();//添加调料
}
(2)组件对象(被装饰对象)
public class Coffee implements Drink {
public Coffee(){
System.out.println("一杯咖啡:");
}
@Override
public void add() {
// TODO Auto-generated method stub
System.out.println("添加:");
}
}
(3)装饰器父类
//装饰器接口,维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口
public class Concoct implements Drink {
private Drink dink;//被装饰对象
@Override
public void add() {//实现装饰方法:
// TODO Auto-generated method stub
dink.add();
}
}
(4)装饰器
//装饰器的具体实现对象,向组件对象添加职责.addmilk()为需要添加的新功能。
public class Concoct_AddMilk extends Concoct {
private Drink drink;
public Concoct_AddMilk(Drink drink){
super();
this.drink=drink;
}
@Override
public void add() {
// TODO Auto-generated method stub
drink.add();
addMilk();
}
public void addMilk(){
System.out.println("加奶!");
}
}
加糖装饰类与加奶装饰类雷同,故不给出具体代码。
通过以上示例可以看出装饰器模式的优点是在于原有的功能上添加新的职责,并且被继承的扩展更加灵活。
JAVA中应用:
Java中的IO是明显的装饰器模式的运用。FilterInputStream,FilterOutputStream,FilterRead,FilterWriter分别为具体装饰器的父类,相当于Decorator类,它们分别实现了InputStream,OutputStream,Reader,Writer类(这些类相当于Component,是其他组件类的父类,也是Decorator类的父类)。继承自InputStream,OutputStream,Reader,Writer这四个类的其他类是具体的组件类,每个都有相应的功能,相当于ConcreteComponent类。而继承自FilterInputStream,FilterOutputStream,FilterRead,FilterWriter这四个类的其他类就是具体的装饰器对象类,即ConcreteDecorator类。通过这些装饰器类,可以给我们提供更加具体的有用的功能。如FileInputStream是InputStream的一个子类,从文件中读取数据流,BufferedInputStream是继承自FilterInputStream的具体的装饰器类,该类提供一个内存的缓冲区类保存输入流中的数据。我们使用如下的代码来使用BufferedInputStream装饰FileInputStream,就可以提供一个内存缓冲区来保存从文件中读取的输入流。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); //其中file为某个具体文件的File或者FileDescription对象
在以下两种情况下可以考虑使用装饰器模式:
(1)需要在不影响其他对象的情况下,以动态、透明的方式给对象添加职责。
(2)如果不适合使用子类来进行扩展的时候,可以考虑使用装饰器模式。