设计模式之装饰者模式(包装模式)

2023-11-17

装饰者模式

1.实例:咖啡订餐项目
  • 咖啡种类/单品咖啡:Espresso(意大利浓咖啡),ShortBlack,LongBlack(美式咖啡),Decaf(无因咖啡):
  • 调料: Milk(牛奶),Soy(豆浆),Chocolate(巧克力)
  • 要求: 订单结算用户点的咖啡价格(用户可以单点咖啡,也可以点咖啡+调料的组合 。。比如点一杯美式咖啡+巧克力)
  • 注: 最好在扩展新的咖啡种类和调料类的同时,不需要改动过多代码,具有良好的扩展性。
2.解决方法
  • 方案1:
    在这里插入图片描述

  •   1. 设计Drink为一个抽象类,表示饮料
      2. description就是对咖啡的简介
      3. cost()就是计算费用,在Drink中作为一个抽象方法
      4. Decaf就是单品咖啡,继承Drink并且实现cost
      5. Espresso+Milk 就是单品咖啡+调料,这种组合很多
      6. 分析: 这样设计会出现很多的类(类爆炸)
      7. 因此: 不提倡使用该种方法
    
  • 方案2:
    在这里插入图片描述

  1. 将调料内置入Drink抽象类内,不会造成类的数量过多
  2. 再使用继承关系,保证了所有的咖啡内部存在若干调味品
  3. 这种方法可以控制类的数量,不至于造成过多的类(不会引起类爆炸)
  4. 在增加或者删除调料种类时,代码的维护量过大
  5. hasXxx调料,作为用户使用调料的开关

方案3
考虑使用装饰者模式解决问题

3.装饰者模式介绍

特点: 动态地将新功能附加到对象上,在对象扩展方便比继承更好,装饰者模式也体现了ocp原则(保证代码的维护性和扩展性)

介绍: 装饰者模式就像给游戏打补丁一样,让它变得越来越强大

角色:

  • 主体(被装饰者): Component(比如前面的Drink)
  • 具体的主体: ConcreteComponent(比如前面的各种单体咖啡实例)
  • 装饰者: Decorator 【在装饰者中关联了被装饰者的抽象类】
  • 具体的装饰者: 具体装饰细节【前面的Chocolate,Soy等】

uml
在这里插入图片描述
代码演示:

//Drink 待强化(拓展)的抽象类 Component
package com.liz.GOF23.decorate.drink;

public abstract class Drink {
    public String des;//对drink的描述
    //价格
    private float prices = 0.0f;

    public void setDes(String des) {
        this.des = des;
    }

    public String getDes() {
        return des;
    }

    public float getPrices() {
        return prices;
    }

    public void setPrices(float prices) {
        this.prices = prices;
    }
    //计算费用
    public abstract float cost();
}
.
//抽象层和实现层之间建立的一个缓冲层

public class Coffee extends Drink {
    @Override
    public float cost() {
        return this.getPrices();
    }
}

//具体的咖啡实体; ConcreteComponent
public class Espresso extends Coffee {
    //创建的时候就给予价格
    public Espresso(){
        setDes("意大利咖啡");
        setPrices(6.0f);
    }

}
public class LongBlack extends Coffee {
    public LongBlack(){
        setDes("longblack");
        setPrices(5.0f);
    }
}
public class ShortBlack extends Coffee{
    public ShortBlack(){
        setDes("short black");
        setPrices(4.0f);
    }
}
//抽象装饰层 Decorator  内部关联了抽象实体类
package com.liz.GOF23.decorate.decorator;

import com.liz.GOF23.decorate.drink.Drink;
//装饰者要实现被装饰者的顶层抽象
public class Decorator extends Drink {
    private Drink obj;

    //!!!开始组合(体现出组合关系)
    public Decorator(Drink obj){
        this.obj = obj;
    }
    @Override
    public float cost() {
        //!!!自己的价格 + 单品咖啡的价格组合
        return getPrices() + obj.cost();
    }

    @Override
    public String getDes() {
        //描述(装饰者信息 + 被装饰者信息)
        return this.des+" "+this.getPrices()+" "+"&&"+obj.getDes()+obj.getPrices();
    }
}

//装饰实例:
//巧克力调味
  public class Chocolate extends Decorator {
    public Chocolate(Drink obj) {
        super(obj);
        setDes("巧克力调味品");
        setPrices(3.0f);//调味品的价格
    }
}
//牛奶调味
public class Milk extends Decorator {

