设计模式:装饰器模式(为对象动态的添加功能)

前言


在学习 Java IO 的时候,记得头几回看到下面的代码,总是有点纳闷:

InputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));

这个代码,首先创建一个 FileInputStream 的对象,然后再传入到 BufferedInputStream 中,这是要干什么,为什么要这么做。

现在回过头来想,当时出现这些疑惑大部分原因是由于:

  1. 对于 Java IO的 stream 不熟悉。实际上比较常用的还有 DataInputStream,ObjectInputStream,FilterInputStream 等。
  2. 没有认识 装饰器模式。如果认识了装饰器模式,那么这些代码会很好理解了。

装饰器模式


作用与好处

装饰器模式 主要用于为对象动态的添加功能。比如说,最简单的 IO 使用可能是如下代码:

InputStream inputStream = new FileInputStream(filePath);

但是我们发现,这个代码 每次进行读取都会进行 一次 IO,IO是比较耗费时间的。 那么有没有什么方法降低 IO 次数呢,自然想到了缓冲。那么此时有两种做法:

  1. 定义一个 专门用于缓冲的文件读写 stream。
  2. 为 FileInputStream 类进行扩展,让它支持 缓冲。

肯定是第二种好,为什么呢。因为如果你按照1方案,
假如我要定义一个支持X功能,此时要定义一个 专门用于这个功能的stream,这个没有问题;
假如我又要定义一个支持Y功能,此时要定义一个专门用于这个功能的stream,这个也没有问题;

但是,假如我要定义一个 同时支持 X,Y功能,由于不能扩展,所以你必须再定义一个类,同时支持X,Y功能。这个就有问题了,问题就是重复了。
但是,2 方案就能解决这个问题, 2方案中当你需要一个同时支持 X,Y 功能的类,那你动态扩展便是了,不用再新定义一个类,同时支持XY。

这个就是装饰器模式的好处。

实现思路


简单来说,实现的方式就是:假如我们要为 A类扩展一个功能X,那么我们就定义一个B类,实现了这个X的功能,创建B类的时候,再把A类的一个对象传入到B类的对象中,那么B类对象就拥有了A类对象的所有权,就可以在执行方法前后执行一些方法,用于扩展功能。

再简单一点来说,就是把 A类对象传入B类对象, B类对象都有了A类对象了,那干什么不行。扩展功能自然不在话下。

代码实现


借用网上的一个图,实现结构就是这样子的:

设计模式:装饰器模式(为对象动态的添加功能)_第1张图片

为什么要搞这么复杂呢,就是为了以后可以更好的扩展。像很多技术我们都知道它的原理,但是你看它们的源码,异常的捕获,各种检测,健壮性,扩展性,别人都考虑得很全,而且已经 code 下来了。不像很多人(包括我),只会动动嘴皮子,要真刀真枪实现的时候,只能呵呵了,借用 Linus 的一句话,’ Talk is cheap, show me the code’,共勉。扯远了,囧。

回到这个图,再对应着下面这个代码说:

InputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));

  • Component:相当于 InputStream,Java IO 中很多的流都是继承于 InputStream。也就是一个基础组件。
  • ConcreteComponent:相当于 FileInputStream,一个基础组件的实现。
  • Decorator:所有具体装饰器类的抽象父类,在Java IO 包中 对应的是 FilterInputStream。
  • ConcreteDecorator:具体的装饰者。相当于BufferedInputStream。

这样其实就是 给 FileInputStream 添加了一个 缓冲的功能。

以下再给出自定义的代码实现:

Component 以及 实现类

public abstract class Component {         
    public abstract void operation();      
} 
public class ConcreteComponent extends Component {      
    public void operation() {      
        //相应的功能处理      
    }         
}  

装饰器抽象类以及具体实现类

public abstract class Decorator extends Component {      
    protected Component component;      

    public Decorator(Component component) {      
        this.component = component;      
    }      

    public void operation() {      
        //转发请求给组件对象,可以在转发前后执行一些附加动作      
        component.operation();      
    }      

}   
public class ConcreteDecoratorA extends Decorator {      
       public ConcreteDecoratorA(Component component) {      
            super(component);      
   }      
       private void operationFirst(){ } //在调用父类的operation方法之前需要执行的操作      
       private void operationLast(){ } //在调用父类的operation方法之后需要执行的操作      
       public void operation() {      
           //调用父类的方法,可以在调用前后执行一些附加动作      
           operationFirst(); //添加的功能      
           super.operation();  //这里可以选择性的调用父类的方法,如果不调用则相当于完全改写了方法,实现了新的功能      
           operationLast(); //添加的功能      
   }      
}      

客户端调用

public class Client{      
   public static void main(String[] args){      
    Component c1 = new ConcreteComponent (); //首先创建需要被装饰的原始对象(即要被装饰的对象)      
    Decorator decoratorA = new ConcreteDecoratorA(c1); //给对象透明的增加功能A并调用      
    decoratorA .operation();      
    Decorator decoratorB = new ConcreteDecoratorB(c1); //给对象透明的增加功能B并调用      
    decoratorB .operation();      
    Decorator decoratorBandA = new ConcreteDecoratorB(decoratorA);//装饰器也可以装饰具体的装饰对象,此时相当于给对象在增加A的功能基础上在添加功能B      
    decoratorBandA.operation();      
  }      
}     

如上代码,关键在于 抽象装饰器类 Decorator 中的维护了一个 Component 对象。这样,在客户端创建的时候,就可以把被装饰者传入到装饰者中,装饰者就有机会对目标进行装饰,也就是扩展功能。

以上就是装饰器模式 个人的一点小小理解。

你可能感兴趣的:(设计模式)