1:从IO引入(奇怪的IO类)
Java IO 类库非常庞大和复杂,有几十个类,负责 IO 数据的读取和写入。
如果对 Java IO 类做一下分类,我们可以从下面两个维度将它划分为四类。具体如下所示:
|
字节流 |
字符流 |
输入流 |
InputStream |
Reader |
输出流 |
OutputStream |
Writer |
基于这4个父类上 扩展出很多子类
曾经对 Java IO 的一些用法产生过很大疑惑:
我们打开文件 test.txt,从中读取数据。
其中,InputStream 是一个抽象类,FileInputStream 是专门用来读取文件流的子类。
BufferedInputStream 是一个支持带缓存功能的数据读取类,可以提高数据读取的效率。
需要先创建一个 FileInputStream 对象,然后再传递给 BufferedInputStream 对象来使用。
Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
InputStream inputStream = new FileInputStream("A:\\test.txt");
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
byte[] data = new byte[128];
while (bufferedInputStream.read(data) != -1) {
//...
}
//继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类
InputStream bufferedFileInputStream = new BufferedFileInputStream("A:\\test.txt");
byte[] data = new byte[128];
while (bufferedFileInputStream.read(data) != -1) {
//...
}
2:基于继承的设计方案
如果 InputStream 只有一个子类 FileInputStream 的话,那我们在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream,
也算是可以接受的,毕竟继承结构还算简单。
但实际上,继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类。
除了支持缓存读取之外,如果我们还需要对功能进行其他方面的增强,
比如下面的 DataInputStream 类,支持按照基本数据类型(int、boolean、long 等)来读取数据。
InputStream inputStream = new FileInputStream("A:\\test.txt");
DataInputStream dataInputStream = new DataInputStream(inputStream);
int data = dataInputStream.readInt();
//继承 FileInputStream 并且支持基本数据类型的 dataFileInputStream 类
InputStream dataFileInputStream = new DataFileInputStream("A:\\test.txt");
int data = dataFileInputStream.readInt();
假如说这时候需要一个支持缓存、又支持按照基本类型读取数据的类,
那就要再继续派生出 BufferedDataFileInputStream
那么后面会变得没完没了。
3:基于装饰器模式的设计方案
之前还讲到“组合优于继承”,可以“使用组合来替代继承”。
针对刚刚的继承结构过于复杂的问题,我们可以通过将继承关系改为组合关系来解决。
下面的代码展示了 Java IO 的这种设计思路。
那装饰器模式就是简单的“用组合替代继承”吗?
当然不是。从 Java IO 的设计来看,装饰器模式相对于简单的组合关系,通常两个比较特殊的地方。
注意要有共同的接口类 或者 抽象类
1:第一个比较特殊的地方是:
装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
我们对 FileInputStream 嵌套了两个装饰器类:
BufferedInputStream 和 DataInputStream,让它既支持缓存读取,又支持按照基本数据类型来读取数据。
第二个比较特殊的地方是:
装饰器类是对功能的增强,这也是装饰器模式应用场景的一个重要特点。
拿比较相似的代理模式和装饰器模式来对比:
代理模式中,代理类附加的是跟原始类无关的功能,
装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
4:实战例子(对IO类的各种增强)
public class BufferedInputStream extends InputStream {
protected volatile InputStream in;
protected BufferedInputStream(InputStream in) {
System.out.println("提供支持缓存读取数据 功能");
this.in = in;
}
//...实现基于缓存的读数据接口...
@Override
public int read() throws IOException {
return 0;
}
}
public class DataInputStream extends InputStream {
protected volatile InputStream in;
protected DataInputStream(InputStream in) {
System.out.println("提供基本类型读取数据 功能");
this.in = in;
}
//...实现读取基本类型数据的接口
@Override
public int read() throws IOException {
return 0;
}
}
public class FileInputStreamDemo {
public void FileInputStreamDemo() throws IOException {
InputStream inputStream = new FileInputStream("A:\\test.txt");
//BufferedInputStream 继承了 InputStream 提供支持缓存读取数据 功能
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
//DataInputStream 继承了 InputStream 提供基本类型读取数据 功能
InputStream dataInputStream =new DataInputStream(bufferedInputStream);
byte[] data = new byte[128];
while (dataInputStream.read(data) != -1) {
//...
}
}
}
5:实战例子(结束订单的积分抵扣)
1:正常的订单接口类(或者抽象类)
2:正常的订单实现类( 或者子类)
3:装饰器类 传入订单实现类 或者子类) 对其同名方法做了增强
4:调用时
可以使用 订单实现类(或者子类)
可以使用 订单实现类(或者子类)的 装饰器类
// 代理模式的代码结构(下面的接口也可以替换成抽象类)
public interface OrderService {:
void finishOrder(int amount);
}
public class OrderServiceImpl implements OrderService {
@Override
public void finishOrder(int amount) {
System.out.println("结束订单 扣除:"+amount);
}
}
public class OrderServiceImplDecorator implements OrderService {
private OrderService orderService;
public OrderServiceImplDecorator(OrderService orderService) {
this.orderService = orderService;
}
@Override
public void finishOrder(int amount) {
//积分抵扣
amount = amount -5;
orderService.finishOrder(amount);
}
}
public class OrderDecoratorDemoStart {
public static void main(String[] args) {
OrderService orderService =new OrderServiceImpl();
orderService.finishOrder(10);
//对OrderService 的实现类的具体方法做了增强
OrderService orderServiceOther =new OrderServiceImpl();
OrderService orderServiceDecorator =new OrderServiceImplDecorator(orderServiceOther);
orderServiceDecorator.finishOrder(10);
}
}
6:装饰器小结
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。
1:它可以可以对原始类嵌套使用多个装饰器。
注意:装饰器类需要跟原始类继承相同的抽象类或者实现相同接口类。
2:它主要的作用是给原始类添加增强功能。
注意:装饰器类方法名 最好与原始类一致
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesujin-design-pattern
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
demo-study模块 下 structure_design_pattern decorator包
网友评论