一个男人的设计模式:装饰模式

本文详细介绍了装饰模式的概念、结构和工作原理,通过代码示例展示了如何使用装饰模式来扩展对象功能。此外,还探讨了装饰模式在Java IO系统中的应用,如BufferedInputStream对FileInputStream的装饰,以实现缓冲功能。装饰模式提供了一种替代继承的扩展机制,允许在不修改原有代码的基础上动态增加功能。

类图结构

在这里插入图片描述

模式描述

装饰者(Decorator)与被装饰者(ConcreteComponent)需要实现统一抽象接口(Component),这样可以保证装饰者和被装饰者有统一的接口规范,即装饰者(Decorator)和被装饰者(ConcreteComponent)的使用方法一致。同时装饰者(Decorator)需要组合一个统一抽象接口(Component)的实例对象,调用装饰者某个方法时,会进行一定的增强处理(装饰)后,再调用组合的统一抽象接口(Component)的实例对象来具体实现最基本的功能。

装饰模式以对客户端透明的方式【装饰者(Decorator)与被装饰者(ConcreteComponent)实现统一抽象接口】扩展对象的功能,是继承关系的一个替代方案。装饰模式可以在不创建更多子类的情况下,将对象的功能加以扩展。

统一抽象接口 Component

public interface Component {
    public void doSomething();
}

被装饰者 ConcreteComponent

public class ConcreteComponent  implements Component{
    public void doSomething() {
        System.out.println("doSomething");
    }
}

装饰者 Decorator

public abstract class Decorator implements Component {
    private Component component;
    Decorator(Component component){
        this.component = component;
    }
    public void doSomething() {
        component.doSomething();
    }
}

装饰者子类 ConcreteDecorator

public class ConcreteDecorator extends Decorator {

    ConcreteDecorator(Component component) {
        super(component);
    }
    @Override
    public void doSomething() {
        System.out.println("before doSomething ");
        super.doSomething();
        System.out.println("after doSomething ");
    }
}

测试代码

public class Client {

    public static void main(String[] args) {
        //ConcreteDecorator 对ConcreteComponent进行进一步封装
        Decorator decorator = new ConcreteDecorator(new ConcreteComponent());
        //调用方式和ConcreteComponent的调用方式一样
        decorator.doSomething();
        }
}
before doSomething
doSomething
after doSomething

装饰模式在JavaIO体系中的应用

Java的I/O库提供了一个称为连接的机制,可以将流与另一个流首尾相接,形成一个流管道的连接,这种机制实际上是一种被称为Decorator(装饰)设计模式的应用。通过流的链接,可以动态增加流的功能,而这种功能的增强是通过组合一些流的基本功能而动态获取的,这就提高了流使用灵活性。

抽象接口被装饰者装饰者装饰者实现类
装饰模式ComponentConcreteComponentDecoratorConcreteDecorator
IO体系InputStreamFileInputStream(节点流均可)FilterInputStream(过滤流)BufferedInputStream(过滤流子类)

JAVA I/O流

Java程序通过流来完成输入输出,流是生产或消费消息的抽象。java中流的实现是在java.io包定义的类层次接口。

输入/输出时相对于程序来说的。程序在使用数据时要么作为数据源,要么作为数据目的地,所作为数据源,即数据的提供者,则为输出流,否则为输入流。

字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

节点流:从特定的地方读写的流类,例如:磁盘或一块内存区域

过滤流:使用节点流作为输出或输出,过滤流是使用一个已经存在的输入流或输出流连接创建的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KQgoCGnN-1613828623383)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210220202109310.png)]

过滤流

在InputStream和OutputStream的子类中派生了FilterInputStream和FilterOutputStream过滤流,本质上这一类过滤流是对InputStream和OutputStream的其它子类进行一层封装(装饰)。

例子

在这里插入图片描述

使用节点流FileInputStream从文件中获取字节流,使用过滤器流BufferedInputStream对FileInputStream进行增强(装饰),使得该流具备了缓冲功能,再通过DataInputStream对已经具有缓冲功能的进行再次增强(装饰),使得具备了读取java基本类型的功能。

BufferedInputStream对FileInputStream的read()装饰

统一抽象接口 InputStream

public abstract class InputStream implements Closeable {
    /**
    抽象统一接口,其它接口省略不写
    */
    public abstract int read() throws IOException;
}

被装饰者 FileInputStream

public class FileInputStream extends InputStream{
    /**
    被装饰者的实现方法
    */
    public int read() throws IOException {
        return read0();
    }
    /**
    调用原生方法
    */
    private native int read0() throws IOException;
}

装饰者FilterInputStream

public class FilterInputStream extends InputStream {
     /**
     组合抽象接口的实例对象字段
     * The input stream to be filtered.
     */
    protected volatile InputStream in;
    //持有节点流对象
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    //读数据
    public int read() throws IOException {
        return in.read();
    }
}

装饰者BufferedInputStream

public class BufferedInputStream extends FilterInputStream {
     /**
     缓存字节的数组对象,字节流会保存在该数组中,当read时会获取素组中的数据
     * The internal buffer array where the data is stored. When necessary,
     * it may be replaced by another array of
     * a different size.
     */
    protected volatile byte buf[];
    
    public int read() throws IOException {
        return in.read();
    }
    //构造函数
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }
    //构造函数保存节点流对象FileInputStream
    public BufferedInputStream(InputStream in, int size) {
        //保存节点流对象
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    //第一步
    public synchronized int read() throws IOException {
        if (pos >= count) {
            //将节点流数据读入缓存数组buffer中
            fill();
            if (pos >= count)
                return -1;
        }
        //从缓存数组buffer中获取1字节数据
        return getBufIfOpen()[pos++] & 0xff;
    }
    //第二步
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        //第三步
        //这里就是调用最初构造 传入的节点流对象,这里是FileInputStream  这里就调用了FileInputStream的reader方法来将数据读入buffer中
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }
    
    //获得在构造过滤流时传入的节点流对象,这里是FileInputStream
    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

}

测试代码

public class Client {

    public static void main(String[] args) throws Exception{
   		//采用装饰模式读取一个字符
        FilterInputStream filterInputStream = new BufferedInputStream(new FileInputStream("testdata.txt"));
        System.out.println("have Buffer first char:"+(char)filterInputStream.read());
        filterInputStream.close();
		//采用非装饰模式读取一个字符
        InputStream inputStream = new FileInputStream("testdata.txt");
        System.out.println("no Buffer first char:"+(char)inputStream.read());
        inputStream.close();        
    }
}
have Buffer first char:d
no Buffer first char:d

装饰模式以对客户端透明的方式【装饰者(Decorator)与被装饰者(ConcreteComponent)实现统一抽象接口】扩展对象的功能,是继承关系的一个替代方案。装饰模式可以在不创建更多子类的情况下,将对象的功能加以扩展。

以上,感谢阅读!!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值