8.设计模式之装饰器模式

前言

装饰器模式以对客户透明的方式动态的给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰器模式可以在不是用创造更多子类的情况下,将对象的功能加以拓展,是继承关系的一个替代方案

本节,就装饰器模式展开详细介绍。

1. 装饰器中的角色

1.1 抽象构件(Component)角色:

定义一个抽象接口以规范准备接收附加责任的对象。

1.2 具体构件(ConcreteComponent)角色:

实现抽象构件,通过装饰角色为其添加一些职责。

1.3 抽象装饰(Decorator)角色:

继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

1.4 具体装饰(ConcreteDecorator)角色:

实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

2. 代码示例

2.1 定义抽象构件

package com.wanlong.design_pattern.structure.decorate;

/**
 * @Description:抽象构件
 * @Author: wanlong
 * @Date: 2023/5/12 17:09
 **/
public interface Component {
    void sampleOpreation();
}

2.2 定义具体构件

package com.wanlong.design_pattern.structure.decorate;

/**
 * @author wanlong
 * @version 1.0
 * @description: 具体构件
 * @date 2022/9/19 14:59
 */
public class ConcreteComponent implements Component {
    @Override
    public void sampleOpreation() {
        // TODO 完成相关的业务代码
        System.out.println("具体类业务逻辑");
    }
}

2.3 定义抽象装饰角色

package com.wanlong.design_pattern.structure.decorate;

/**
 * @author wanlong
 * @version 1.0
 * @description: 抽象装饰角色
 * @date 2022/9/19 14:59
 */

public abstract class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void sampleOpreation() {
        //委派给构件
        component.sampleOpreation();
    }

}

2.4 定义具体装饰角色

2.4.1 定义具体装饰器A

package com.wanlong.design_pattern.structure.decorate;

/**
 * @author wanlong
 * @version 1.0
 * @description: 具体装饰器A
 * @date 2022/9/19 15:00
 */

public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void sampleOpreation() {
        super.sampleOpreation();
        //TODO 完成相关的业务代码
        System.out.println("包装类做额外业务逻辑A");
    }
}

2.4.2 定义具体装饰器B

package com.wanlong.design_pattern.structure.decorate;

/**
 * @author wanlong
 * @version 1.0
 * @description: 具体装饰器B
 * @date 2022/9/19 15:00
 */

public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void sampleOpreation() {
        super.sampleOpreation();
        //TODO 完成相关的业务代码
        System.out.println("包装类做额外业务逻辑B");

    }
}

2.5 客户端测试

package com.wanlong.design_pattern.structure.decorate;

import org.junit.Test;

/**
 * @author wanlong
 * @version 1.0
 * @description:
 * @date 2022/9/19 15:00
 */
public class Client {

    @Test
    public void testDecorator() {

        Component componentA = new ConcreteDecoratorA(new ConcreteComponent());
        componentA.sampleOpreation();
        System.out.println("===============");

        Component componentB = new ConcreteDecoratorB(new ConcreteComponent());
        componentB.sampleOpreation();
        System.out.println("===============");

        Component componentC = new ConcreteDecoratorB(componentA);
        componentC.sampleOpreation();

    }
}

代码运行结果

具体类业务逻辑
包装类做额外业务逻辑A
`===============
具体类业务逻辑
包装类做额外业务逻辑B
·===============
具体类业务逻辑
包装类做额外业务逻辑A
包装类做额外业务逻辑B

3. 总结

3.1 优缺点

3.1.1 优点:

  1. 装饰器模式是对继承的有利补充,比继承灵活,在不改变原有对象的情况下,可以动态的增加或删除对象的职责,即插即用
  2. 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同的效果,得到功能更加强大的对象
  3. 装饰器模式完全遵循“开闭原则”
  4. 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。

3.1.2 缺点:

  1. 装饰器模式会增加许多子类,过度使用会增加程序的复杂性
  2. 装饰模式易于出错,调试排查比较麻烦。
  3. 产生很多小对象。大量小对象占据内存,一定程度上影响性能。

3.2 使用场景

  1. 需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  2. 需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现。
  3. 对象的功能要求可以动态地添加,也可以再动态地撤销。

3.3 装饰器模式与桥接模式的区别

两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。

  1. 装饰器模式的办法就是把每个子类中比基类多出来的行为放到单独的类里面。这样当这些描述额外行为的对象被封装到基类对象里面时,就得到了所需要的子类对象,将这些描述额外行为的类,排列组合可以造出很多的功能组合来,如果用静态继承的办法创建这些组合出来的类所涉及到工作量很大,以致实际上很难做到。装饰器模式要求所有的这些“额外行为类”具有相同的接口

  2. 桥接模式的解决办法则又有所不同,桥接模式把原来的两个基类的实现化细节抽出来,再建造一个实现化的等级结构中,然后再把原有的基类改造成一个抽象化等级结构。桥接模式中抽象化角色的子类不能像装饰器模式那样嵌套,桥接模式却可以连续使用。换言之,一个桥接模式的实现化角色可以成为下一步桥接模式的抽象化角色。

3.4 装饰器模式与适配器模式的区别

装饰器与适配器都有一个别名叫做 包装模式(Wrapper),它们看似都是起到包装一个类或对象的作用,但是使用它们的目的很不一样。

适配器模式的意义是要将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的。

而装饰器模式不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能,或者改变原有对象的处理方式而提升性能。所以这两个模式设计的目的是不同的。

在jdk中,InputStreamReader是一个适配器,因为它把InputStream的API转换成Reader的API。InputStream是被适配的类,而 Reader是适配的目标类。InputStreamReader做为适配器类把InputStream类的一个实例包装起来,从而能够把InputStream的API转换成Reader的API。

而BufferReader是一个装饰器类,因为它实现Reader,并且包装了一个Reader。一些流处理器可以对另一些流处理器起到装饰作用,形成新的、具有改善功能得流处理器。类似地,BufferedInputStream、BufferOutputStream、BufferWriter 各自都是它们自己的装饰类。LineNumberReader、FilterReader和 PushbackReader均是Reader的装饰类,因为它们自己是Reader类,而且包装其他的Reader类。

CharArrayReader、FileReader、PipedReader和StringReader类不是装饰类,因为它们包装的是字符数值组、File、PipedWriter和String类。它们应该被看做字符数值组、File、PipedWriter 和String类的适配器类。

3.5 开发中使用场景

  1. IO中输入流和输出流的设计
  2. Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类
  3. HttpServletRequestWrapper,HttpServletRequestWrapper 类,增强了request对象的功能。

其中IO流中使用装饰器模式各个角色如下:

  1. Component抽象构件角色:
    io流中的InputStream、OutputStream、Reader、Writer
  2. ConcreteComponent 具体构件角色:
    io流中的FileInputStream、FileOutputStream
  3. Decorator装饰角色:
    持有一个抽象构件的引用:io流中的FilterInputStream、FilterOutputStream
  4. ConcreteDecorator具体装饰角色:
    负责给构件对象增加新的责任。Io流中的BufferedOutputStream、BufferedInputStream等。

以上,本人菜鸟一枚,如有问题,请不吝指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值