结构型模式 - 装饰者模式 (Decorator Pattern)

news/2025/2/27 2:24:35

结构型模式 - 装饰者模式 (Decorator Pattern)

在展开讲装饰者模式之前,不得不提一下代理模式,因为这两者在一定的层度上是有相似性的, 通过对比可以让我们更好的理解装饰者.

定义与核心目的

  • 装饰者模式
    • 定义:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
    • 核心目的:主要是为对象添加额外的功能,强调的是对对象功能的增强和扩展,是在不改变原有对象结构的基础上,给对象增加新的行为。
  • 代理模式
    • 定义:为其他对象提供一种代理以控制对这个对象的访问。
    • 核心目的:主要是控制对对象的访问,代理对象充当了客户端和目标对象之间的中介,客户端通过代理对象来间接访问目标对象,从而可以在访问前后进行一些额外的操作,如权限验证、缓存等。

接下来通过代码对比一下装饰者模式、代理模式

// 装饰者模式

// 定义一个接口
interface Component {
    void operation();
}

// 具体组件
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("执行基本操作");
    }
}

// 装饰者抽象类
abstract class Decorator implements Component {
    protected Component component;

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

    @Override
    public void operation() {
        component.operation();
    }
}


// 具体装饰者
class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("添加额外操作");
    }
}

// 测试代码
public class DecoratorPatternExample {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        Component decoratedComponent = new ConcreteDecorator(component);
        decoratedComponent.operation();
    }
}

如果把上面装饰者的例子中 Decorator 类去掉抽象 abstract 修饰, 然后把持有的 Component 改成具体对象 ConcreteComponent 并在构造方法里面直接创建要代理的对象, 那它就变成静态代理模式!!!

下面这是一个代理模式例子, 来看看它俩的相似之处吧.

// 定义一个接口
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("执行真实请求");
    }
}

// 代理主题
class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject() {
        this.realSubject = new RealSubject();
    }

    @Override
    public void request() {
        System.out.println("在请求前进行一些操作");
        realSubject.request();
        System.out.println("在请求后进行一些操作");
    }
}

// 测试代码
public class ProxyPatternExample {
    public static void main(String[] args) {
        ProxySubject proxy = new ProxySubject();
        proxy.request();
    }
}

再看一个经典的装饰者模式, 来加强一下装饰者的理解

// 抽象组件:饮品
interface Beverage {
    String getDescription();
    double cost();
}

// 具体组件:浓缩咖啡
class Espresso implements Beverage {
    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }

    @Override
    public double cost() {
        return 2.0;
    }
}

// 抽象装饰者:调料
abstract class CondimentDecorator implements Beverage {
    protected Beverage beverage;

    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// 具体装饰者:牛奶
class Milk extends CondimentDecorator {
    public Milk(Beverage beverage) {
        super(beverage);
    }

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

    @Override
    public double cost() {
        return beverage.cost() + 0.5;
    }
}

// 具体装饰者:巧克力
class Chocolate extends CondimentDecorator {
    public Chocolate(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",加巧克力";
    }

    @Override
    public double cost() {
        return beverage.cost() + 1.0;
    }
}

// 测试类
public class CoffeeShopExample {
    public static void main(String[] args) {
        // 创建一个浓缩咖啡
        Beverage espresso = new Espresso();
        System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");

        // 给浓缩咖啡加牛奶
        Beverage espressoWithMilk = new Milk(espresso);
        System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");

  		// 给加了牛奶的浓缩咖啡再加巧克力
        Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);
        System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");
    }
}

这时候我们可以再创建一个装饰者, 比如配送方式的装饰者,看顾客是到店自取还是外卖配送, 因为这两种方式价格是不一样的.
这时候我们只需要另外建一个具体装饰者类即可.

// 抽象配送装饰者
abstract class DeliveryDecorator implements Beverage {
    protected Beverage beverage;

    public DeliveryDecorator(Beverage beverage) {
        this.beverage = beverage;
    }
}

// 具体配送装饰者:外卖配送
class DeliveryByCourier extends DeliveryDecorator {
    public DeliveryByCourier(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",外卖配送";
    }

    @Override
    public double cost() {
        return beverage.cost() + 3.0; // 假设外卖配送费 3 元
    }
}

// 具体配送装饰者:到店自取
class PickupInStore extends DeliveryDecorator {
    public PickupInStore(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ",到店自取";
    }

    @Override
    public double cost() {
        return beverage.cost(); // 到店自取无额外费用
    }
}

