设计模式——装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

装饰器模式结构图

  • Component(抽象构件):它是装饰类和具体构件的公共父类(一般是接口或者抽象类);
  • ConcreteComponent(具体构件):具它是抽象构件对象的子类,用来定义具体的构件对象(Component的子类或者具体实现);
  • Decorator(抽象装饰类):进继承抽象构件,用于给具体的构件添加一些新的职责(一般是一个继承了Component的接口);
  • ConcreteDecoraror(具体装饰类):实现了抽象装饰类,它负责向构件添加新的职责;

代码演示

业务场景:现我们现在模拟这样一个场景,我们点了一杯奶茶,然后给奶茶中加了冰块,加了珍珠,最后我们还想再给加点红豆,这里加红豆就使用了装饰者。

我们先来创建一个奶茶的抽象类,这个就是上面的Component角色

public interface MilkyTea {

   public void recipe();
}

我们再来创建要给奶茶的具体子类,相当于ConcreteComponent

public class MilkyTeaA implements MilkyTea {
   @Override
   public void recipe() {
       System.out.println("老板来一杯奶茶,加冰块");
   }
}

接下来创建一个装饰类,相当于Decorator

public class Decorator implements MilkyTea {

   private MilkyTea milkyTea;

   public void setMilkyTea(MilkyTea milkyTea) {

       this.milkyTea = milkyTea;
   }

   @Override
   public void recipe() {
       milkyTea.recipe();
   }
}

创建装饰类的子类,添加珍珠,相当于ConcreteDecorator

public class MilkyTeaADecorator extends Decorator {

   @Override
   public void recipe() {
       super.recipe();
       //对现有类进行功能增强
       recipeZZ();
   }

   // 加珍珠
   public void recipeZZ() {
       System.out.println("老板再加点珍珠吧");
   }
}

创建装饰者的子类,添加红豆,相当于ConcreteDecorator

public class MilkyTeaBDecorator extends Decorator {

   @Override
   public void recipe() {

       super.recipe();
       recipeHD();
   }

   public void recipeHD() {

       System.out.println("老板你再给加点红豆吧");
   }
}

最后我们测试一下看下结果:

public class Test {

   public static void main(String[] args) {

       MilkyTeaA milkyTea = new MilkyTeaA();

       MilkyTeaADecorator milkyTeaA = new MilkyTeaADecorator();
       MilkyTeaBDecorator milkyTeaB = new MilkyTeaBDecorator();

       milkyTeaA.setMilkyTea(milkyTea);
       milkyTeaB.setMilkyTea(milkyTeaA);

       milkyTeaB.recipe();
   }
}

JDK中的装饰器模式

JDK中,IO部分的很多类用到了装饰器模式。

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

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

FilterInputStream作为装饰器JDK中是一个普通类,其下面有多个具体装饰器比如BufferedInputStreamDataInputStream等。我们以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,是一致的,这里就不再赘述了。

简单总结

  • 装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。
  • 装饰器模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component(因为Decorator本身就是继承自Component的),实现的还是is-a的关系。
  • 装饰模式可以动态地扩展一个实现类的功能,比如在I/O系统中,我们直接给BufferedInputStream的构造器直接传一个InputStream就可以轻松构件一个带缓冲的输入流,如果需要扩展,我们继续“装饰”即可。

  但是也有其自身的缺点:

  多层的装饰是比较复杂的。为什么会复杂?你想想看,就像剥洋葱一样,你剥到最后才发现是最里层的装饰出现了问题,可以想象一下工作量。这点从我使用Java I/O的类库就深有感受,我只需要单一结果的流,结果却往往需要创建多个对象,一层套一层,对于初学者来说容易让人迷惑。

理论的学习还是为了实践。实战中如果需要用到装饰器模式,可以从模仿 Java IO 部分的装饰器模式开始。模仿是创新的开始。

参考

  • https://www.runoob.com/design-pattern/decorator-pattern.html
  • https://my.oschina.net/u/3178270/blog/2906791
  • https://www.cnblogs.com/volcano-liu/p/10897897.html

原文地址:https://www.cnblogs.com/54chensongxia/p/12467971.html

时间: 2024-11-10 08:05:29

设计模式——装饰器模式的相关文章

说说设计模式~装饰器模式(Decorator)

