zoukankan      html  css  js  c++  java
  • 装饰器模式

    一、定义

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

    在类图中,各个角色的说明如下:
    Component,抽象构件:Component是一个接口或者抽象类,是定义我们最核心的对象,也可以说是最原始的对象
    ConcreteComponent,具体构件,或者基础构件:ConcreteComponent是最核心、最原始、最基本的接口或抽象类Component的实现,可以单独用,也可将其进行装饰
    Decorator,装饰角色:一般是一个抽象类,继承自或实现Component,在它的属性里面有一个变量指向Component抽象构件,我觉得这是装饰器最关键的地方。
    ConcreteDecorator,具体装饰角色:ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,它们可以把基础构件装饰成新的东西。

    二、装饰器模式的应用场景

     装饰器模式其实在生活和生产中的案例特别多,例如IO文件流就是一个很直接简单的装饰器模式,在生活中也有,例如给窗户挂窗帘,可以给窗帘上加个小挂件装饰下,装饰完后觉得一个挂件不够再装一个。山东的姐夫让我想起了吃货的一个场景。山东人吃煎饼,喜欢在里面包东西,每个人想在里面包的菜都不一样,这里面就可以以大饼和前面说的窗帘为主体,挂件菜啥的就是装饰。
    //抽象构件
    public abstract class Component {
        // 抽象的方法
        public abstract void cost();
    }
    //具体基础构件
    public class ConcreteComponent extends Component{
        public void cost(){
            System.out.println("这是具体基础构件");
        }
    }
    //抽象装饰角色
    public abstract class Decorator  extends Component{
        private Component component = null;
        public Decorator(Component component){
            this.component = component;
        }
        @Override
        public void cost(){
            this.component.cost();
        }
    }
    //具体装饰角色
    public class ConcreteDecorator extends Decorator{
        public ConcreteDecorator(Component component){
            super(component);
        }
    
        // 定义自己的修饰逻辑
        private void decorateMethod(){
            System.out.println("定义自己的修饰逻辑");
        }
    
        // 重写父类的方法
        public void cost(){
            this.decorateMethod();
            super.cost();
        }
    }
    public class DecoratorDemo {
        public static void main(String[] args){
            Component component = new ConcreteComponent();
            // 第一次修饰,比如,加一个花边
            component = new ConcreteDecorator(component);
            // 第二次修饰,比如,加一个图片
            component = new ConcreteDecorator(component);
            // 修饰后运行,将所有加一起
            component.cost();
        }
    }

    三、 装饰器模式在Java I/O系统中的实现

    前面说过IO用的就是装饰器

    InputStream作为抽象构件,其下面大约有如下几种具体基础构件,从不同的数据源产生输入:

    • ByteArrayInputStream,从字节数组产生输入;
    • FileInputStream,从文件产生输入;
    • StringBufferInputStream,从String对象产生输入;
    • PipedInputStream,从管道产生输入;
    • SequenceInputStream,可将其他流收集合并到一个流内;

    FilterInputStream作为装饰器在JDK中是一个普通类,其下面有多个具体装饰器比如BufferedInputStream、DataInputStream等。我们以BufferedInputStream为例,使用它就是避免每次读取时都进行实际的写操作,起着缓冲作用。我们可以在这里稍微深入一下,站在源码的角度看下。

    FilterInputStream内部封装了基础构件:

    protected volatile InputStream in;

    而BufferedInputStream在调用其read()读取数据时会委托基础构件来进行更底层的操作,而它自己所起的装饰作用就是缓冲,在源码中可以很清楚的看到这一切:

    public synchronized int read() throws IOException {
            if (pos >= count) {
                fill();
                if (pos >= count)
                    return -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;
            // 看这行就行了,委托基础构件来进行更底层的操作
            int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
            if (n > 0)
                count = n + pos;
        }
    
        private InputStream getInIfOpen() throws IOException {
            InputStream input = in;
            if (input == null)
                throw new IOException("Stream closed");
            return input;
        }

    这部分的代码很多,这里没有必要考虑这段代码的具体逻辑,只需要看到在BufferedInputStream的read方法中通过getInIfOpen()获取基础构件从而委托其进行更底层的操作(在这里是读取单个字节)就可以说明本文所要说的一切了。至于I/O类库中的其他设计诸如OutputStream、Writer、Reader,是一致的

    四、总结

    装饰者模式的优缺点
    优点:
    1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
    2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
    3、装饰者完全遵守开闭原则。
    缺点:
    1、会出现更多的代码,更多的类,增加程序复杂性。
    2、动态装饰时,多层装饰时会更复杂。
    补充:代理模式和装饰器模式看起来很像,但是这两种设计模式所面向的功能扩展不一样,装饰器模式强调自身功能的扩展,Decorator所做的就是增强ConcreteComponent的功能,主体对象为ConcreteComponent,着重功能的变化;代理模式强调对代理过程过程的控制,Proxy完全掌握对RealSubject的访问控制,因此Proxy可以决定对RealSubject进行功能的扩展
     

      

    这短短的一生我们最终都会失去,不妨大胆一点,爱一个人,攀一座山,追一个梦
  • 相关阅读:
    人生中最重要的三位老师
    自我介绍
    秋季学习总结
    第五周学习总结
    第四周总结
    第三周基础作业
    判断上三角矩阵
    第二周作业及编程总结
    求最大值及其下标
    查找整数
  • 原文地址:https://www.cnblogs.com/xing1/p/14594915.html
Copyright © 2011-2022 走看看