Java设计模式 —— 【结构型模式】装饰者模式详解

server/2024/12/21 22:22:00/

文章目录

  • 前言
  • 结构说明
  • 案例演示
  • 小结
  • 静态代理和装饰者的区别


前言

在日常生活中,我们常会遇到一种场景:去快餐店吃饭,里面琳琅满目的主食,还有各式各样的配菜作为消费者,只管挑选就行,但是如果让我们来设计系统的话,我们该如何设计,每种搭配来一个类?
在这里插入图片描述
像上述设计,显然问题明显:

  • 扩展性不好
    • 如果要再加一种配料(火腿肠),我们就会发现需要给“炒饭”和“FriedNoodles”分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。
  • 产生过多的子类

以此引入装饰者模式(定义):

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。


结构说明

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

案例演示

使用装饰者模式对上述模型进行改进,UML类图如下:
在这里插入图片描述
快餐接口【抽象构件角色】:

java">//快餐抽象类
public abstract class FastFood {private float price;private String desc;public FastFood(float price, String desc) {this.price = price;this.desc = desc;}public float getPrice() {return price;}public String getDesc() {return desc;}public abstract float cost();  //获取价格
}

主食类【具体构建角色】:

java">//炒饭(具体构建角色)
public class FriedRice extends FastFood {public FriedRice() {super(10, "炒饭");}@Overridepublic float cost() {return getPrice();}
}//炒面(具体构建角色)
public class FriedNoodles extends FastFood {public FriedNoodles() {super(12, "炒面");}@Overridepublic float cost() {return getPrice();}
}

装饰者【抽象装饰者角色】:

java">//装饰者(抽象装饰者角色)
public abstract class Garnish extends FastFood {//声明快餐类变量private FastFood fastFood;public Garnish(FastFood fastFood, float price, String desc) {super(price, desc);this.fastFood = fastFood;}public FastFood getFastFood() {return fastFood;}
}

配菜【具体装饰者角色】:

java">//鸡蛋类(具体的装饰者角色)
public class Egg extends Garnish {public Egg(FastFood fastFood) {super(fastFood, 1, "鸡蛋");}@Overridepublic float cost() {//计算价格return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return getFastFood().getDesc() + "加" + super.getDesc();}
}//培根类
public class Bacon extends Garnish {public Bacon(FastFood fastFood) {super(fastFood, 2, "培根");}@Overridepublic float cost() {//计算价格return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return getFastFood().getDesc() + "加" + super.getDesc();}
}

测试:

java">public class Client {public static void main(String[] args) {//点一份炒饭FastFood food = new FriedRice();System.out.println(food.getDesc() + ": " + food.cost() + "元");//在上面的炒饭中加一个鸡蛋food = new Egg(food);System.out.println(food.getDesc() + ": " + food.cost() + "元");//再加一个培根food = new Bacon(food);System.out.println(food.getDesc() + ": " + food.cost() + "元");//点一份炒面FastFood noodles = new FriedNoodles();System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");//在上面的炒饭中加一个鸡蛋noodles = new Egg(noodles);System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");//再加一个培根noodles = new Bacon(noodles);System.out.println(noodles.getDesc() + ": " + noodles.cost() + "元");}
}

在这里插入图片描述


小结

优势:

  • 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。

  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

使用场景:

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

    不能采用继承的情况主要有两类:

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
    • 第二类是因为类定义不能继承(如final类)
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。


静态代理和装饰者的区别

  • 相同点:
    • 都要实现与目标类相同的业务接口
    • 在两个类中都要声明目标对象
    • 都可以在不修改目标类的前提下增强目标方法
  • 不同点:
    • 目的不同
      装饰者是为了增强目标对象
      静态代理是为了保护和隐藏目标对象
    • 获取目标对象构建的地方不同
      装饰者是由外界传递进来,可以通过构造方法传递
      静态代理是在代理类内部创建,以此来隐藏目标对象

http://www.ppmy.cn/server/152063.html

相关文章

96 vSystem

vSystem系统 1 技术背景 网络虚拟化旨在构建出一套与网络底层物理拓扑相互独立的逻辑网络环境,提供给不同需求的用户使用。基于这种思想,诞生出了 VLAN 技术和 VPN 技术。近年来, 随着以 VMM(Virtual Machine Monitor&#xff0c…

ensp 静态路由配置

A公司有广州总部、重庆分部和深圳分部3个办公地点,各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3,为路由器配置静态路由,使所有计算机能够互相访问,实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…

1.metagpt中的软件公司智能体 (PrepareDocuments Action)

1. PrepareDocuments Action 定义了一个 PrepareDocuments 类,它继承自 Action 类,并实现了一个用于准备项目文档的功能。具体来说,它的主要作用是初始化项目文件夹,设置 Git 环境,并将新增的需求写入 docs/requireme…

电商数据采集电商,行业数据分析,平台数据获取|稳定的API接口数据

电商数据采集可以通过多种方式完成,其中包括人工采集、使用电商平台提供的API接口、以及利用爬虫技术等自动化工具。以下是一些常用的电商数据采集方法: 人工采集:人工采集主要是通过基本的“复制粘贴”的方式在电商平台上进行数据的收集&am…

如何更改 maven 指定的 java 版本 set JAVA_HOME=C:\Program Files\Java\jdk1.8

当我们用 mvn 在终端执行的时候 例如 mvn clean test执行结果如下: 此时我们想要修改 maven 指定的JAVA_HOME 找到maven的安装目录,打开 mvn.cmd 然后鼠标右键,点击编辑按钮 将 第一行 JAVA_HOME 设置为自己的本地java目录即可 然后再次…

OpenLinkSaas 2025年1月开发计划

先来看看OpenLinkSaas的大目标 在OpenLinkSaas的产品目标中,让开发人员更加方便的使用云资源是目标之一。通过各大云厂商的API,来可视化云上基础设施的数据是远远不够的。我们准备在2025年1月份增加方便管理和运营研发场景下服务器的能力。 这部分的功能…

自动化立体仓库堆垛机SRM控制系统货叉控制功能块开发设计

1、堆垛机SRM控制系统硬件组态如下图 货叉控制G120变频器,通信报文111 G120变频器配置调试 2、堆垛机SRM控制系统HMI屏幕页面如下图 运行、起升、货叉相关参数设定 3、堆垛机SRM控制系统中相关变量定义如下图 其中包含货叉控制相关变量:货叉左极限、货叉左居中 货叉右极限…

第八章:持续集成管理

持续集成管理 一、整体架构说明 本文档详细描述DevOps持续构建(CI)管理系统的设计方案,包括流水线管理员配置和流水线管理两大核心模块,以及相关的具体实现细节和最佳实践案例。 系统设计目标 持续构建(CI)管理系统旨在提供一个完整的构建流水线解决…