其他分享
首页 > 其他分享> > 设计模式09 - 设计模式 - 装饰器模式(结构型)

设计模式09 - 设计模式 - 装饰器模式(结构型)

作者:互联网

一、定义

  装饰器(Decorator)模式:指不改变现有对象结构的情况下,动态地给该对象增加额外功能。它是继承方式的一种替代方案。

  这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供额外的功能。

  简单的说:就是用一个装饰类A包装一个原有的类B,在不改变B类完整性的情况下扩展其某些功能的设计模式;这么做的好处就是可以动态的给B添加一些额外的功能,但是又不影响到B类本身的职责。

 二、实现方式

 装饰器模式中的角色有:

1、抽象构件角色 - Component

       给出一个抽象接口,以规范准备接受附加责任的对象,Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。

2、具体构件角色 - ConcreteComponent

      定义一个将要接受附加责任的类,ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。

3、抽象装饰角色 - Decorator

      一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法,在它的属性里必然有一个private变量指向Component抽象构件

4、具体装饰角色

      ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西。也就是负责给构建对象贴上附加的责任

IO设计

      装饰器模式在Java体系中的经典应用是Java I/O,下面先讲解字节输入流InputStream:

 

 

 

InputStream是一个顶层的接口,文章开头就说,装饰器模式是继承关系的一种替代方案,看一下为什么:

  1. InputStream假设这里写了两个实现类,FileInputStream,ObjectInputStream分别表示文件字节输入流,对象字节输入流

  2. 现在我要给这两个输入流加入一点缓冲功能以提高输入流效率,使用继承的方式,那么就写一个BufferedInputStream,继承FileInputStream、ObjectInputStream,给它们加功能

  3. 现在我有另外一个需求,需要给这两个输入流加入一点网络功能,那么就写一个SocketInputStream,继承继承FileInputStream,ObjectInputStream,给它们加功能

这样就导致两个问题:

  1. 因为我要给哪个类加功能就必须继承它,比如我要给FileInputStream,ObjectInputStream加上缓冲功能、网络功能就得扩展出2*2=4个类,更多的以此类推,这样势必导致类数量不断膨胀

  2. 代码无法复用,给FileInputStream,ObjectInputStream加入缓冲功能,本身代码应该是一样的,现在却必须继承完毕后把一样的代码重写一遍,多此一举,代码修改的时候必须修改多个地方,可维护性很差

所以,这个的时候我们就想到了一种解决方案:

  1. 在要扩展的类比如BufferedInputStream中持有一个InputStream的引用,在BufferedInputStream调用InputStream中的方法,这样扩展的代码就可以复用起来

  2. 将BufferedInputStream作为InputStream的子类,这样客户端只知道我用的是InputStream而不需要关心具体实现,可以在客户端不知情的情况下,扩展InputStream的功能,加上缓冲功能

这就是装饰器模式简单的由来,一切都是为了解决实际问题而诞生。下一步,根据UML图,我们来划分一下装饰器模式的角色。

