设计模式-装饰者模式

news2024/11/19 15:21:02

装饰者模式-简介

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

装饰器模式通过将对象包装在装饰器类中,以便动态地修改其行为。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

举个例子:
快餐店中,有炒面、炒饭这些主食,可以额外附加 鸡蛋、培根、火腿等等这些配菜,当然加了配菜就需要多加钱,每个配菜的价格是不一样的,那么计算总价就会显得非常麻烦,如果我们采用继承的方式实现以上需求。
如下图所示:
在这里插入图片描述

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

  • 拓展性不好,如果我们增加一种配菜(火腿肠),我们就需要给FriedRice和FriedNoodles分别定义一个子类,如果要新增一个快餐主食类(炒河粉),就需要定义更多的子类。
  • 定义过多的子类,会导致类爆炸。

为什么需要装饰者模式?

一般的,我们为了拓展一个类经常使用继承方式来实现,由于继承为类引入了静态特征,并且随着拓展功能的增多,子类会很膨胀。
当我们在不想增加很多子类的情况下拓展类的功能,装饰者模式相比于生成子类更为灵活。

装饰者模式-结构组成

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

代码示例

我们使用装饰者模式对快餐店案例进行改进:
类图如下
在这里插入图片描述
代码如下

//快餐接口
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("========");
        //点一份加鸡蛋的炒饭
        FastFood food1 = new FriedRice();

        food1 = new Egg(food1);
        //花费的价格
        System.out.println(food1.getDesc() + " " + food1.cost() + "元");

        System.out.println("========");
        //点一份加培根的炒面
        FastFood food2 = new FriedNoodles();
        food2 = new Bacon(food2);
        //花费的价格
        System.out.println(food2.getDesc() + " " + food2.cost() + "元");
    }
}

FastFood - 抽象构件角色(Component)
FriedRice、FriedNoodles - 具体构件角色(Concrete Component)
Garnish - 抽象装饰角色(Decorator)
Egg、Bacon - 具体装饰角色(Concrete Decorator)

其实我们可以发现,配料类(Garnish)本身是继承自快餐类(FastFood),所以配料类本身也是属于快餐的一种,但是配料类中包含了一个FastFood的成员变量,这个成员变量是 “需要被装饰的对象”,所以,装饰者类其实就是将 “需要被装饰的对象” 包含在自己的成员变量中,同时自己也是和这个对象继承自同一个类。

总结

优点:

  • 装饰者模式可以带来比继承更加灵活的拓展功能,使用起来更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果,装饰者模式比继承更加具有良好的拓展性,完美的遵循“开闭原则”,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承模式的一个替代模式,装饰模式可以动态的拓展一个实现类的功能。

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的子实现类进行了增强,添加了缓冲区,提高了写数据的效率。

静态代理模式和装饰者模式的区别

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

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

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

相关文章

Unity中Shader的深度偏移Offset

文章目录 前言一、深度偏移一般用于什么时候1、深度偏移一般用于两个模型 重合在同一平面时,在其中一个模型上使用深度偏移后,就能区别出两个模型的深度,从而消除闪动2、虽然,可以让两个模型在深度上错开一点点,来解决…

工业级开源facechain人物写真sd-webui插件使用方式

