装饰模式

1.什么是装饰模式

装饰模式又称为包装(Wrapper)模式,装饰模式以客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

主要解决“过渡的使用继承来扩展对象的功能“,继承为类型引入的静态特质使得这种扩展方式缺乏灵活性,并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀,继承为类型引入的静态特质的意思是说以继承的方式使得某一类型要获得功能是在编译时,静态是指在编译时,动态是指在运行时。

特点:扩展特定对象的功能,不是扩展某个类的功能;不需要子类,防止由于子类带来的复杂和混乱;对于一个给定对象,同时可能有不同的装饰对象,客户端可以通过它的需要选择合适的装饰对象。动态的给一个对象添加一些额外的功能,就增加功能而言,装饰模式比生成子类更加灵活。

2.装饰模式的结构

装饰模式以对客户端透明的方式为某个对象动态的附加更多功能,换言之,客户端不会觉得对象在装饰前后有什么区别。装饰模式可以在不使用创造更多子类的情况下,将对象功能进行扩展。

装饰模式的类图:


装饰模式中的角色:

抽象构件(Component)角色:给出一个抽象接口,以规范接受附加责任的对象;

具体构件(ConcreteComponent)角色:定义一个接受附加责任的类;

装饰(Decorator)角色:持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口;

具体装饰(ConcreteDecorator)角色:负责给构件对象贴上附加责任;

源代码如下:

//抽象构件角色
public interface Component(){
  public void sampleOption();
}
//具体构件角色
public class ConcreteComponent implements Component(){
  public void sampleOption(){
      //some codes
  }
}
//装饰角色
public Decorator implements Component(){
  private Component component;
  public Decorator(Component component){
    this.component = component;
  }
  public void sampleOption(){
    this.component.sampleOption();
  }
}
//具体装饰角色
public ConcreteDecorator extends Decorator(){
  public ConcreteDecorator(Component component){
    super(component);
  }
  public void sampleOption(){
    this.component.sampleOption();
    //增加一些功能
  }
}

这里我们可以看一个例子:

我们要为超市的收银台设计一个打印票据的程序,有的需要打印票据的头信息,有的需要打印票据的页脚信息,有的只需要打印票据的内容。如果针对每一种情况都修改一次程序,势必会很麻烦。这时我们可以考虑使用Decorator模式。其结构类图如下:



abstact class Component(){
  //这里定义一个抽象类,用于规范接受附加责任的对象
  public abstact void printTicket();
}
class SalesTicket extends Component(){
  //这个是负责接受附加责任的类,因此它要实现printTicket方法
  public void printTicket(){
    System.out.println("print SalesTicket.");
  }
}
abstract class TicketDecorator extends Component(){
  private Component myTrailer;
  public Decorator(Component component){
    this.myTrailer = component;
  }
  //这里是装饰对象要附加的责任,也就是callTrailer方法
  public void callTrailer(){
    if(myTrailer != NULL)
      myTrailer.printTicket();
  }
}
class Header extends TicketDecorator(){
  public Header(Component component){
    super(component);
  }
  //这里是具体的装饰对象,它负责实现具体要添加的责任
  //同时它还是以抽象构件的函数形式展示给客户端,这样增加功能对客户端是透明的
  public void printTicket(){
    System.out.println("print SalesTicket Header.");
    super.callTrailer();
  }
}
class Footer extends TicketDecorator(){
  public Footer(Component component){
    super(component);
  }
  public void printTicket(){
    super.callTrailer();
    System.out.println("print SalesTicket Footer.");
  }
}
public class client(){
  public static void main(String [] args){
    System.out.println("====================================");
    new Header(new Footer(new SalesTicket())).printTicket();
    System.out.println("====================================");
    new Footer(new Header(new SalesTicket())).printTicket();
    System.out.println("====================================");
  }
}

