设计模式(十一)----结构型模式之装饰者模式

news2024/9/28 1:18:09

1、概述

我们先来看一个快餐店的例子。

快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。

使用继承的方式存在的问题:

  • 扩展性不好

    如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。

  • 产生过多的子类

定义:

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

2、结构

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

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。上图中的抽象快餐类

  • 具体构件(ConcreteComponent)角色 :实现抽象构件,通过装饰角色为其添加一些职责。炒米粉炒面等具体角色

  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。加鸡蛋加火腿抽象类

  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。具体加鸡蛋还是加火腿

3、案例

我们使用装饰者模式对快餐店案例进行改进,体会装饰者模式的精髓。

类图如下:

代码如下:

//快餐接口
public abstract class FastFood {
    private float price;
    private String desc;
​
    public FastFood() {
    }
​
    public FastFood(float price, String desc) {
        this.price = price;
        this.desc = desc;
    }
​
    public void setPrice(float price) {
        this.price = price;
    }
​
    public float getPrice() {
        return price;
    }
​
    public String getDesc() {
        return desc;
    }
​
    public void setDesc(String desc) {
        this.desc = desc;
    }
​
    public abstract float cost();  //获取价格
}
​
//炒饭
public class FriedRice extends FastFood {
​
    public FriedRice() {
        super(10, "炒饭");
    }
​
    public float cost() {
        return getPrice();
    }
}
​
//炒面
public class FriedNoodles extends FastFood {
​
    public FriedNoodles() {
        super(12, "炒面");
    }
​
    public float cost() {
        return getPrice();
    }
}
​
//配料类  装饰者
public abstract class Garnish extends FastFood {
​
    private FastFood fastFood;
​
    public FastFood getFastFood() {
        return fastFood;
    }
​
    public void setFastFood(FastFood fastFood) {
        this.fastFood = fastFood;
    }
​
    public Garnish(FastFood fastFood, float price, String desc) {
        super(price,desc);
        this.fastFood = fastFood;
    }
}
​
//鸡蛋配料
public class Egg extends Garnish {
​
    public Egg(FastFood fastFood) {
        super(fastFood,1,"鸡蛋");
    }
​
    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }
​
    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
​
//培根配料
public class Bacon extends Garnish {
​
    public Bacon(FastFood fastFood) {
​
        super(fastFood,2,"培根");
    }
​
    @Override
    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }
​
    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
​
//测试类
public class Client {
    public static void main(String[] args) {
        //点一份炒饭
        FastFood food = new FriedRice();
​
        System.out.println(food.getDesc() + "  " + food.cost() + "元");
​
        System.out.println("===============");
​
        //在上面的炒饭中加一个鸡蛋
        food = new Egg(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "元");
​
        System.out.println("================");
        //再加一个鸡蛋
        food = new Egg(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "元");
​
        System.out.println("================");
        food = new Bacon(food);
        System.out.println(food.getDesc() + "  " + food.cost() + "元");
    }
}

测试结果

好处:

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

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

4、使用场景

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

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

    • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;

    • 第二类是因为类定义不能继承(如final类)

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 当对象的功能要求可以动态地添加,也可以在动态地撤销时。比如鸡蛋已经卖完了,这时候只需要移除鸡蛋类就可以了。

5、JDK源码解析

IO流中的包装类使用到了装饰者模式。BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。

我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter

public class Demo {
    public static void main(String[] args) throws Exception{
        //创建BufferedWriter对象
        //创建FileWriter对象
        FileWriter fw = new FileWriter("C:\\Users\\Think\\Desktop\\a.txt");
        BufferedWriter bw = new BufferedWriter(fw);
​
        //写数据
        bw.write("hello Buffered");
​
        bw.close();
    }
}

使用起来感觉确实像是装饰者模式,接下来看它们的结构:

小结:

BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为多态基类声明virtual析构函数

我们知道,有时会让一个基类指针指向用 new 运算符动态生成的派生类对象(类似接口的作用);同时,用 new 运算符动态生成的对象都是通过 delete 指向它的指针来释放的。如果一个基类指针指向用 new 运算符动态生成的派生类…

算法23:多叉树_派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每…

MySQL知识点小结

事务 进行数据库提交操作时使用事务就是为了保证四大特性,原子性,一致性,隔离性,持久性Durability. 持久性:事务一旦提交,对数据库的改变是永久的. 事务的日志用于保存对数据的更新操作. 这个操作T1事务操作的会发生丢失,因为最后是T2提交的修改,而且T2先进行一次查询,按照A…

线性回归算法和逻辑斯谛回归算法详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解决策树算法和CART决策树算法详细介绍及其原理详解线性回归算法和逻辑斯谛回归算法详细介绍及其原理详解 文章目录相关文章前言一、线性回归二、逻辑斯谛回归总结前言 今天给大家…

2020 NOC 编程猫创新编程全国决赛小学组第一部分(客观题)

Q1. 某角色的造型区及未完成的脚本如下,当前造型名称为猫,若想切换到熊冬眠的 浩型,变量次数的值不可能为( ) A. 2 B. 3 C. 4 D. 5 Q2. 运行下图所示脚本,角色的运动轨迹为( ) Q3. 运行脚本,变量“y”的值为( ) A. 200 B. 2200 C. 2000 D. 4000 Q4. 运行…

盘点:9款身份和访问管理工具

身份和访问管理(IAM)长期以来一直是安全领导者职业生涯的关键“试炼场”,许多人在身份技术部署方面做出了事关成败的决定。 确保安全访问和身份管理是网络安全态势的两大基础 。同时,人员、应用程序和系统登录的方式以及它们彼此集…