    public Milk(Drink obj) {
        super(obj);
        setDes("牛奶");
        setPrices(2.0f);
    }
}
//豆浆调味
public class Soy extends Decorator {
    public Soy(Drink obj) {
        super(obj);
        setDes("豆浆");
        setPrices(1.5f);
    }
}

//使用装饰者模式解决咖啡订单问题
public class CoffeeStore {
    //装饰者模式下订单: 2份巧克力 + 一份牛奶 的LongBlack
    public static void main(String[] args) {
        //1.LongBlack
        Drink order = new LongBlack();
        System.out.println("单品信息:"+order.getDes()+"   费用:"+order.cost());

        //2.加入一份牛奶(装饰者)
        order = new Milk(order);
        System.out.println("订单1:"+order.getDes()+"   费用:"+order.cost());

        //3.加入两份巧克力
        //加入第一份
        order = new Chocolate(order);
        //加入第二份
        order = new Chocolate(order);
        System.out.println("订单2:"+order.getDes()+"   费用:"+order.cost());
        //链式思想
    }

在这里插入图片描述

4.总结一波

装饰者模式可以做到在不修改任何底层代码的情况下,给对象增加新的方法,拓展类的功能。

优点: 装饰者模式比继承灵活,在不改变原有对象的情况下给对象扩展功能,符合开闭原则。

缺点:

  • 装饰模式会导致设计出大量的ConcreteDecorator类【所谓的装饰实体类 类似上面具体的Soy Chocolate等具体强化逻辑】,增加系统的复杂性。
  • 对于多次装饰的对象,一旦出现错误,排错繁琐;
5.java源码中的装饰者模式

java中的io流中装饰者模式就体现的淋漓尽致,举个栗子:

 DataInputStream dis = new DataInputStream(new FileInputStream("D://xxx.txt"));

结构简图:
在这里插入图片描述

 FileInputStream 具体的实现类(类似于上述的单品咖啡)
 DataInputStream 具体的装饰类(类似于上述的调味品实例)
 FilterInputStream 抽象的装饰类(类似于上述的抽象调味品)[含有被装饰者]
 InputStream  抽象的实现类(类似于上述的抽象Drink)

在源码中可以看出,FilterInputStream继承了InputStream,同时继承自FilterInputStream的FileInputStream等一系列具体类用来强化流的功能

在这里插入图片描述
tip: 装饰抽象类FilterInputStream 继承了抽象主体类InputStream,同时内部组合了InputStream,用来强化主题的功能(FilterInputStream内部有各种具体的装饰类: bufferinputStream DataInputStream 等…)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式之装饰者模式(包装模式) 的相关文章

  • 如何确定一次完整的请求过程——服务链路跟踪

    微服务体系下 一个请求会调用多个服务 整个请求就会形成一个调用链 普通的日志输出是无法将整个体系串联起来 调用过程中某一个节点出现异常 定位排查难度系数增高 这种情况下就需要一个组件 来分析系统性能 展现调用链路 以便出现故障时快速定位并解

随机推荐

  • 【笔记整理】通信原理第四章复习——数字基带传输

    4 1 引言 数字基带信号 数字信号 补充 基带信号 指未经调制的信号 特征是其频谱从零频率或很低频率开始 占据较宽的频带 基带在传输前 必须经过一些处理或某些变换 比如码型变换 波形变换和频谱变换 才能送入信道中传输 处理或变换是为了使信
  • Django-rest-framework框架

    目录 一 Web应用模式 1 1 前后端不分离 二 API接口 三 接口测试工具 Postman 四 RESTful API规范 4 1 数据的安全保障 4 2 接口特征表现 4 3 多数据版本共存 4 4 数据即是资源 均使用名词 可复数
  • VHDL语言实现8位LED流水灯

    VHDL语言实现8位LED流水灯 包含对50MHz时钟信号分频产生1Hz信号 library ieee use ieee std logic 1164 all use ieee std logic unsigned all entity l
  • 1、mos管的工作原理

    文章目录 一 导体 绝缘体 半导体 二 半导体的制作 掺杂 pn结 半导体的单向导电性 三 mos管的工作原理 源极 栅极和漏极 nmos和pmos 电路符号 四 总结 一 导体 绝缘体 半导体 导体 能够导电的介质 绝缘体 不能导电的介质
  • 程序猿眼中的协议:TCP / IP 五层网络模型

    哈喽 大家好 我是你们的老朋友 保护小周 本期为大家带来的是 网络基础原理中的 TCP IP 五层网络模型 主要从协议的概念 网络模型 数据分层传输的流程 几个方面讲解 看完之后可以轻松的理解数据是如何在网络中传输的 确定不来看看嘛 更多精
  • List循环删除集合