装饰器模式,也叫又叫装饰者模式,顾名思义,将一个对象进行包裹,包装,让它变成一个比较满意的对象,这种模式在我们平时项目开发中,经常会用到,事实上,它是处理问题的一种技巧,也很好的扩展了程序,让程序代码不那么死板! 何时能用到它? 1. 需要扩展一个类的功能,或给一个类添加附加职责. 2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销. 3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实. 4. 当不能采用生成子类的方法进行扩充时. 其中我们认为第四种

PHP设计模式-装饰器模式

1.概念: 装饰器模式又叫做装饰者模式,是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.传统的编程模式都是子类继承父类实现方法的重载,使用装饰器模式,只需添加一个新的装饰器对象,更加灵活,避免类数目和层次过多. 2.角色: Component(被装饰对象基类):定义一个对象接口,以规范准备接受附加责任的对象. ConcreteComponent(具体被装饰对象):具体组件角色,即将要被装饰增加功能的类. Decorator(装饰者基类):装饰器接口. ConcreteDecor

设计模式-装饰器模式

前言 点击查看:设计模式系列 装饰器模式(Decorator) 定义:不通过派生类增改类属性动作,而是通过模式设计动态的达到这种效果,而且比继承更方便灵活减少程序的复杂性. 举例: 汪峰打造冠军团队. 首先团队类为空,经过汪峰不断的努力,为团队争取学员,也为团队队员打造合适的平台,让其发挥. 团队不断的变强,变完整,是由装饰者,根据不同的需求,给基类进行增改,一致最后赢得你的赞同,满足你的需求. 实现装配器模式的类图: 战队组建代码 //汪峰战队 abstract class WangFengT

设计模式—装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能. 意图:动态地给一个对象添加一些额外的职责.就增加功能来说,装饰器模式相比生成子类更为灵活. 主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀. 何时使用:在不想增加

设计模式 —— 装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern) 概念 装饰器模式 允许向一个现有的对象添加新的功能,同时又不改变其结构.装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的. 组成 装饰器模式由组件和装饰者组成. 抽象组件(Component):需要装饰的抽象对象. 具体组件(ConcreteComponent):是我们需要装饰的对象 抽象装饰类(Decorator):内含指向抽象组件的引用及装饰者共有的方法. 具体装饰类(ConcreteDecorator):被装饰的对

.net设计模式-装饰器模式

结构型设计模式:关注类与类之间的关系 装饰器模式:用组合+继承的方式对类作出扩展,可以动态为类型添加功能,甚至调整功能顺序,不修改业务类: 一个抽象类 1 public abstract class AbstractStudent 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 6 public abstract void Study(); 7 } 组合+继承该抽象类 1 public class Ba

设计模式-装饰器模式(Decrator Model)

文 / vincentzh 原文连接:http://www.cnblogs.com/vincentzh/p/6057666.html 目录 1.概述 2.目的 3.结构组成 4.实现 5.总结 1.概述 装饰器模式在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.通常给一个对象添加功能,要么在类中直接进行修改,要么通过子类继承来扩展对象的功能,又或者使用装饰模式将相应的功能进行任意的组合,使功能的扩展变的具有灵活性和动态性.装饰模式是通过创建一个包装对象,也就是装饰来包裹真实的对象,

PHP设计模式——装饰器模式

<?php /** * 装饰器模式 * 如果已有对象的部分内容或功能发生变化,但是不需要修改原始对象的结构,应使用装饰器模式 * * 为了在不修改对象结构的前提下对现有对象的内容或功能稍加修改,应使用装饰器模式 */ class Base{ protected $_content; public function __construct($content) { $this->_content = $content; } public function edit() { return $this

java设计模式----装饰器模式

Decorator装饰器,顾名思义,就是动态地给一个对象添加一些额外的职责,就好比为房子进行装修一样.因此,装饰器模式具有如下的特征: 它必须具有一个装饰的对象. 它必须拥有与被装饰对象相同的接口. 它可以给被装饰对象添加额外的功能. 用一句话总结就是:保持接口,增强性能. 装饰器通过包装一个装饰对象来扩展其功能,而又不改变其接口,这实际上是基于对象的适配器模式的一种变种.它与对象的适配器模式的异同点如下. 相同点:都拥有一个目标对象. 不同点:适配器模式需要实现另外一个接口,而装饰器模式必须实