以业务行为驱动的反入侵安全能力建设

0x0 背景 最近听到一些甲方安全领域的专家分享了部分安全建设的经验,对安全运营下的反入侵技术能力建设有了些新的看法,依靠单个/多个异构的安全产品的关联能力形成的安全中台并不能在实际的攻防对抗当中占据主动地位,且很容易达到一个天花板…

Mr. Cappuccino的第45杯咖啡——Kubernetes之部署SpringBoot项目

Kubernetes之部署SpringBoot项目创建一个SpringBoot项目将SpringBoot项目打成Jar包使用Dockerfile制作镜像部署SpringBoot项目创建一个SpringBoot项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache…

一、前端稳定性规约该如何制定

前言 稳定性是数学或工程上的用语&#xff0c;判别一系统在有界的输入是否也产生有界的输出。若是&#xff0c;称系统为稳定&#xff1b;若否&#xff0c;则称系统为不稳定。 前端稳定性的体系建设大约可以分为了发布前&#xff0c;发布后&#xff0c;以及事故解决后三个阶段…

建立做机器学习项目的范式

建立起做机器学习项目的范式&#xff0c;萃取出核心步骤&#xff0c;避免后面做项目没有明确的方向。 核心步骤&#xff1a; 1、明确自己想做什么样的项目&#xff0c;感兴趣的领域&#xff1b; 2、找到满足项目的数据集&#xff0c;开源的或者自建数据集&#xff1b; 数据…

《数据结构》二叉树面试题

&#x1f451;作者主页&#xff1a;Java冰激凌 &#x1f4d6;专栏链接&#xff1a;数据结构 目录 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先力扣 思路分析 代码 思路拓展 根据一棵树的前序遍历与中序遍历构造二叉树。力扣 思路分析 代码 根据一棵树的中序遍历与…

Linux系统看门狗应用编程

目录看门狗应用编程介绍打开设备获取设备支持哪些功能&#xff1a;WDIOC_GETSUPPORT获取/设置超时时间&#xff1a;WDIOC_GETTIMEOUT、WDIOC_SETTIMEOUT开启/关闭看门狗&#xff1a;WDIOC_SETOPTIONS喂狗&#xff1a;WDIOC_KEEPALIVE看门狗应用编程实战在产品化的嵌入式系统中&…

[计算机网络(第八版)]第二章 物理层(章节测试/章节作业)

章节作业 带答案版 选择题 (单选题)双绞线是用两根绝缘导线绞合而成的&#xff0c;绞合的目的是&#xff08; &#xff09;。 A. 减少干扰 B. 提高传输速度 C. 增大传输距离 D. 增大抗拉强度(单选题)在电缆中采用屏蔽技术可以带来的好处主要是&#xff08; &#xff09;。 A…

DNS 域名解析

介绍域名 网域名称&#xff08;英语&#xff1a;Domain Name&#xff0c;简称&#xff1a;Domain&#xff09;&#xff0c;简称域名、网域。 域名是互联网上某一台计算机或计算机组的名称。 域名可以说是一个 IP 地址的代称&#xff0c;目的是为了便于记忆。例如&#xff0c…

Latex中的表格(2)

Latex中的表格一、一个加脚注的三线表的例子二、一个表格中加注释的例子三、两个并排的两个表格的例子3.1 使用小页环境并排表格3.2 使用子表格并排表格四、一个格式复杂的表格的例子五、一个长表格的例子这篇文章主要罗列一些特殊的表格例子。内容来自&#xff1a;一篇北师大学…

深度剖析数据在内存的存储

目录1.深度剖析数据在内存的存储(前言)数据类型介绍类型的基本归类整形在内存中的存储原码、反码、补码大小端练习总结1.深度剖析数据在内存的存储(前言) 今天就让我戴佳伟给大家讲一下数据在内存中的存储。之中有好多让我们深思的点&#xff0c;大家都拿起笔记本&#xff0c;…

【机器学习笔记】Python基础笔记

目录基础语法加载数据&#xff1a;pd.read_csv查看数据大小&#xff1a;shape浏览数据行字段&#xff1a;columns浏览少量数据&#xff1a;head()浏览数据概要&#xff1a;describe()基础功能语法缺省值去除缺失值&#xff1a;dropna按行删除&#xff1a;存在空值&#xff0c;即…

【数据库】第二章 关系数据库

第二章 关系数据库 2.1关系数据结构及形式化定义 关系 域&#xff08;domain) :域是一组具有相同数据类型的值的集合&#xff0c;可以取值的个数叫基数 笛卡尔积 &#xff1a;一个记录叫做一个元组&#xff08;tuple),元组中每一个属性值&#xff0c;叫一个分量 基数&…

35测试不如狗?是你自己技术不够的怨怼罢了

一、做软件测试怎么样&#xff1f; 引用著名软件测试专家、清华大学郑人杰教授的说法&#xff1a;软件测试工程师是一个越老越吃香的职业。 其中就表达了软件测试工作相对稳定、对年龄没有限制、而且随着项目经验的不断增长和对行业背景的深入了解&#xff0c;会越老越吃香。…

刚上岸字节测试开发岗,全网最真实的大厂面试真题

首先我来解释一下为什么说是全网最真实的面试题&#xff0c;相信大家也发现软件测试面试题在网上流传也已不少&#xff0c;但是经过仔细查看发现了两个很重要的问题。 第一&#xff0c;网上流传的面试题的答案并不能保证百分百正确。也就是说各位朋友辛辛苦苦花了很多时间准备…