设计模式之装饰器模式

一、介绍

装饰器模式(Decoration Pattern),属于结构型设计模式 ,用于在不改变现有对象 的基础上,对该对象的方法动态 地添加新的功能,实现对该对象原有方法的增强

装饰器模式的设计思想是将对象的核心功能附加功能独立开来。核心功能由现有对象提供,附加功能由装饰器提供。

装饰器的实现思路是存在一个抽象的装饰器类 ,该装饰器类用于对现有的对象进行包装,然后通过该装饰器的具体子类对包装的类的方法进行增强。推而论之,在存在多个装饰器具体子类的情况下,可以动态地对现有对象随心所欲进行嵌套包装,对现有对象进行包装后,可以在已包装基础上进行多层嵌套包装

二、主要角色

在装饰器模式中,主要包含以下四个角色:

  • 核心组件抽象接口(Component)

规定了被装饰对象的行为。

  • 核心组件具体实现(ComponentImpl)

实现核心组件抽象接口,对接口规定的行为进行具体实现。

  • 抽象装饰器(AbsDecoration)

通用的装饰ComponentImpl的装饰器,该装饰器必须包含一个以被装饰对象为参数的构造方法。

该装饰器设定为抽象类的原因是通过其构造函数管理被装饰的对象,以此来限制具体装饰器的构造方法必须传入被装饰的对象。

  • 具体装饰器(Decoration)

继承抽象装饰器。用于增强对被装饰对象某一功能的特定装饰逻辑

三、案例

核心组件抽象接口

复制代码
public interface Coffee {
    /**
     * 获取描述信息
     */
    String getDescription();

    /**
     * 获取花费
     */
    double getCost();
}

核心组件具体实现

黑咖啡 就是核心

java 复制代码
public class BlackCoffee implements Coffee{
    @Override
    public String getDescription() {
        return "黑咖啡";
    }

    @Override
    public double getCost() {
        return 10;
    }
}

抽象装饰器

java 复制代码
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
    @Override
    public String getDescription() {
        return coffee.getDescription();
    }

    @Override
    public double getCost() {
        return coffee.getCost();
    }
}

具体装饰器

给咖啡加牛奶

java 复制代码
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription()+ " 加奶";
    }

    @Override
    public double getCost() {
        return super.getCost()+ 5 ;
    }

}

给咖啡加糖

java 复制代码
public class SugarDecorator extends CoffeeDecorator{
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return super.getCost() + 2;
    }
}

测试

在存在多个装饰器具体子类的情况下,可以动态地对现有对象随心所欲进行嵌套包装

上面的例子也可以加2分牛牛奶一份糖,可以随心所欲的进行装饰。

四、装饰器模式在jdk的应用

java中的IO是明显的装饰器模式的运用。FilterInputStream,FilterOutputStream,FilterRead,FilterWriter分别为具体装饰器的父类,相当于Decorator类,它们分别实现了InputStream,OutputStream,Reader,Writer类(这些类相当于Component,是其他组件类的父类,也是Decorator类的父类)。

继承自InputStream,OutputStream,Reader,Writer这四个类的其他类是具体的组件类,每个都有相应的功能,相当于ConcreteComponent类。而继承自FilterInputStream,FilterOutputStream,FilterRead,FilterWriter这四个类的其他类就是具体的装饰器对象类,即ConcreteDecorator类。通过这些装饰器类,可以给我们提供更加具体的有用的功能。如FileInputStream是InputStream的一个子类,从文件中读取数据流,BufferedInputStream是继承自FilterInputStream的具体的装饰器类,该类提供一个内存的缓冲区类保存输入流中的数据。我们使用如下的代码来使用BufferedInputStream装饰FileInputStream,就可以提供一个内存缓冲区来保存从文件中读取的输入流

①组件接口,相当于Component:

InputStream

OutputStream

Reader

Writer

②组件的实现类,相当于ConcreteComponent

FileInputStream

③装饰器接口,相当于Decorator

FilterInputStream

FilterOutputStream

FilterRead

FilterWriter

④装饰器类

BufferedInputStream继承自FilterInputStream

使用说明:我们可以用BufferedInputStream修饰FileInputStream

例如

java 复制代码
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); 
//其中file为某个具体文件的File或者FileDescription对象
java 复制代码
FileWriter file = new FileWriter("sample.txt");
BufferedWriter writer = new BufferedWriter(file);
writer.write("a small amount of sample text");
writer.newLine();
writer.close();

五、优缺点

优点

由于装饰器对象和被装饰的对象都实现于核心抽象接口,根据面向接口编程原则,它们具有相同的行为。

装饰器不仅可以将已有对象进行包装,也可以对装饰器对象嵌套包装。

通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。

避免多个被装饰对象与装饰器而导致最终类数量上的膨胀。
缺点

每当为被装饰对象添加新的功能,都需要为其新建一个装饰类

六、适用场景

动态功能扩展: 运行时为对象添加或移除功能(如日志、缓存、加密)
避免多层继承: 替代复杂的继承树(如 BufferedFileReader、EncryptedFileReader)
透明性要求: 客户端无需感知对象是否被装饰(保持接口一致性)。
功能组合复用: 支持多种功能的自由组合(如 压缩 + 加密 文件流)

相关推荐
桦说编程1 小时前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
躺平大鹅3 小时前
Java面向对象入门(类与对象,新手秒懂)
java
静水流深_沧海一粟4 小时前
04 | 别再写几十个参数的构造函数了——建造者模式
设计模式
StarkCoder4 小时前
从UIKit到SwiftUI的迁移感悟:数据驱动的革命
设计模式
初次攀爬者4 小时前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺4 小时前
搞懂@Autowired 与@Resuorce
java·spring boot·后端
Derek_Smart5 小时前
从一次 OOM 事故说起:打造生产级的 JVM 健康检查组件
java·jvm·spring boot
NE_STOP6 小时前
MyBatis-mybatis入门与增删改查
java
孟陬10 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端