从这个例子我们可以看出,Decorator模式把问题分为两部分:
①如何实现提供新功能的对象。
②如何为每种特殊情况组织对象。
这样能够将Decorator对象的实现与决定如何使用Decorator的对象分离开来,从而提高了内聚性,因为每个Decorator对象只用关心自己添加的功能,无需关心自己是如何被加入到对象链中。还可以任意地重排Decorator的顺序,无需改变其任何代码。
小结:Decorator模式的适用场合是,各种可选的功能在另一个肯定要执行的功能之前或之后执行。

3.装饰模式的简化

①如果只有一个ConcreteComponent类,那么可以去掉抽象的Component类(接口),把Decorator类当做ConcreteComponent子类,如下图所示:


如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。甚至在只有两个ConcreteDecorator类的情况下,都可以这样做。如下图所示:


半透明的装饰模式:

装饰模式的用意是在不改变接口的前提下,增强所考虑的类的性能。在增强性能的时候,往往需要建立新的公开的方法。这就导致了大多数的装饰模式的实现都是“半透明”的,而不是完全透明的。换言之,允许装饰模式改变接口,增加新的方法。这意味着客户端可以声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法。半透明的装饰模式是介于装饰模式和适配器模式之间的。适配器模式的用意是改变所考虑的类的接口,也可以通过改写一个或几个方法,或增加新的方法来增强或改变所考虑的类的功能。大多数的装饰模式实际上是半透明的装饰模式,这样的装饰模式也称做半装饰、半适配器模式。

4.装饰模式的优缺点

装饰模式的优点:

①装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
②通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
装饰模式的缺点
由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

5.装饰模式与继承的比较

①前者扩展某类对象的功能,后者扩展一类对象的功能;

②前者是动态的,在运行时分配职责,后者是静态的,在编译时分配职责;

③前者灵活多变,不需要子类,防止由于子类而导致的复杂与混乱,对于一个给定的对象,用户可以利用合适的装饰对象给它提供各种新功能,后者会产生很多子类;

6.装饰模式在JAVA I/O中的应用

由于Java I/O库需要很多性能的各种组合,如果这些性能都是用继承的方法实现的,那么每一种组合都需要一个类(可以用排列组合的方式算一算),这样就会造成大量性能重复的类出现。而如果采用装饰模式,那么类的数目就会大大减少,性能的重复也可以减至最少。因此装饰模式是Java I/O库的基本模式。

Java I/O库的对象结构图如下,由于Java I/O的对象众多,因此只画出InputStream的部分:


抽象构件角色:由InputStream扮演,这是个抽象类,为各种子类型提供统一的接口;

具体构件角色:由FileInputStream、StringBufferInputStream等类扮演,它们实现了抽象构件提供的接口;

抽象装饰角色:由FilterInputStream扮演,它实现了InputStream的接口;

具体装饰角色:由BufferedInputStream等类扮演;

7.半透明的装饰模式

理想装饰模式要求在对被装饰对象进行功能增强的同时,具体的构件角色、装饰角色与抽象的构件角色提供完全一致的接口,而适配器不要求对源对象的功能进行增强,但是会改变源对象的接口,以便和目标接口相符合。

装饰模式有透明和半透明两种,这两种的区别就在于装饰角色的接口与抽象构件角色的接口是否完全一致。透明的装饰模式也就是理想的装饰模式,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。相反,如果装饰角色的接口与抽象构件角色接口不一致,也就是说装饰角色的接口比抽象构件角色的接口宽的话,装饰角色实际上已经成了一个适配器角色,这种装饰模式也是可以接受的,称为“半透明”的装饰模式,如下图所示:


在适配器模式里面,适配器类的接口通常会与目标类的接口重叠,但往往并不完全相同。换言之,适配器类的接口会比被装饰的目标类接口宽。
显然,半透明的装饰模式实际上就是处于适配器模式与装饰模式之间的灰色地带。如果将装饰模式与适配器模式合并成为一个“包装模式”的话,那么半透明的装饰模式倒可以成为这种合并后的“包装模式”的代表。


参考资料:

http://www.cnblogs.com/java-my-life/archive/2012/04/20/2455726.html

http://www.blogjava.net/flustar/archive/2007/11/28/decorator.html

http://blog.csdn.net/qiwancong/article/details/6989958









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值