设计模式——装饰器模式

news2024/11/24 17:36:44

装饰器模式

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

装饰器模式通过将对象包装在装饰器类中,以便动态地修改其行为。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

优缺点

优点

  1. 不改动原有代码,动态增加功能。
  2. 对象间不会相互依赖、松耦合。
  3. 符合开闭原则,扩展性好,便于维护。

缺点

  1. 装饰器环节过多的话,导致装饰器类膨胀。
  2. 装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐。

装饰器模式的结构

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标。下面来分析其基本结构和实现方法。

模式的结构

装饰器模式主要包含以下角色。

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

装饰器模式的结构图如图所示。

动图封面

装饰器模式实例:

实例——图画

不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

代码如下:

画(Painting接口)

public interface Painting {
  public void show();
}

唐宫仕女图(TangGong类)

public class TangGong implements Painting {
  @Override
  public void show(){
    System.out.println("这是一副唐宫仕女图");
  }
}

装饰器类

public class Decorator implements Painting {
  private Painting painting;
  public Decorator(Painting monaLisa){
    this.painting = monaLisa;
  }
  @Override
  public void show() {
    System.out.println("先加上相框");
    painting.show();
    System.out.println("再扣上玻璃");
  }
}

测试类

public class DecoratorTest {
  public static void main(String[] args) {
    Painting painting = new TangGong();
    Painting monaLisa = new Decorator(painting);

    TangGong.show();
  }
}

实现方式 ——蜜雪冰城奶茶

秋天到了,女朋友非要喝秋天的第一杯奶茶,到了“蜜雪冰城”奶茶店后,给女朋友点了一杯奶茶,加了珍珠、芒果等配料,给自己点了一杯加冰柠檬水,加了冰块、柠檬片等配料,这时候就可以使用装饰器模式。

奶茶:抽象构件
珍珠芒果奶茶、柠檬水:具体构件
配料:装饰角色
珍珠、芒果、柠檬:具体装饰角色

代码实现:

抽象构件(Component)角色:奶茶

public interface IMilktea {
    void addDosing();
}

具体构件(ConcreteComponent)角色:珍珠奶茶

public class PearlMilktea implements IMilktea{
    @Override
    public void addDosing() {
        System.out.println("开始制作:珍珠奶茶");
    }
}

柠檬水

public class LemonMilktea implements IMilktea{
    @Override
    public void addDosing() {
        System.out.println("开始制作:柠檬水");
    }
}

装饰(Decorator)角色:配料

public abstract  class Dosing implements IMilktea{

    IMilktea iMilktea;

    public Dosing(IMilktea iMilktea){
        this.iMilktea = iMilktea;
    }

    @Override
    public void addDosing() {
        this.iMilktea.addDosing();
    }
}

具体装饰(ConcreteDecorator)角色:

加珍珠

public class Pearl extends Dosing {

    public Pearl(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加珍珠");
    }
}

加芒果

public class Mango extends Dosing {

    public Mango(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加芒果");
    }
}

加柠檬

public class Lemon extends Dosing {
    public Lemon(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加柠檬");
    }
}

加冰

public class Ice extends Dosing {

    public Ice(IMilktea iMilktea) {
        super(iMilktea);
    }

    @Override
    public void addDosing() {
        super.addDosing();
        System.out.println("制作中:加冰");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        System.out.println("服务员:你好,需要点什么呀?");
        System.out.println("我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水");
        System.out.println("服务员:好的。");
        PearlMilktea pearlMilktea = new PearlMilktea();
        Pearl pearl = new Pearl(pearlMilktea);
        Mango mango = new Mango(pearl);
        Ice ice = new Ice(mango);
        ice.addDosing();

        System.out.println("第一杯制作完成");
        LemonMilktea lemonMilktea = new LemonMilktea();
        Lemon lemon = new Lemon(lemonMilktea);
        Ice ice1 = new Ice(lemon);
        ice1.addDosing();
        System.out.println("第二杯制作完成");

        System.out.println("我:珍珠奶茶怎么加冰了?");
        System.out.println("服务员:对不起,珍珠奶茶做错了,重新给您做。");

        mango.addDosing();
        System.out.println("不加冰的珍珠奶茶制作完成");
        System.out.println("我:好的,谢谢!");
    }
}

输出结果

服务员:你好,需要点什么呀?
我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水
服务员:好的。
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
制作中:加冰
第一杯制作完成
开始制作:柠檬水
制作中:加柠檬
制作中:加冰
第二杯制作完成
我:珍珠奶茶怎么加冰了?
服务员:对不起,珍珠奶茶做错了,重新给您做。
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
不加冰的珍珠奶茶制作完成
我:好的,谢谢!

到此,女朋友喝到了秋天的第一杯奶茶。

应用场景

  • 动态的增加对象的功能;
  • 不能以派生子类的方式来扩展功能;
  • 限制对象的执行条件;
  • 参数控制和检查等;

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

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

相关文章

前端页面常见的布局分享

前端页面常见的布局分享 一、css盒模型 页面中的每一个元素都被看做一个矩形盒子。它包括:外边距、边框、内边距以及实际的内容。 网页设计中常听的属性名:内容(content)、填充(padding)、边框(border)、边界(margin),CSS盒子模型都具备这些…

为什么说ChatGPT地位难保

似乎可以说,从ChatGPT推出以来,OpenAI一直是生成式人工智能的王者。但这种状况可能持续不了太久了。 自11月面世以来,OpenAI的聊天机器人颠覆了教学、写作、科技等多个领域[1]。它把世界上最大的科技公司如Meta和谷歌打了个措手不及&#x…

HJ31 单词倒排 题解

