首页 > 其他分享 >装饰器模式

装饰器模式

时间:2024-04-16 10:55:55浏览次数:24  
标签:... Java 模式 InputStream public new 装饰

1、从经典的 Java IO 来了解装饰器模式

  Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:

                                

   针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类。具体如下所示:

                   

  初学 Java 的时候,曾经对 Java IO 的一些用法产生过很大疑惑,比如下面这样一段代码。我们打开文件 test.txt,从中读取数据。其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
  //...
}

  初看上面的代码,可能会觉得 Java IO 的用法比较麻烦,需要先创建一个 FileInputStream 对象,然后再传递给 BufferedInputStream 对象来使用。那么,Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类呢?这样就可以像下面的代码中这样,直接创建一个 BufferedFileInputStream 类对象,打开文件读取数据,用起来岂不是更加简单?

InputStream bin = new BufferedFileInputStream("/user/wangzheng/test.txt");
byte[] data = new byte[128];
while (bin.read(data) != -1) {
  //...
}

2、两种实现方案——继承和装饰器模式

(1)基于继承的设计方案

  如果 InputStream 只有一个子类 FileInputStream 的话,那w在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream,也算是可以接受的,毕竟继承结构还算简单。但实际上,继承 InputStream 的子类有很多。需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类。

  除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,比如下面的 DataInputStream 类,支持按照基本数据类型(int、boolean、long 等)来读取数据。

FileInputStream in = new FileInputStream("/user/wangzheng/test.txt");
DataInputStream din = new DataInputStream(in);
int data = din.readInt();

  在这种情况下,如果我们继续按照继承的方式来实现的话,就需要再继续派生出 DataFileInputStream、DataPipedInputStream 等类。如果我们还需要既支持缓存、又支持按照基本类型读取数据的类,那就要再继续派生出 BufferedDataFileInputStream、BufferedDataPipedInputStream 等 n 多类。这还只是附加了两个增强功能,如果我们需要附加更多的增强功能,那就会导致组合爆炸,类继承结构变得无比复杂,代码既不好扩展,也不好维护。

(2)基于装饰器模式的设计方案

  针对继承结构过于复杂的问题,可以通过将继承关系改为组合关系来解决,这就是很多人提到到“组合优于继承”的问题。下面的代码展示了 Java IO 的这种设计思路(做了简化,只抽象了必要的代码结构)。

public abstract class InputStream {
  //...
  public int read(byte b[]) throws IOException {
    return read(b, 0, b.length);
  }
  
  public int read(byte b[], int off, int len) throws IOException {
    //...
  }
  
  public long skip(long n) throws IOException {
    //...
  }

  public int available() throws IOException {
    return 0;
  }
  
  public void close() throws IOException {}

  public synchronized void mark(int readlimit) {}
    
  public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
  }

  public boolean markSupported() {
    return false;
  }
}

public class BufferedInputStream extends InputStream {
  protected volatile InputStream in;

  protected BufferedInputStream(InputStream in) {
    this.in = in;
  }
  
  //...实现基于缓存的读数据接口...  
}

public class DataInputStream extends InputStream {
  protected volatile InputStream in;

  protected DataInputStream(InputStream in) {
    this.in = in;
  }
  
  //...实现读取基本类型数据的接口
}

  那装饰器模式就是简单的“用组合替代继承”吗?当然不是。从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,还有两个比较特殊的地方。

——第一个比较特殊的地方是:装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。比如,下面这样一段代码,我们对 FileInputStream 嵌套了两个装饰器类:BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();

——第二个比较特殊的地方是:装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。实际上,符合“组合关系”这种代码结构的设计模式有很多,比如代理模式,还有现在的装饰器模式。尽管它们的代码结构很相似,但是每种设计模式的意图是不同的。就拿比较相似的代理模式和装饰器模式来说吧,代理模式中,代理类附加的是跟原始类无关的功能,而在装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。

// 代理模式的代码结构(下面的接口也可以替换成抽象类)
public interface IA {
  void f();
}
public class A impelements IA {
  public void f() { //... }
}
public class AProxy implements IA {
  private IA a;
  public AProxy(IA a) {
    this.a = a;
  }
  
