大话设计模式-装饰器模式

news2025/1/15 22:43:05

大话设计模式书中,作者举了一个穿衣服的例子来为我们引入装饰器模式。


概念


定义

装饰模式在书中的定义是:
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

这句话直接去理解可能会有点抽象,我结合书中的例子来讲讲自己的理解。假设有一天,女朋友要你陪她去逛商场,她今天要做很多事情:

  • 去干洗店拿干洗的衣服
  • 去服装店取定制地衣服
  • 去做头发
  • 去美甲
  • 去美食街吃夜宵
  • ......

她去做这些事情地顺序是不确定的,而且这些事情之间也没有什么太大的关联。那我们在程序中去把这些事情串联起来呢?简单的为每一种操作写一个类,然后在主函数中排列组合吗?这样做也不是不可以,但是这就破坏了我们程序当中的封装性。用书中的话说就是:

你光着身子, 当着大家的面,先穿T恤,再穿裤子,再穿鞋,仿佛在跳穿衣舞。难道你穿衣服都是在众目睽睽下穿的吗?

实际上,大多数时候我们不希望将太多的细节暴露给用户,因此面对这种情况,我们可能就需要用到装饰模式。让我们先来看看它的结构图。

结构

这是作者在书中给出的原图。同样,直接看图可能很难让我们理解什么是策略模式。我们继续用上面那个例子来类比举例。

Component是定义一个对象接口,可以给这些对象动态地添加职责。

这句话怎么理解,其实在上面这个例子中,Conponent其实就相当于“人”。为什么这么说,因为只有“人”才能完成拿衣服,买衣服,做头发等等这些事情(不要抬杠哈,你懂我意思)。那这个Operation()方法就相当于是做完了的事情(可以是空的或者多件事情的排列组合)。

ConcreteComponent是定义了一个具体的对象,也可以给这个 对象添加一些职责。

这句话又怎么理解呢,你可以把ConcreteComponent看成是女朋友,她是一个具体的人,是她要去完成这些行为。同样Operation()方法就相当于是她已经做完了的事情(可以是空的或者多件事情的排列组合)。

Decorator,装饰抽象类,继承了Component, 从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰 对象,起到给Component添加职责的功能。

这句话比较长,也比较抽象。什么是装饰抽象类,在这个例子中还真不是很好解释,你可以理解为一个“盒子”,里面装的是“前一刻的女朋友”(可能比较牵强哈)。或者更好的理解是把他当成一种规则:你这个人在进我们店之前是什么样的,带了哪些东西;你带来的东西我们原封不动,我们店给你提供了一些服务之后,你还是完完整整的一个人从我这里走出去。所有的店都必须遵循这个规则(不然就是黑店了)。ConcreteDecorator就相当于是遵循这些规则的具体的干洗店,服装店,理发店,美甲店,小吃店等等了。

那么整个流程是怎么样的呢。

  1. 女朋友出门饿了,先来小吃店吃了小吃,变成了“吃了小吃的女朋友”
  2. “吃了小吃的女朋友”吃太饱了,准备去干洗店先拿一下衣服,变成了“拿了衣服的吃了小吃的女朋友”
  3. “拿了衣服的吃了小吃的女朋友”觉得走累了,先去理发店做个于是头发休息一下,变成了“做了头发的拿了衣服的吃了小吃的女朋友”
  4. ......
  5. 最后女朋友做完了所有的事情

经过这个流程,大家应该能够理解了具体装饰类的作用了吧。比如装饰类A的作用就是,把B的C变成A的B的C

优缺点

优点:

在书中对于装饰模式的优点是这样说的:

装饰模式是利用SetComponent来对对象进行包 装的。这样每个装饰对象的实现就和如何使用这个对象分离开了, 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

其实我觉得它的主要优点还是增强了扩展对象功能的灵活性。并且减少了细节的暴露。

缺点:

主要的缺点就是:可能会增加程序的复杂性,因为它的结构相较于直接在主程序中“跳穿衣舞”更加难以理解,因为它需要将对象一层一层的包裹起来,如果过度使用的话可能会让程序变得复杂。

例子


