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,也能正常运行。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性