题目描述:单词倒排_牛客题霸_牛客网 (nowcoder.com) 对字符串中的所有单词进行倒排。 1、构成单词的字符只有26个大写或小写英文字母; 2、非构成单词的字符均视为单词间隔符; 3、要求倒排后的单词间隔符以一个空格表示;如果原字符…

如何拼接两个视频在一起?

如何拼接两个视频在一起?在度过一个美好周末的时候,我和朋友一起拍摄了两组视频,准备将两个视频合并成一个并发布到朋友圈。这个想法非常棒,但是我在第一步就遇到了麻烦:如何将这两个视频拼接在一起?这听起…

MyBatis分页思想和特殊字符

目录 一、MyBatis分页思想 1.1 使用场景 1.2 代码演示 二、MyBatis特殊字符 2.1代码演示 一、MyBatis分页思想 1.1 使用场景 Mybatis分页应用场景: MyBatis是一个Java持久层框架,它提供了一种将SQL查询和结果映射到Java对象的简单方式。分页是MyBa…

【LeetCode】面试题总结 消失的数字 最小k个数

1.消失的数字 两种思路 1.先升序排序,再遍历并且让后一项与前一项比较 2.转化为数学问题求等差数列前n项和 (n的大小为数组的长度),将根据公式求得的应有的和数与数组中实际的和作差 import java.util.*; class Solution {public …

龙迅半导体,LT9611 MIPIDSI/CSI转HDMI,双端口MIPI接收,HDMI支持4K30HZ,免费提供完善的资料和选型推荐

龙迅LT9611 1.描述: LT9611 MIPIDSI/CSI到HDMI1.4桥具有双端口MIPID-PHY接收器前端配置,每个端口有4个数据通道,每个数据通道运行2Gbps,最大输入带宽为16Gbps。该桥提供了一个HDMI数据输出与可选的S/PDIF或8通道I2S串行音频输入…

Excel变天!微软把Python「塞」进去了,直接可搞机器学习

量子位 | 公众号 QbitAI 喜大普奔! 微软把Python弄进Excel了! 搭建一个机器学习天气预测模型,在Excel里即可实现。 而且无需任何设置,在单元格里输入“PY”,就能开搞。 数据清理、预测分析、可视化等等任务&#xf…

Harbor平台离线搭建

之前我有写过如何搭建harbor以及配置harbor,本文主要讲一下在不联网的情况下如何搭建Harbor平台。 环境:centos 7.9 docker版本:20.10.17 harbor版本:v1.10.10 一、离线安装docker 安装包官方地址:Index of linux/sta…

麒麟系统上安装 MySQL 8.0.24

我介绍一下在麒麟系统上安装 MySQL 8.0.24 的详细步骤,前提是您已经下载了 mysql-8.0.24-linux-glibc2.12-x86_64.tar.xz 安装包。其实安装很简单,但是有坑,而且问题非常严重!由于麒麟系统相关文章博客较少,导致遇到了…

[MyBatis系列③]动态SQL

目录 1、简介 2、if标签 3、foreach标签 4、SQL抽取 ⭐MyBatis系列①:增删改查 ⭐MyBatis系列②:两种Dao开发方式 1、简介 开发中在MyBatis映射文件配置SQL语句,但是前面配置的都是比较简单的,不涉及稍复杂的业务场景。想要应…

守护进程(精灵进程)

目录 前言 1.如何理解前台进程和后台进程 2.守护进程的概念 3.为什么会存在守护进程 4.如何实现守护进程 5.测试 总结 前言 今天我们要介绍的是关于守护进程如何实现,可能有小伙伴第一次听到守护进程这个概念,感觉很懵,知道进程的概念&…

实验八 网卡驱动移植

【实验目的】 掌握 Linux 内核配置的基本方法,完成对网卡驱动、NFS 等相关功能的配置 【实验环境】 ubuntu 14.04 发行版FS4412 实验平台交叉编译工具:arm-none-linux-gnueabi- 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行&…

基于labview设备状态监测和故障诊断(一)NI软硬件介绍

随着近代工业逐步向机电一体化方向发展。设备的自动化、智能化、大型化与复杂 化程度的不断提高,设备发生故障给企业所带来的经济损失越来越大,因此对设备的运 行状况进行实时监测和故障诊断势在必行。可以这样说,机械设备的工作状况监测和…

C++设计模式(工厂模式)

文章目录 前言一、什么是工厂模式二、简单工厂模式三、简单工厂模式优点和缺点四、简单工厂适用场景五、简单工厂类的使用总结 前言 本篇文章正式带大家来学习C中的设计模式,这篇文章主要带大家学习工厂模式。 一、什么是工厂模式 工厂模式(Factory P…

洗涤护理门店小程序DIY制作教程

随着移动互联网的快速发展,小程序成为了各行各业推广和服务的新平台。对于干洗店来说,拥有一个专属的洗护小程序不仅可以提升用户体验,还能增加店铺的曝光度和销售额。那么,如何DIY制作一个干洗店洗护小程序呢? 首先&a…

在Ubuntu上安装和设置RabbitMQ服务器,轻松实现外部远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

【最全】MySQL知识点总结

先简单的大致了解一下,学习知识的目的是运用!!! MySQL是什么? 一种关系型数据库管理系统,也就是说这是用来管理数据库的工具。 SQL相关命令 数据库相关 创建数据库:CREATE DATABASE 数据库名; …

12、Pinia 快速入门

1、什么是Pinia Pinia 是 Vue 的最新 状态管理工具 ,是 Vuex 的 替代品 2、手动添加Pinia到Vue项目 在实际开发项目的时候,关于Pinia的配置,可以在项目创建时自动添加 现在我们初次学习,从零开始: 1.使用 Vite 创建一个空的 Vue3 项目 n…

【JUC系列-02】深入理解CAS底层原理和基本使用

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786 深入理解cas的底层原理和基本使用…