    目录 For循环遍历List 增强For循环遍历List 迭代器iterator的remove方法 创建新的对象添加值 For循环遍历List 删除后list大小发生变化 因此索引发生变化 所以删除的元素不是你想要的 解决办法 倒着遍历li
  • qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果

    应大家的要求 还是把完整的工程文件贴出来 大家省点事 http www kuaipan cn file id 48923272389086450 htm 先看看运行效果 我用的群创7寸屏 主机是mini2440 分辨率是800 480 程序
  • 转】M1卡密钥破解,收藏

    M1卡说明及使用proxmark3破解方法 看了网上写的一些关于M1卡的文章 多数有些误导之嫌 首先谈谈M1卡的规格 M1卡的容量为1KB 好多网上写8KB 这里其实是有个误区 应该是8K位 1Byte 1B 8位 其实也就是说8k位想到于
  • Oracle数据库启动过程

    一 Oracle数据库的四种状态 Oracle数据库有四种状态 SHUTDOWN NOMOUNT MOUNT OPEN 二 Oracle数据库的启动过程详解 Oracle数据库启动主要包括三个过程 1 shutdown状态 数据库没有启动
  • Linux·设备文件devfs

    目录 设备文件系统 devfs udev mdev 关于file和inode数据结构在内核中的探究 设备文件系统 Linux引入了虚拟文件系统 从而使设备的访问可以像访问普通文件系统一样 因此在内核中描述打开文件的数据inode中的rdev
  • Python实现自动化办公

    人工智能 AI 是当今世界最为热门的话题之一 而Python是从事AI领域最为流行的编程语言 在本文中 我将介绍如何使用Python实现自动化办公和处理工资表等知识教程 帮助您从零入门人工智能Python 一 制作Excel可视化报表 在传
  • Qt槽和信号参数

    Qt的信号只需要申明 不需要定义 signals void signalA int a Qt的槽和普通的成员函数是一样的 可以加上public potected 以及virtual等标记 public void slotA int a 把这
  • JAVA远程调试功能

    JAVA 支持调试功能 本身提供了一个简单的调试工具JDB 支持设置断点及线程级的调试同时 不同的JVM通过接口的协议联系 本地的Java文件在远程JVM建立联系和通信 一 基本使用 服务端配置 1 服务器端开启调试模式 启动时增加JVM启
  • unity利用Image画直线,以及折线图

    using System Collections using System Collections Generic using UnityEngine using UnityEngine UI
  • QT qrand()随机函数

    在Qt中 生成随机数的函数为 qrand 该函数是标准C 函数 rand 的线程安全版本 如果我们仅仅只是调用该函数来生成随机数 那么每次得到的随机数都将是相同的 这是因为Qt生成的随机数严格来说是一个 伪随机 它的产生是根据随机数种子计算
  • 山东大学 研一 高级算法设计与分析期末考试回忆版

    新鲜出炉 1 给了一个无序的数组 要求给定时间复杂度为n的平方和nlogn的2中排序算法进行排序 并证明其时间复杂度 2 证明WPAR问题是npc问题 即给定一个集合 我们能够找到它的一个子集 剩余部分是这个子集的C倍 C是整数 1 利用划
  • TensorFlow在MNIST中的应用 识别手写数字(OpenCV+TensorFlow+CNN)

    参考 1 TensorFlow技术解析与实战 2 http blog csdn net sparta 117 article details 66965760 3 http blog csdn net HelloZEX article de
  • Linux内核--内存管理

    内存管理单元MMU memory management unit 的主要功能是虚拟地址 virtual memory addresses 到物理地址 physical addresses 的转换 除此之外 它还可以实现内存保护 memory
  • 去掉softmax后Transformer会更好吗?复旦&华为诺亚提出SOFT:轻松搞定线性近似

    点击上方 计算机视觉工坊 选择 星标 干货第一时间送达 作者丨happy 编辑丨极市平台 导读 本文介绍了复旦大学 华为诺亚提出的一种新颖的softmax free的Transformer SOFT 所提SOFT显著改善了现有ViT方案的计
  • 设计模式之装饰者模式(包装模式)

    装饰者模式 1 实例 咖啡订餐项目 咖啡种类 单品咖啡 Espresso 意大利浓咖啡 ShortBlack LongBlack 美式咖啡 Decaf 无因咖啡 调料 Milk 牛奶 Soy 豆浆 Chocolate 巧克力 要求 订单结算