装饰模式续

上面的实例代码中,我们新增的功能(增加滚动条和黑色边框)都是加入到被装饰的原始类即Component的方法中,客户端并不会调用这些方法:

//滚动条装饰类
class ScrollBarDecorator extends  ComponentDecorator{

       public ScrollBarDecorator(Component  component){
              super(component);
       }

       public void display(){
              this.setScrollBar();
              super.display();
       }

       public void setScrollBar(){
              System.out.println("为构件增加滚动条!");
       }
}

其中,审批装饰类代码具体如下:

//审批装饰类
public class Approver extends Decorator{

      public Approver(Document document){
             super(document);
      }

      public void approve(){
             System.out.println("审批!");
      }
}

这个审批功能与原始类的显示方法(display())没有关系,那么客户在使用Component c = new PurchaseRequest()定义一个采购单时,由于Component中只有display()方法,新增的审批功能无法被调用。所以,客户端就不能使用这种面向抽象的编程方式,而必须使用PurchaseRequest request = new PurchaseRequet()这种方式。

装饰模式的透明与半透明

构建基础构件例子中演示的装饰模式,称之为透明装饰模式。这种方式可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别,此外,还可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象。

OA系统例子中演示的装饰模式,称之为半透明装饰模式。半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。

为什么半透明装饰模式不能实现对同一个对象的多次装饰

如果进行多次装饰,最终功能只会增加一个,因为客户端至多能调用到最外层的那个装饰功能。

JDK中的装饰模式

JDK中最典型的装饰模式的应用就是I/O流,看如下代码:

try (DataInputStream inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt")))){

    //do something with inputStream
    ......

} catch (FileNotFoundException e) {
    e.printStackTrace();
}catch (IOException e){
    e.printStackTrace();
}

这里,FileInputSream相当于原始的被装饰的类,它经过了BufferedInputSream装饰(拥有了buffer功能),又经过DataInputStream装饰。

这里的InputStream相当于ComponentFileInputStream相当于ConcreteComponent,而FilterInputStream相当于DecoratorBufferedInputStreamDataInputStream相当于ConcreteDecorator

输出流OutputStream与之类似。

参考

扩展系统功能--装饰模式:文字和代码大多来自于此

《研磨设计模式 第22章 装饰模式》:JDK中装饰模式

Last updated