我们尝试用Java实现一下上面的例子。并对其中的一些细节进行相应的优化。

  1. 由于整个事情中,出现的人只有女朋友一个,因此可以将人这个父类省略。女朋友类的代码如下:
    /**
     * @Author yirui
     * @Date 2024/4/17 20:58
     * @Version 1.0
     */
    public class GirlFriend {
        public void Operation(){
            System.out.println("女朋友");
        }
    }
    

  2. 由于没有人这个父类(父子类的概念参考简单工厂模式中所介绍的),那修饰抽象类只能继承女朋友类了,规则变成:女朋友在进店之前是什么样的,带了哪些东西;带来的东西我们原封不动,我们店给女朋友提供了一些服务之后,女朋友还是完完整整的一个人从我这里走出去。(其实这个例子中,这个类都可以不要,直接让具体修饰类继承女朋友类就可以了,但是为了方便理解,还是把它写出来了):

    /**
     * @Author yirui
     * @Date 2024/4/17 21:03
     * @Version 1.0
     */
    public class Decorator extends GirlFriend{
        GirlFriend girlFriend;
    
        public void setGirlFriend(GirlFriend girlFriend) {
            this.girlFriend = girlFriend;
        }
    
        @Override
        public void Operation() {
            girlFriend.Operation();
        }
    }
    
  3. 接下来就是具体修饰类了,继承修饰抽象类。(这里为了省事,只写干洗店和小吃店。)
     

    /**
     * @Author yirui
     * @Date 2024/4/17 21:08
     * @Version 1.0
     */
    public class ClothingStore extends Decorator{
        private void buyCloth(){
            System.out.println("买了一件衣服。");
        }
    
        @Override
        public void Operation() {
            super.Operation();
            buyCloth();
        }
    }
    
    
    /**
     * @Author yirui
     * @Date 2024/4/17 21:11
     * @Version 1.0
     */
    public class FoodStore extends Decorator{
        private void eatFood(){
            System.out.println("吃了一碗麻辣烫。");
        }
    
        @Override
        public void Operation() {
            super.Operation();
            eatFood();
        }
    }
    
  4. 主程序:
     

    /**
     * @Author yirui
     * @Date 2024/4/17 21:13
     * @Version 1.0
     */
    public class Program {
        public static void main(String[] args) {
            GirlFriend girlFriend = new GirlFriend();
            ClothingStore clothingStore = new ClothingStore();
            FoodStore foodStore = new FoodStore();
    
            clothingStore.setGirlFriend(girlFriend);
            clothingStore.Operation();
            System.out.println("**********************");
            foodStore.setGirlFriend(clothingStore);
            foodStore.Operation();
        }
    }
    
  5. 结果如图,可以看到,女朋友先去了服装店,变成了女朋友买了一件衣服。这个时候,买了一件衣服的女朋友又去了小吃店,最终变成了,女朋友买了一件衣服,吃了一碗麻辣烫。

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

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

相关文章

有没有手机上使用的库存软件

库存软件是一种仓库的信息管理系统,它主要针对出库与入库这些数据进行管理,传统的库存管理都是在电脑上安装一个专门的数据库管理系统进行管理,这也是一种比较成熟的管理方式,那么有没有手机上使用的库存软件。 手机上使用的库存软…

学习Python先从了解Python开始

Python是一种高级编程语言,它的语法简洁易读,功能强大,应用领域广泛。Python不仅适用于数据科学、机器学习、Web开发等领域,还可以用于自动化脚本编写、游戏开发等。在本文中,我们将探讨Python的特点、应用领域以及未来…

搭建HBase2.x完全分布式集群(CentOS 9 + Hadoop3.x)

Apache HBase™是一个分布式、可扩展、大数据存储的Hadoop数据库。 当我们需要对大数据进行随机、实时的读/写访问时,可以使用HBase。这个项目的目标是在通用硬件集群上托管非常大的表——数十亿行X数百万列。Apache HBase是一个开源、分布式、版本化的非关系数据库…

Echarts-丝带图

Echarts-丝带图 demo地址 打开CodePen 什么是丝带图? 丝带图是Power BI中独有额可视化视觉对象,它的工具提示能展示指标当期与下期的数据以及排名。需求:使用丝带图展示"2022年点播订单表"不同月份不同点播套餐对应订单数据。 …

STM32直接存储器存取DMA

前提知识: 1、STM32F103内部存储器结构以及映射 STM32F103的程序存储器、数据存储器、寄存器和IO端口被组织在同一个4GB的线性地址空间内。数据字节以小端模式存放在存储器中。即低地址中存放的是字数据的低字节,高地址中存放的是字数据的高字节 可访问…

k8s部署Eureka集群

