18.装饰者模式能解决什么问题?

1.装饰者模式能解决啥问题?

在讨论这个问题之前,我们先来复习下装饰者模式的定义:

装饰者模式动态的将责任附加到对象上。 如果要扩展功能,装饰者提供了比继承更有弹性的替代方案。

上面的定义中的关键是: 动态的将责任附加到对象上。 我们该如何理解这句话?
我们可以这样理解:有一个对象A,它有一个方法sayHello,这个方法已经实现用英文打印 hello。
下面需求来了,客户让你在不改变源代码的情况下让这个sayHello方法还要能打印中文 你好。
在我们没有接触装饰者模式情况下,我们可能想到新增一个类继承这个对象A,然后重写sayHello这个方法,让其能满足我们的需求,但是当有新的需求时,我们有不得不再建一个新类继承…,这样无穷无尽。

但是有了装饰者模式,我们可以将变化的部分抽象出来,将其作为原子装饰者,用组合的形式对对象A进行修饰增强sayHello的功能,这样我们就能以有限的设计应对无限的变化!!

装饰者赋予我们动态的给指定对象附加责任的能力。

1.1 复习装饰者模式

带着上面的理解,我们来复习下装饰者模式博客中的实例(看不懂的同学请移步:装饰者模式)
在这里插入图片描述

我们首先来分辨上面的图中那些是装饰者,那些是被装饰者:

DarkRoast, HouseBlend, Decaf是被装饰者。 Milk,Soy,Mocha,Whip是装饰者。

首先通过UML我们可以看到,装饰者和被装饰者都会继承同一个接口。 为什么会这样呢? 因为我们保证装饰者在装饰完对象后,有一个引用能接住它,而不是用其他引用来接住(面对抽象的好处),这样客户端就无需做出改变了。

综上,我们可以得出结论,要使用装饰者模式,首先装饰者和被装饰者要是同宗的,其次我们要考虑使用了装饰者模式是否能提高系统的开发效率。切记,不要为了设计而设计。

1.2 真实项目中什么地方会用到装饰者模式呢?

使用装饰者模式,我们应该问自己:是否有动态的为对象增加责任(能力)的场景?如果有,那么我们用装饰者模式来进行设计能为我们节省多少时间。

1.2.1 应用场景

1.2.1.1 JDK IO流功能的设计

其实在JDK中,已经用装饰者模式来对IO流进行设计了,下面我们从装饰者的视角来学习一下JDK的IO流。

首先,我们要知道什么是流?
在java中,输入/输出流 就是具备向文件,网络,内存等目的地读写数据能力的对象。

(1)抽象类InputStream
这个类在装饰者模式中就属于装饰者和被装饰者都需要继承的父类,它里面定义了一个输入流应该有的基本的方法。
下面来了解一下:

//从源中读取数据的下一个字节
public abstract int read() throws IOException;

//从源中读取一些字节,并将其存入字节数组b中
public int read(byte b[]) throws IOException 

//从源中读取最多len个字节的数据到字节数组b中,从b的off位置开始存储
public int read(byte b[], int off, int len) throws IOException 

//跳过并丢弃来自源的n字节数据
public long skip(long n) throws IOException
//返回从该输入流中可以读取(或跳过)的字节数的估计值,
//而不会被下一次调用此输入流的方法阻塞。
public int available() throws IOException 

//关闭此输入流并释放与流相关联的任何系统资源。
public void close() throws IOException {}

//关闭此输入流并释放与流相关联的任何系统资源。
public synchronized void mark(int readlimit) {}

//将此流重新定位到上次在此输入流上调用 mark方法时的位置。
public synchronized void reset() throws IOException 

//测试这个输入流是否支持 mark和 reset方法。
public boolean markSupported() 

FileInputStream使用学习

public class InputStreamStudy {
    public static void main(String[] args) throws IOException {
        //BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("text.txt"));
        InputStream fileInputStream = new FileInputStream("FileInputStreamStudy.txt");
        //从流中读取一个字节
        System.out.println((char) fileInputStream.read());
        //从流中读取一组字节
        byte[] b = new byte[5];
        fileInputStream.read(b);
        for(byte a:b){
            System.out.print((char)a);
        }
        System.out.println();
        //从流中读取指定数量的字节
        byte[] c = new byte[6];
        fileInputStream.read(c,2,2);
        for(byte a:c){
            System.out.print((char)a);
        }
        System.out.println();

        //丢弃一个字节
        fileInputStream.skip(1);
        System.out.println((char) fileInputStream.read());
        fileInputStream.close();
    }
}

在这里插入图片描述
在这里插入图片描述

(2)IO流中使用的装饰者模式
JDK 的IO流中,为了减少对文件操作的次数,提供了BufferdInputStream装饰者对FileInputStream进行装饰,使我们能高效的使用输入流。
UML图如下:
在这里插入图片描述

我们就以read方法为例来进行学习,上图中,装饰者BufferedInputStreram可以为被装饰者FileInputStream动态的附加减少对文件操作次数的责任(在第一次读取文件的时候一次性取出大量数据缓存在内存中)。
具体的操作就是,在使用BufferedInputStream装饰FileInputStream后,其read的操作变为如下:

public synchronized int read() throws IOException {
    if (pos >= count) {
        //将文件中的数据读取出来缓存在buf中
        fill();
        if (pos >= count)
            return -1;
    }
    //直接返回内存中缓存的字节
    return getBufIfOpen()[pos++] & 0xff;
}

操作实例如下:

public class InputStreamStudy {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new BufferedInputStream(new FileInputStream("FileInputStreamStudy.txt"));
        System.out.println(inputStream.read());
        inputStream.close();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值