ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-08-21 10:36:03  阅读:138  来源: 互联网

标签:BufferedInputStream 09 InputStream 装饰 new FileInputStream 设计模式 public 结构型


一、定义

  装饰器(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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有