Java:Filter模式

问题来源

Java的IO标准库提供的输入流InputStream是一个抽象类,可以利用多态InputStream承接它的具体实现类

Java的InputStream根据来源可以分为:

  • FileInputStream:从文件IO;
  • ServletInputStream:从HTTP请求IO;
  • Socket.getInputStream:从TCP连接IO;
  • ...

但是如果要为这些实现类添加具体功能

  • 缓冲
  • 签名
  • 加密解密

就需要分别从这些类派生出相关的类。如果为每一种InputStream都这样做,就会导致子类爆炸的情况。

而为了解决这种情况,提出了Filter设计模式

如何实现

①基础类与功能类

JDK将InputStream分为两大类:基础类与功能类;

作为数据源的基础InputStream

  • FileInputStream;
  • ByteArrayInputStream;
  • ServletInputStream;
  • ...

作为功能类的InputStream

  • BufferedInputStream;
  • DigestInputStream;
  • CipherInputStream;
  • ...

②根据数据源构建基础类

以数据源是File为例:

InputStream file = new FileInputStream("test.gz");

③为创建的InputStream添加功能

缓冲

假设要为它添加缓冲功能,因此我们可以用BufferedInputStream包装之前的源InputStream

//这里的file就是我们在②中创建的源InputStrem类
InputStream buffered = new BufferedInputStream(file);

得到的包装类是BufferedInputStream,但是它仍被视为一个InputStream

解压缩

假设该文件已经用gzip压缩,我们希望直接读取压缩的内容,就可以再包装一个GZIPInputStream:

//这里的buffered就是之前已经实现了buffer功能的BufferedInputStream
InputStreak gzip = new GZIPInputStream( buffered );

④结果

这样,无论我们包装几次,得到的对象始终是InputStream,我们用InputStream也能正常引用读取

 

 这种通过基础组件叠加功能组件,从而实现运行期间动态增加功能的模式,称之为Filter模式(或Decorator模式)。这样,我们就能通过少量类实现各种功能组合

 

 类似的,OutputStream也可以利用这种模式来实现各种功能。

 

编写FilterInputStream

我们通过以下格式编写自己的FilterInputStream,这样就能利用之前所说的方式将我们的FilterInputStream叠加到任何一个InputStream中:

class XXXInputStream extends FilterInputStream{ ... }

下边的例子演示了如何编写一个CountInputStream,其作用是对输入的byte进行计数:

前提:FilterInputStream中有一个字段 in,标识它对应的InputStream

复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        byte[] data = "hello,world!".getBytes("UTF-8");
        try (CountInputStream input = new CountInputStream(new ByteArrayInputStream(data))) {
            int n;
            while ((n = input.read()) != -1)
                System.out.println((char) n);
            System.out.println("Total read " + input.getBytesRead() + " bytes");
        }
    }
}

class CountInputStream extends FilterInputStream {
    private int count = 0;

    CountInputStream(InputStream in) {
        super(in);
    }

    public int getBytesRead() {
        return this.count;
    }

    public int read() throws IOException {
        int n = in.read();
        if (n != -1)
            this.count++;
        return n;
    }

    public int read(byte[] b, int off, int len) throws IOException {
        int n = in.read(b, off, len);
        if (n != -1)
            this.count += n;
        return n;
    }
复制代码

 

编写这个FileterInputStream时,覆写read()read( byte[ ] b , int off , int len )时,在方法中用in.read( ... )便可以正确调用相关的read方法。

此外,上文中的所有this.count都可以写成count,也能正常运行。

posted @   ShineLe  阅读(269)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示