结构型模式 - 装饰者模式

news2024/11/28 18:58:10

概述

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

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

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

  • 扩展性不好

    如果要再加一种配料(火腿肠),我们就会发现需要给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/769290.html

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

相关文章

5G工业路由器实现AGV远程控制,智联物联无线物联网方案

随着AGV在制造业应用逐渐广泛,在生产车间传统的布线网络下,存在着接口不足、网络不稳定、数据丢失、故障异常的情况,技术人员无法及时观察AGV的数据情况,导致AGV出错率高,维护成本高等问题。 传统的AGV通信方式一般是…

【Python基础函数笔记】获取当前时间并写入日志

1.获取当前时间 import os from datetime import datetime import pytzdef get_cur_time():# 获取当前时间return datetime.strftime(datetime.now(pytz.timezone(Asia/Singapore)), %Y-%m-%d_%H-%M-%S)# 基础目录 basedir a logdir os.path.join(basedir, logs, str(args.n…

S3C2440的串口通信(UART)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、UART二、2440的uart资源2.1.uart配置流程2.2 RS2322.3 RS232接口 三. UART编程实现(无缓存)四. UART编程实现(中断&…

Python多线程 threading 和多进程 multiprocessing

1. 并发 vs 并行 线程是程序执行的最小单位,一个进程可以由一个或多个线程组成,各个线程之间也是交叉执行。 并发,相当于单核CPU,宏观同时执行,微观高速切换 交替执行。多线程、高并发这些词语更多地出现在服务端程序…

机器学习(十七):实操_在Sklearn中的实现CART树的基本流程

全文共8000余字,预计阅读时间约16~27分钟 | 满满干货(附代码),建议收藏! 代码下载点这里 一、介绍 CART(Classification and Regression Trees)即分类回归树,是一种重要的机器学习算法,既可以…

3.8 Bootstrap 面包屑导航(Breadcrumbs)

文章目录 Bootstrap 面包屑导航(Breadcrumbs) Bootstrap 面包屑导航(Breadcrumbs) 面包屑导航(Breadcrumbs)是一种基于网站层次信息的显示方式。以博客为例,面包屑导航可以显示发布日期、类别或…

解决win10系统中ping localhost被解析为 ::1的问题

目录 问题描述 问题分析 解决方案 一、修改host文件 二、修改注册表 三、修改IPv6的优先级 问题描述 本机为win10系统,在命令行窗口ping localhost时,本机IP127.0.0.1被解析为了 ::1的问题 1、在命令行窗口 ping 127.0.0.1 2、在命令行窗口 ping…

Linux常用命令——ed命令

在线Linux命令查询工具 ed 单行纯文本编辑器 补充说明 ed命令是单行纯文本编辑器,它有命令模式(command mode)和输入模式(input mode)两种工作模式。ed命令支持多个内置命令,常见内置命令如下&#xff…

leetcode 59.螺旋矩阵

记录一下&#xff0c;觉得倒水思想来做 总体看起来还是比较清晰的。 class Solution { public:vector<vector<int>> generateMatrix(int n) {int a[4][2] {{0,1}, {1,0}, {0,-1},{-1,0}};int direction0; //方向int num0;int S n*n;int x 0;int y 0;vector<…

解析基因影响:孟德尔随机化的创新思维

一、引言 在当今的遗传学和生物学研究中&#xff0c;我们对基因对个体特征和性状的影响的理解变得更加深入。然而&#xff0c;基因影响的复杂性和多样性给我们带来了巨大的挑战。为了更好地揭示基因影响的本质和机制&#xff0c;我们需要采用创新的研究思维和方法。 本文的目的…

听GPT 讲K8s源代码--pkg(四)

/pkg/controlplane、/pkg/credentialprovider、/pkg/kubeapiserver是Kubernetes中的三个核心包&#xff0c;它们分别实现了不同的功能。 /pkg/controlplane包 /pkg/controlplane是Kubernetes的一个包&#xff0c;它包含了控制平面组件的实现&#xff0c;例如API Server、Contro…

妙记多 Mojidoc 模版投稿活动招募

妙记多 Mojidoc 开始征集模板啦! 快来投稿吧&#xff01;&#x1f389;&#x1f389;&#x1f389; 优秀模板将被选录进官方模板中心&#xff0c;让你的灵感和创意被更多人看见&#xff01;选录后&#xff0c;你可直接解锁「高级体验官」称号&#xff0c;并有机会获得妙记多 M…

IDELAYG/ODELAY/IDELAYCTRL

如下是7系列FPGA HP Bank I/O 资源&#xff1a; 其中ILOGIC是由许多的数据选择器和一个IDDR触发器构成。 在HP BANK中&#xff0c;ILOGIC被称为ILOGICE2&#xff0c;在HR BANK中&#xff0c;ILOGIC被称为ILOGICE3 IDELAY 简单介绍 输入信号延迟模块。每个I/O模块都包含了一…

内存分区,编译链接,ARCMRC,消息传递消息转发,对象的底层

文章目录 前言内存分区栈区堆区全局区文字常量区程序代码区运行之前运行之后 编译&#xff0c;链接编译的过程链接 ARC&#xff0c;MRC在编译期干了什么 对象的底层消息传递&#xff0c;消息转发消息转发消息传递IMP指针IMP与SEL的区别与联系 前言 对第一周学习内容做个概括 提…

no module named paddle pip install paddlepaddle报错

!python -m pip install paddlepaddle2.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple

Python实战项目——餐厅订单数据分析(一)

项目背景 餐厅经营的好坏需要用数据来说明&#xff0c;如果一个餐厅生意惨淡&#xff0c;那么应该先收集最近的数据&#xff0c;然后进行数据分析&#xff0c;再对应相应出现的问题进行解决和做出对应的商业调整。今天开始我们分析一来家餐厅的数据。 认识数据并预处理 拿到…

GUI-Menu菜单实例

运行代码&#xff1a; //GUI-Menu菜单实例 #include"std_lib_facilities.h" #include"GUI/Simple_window.h" #include"GUI/GUI.h" #include"GUI/Graph.h" #include"GUI/Point.h"struct Lines_window :Window {Lines_window…

Appium+python自动化(十二)- Android UIAutomator终极定位凶器(超详解)

简介 乍眼一看&#xff0c;小伙伴们觉得这部分其实在异性兄弟那里就做过介绍和分享了&#xff0c;其实不然&#xff0c;上次介绍和分享的大哥是uiautomatorviewer&#xff0c;是一款定位工具。今天介绍的是一个java库&#xff0c;提供执行自动化测试的各种API。 Android团队在4…

小程序控制台警告:DevTools failed to load SourceMap(控制台报错DevTools 无法加载来源映射)

在调试项目的时候&#xff0c;控制台报错:**DevTools failed to load SourceMap: Could not load content for http://xxx.js. 这段报错的意思是dev工具未能成功加载source map&#xff08;文件映射&#xff09;。这里的报错实际上和项目本身的代码没有任何关系&#xff0c;而是…

基于 Fedora 38 的预期版本 Nobara 38 发布

导读基于 Fedora 38 的预期版本 Nobara 38 终于发布了&#xff0c;它带来了一系列用户友好的修复和功能增强。Nobara 是 Fedora Linux 的修改版本&#xff0c;旨在解决用户面临的常见问题&#xff0c;并提供开箱即用的顺滑的游戏、流媒体和内容创建体验。凭借一系列附加软件包和…