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

devtools/2024/9/23 1:07:36/

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


概念


定义

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

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

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

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

你光着身子, 当着大家的面,先穿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. 由于整个事情中,出现的人只有女朋友一个,因此可以将人这个父类省略。女朋友类的代码如下:
    java">/*** @Author yirui* @Date 2024/4/17 20:58* @Version 1.0*/
    public class GirlFriend {public void Operation(){System.out.println("女朋友");}
    }
    

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

    java">/*** @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;}@Overridepublic void Operation() {girlFriend.Operation();}
    }
    
  3. 接下来就是具体修饰类了,继承修饰抽象类。(这里为了省事,只写干洗店和小吃店。)
     

    java">/*** @Author yirui* @Date 2024/4/17 21:08* @Version 1.0*/
    public class ClothingStore extends Decorator{private void buyCloth(){System.out.println("买了一件衣服。");}@Overridepublic 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("吃了一碗麻辣烫。");}@Overridepublic void Operation() {super.Operation();eatFood();}
    }
    
  4. 主程序:
     

    java">/*** @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.ppmy.cn/devtools/9431.html

相关文章

python面向对象的使用(2)

题目 面向对象模拟电影院自动售票系统实现自动选择电影、场次、座位。 思路 通过类定义电影的相关信息,输出输入相关电影信息,对座位进行顺序取 代码解释 class Movie:def __init__(self, title, duration):self.title titleself.duration durati…

将组件 赋值给变量li 想拿到 组件的html页面结构 但是 打印出来的是 文件路径 该如何实现呢?

需求 import { ref } from vue; import refrigerationRight from /views/bim3D/components/right/refrigerationRight.vue const liref({lis:refrigerationRight }) const refrigerationclick()>{console.log(li) }##方法 import { createApp } from vue; import refriger…

计算机服务器中了locked勒索病毒怎么办,locked勒索病毒解密工具流程步骤

随着网络技术的不断应用与发展,越来越多的企业离不开网络,网络大大提升了企业的办公效率水平,也为企业的带来快速发展,对于企业来说,网络数据安全成为了大家关心的主要话题。近日,云天数据恢复中心接到多家…

在群晖上安装GPT4Free

什么是 GPT4Free ? GPT4Free 简称 G4F,是一个强大的大型语言模型命令行界面(LLM-CLI),旨在去中心化并提供免费访问先进人工智能技术的能力。G4F 的目标是通过提供用户友好和高效的工具,使人工智能民主化&am…

工业控制(ICS)---组态软件分析

组态软件 什么是组态软件? 组态软件就是一些数据采集与过程控制的专用软件,它们是在自动控制系统监控层一级的软件平台和开发环境,使用灵活的组态方式,为用户提供快速构建工业自动控制系统监控功能的通用层次的软件工具。 组态软…

lesson03:类和对象(中)续

1.运算符重载 2.const成员函数 3.取地址操作符及const取地址操作符重载 1.运算符重载 1.1运算符重载 c为了增强代码的可读性,引入了运算符重载,运算符重载函数是具有特殊函数名的函数。 函数名:关键字operator后面接需要重载的运算符符号…

代码随想录算法训练营第四十八天| 198.打家劫舍,213.打家劫舍II,337.打家劫舍III

题目与题解 198.打家劫舍 题目链接:198.打家劫舍 代码随想录题解:​​​​​​​198.打家劫舍 视频讲解:动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍_哔哩哔哩_bilibili 解题思路: 这道…

GLID: Pre-training a Generalist Encoder-Decoder Vision Model

1 研究目的 现在存在的问题是: 目前,尽管自监督预训练方法(如Masked Autoencoder)在迁移学习中取得了成功,但对于不同的下游任务,仍需要附加任务特定的子架构,这些特定于任务的子架构很复杂&am…