一、简介 facechain人物写真应用自8月11日开源了第一版证件照生成后。目前在github(https://github.com/modelscope/facechain)上已有近6K的star,论文链接:FaceChain: A Playground for Identity-Preserving Portrait Generation…

Linux-JVM-CPU爆表调优

CPU爆表调优 一、自定义一个死循环测试类二、运行TestDemo类三、调优1、执行top命令2、执行ps命令3、执行jstack命令 一、自定义一个死循环测试类 第7行一定会死循环,永远出不去 public class TestDemo {public static void main(String[] args) {new Thread(null,(…

安装mmcv及GPU版本的pytorch及torchvision

一、先装GPU版本的pytorch和torchvision pip install torch1.9.1cu111 torchvision0.10.1cu111 torchaudio0.9.1 -f https://download.pytorch.org/whl/torch_stable.html注意:以上适用cuda11.1版本 如果想离线安装,就看这篇文章 二、安装mmcv 看这篇…

Python学习之Python3.10中match-case的用法和示例

在 Python 3.10 中引入了新的 match-case 语法,它是一种用于模式匹配的结构。它类似于 switch-case 语句,可以根据不同的模式匹配执行不同的代码块。 match-case 语法的基本结构如下: match expression:case pattern1:# 执行代码块1case p…

SpringCloud-Seata

一、介绍 (1)实现分布式事务 (2)解决Spring只支持单机事务 (3)事务ID TC(事务协调者) TM(事务管理者) RM(资源管理者)

2021-arxiv-Prefix-Tuning- Optimizing Continuous Prompts for Generation

2021-arxiv-Prefix-Tuning- Optimizing Continuous Prompts for Generation Paper:https://arxiv.org/pdf/2101.00190.pdf Code:https://github.com/XiangLi1999/PrefixTuning 前缀调优:优化生成的连续提示 prefix-tunning 的基本思想也是想…

西湖大学利用 Transformer 分析百亿多肽的自组装特性,破解自组装法则

多肽是两个以上氨基酸通过肽键组成的生物活性物质,可以通过折叠、螺旋形成更高级的蛋白质结构。多肽不仅与多个生理活动相关联,还可以自组装成纳米粒子,参与到生物检测、药物递送、组织工程中。 然而,多肽的序列组成过于多样&…

瞬态抑制二极管TVS的工作原理?|深圳比创达电子EMC(上)

TVS二极管具有响应速度快、漏电流小、钳位电压稳以及无寿命衰减的特性,从小到信号线静电防护,大到电力系统抗雷击浪涌,TVS都发挥着至关重要的作用。本章对瞬态抑制二极管TVS工作机理展开分析,供产品选型参考。接下来就跟着深圳比创…

用git stash暂存修改

git stash命令用于保存当前工作目录的临时状态,包括暂存区和已修改但未暂存的文件。它会将这些修改保存在一个临时区域(即“堆栈”)中,让你能够回到一个干净的工作目录,可以进行其他操作。等到你完成其他任务后&#x…

立即报名 | 云原生 + AI Meetup 成都站 11.4 正式开启

2023 年,KubeSphere 社区已经在深圳、杭州、上海三个城市各组织了一场线下 Meetup。第四站,我们将走进天府成都。 11 月 4 日,云原生 AI Meetup 成都站将正式开启! 此次 Meetup,我们邀请到了蚂蚁集团、云猿生数据、…

系列十二、Redis的主从复制

一、概述 主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据。 二、架构图 三、搭建主从复制 # 1、准备3台机器并修改配置 -- master port 7379-- slave1 port 7380 slaveof masterip masterport-- slave2 port 7381 slaveof masterip masterpor…

美创科技入选“内蒙古自治区第一届网络安全应急技术支撑单位”

近日,内蒙古自治区党委网信办、国家网络应急技术处理协调中心内蒙古分中心评选“内蒙古自治区网络安全应急技术支撑单位”结果公布。 经自治区各地区、各部门和单位推荐各单位自主申报,资料审查和专家评审等环节,美创科技成功入选“内蒙古自治…

win10下yolov7 tensorrt模型部署

TensorRT系列之 Win10下yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov8 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov7 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov6 tensorrt模型加速部署 TensorRT系列之 Linux下 yolov5 tensorrt模型加速部署…

【数据结构】排序--归并排序

目录 一 基本思想 二 代码实现 三 非递归归并排序 一 基本思想 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并&#xff…

linux常见命令-文件目录类

9.4 文件目录类 (1)pwd 指令:显示当前工作目录的绝对路径 (2)Is指令:查看当前目录的所有内容信息 基本语法: ls [选项,可选多个] [目录或是文件] 常用选项:-a:显示当前目录所有的文件和目录,包括隐藏的…

02Nacos和Feign及Gateway配置

一、Nacos配置管理 1.统一配置管理 配置更改热更新 ①在Nacos中添加配置信息: ②在弹出表单中填写配置信息: 2.配置获取的步骤如下 配置文件bootstrap.yml的优先级比application.yml优先级高。把nacos地址放入bootstrap.yml。 ①在userservice中引入N…

Python —— Reference | hou

Houdini有非常多的Python模块可使用,不止一个hou,如hutil、toolutils、husd、kramautils等,只是帮助文档并没有提起,可从源代码docstring中查询; //查询有效的模块 help(modules) hou模块按功能可分为三类:…

windows 10下安装Pytorch的操作文档与避坑指南

前言 PyTorch3D 是 Facebook AI Research (FAIR) 开发的一个用于 3D 计算机视觉任务的开源深度学习库。它是建立在 PyTorch 框架之上的,并旨在为研究人员和工程师提供处理 3D 数据、渲染和几何计算的工具和模块。 PyTorch3D的用途: 3D 几何操作&#x…

Linux 内核启动分析

Linux 内核启动分析-BugMan-ChinaUnix博客 通过《Linux应用程序elf描述》,我们了解到一个应用程序编译后,最终会按照指定方式进行链接,而我们通过ld --verbose可以查看对应应用的默认链接方式。那么对于Linux内核呢?毫无疑问&…