结构型模式-装饰者模式

news2024/11/27 0:16:29

装饰者模式

概述

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

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

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

  • 扩展性不好

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

  • 产生过多的子类

定义:

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

结构

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

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

案例

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

类图如下:

在这里插入图片描述

代码如下:

//快餐接口
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() + "元");
    }
}

好处:

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

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

使用场景

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

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

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

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

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/471433.html

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

相关文章

98-Linux_HTTP协议与web服务器

HTTP协议与web服务器 1.浏览器与服务器通信过程(1)浏览器与服务器通信过程(2)什么是长连接,什么是短连接? 2.常见的web服务器有:3.http请求方法和应答状态码(1)HTTP的请求报头结构(2)Http请求报头实例(3)http的请求方法 4.HTTP应答报头和应答状态(1)HTTP的应答报头结构(2)HTTP…

Spring动态添加数据源(全自动)

一、使用场景 1、现在网上所有写的动态数据源,都是假动态数据,都是预先知道要连接几个数据库,但这不满足具体真正需要动态添加连接数据库的需求; 2、在很多业务场景下,如项目默认连一个主数据库,服务启动…

必知的Facebook广告兴趣定位技巧,更准确地找到目标受众

在Facebook广告投放中,兴趣定位是非常重要的一环。兴趣定位不仅可以帮助我们找到我们想要的目标受众,还可以帮助我们避免一些常见的坑。今天,就让我们一起来看看必知的Facebook广告兴趣定位技巧,更准确地找到目标受众。 1.不要只关…

北京/西安/杭州/深圳CDGA/CDGP数据治理认证班于2023年5月7日开班

DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…

蓝牙基础介绍

目录 一、概述(Overview) 二、框架结构(Frame Structure) 三、蓝牙协议(Bluetooth Protocol) 一、概述(Overview) 什么是蓝牙?就像你们大多数人想知道任何你从未听说过…

Redis-01-基础-redis简介安装、数据类型、常用命令、使用Spring Data Redis封装好的命令方法操作redis

文章目录 Redis基础课程内容1. 前言1.1 什么是Redis1.2 使用Redis能做什么 2. Redis入门2.1 Redis简介2.2 Redis下载与安装2.2.1 Redis下载2.2.2 Redis安装 2.3 Redis服务启动与停止2.4 Redis配置文件 3. Redis数据类型3.1 介绍3.2 Redis 5种常用数据类型 4. Redis常用命令4.1 …

Unity TextMeshPro文本存在背景框的问题研究

在使用TextMeshPro的时候遇到了字体黑底的问题,类似下图这样 当字体较大的时候表现正常,当缩小到一定程度就会出现黑底。这个情况让人第一时间就是怀疑SDF计算缩放的时候存在问题。在我们重新导出字体,调整图集字体大小以及Padding后&#xf…

基于GA遗传算法的列车交路优化MATLAB代码

资源地址: 基于GA遗传优化算法的列车交路方案优化matlab代码资源-CSDN文库 问题描述: 列车交路是指列车在规定的运行线路上往返运行的方式,规定了列车运行区段、折返车站以及按不同交路运行的列车对数. 对于既有线路,正在进行机…

【2023年五一数学建模竞赛B题】快递需求分析问题--完整思路和代码

1.问题背景与描述 赛题分析:这道题出的比较好,考察面较多,难度循环渐进,相对C题是比较有层次的一道题 2.解题思路分析 2.1 问题一的分析 请从收货量、发货量、快递数量增长/减少趋势、相关性等多角度考虑,建立数学模…

d3.js学习笔记②搭建服务器(含报错解决)

强烈建议自己搭建一个服务器,否则在后续往js里导入本地数据(比如csv、json文件等)的时候会报错。我用的是Apache服务器,下载、安装过程参考这篇文章:Apache安装配置 在浏览器输入http://localhost/或者http://127.0.0…

2023轻薄投影仪选哪款?极米Z6X Pro成年轻人租房首选投影

生活在哪里?大隐隐于市,小隐隐于出租屋。在那小小出租屋里,租房人开始选择自己打造一个“家”。一般情况下,在外租房的房间整体面积比较小,选择轻薄的投影仪是一个很好的选择。而国内知名投影品牌极米科技推出的这款轻…

WebLogic LinkRef 反序列化远程代码执行漏洞(CVE-2023-21931)

漏洞描述 Oracle WebLogic Server 是一款Java EE应用服务器。 受影响版本的WebLogic中WLNamingManager#getObjectInstance()存在JNDI查找逻辑,导致攻击者可以通过IIOP协议传入特定的对象触发反序列化逻辑,执行任意代码。 当传入boundObject 对象是 Li…

MySQL的数据库引擎介绍

1、什么是数据库引擎 数据库引擎就是操作数据库的一段程序或程序段,用于存储、处理和保护数据的核心服务。 利用数据库引擎可控制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程序的要求。数据库应用项目是通过数据库引擎与数据库…

干货好文 | 两地三中心到异地双活演变及关键技术探讨

两地三中心和异地多活都是分布式系统的关键技术,用于保证系统的高可用性和容错性。其中最关键的技术无疑是数据同步、同步防环和数据冲突解决。 异地容灾 & 两地三中心 两地三中心架构是一种分布式系统的架构模式,用于保证系统的高可用性和容错性。…

Numpy从入门到精通——节省内存|通用函数

这个专栏名为《Numpy从入门到精通》,顾名思义,是记录自己学习numpy的学习过程,也方便自己之后复盘!为深度学习的进一步学习奠定基础!希望能给大家带来帮助,爱睡觉的咋祝您生活愉快! 这一篇介绍《…

适合学生党的蓝牙耳机品牌有哪些?学生性价比高的蓝牙耳机排行

近几年,蓝牙耳机凭借便捷的使用迅速成为人们外出常备的数码产品之一。现如今,市面上的蓝牙耳机越来越多,不同品牌,不同型号的蓝牙耳机数不胜数。那么,有没有适合学生党的蓝牙耳机品牌?针对这个问题&#xf…

试试这几个冷门但好用的软件吧

软件一:探记 探记是一款专注于个人记录每一条记录的工具,主要特点如下: 简单易用:探记的界面设计简洁明了,操作流程简单易用,用户可以快速、方便地添加记录。 多样化记录类型:探记支持多种记…

接口自动化测试面试、拿下10个题,你可以游刃有余

目录 1.请问你是如何做接口测试的? 2.接口测试如何设计测试用例? 3.接口测试执行中需要比对数据库吗? 4.接口测试质量评估标准是什么? 5.接口产生的垃圾数据如何清理 6.其他接口要先获取接口信息,如何让登录的接…

土木人职场受挫该如何破局?转行IT互联网貌似已成首选!

“大学毕业两年,一直在内耗。既不想继续做工程,又不知道出了工地,自己还能做什么?” 本人毕业于一类院校的建筑环境与能源应用工程专业,通俗的说就是土木工程。 进施工单位是大部分土木人的归宿,本科毕业生…

户外专家REI的EDI需求详解

REI是美国著名的户外用品零售商,成立于1938年,总部位于西雅图。REI以提供高品质户外用品和服务为主要目标,包括登山、露营、徒步旅行、滑雪、骑行等各种户外活动所需要的装备和用品。REI的供应商来自世界各地,包括美国本土和国际市…