1、InputStream是一个抽象构件角色:

 1 public abstract class InputStream implements Closeable { {
 2     ...
 3      public abstract int read() throws IOException;
 4      public int read(byte b[], int off, int len) throws IOException {
 5       。。。
 6       read();
 7       。。。
 8       return i;
 9     }
10 }

 

2、具体构建角色:ByteArrayInputStream、FileInputStream、ObjectInputStream等,它们实现了抽象构件角色所规定的接口

1 public  class FileInputStream extends InputStream
2 {
3       public int read() throws IOException {
4         return read0(); //read0()为native方法
5     }  
6 }

 

3、抽象装饰角色:FilterInputStream无疑就是一个装饰角色,IO的设计里,FilterInputStream是装饰器的基类,因为FilterInputStream实现了InputStream内的所有抽象方法并且持有一个InputStream的引用:

 1 public class FilterInputStream extends InputStream {
 2     /**
 3      * The input stream to be filtered. 
 4      */
 5     protected volatile InputStream in;
 6     protected FilterInputStream(InputStream in) {
 7         this.in = in;
 8     }
 9     
10     public int read() throws IOException {
11         return in.read();
12     }
13     ...
14 }

 

4、具体装饰角色:BufferedInputStream、DataInputStream等

 1 public class BufferedInputStream extends FilterInputStream {
 2    protected volatile byte buf[];
 3    public BufferedInputStream(InputStream in, int size) {
 4         super(in);
 5         if (size <= 0) {
 6             throw new IllegalArgumentException("Buffer size <= 0");
 7         }
 8         buf = new byte[size];
 9     }
10     //...实现基于缓存的读数据接口... 
11     //带有缓冲的read功能,read()方法的增强
12     public synchronized int read() throws IOException {
13         if (pos >= count) {
14             fill();
15             if (pos >= count)
16                 return -1;
17         }
18         return getBufIfOpen()[pos++] & 0xff;
19     }
20 ​
21 }
22 ​
23 //支持按照基本类型读取数据的类
24 public class DataInputStream extends FilterInputStream implements DataInput {
25  public DataInputStream(InputStream in) {
26         super(in);
27     } 
28     //read方法的增强,方法里边用到了read()方法
29    public final char readChar() throws IOException {
30         int ch1 = in.read();
31         int ch2 = in.read();
32         if ((ch1 | ch2) < 0)
33             throw new EOFException();
34         return (char)((ch1 << 8) + (ch2 << 0));
35     }
36 ​
37 }
5、使用:
1 public static void main(String[] args) throws Exception
2 {
3     File file = new File("D:/aaa.txt");
4     InputStream in0 = new FileInputStream(file);
5     InputStream in1 = new BufferedInputStream(new FileInputStream(file)); 
6     InputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
7 }

我们这里实例化出了三个InputStream的实现类:

1、in0这个引用指向的是new出来的FileInputStream,这里简单构造出了一个文件字节输入流

2、in1这个引用指向的是new出来的BufferedInputStream,它给FileInputStream增加了缓冲功能,使得FileInputStream读取文件的内容保存在内存中,以提高读取的功能

3、in2这个引用指向的是new出来的DataInputStream,它也给FileInputStream增加了功能,因为它有DataInputStream和BufferedInputStream两个附加的功能

通用装饰器设计

//抽象接口
public interface Component {
    void method();  
}
//需要装饰的类
public class ConcreteComponent implements Component{
    public void method() {
        System.out.println("原来的方法");
    }
}
//装饰器基类
public abstract class Decorator implements Component{
    protected Component component;
    public Decorator(Component component) {
        super();
        this.component = component;
    }
    public void method() {
        component.method();
    }    
}
​
//装饰器A
public class ConcreteDecoratorA extends Decorator{
​
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
    
    public void methodA(){
        System.out.println("被装饰器A扩展的功能");
    }
​
    public void method(){
        System.out.println("针对该方法加一层A包装");
        super.method();
        System.out.println("A包装结束");
    }
}
​
//装饰器B
public class ConcreteDecoratorB extends Decorator{
​
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
​
    public void methodB(){
        System.out.println("被装饰器B扩展的功能");
    }
​
    public void method(){
        System.out.println("针对该方法加一层B包装");
        super.method();
        System.out.println("B包装结束");
    }
}
​
使用:
       Component componentB = new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteComponent()));
       componentB.method();
输出:
    针对该方法加一层B包装
    针对该方法加一层A包装
    原来的方法
    A包装结束
    B包装结束

半透明装饰器模式与全透明装饰器模式

        半透明装饰器模式与全透明装饰器模式,它们的区别是:

  1. 对于半透明装饰器模式,装饰后的类未必有和抽象构件角色同样的接口方法,它可以有自己扩展的方法
  2. 对于全透明装饰器模式,装饰后的类有着和抽象构件角色同样的接口方法

全透明装饰器模式是一种比较理想主义的想法,现实中不太可能出现。

        比如BufferedInputStream吧,我把FileInputStream装饰为BufferedInputStream,难道BufferedInputStream就完全没有自己的行为?比如返回缓冲区的大小、清空缓冲区(这里只是举个例子,实际BufferedInputStream是没有这两个动作的),这些都是InputStream本身不具备的,因为InputStream根本不知道缓冲区这个概念,它只知道定义读数据相关方法。

三、使用场景-待补充

      JDK中的IO流 + 业务场景

标签:BufferedInputStream,09,InputStream,装饰,new,FileInputStream,设计模式,public,结构型
来源: https://www.cnblogs.com/baopeer/p/16608961.html