// 测试类
public class CoffeeShopExample {
    public static void main(String[] args) {
        // // 创建一个浓缩咖啡
        // Beverage espresso = new Espresso();
        // System.out.println(espresso.getDescription() + ",价格:" + espresso.cost() + " 元");

        // // 给浓缩咖啡加牛奶
        // Beverage espressoWithMilk = new Milk(espresso);
        // System.out.println(espressoWithMilk.getDescription() + ",价格:" + espressoWithMilk.cost() + " 元");

        // // 给加了牛奶的浓缩咖啡再加巧克力
        // Beverage espressoWithMilkAndChocolate = new Chocolate(espressoWithMilk);
        // System.out.println(espressoWithMilkAndChocolate.getDescription() + ",价格:" + espressoWithMilkAndChocolate.cost() + " 元");

		// 使用的时候就可以 2 选 1 种配送
        // 选择外卖配送
        Beverage espressoWithAllAndDelivery = new DeliveryByCourier(espressoWithMilkAndChocolate);
        System.out.println(espressoWithAllAndDelivery.getDescription() + ",价格:" + espressoWithAllAndDelivery.cost() + " 元");

        // 选择到店自取
        Beverage espressoWithAllAndPickup = new PickupInStore(espressoWithMilkAndChocolate);
        System.out.println(espressoWithAllAndPickup.getDescription() + ",价格:" + espressoWithAllAndPickup.cost() + " 元");
    }
}

这时候假设我们需求又双叒变更了, 我们只需要增加装饰者, 比如说要按顾客的会员等级对订单总额打折处理… 发挥你的想象吧, 它有无限的可能~


http://www.niftyadmin.cn/n/5869387.html

相关文章

江协科技/江科大-51单片机入门教程——P[1-1] 课程简介P[1-2] 开发工具介绍及软件安装

本教程也力求在玩好单片机的同时了解一些计算机的基本概念,了解电脑的一些基本操作,了解电路及其元器件的基本理论,为我们学习更高级的单片机,入门IT和信息技术行业,打下一定的基础。 目录 1.课程简介 2.开发工具及…

SQL: DDL,DML,DCL,DTL,TCL,

Structured Query Language,结构化查询语言, 是一种用于管理和操作关系数据库的标准编程语言。 sql的分类 DQL(Data Query Language):数据查询语言 DDL(Data Definition Language):数据定义语…

构成正方形的数量

构成正方形的数量 真题目录: 点击去查看 E 卷 100分题型 题目描述 输入N个互不相同的二维整数坐标&#xff0c;求这N个坐标可以构成的正方形数量。[内积为零的的两个向量垂直] 输入描述 第一行输入为N&#xff0c;N代表坐标数量&#xff0c;N为正整数。N < 100 之后的 …

前端页面什么是全屏嵌入/什么是局部嵌入

1. 什么是 <div> 容器标签&#xff1f; 通俗来说&#xff0c;<div> 标签就像一个“盒子”&#xff0c;你可以把任何东西放进去&#xff0c;比如文字、图片、按钮等。它是一个非常灵活的标签&#xff0c;用来组织和分隔网页内容。 举个例子&#xff1a; 想象你有…

Redis面试题----MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

要保证 Redis 中存储的 20w 数据都是热点数据&#xff0c;可以从数据筛选和数据淘汰两个大的方面来考虑&#xff0c;以下是详细的实现思路和方法&#xff1a; 数据筛选 1. 基于业务规则 分析业务场景&#xff1a;不同的业务场景有不同的热点数据特征。例如&#xff0c;在电商…

数字IC低功耗后端设计实现之power gating和isolation技术

考虑低功耗设计需求&#xff0c;下图中间那个功能模块是需要做power domain的&#xff0c;即这个模块需要插MTCMOS。需要开启时&#xff0c;外面的VDD会和这个模块的LOCAL VDD形成通路&#xff0c;否则就是断开即power off状态。 这些低功耗设计实现经验&#xff0c;你真的懂了…

如何在 WPS 中集成 DeepSeek

如何在 WPS 中集成 DeepSeek&#xff1a;从零基础到高阶开发的完整指南 DeepSeek 作为国内领先的 AI 办公助手&#xff0c;与 WPS 的深度整合可显著提升文档处理效率。本文提供 ​4 种集成方案&#xff0c;覆盖从「小白用户」到「企业开发者」的全场景需求&#xff0c;并包含 …

flutter Running Gradle task ‘assembleDebug‘...

这个和单独的android还不太一样 Flutter 在Android studio运行时卡在Running Gradle task assembleDebug... - 简书