  public void f() {
    // 新添加的代理逻辑
    a.f();
    // 新添加的代理逻辑
  }
}

// 装饰器模式的代码结构(下面的接口也可以替换成抽象类)
public interface IA {
  void f();
}
public class A implements IA {
  public void f() { //... }
}
public class ADecorator implements IA {
  private IA a;
  public ADecorator(IA a) {
    this.a = a;
  }
  
  public void f() {
    // 功能增强代码
    a.f();
    // 功能增强代码
  }
}

 

标签:...,Java,模式,InputStream,public,new,装饰
From: https://www.cnblogs.com/jing-yi/p/18137645

相关文章

  • Java分布式架构:应用+特点+架构模式
    Java分布式架构是一个复杂的主题,它涉及到许多不同的概念和技术。在本文中,我们将介绍Java分布式架构的应用、特点和架构模式,以便更好地了解该主题。应用:Java分布式架构可以应用于许多不同的场景,例如:电子商务网站:电子商务网站需要处理大量的交易和订单,而Java分布式......
  • day11_我的Java学习笔记 (static_静态成员变量+静态成员方法_工具类、代码块_静态代码
    0.面向对象进阶1.static静态关键字1.1static是什么,static修饰成员变量的用法Java成员变量成员方法Python类(对象)属性类(对象)方法static修饰成员变量的应用:在线人数统计1.2static修饰成员变量的内存原理1.3static修饰成员方法的基本......
  • 08、M-LAG维护模式升级原理
    M-LAG维护模式升级原理M-LAG维护模式下的升级的基本原理是在维护模式下,通过OSPF/OSPFv3/BGP/BGP4+和LACP命令将流量先切换到备份链路上,再升级设备。升级完成后,依次删除LACP和OSPF/OSPFv3/BGP/BGP4+命令将流量回切,再退出维护模式。整个维护模式下的升级过程相对传统的升级方式提......
  • openGauss DBMind模式说明
    DBMind模式说明用户可通过gs_dbmind命令调用AI4DB的全部功能,该命令可实现下列基本功能:服务功能:service子命令,包括创建并初始化配置目录、启动后台服务、关闭后台服务等;调用组件:component子命令,AI4DB功能(如索引推荐、参数调优等)可通过该模式进行即时调用;设置参数:set子命令,通过......
  • 函数对象,闭包函数及装饰器了解
    函数对象【1】定义函数对象指的是函数可以被当做数据来处理【2】可以直接被引用定义一个函数用一个新的变量名来存,用新的变量名来调用#定义一个函数defadd(x,y):returnx+y#将函数地址绑定给一个变量func=add#通过这个变量找到对应的地址,从而调用函数res......
  • Docker Swarm模式下创建服务认证harbor
    dockerservicecreate--with-registry-auth 命令是在DockerSwarm模式下创建服务时使用的,它允许Docker将本地的注册表认证信息(如私有仓库的登录凭证)随着服务创建命令一起发送出去,使得Swarm集群中的每个节点在拉取受保护的私有仓库镜像时无需单独登录。具体用法如下:dockers......
  • vscode使用ES6装饰器器语法
    1.react项目配置package.json需要安装npmi@babel/plugin-proposal-decorators可能需要重启项目{"babel":{"plugins":[["@babel/plugin-proposal-decorators",{"legacy":true}......
  • 工厂方法模式
    为了避免简单工厂模式的缺点,不完全满足OCP(对扩展开放,对修改关闭)。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立的模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。这样在简单工厂模式里集中在工厂方法上的压力可以由工......
  • blender创建圆柱并进入编辑模式得代码
    importbpy#定义创建圆柱体并进入编辑模式的函数defcreate_cylinder_and_edit(radius,depth,location):#创建圆柱体bpy.ops.mesh.primitive_cylinder_add(radius=radius,depth=depth,enter_editmode=False,location=location)#选择新创建的圆......
  • Linux-vim文本编辑器-三种模式-vim里的替换
    1.vi和vim命令是linux中强大的文本编辑器,由于Linux系统一切皆文件,而配置一个服务就是在修改其配置文件的参数。vim编辑器是运维工程师必须掌握的一个工具,没有它很多工作都无法完成。vim其实是vi的升级版 2.vim三种工作模式Vim编辑器中设置了三种模式:命令模......