部署有状态负载 镜像配置: 环境变量如下: AUTHENTICATE_ENABLEtrue JAVA_OPTS-Dauth.userName账号 -Dauth.password密码 MY_POD_NAMEmetadata.name BOOL_REGISTERtrue BOOL_FETCHtrue APPLICATION_NAME负载名称 EUREKA_INSTANCE_HOSTNAME${MY_POD_NA…

Java基础之JVM基础调优与常见问题

常见命令 以下命令的介绍,全部在jdk8环境下运行的; jps ☆☆☆☆☆ 查看当前运行的进程号; jmap ☆☆☆ jmap命令可以查看jvm的内存信息,class对应的实例个数以及占用的内存大小 jmap -histo 查看当前java进程 [rdVM-8-12-c…

光伏无人机勘探技术应用分析

光伏无人机勘探与传统勘探想必,具有智能化作业、测控精度高、环境适应性强等明显优势;卫星勘探辅助其能更快速甚至实时完成测绘拼图;在进行勘察时,可根据需要自由更换机载设备;自动诗经建模使数据更直观,工…

Python 基于docker部署的Mysql备份查询脚本

前言 此环境是基于docker部署的mysql,docker部署mysql可以参考如下链接: docker 部署服务案例-CSDN博客 颜色块文件 rootbogon:~ 2024-04-18 16:34:23# cat DefaultColor.py ######################################################################…

stm32二刷-GPIO

一 什么是 GPIO: GPIO(general porpose intput output), 通用输入输出端口 . 二 我们先认识芯片控制 GPIO 输出控制。 2.1LED 硬件原理如图: 当电流从这根电线流通, LED 亮。当电流不通过这根电线, LED 灭。 上面 PF** ,芯片电…

如何安全、高速、有效地利用IP代理爬取数据

陈老老老板🧙‍♂️ 👮‍♂️本文专栏:生活(主要讲一下自己生活相关的内容)生活就像海洋,只有意志坚强的人,才能到达彼岸。 🤴本文简述:如何安全、高速、有效地利用IP代理爬取数据 &#x1f473…

HTX亮相迪拜Blockchain Life 2024:推动加密应用广泛落地

4月15-17日,HTX独家赞助于迪拜举办的Blockchain Life 2024峰会注册处,并以峰会蓝宝石赞助商身份亮相展区。 作为全球领先的头部交易所,HTX致力于推动区块链技术的创新与发展。此次赞助顶级加密峰会Blockchain Life 2024,不仅是对…

微信小程序酒店选择日期和入住人数(有效果图)

效果图 app.vue onLaunch:function(options){this.defaultcache()}defaultcache(){// 入住信息缓存var arr this.getDateTime();var ReserVation {reservType:0,//1 人数 2日期InCheckin:{},//入离日期peopleArr:[{title:成人,num:2},{title:儿童,num:0},{title:宝子,num:1…

外包干了6天,技术明显退步。。。

我是一名大专生,自19年通过校招进入湖南某软件公司以来,便扎根于功能测试岗位,一晃便是近四年的光阴。今年3月,我如梦初醒,意识到长时间待在舒适的环境中,已让我变得不思进取,技术停滞不前。更令…

Windows:web端UI自动化=python+selenium+pycharm框架

本篇写怎么写一个UI自动化代码。mac和Windows是一样的 都是这样写 不过,习惯用Windows了 如果python没有安装可以看我另一篇安装python的教程 先安装python先 下载完python 下载pip 1 安装pip $ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py # 下载…

k8s配置configmap指定到容器的指定文件

我们需要将名称为walletkey.properties的文件做成configmap,然后将walletkey.properties文件单独挂载出来到/data/walletkey.properties,且不能覆盖/data目录,具体如下 1、创建configmap configmap文件内容 其中walletkey.properties: >-引…

统一SQL-支持CHAR和VARCHAR2 (size BYTE|CHAR)转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库:Oracle 目标数据库:Postgresql,TDSQL-MySQL,达梦8,LightDB-Oracle 操作目标 在Oracle中的CHAR和VARCHAR2数据类型&…

【STM32HAL库】外部中断

目录 一、中断简介 二、NVIC 1.寄存器 2.工作原理 3.优先级 4.使用NVIC 三、EXTI 1.简介 2.AFIO:复用功能IO,主要用于重映射和外部中断映射配置​编辑 3. 中断使用 4.HAL库配置使用 一、中断简介 中断的意义:高效处理紧急程序,不会…

小程序 前端如何用wx.request获取 access_token接口调用凭据

在微信小程序中,获取access_token通常是通过wx.request方法来实现的。以下是一个简单的示例代码: 1.获取小程序的appID 与 secret(小程序密钥) 登录之后,请点击左侧的"开发管理">点击"开发设置" 就可以找…

【AI开发:音频】一、GPT-SoVITS整合工具包的部署问题解决(GPU版)

前言 目前GPT-SoVITS的合成效果比较不错,相比较其他厂商的产品要规整的多。众多厂家中也是国内使用最多的一款了,并且这个整合包里携带了,除背景音、切割、训练、微调、合成、低成本合成等一些列完整的工具,也可以作为API进行使用…