当前位置: 首页 > news >正文

23种设计模式之装饰者模式

深入理解装饰者模式

  • 一、装饰者模式简介
    • 1.1 定义
    • 1.2 模式类型
    • 1.3 主要作用
    • 1.4 优点
    • 1.5 缺点
  • 二、模式动机
  • 三、模式结构
  • 四、 装饰者模式的实现
    • 4.1 组件接口
    • 4.2 具体组件
    • 4.3 装饰者抽象类
    • 4.4 具体装饰者
    • 4.5 使用装饰者模式
    • 4.6 输出结果:
  • 五、 应用场景
    • 5.1 图形用户界面
    • 5.2 数据流处理
    • 5.3 日志功能
    • 5.4 监控和计数
  • 六、总结

在这里插入图片描述

一、装饰者模式简介

1.1 定义


  装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地向对象添加新的功能,而不改变其结构和实现。装饰者模式通过组合来实现功能的扩展,它将功能划分成单一职责的类,并在需要时动态地组合这些功能实例化对象。

  根据《设计模式:可复用面向对象软件的基础》一书的定义,装饰者模式是:

动态地给对象添加一些额外的职责。就增加功能来说,装饰者模式相比生成子类更为灵活。

1.2 模式类型


  结构型

1.3 主要作用


  1. 扩展功能:允许在运行时动态地给对象添加新的功能,而不需要修改对象的结构。这有助于遵循 OCP 原则。
  2. 灵活性和可重用性:装饰者模式通过创建装饰类来包裹原始对象,并在装饰类中添加功能,这样可以灵活组合不同的装饰器以达到不同的功能组合,同时原始类和装饰类都可以独立复用。
  3. 简化复杂度:对于有大量可选功能的对象,如果使用继承来实现每种可能的功能组合,会导致类的数量爆炸式增长。装饰者模式通过组合而非继承的方式来添加功能,大大减少了类的数量,简化了系统的复杂度。
  4. 保持接口一致性:装饰者模式保持了被装饰对象的接口一致,客户端代码可以透明地使用装饰后的对象,无需关心是否以及如何被装饰,这有利于代码的维护和扩展。
  5. 易于管理对象的责任:每个装饰者都负责单一职责,即添加特定的功能,这使得对象的责任更加清晰,便于管理和调试。

1.4 优点


  1. 灵活性高:可以在运行时任意组合装饰者,动态地扩展对象功能。
  2. 符合单一职责原则:每个装饰者类只负责增加一种功能,类的职责更加单一。
  3. 降低类复杂度:相比通过继承扩展功能,装饰者模式避免了大量的子类生成,从而减少类的复杂度。

1.5 缺点


  1. 对象数量增多:由于装饰者和被装饰者都是对象,这会增加系统中对象的数量,增加管理成本。
  2. 依赖过多:容易产生过多的小型对象,增加系统复杂性,理解和维护变得更加困难。

二、模式动机


装饰者模式的主要动机是应对以下几个问题:

  • 避免类爆炸:通过继承添加新功能会导致子类数量激增,维护起来十分困难。
  • 灵活组合功能:继承的组合方式是静态的,不够灵活。装饰者模式允许动态地组合功能。
  • 单一职责原则:通过将功能分解到不同的装饰类中,装饰者模式使每个类的职责更加单一,代码更易读易维护。

三、模式结构

在这里插入图片描述

装饰者模式通常涉及以下几个角色:

  • Component(抽象组件):定义一个对象接口,可以给这些对象动态地添加职责。
  • ConcreteComponent(具体组件):实现 Component 接口的具体对象,可以给这些对象添加一些职责。
  • Decorator(装饰者抽象类):继承 Component 接口,通常持有一个 Component 对象的引用,并定义一个与 Component 接口一致的接口。
  • ConcreteDecorator(具体装饰者):扩展 Decorator 类的具体装饰者,负责向组件添加新的职责。

四、 装饰者模式的实现


  我们将以一个具体的咖啡示例来介绍装饰者模式的实现。在这个例子中,我们有一个基本的咖啡对象,可以动态地添加不同的配料(如牛奶和糖)。

4.1 组件接口

// Java实现
public interface Coffee {double cost();String getDescription();
}

4.2 具体组件

public class SimpleCoffee implements Coffee {public double cost() {return 5.0;}public String getDescription() {return "Simple Coffee";}
}

4.3 装饰者抽象类

public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}public double cost() {return coffee.cost();}public String getDescription() {return coffee.getDescription();}
}

4.4 具体装饰者

public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}public double cost() {return super.cost() + 1.5;}public String getDescription() {return super.getDescription() + ", Milk";}
}public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}public double cost() {return super.cost() + 0.5;}public String getDescription() {return super.getDescription() + ", Sugar";}
}

4.5 使用装饰者模式

public class CoffeeShop {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();System.out.println(coffee.getDescription() + " Cost: $" + coffee.cost());coffee = new MilkDecorator(coffee);System.out.println(coffee.getDescription() + " Cost: $" + coffee.cost());coffee = new SugarDecorator(coffee);System.out.println(coffee.getDescription() + " Cost: $" + coffee.cost());}
}

4.6 输出结果:

Simple Coffee Cost: $5.0
Simple Coffee, Milk Cost: $6.5
Simple Coffee, Milk, Sugar Cost: $7.0

五、 应用场景

在这里插入图片描述

5.1 图形用户界面


  在图形用户界面(GUI)框架中,装饰者模式广泛应用于实现各种控件的装饰功能。控件如按钮、文本框等可以用不同的装饰者类装饰来添加多种新功能,如滚动条、边框等。例如,Java Swing框架中的JComponent类就是使用类似装饰者模式的理念实现的。

5.2 数据流处理


  在数据流处理框架中,输入输出流是装饰者模式的另一个经典应用。JavaI/O流设计就是实现装饰者模式的一个优秀示例,如FileInputStreamBufferedInputStreamDataInputStream等通过装饰者模式组合,实现了灵活而强大的数据流操作功能。

5.3 日志功能


  日志记录功能的实现也是装饰者模式的理想应用场景。例如,传统的日志记录器Logger可以通过装饰者模式添加不同的日志处理和记录策略,如格式化日志、输出到文件、发送电子邮件等。

5.4 监控和计数


  在监控系统中,可以动态地为程序添加计数器、性能监控器等功能,以增强对系统运行状态的了解。

六、总结

  装饰者模式是一种强大的设计模式,它提供了一种灵活、可扩展的方式来动态地为对象添加新功能。通过这一模式,我们可以在不修改已有类代码的情况下,灵活地扩展系统功能,符合开闭原则单一职责原则。无论是在GUI编程中、日志功能增强,还是在数据流处理以及监控系统中,装饰者模式都能发挥重要作用。

相关文章:

23种设计模式之装饰者模式

深入理解装饰者模式 一、装饰者模式简介1.1 定义1.2 模式类型1.3 主要作用1.4 优点1.5 缺点 二、模式动机三、模式结构四、 装饰者模式的实现4.1 组件接口4.2 具体组件4.3 装饰者抽象类4.4 具体装饰者4.5 使用装饰者模式4.6 输出结果: 五、 应用场景5.1 图形用户界面…...

数据结构--单链表实现

欢迎光顾我的homepage 前言 链表和顺序表都是线性表的一种,但是顺序表在物理结构和逻辑结构上都是连续的,但链表在逻辑结构上是连续的,而在物理结构上不一定连续;来看以下图片来认识链表与顺序表的差别 这里以动态顺序表…...

2024攻防演练:亚信安全推出MSS/SaaS短期定制服务

随着2024年攻防演练周期延长的消息不断传出,各参与方将面临前所未有的挑战。面对强大的攻击队伍和日益严格的监管压力,防守单位必须提前进行全面而周密的准备和部署。为应对这一形势,亚信安全特别推出了为期三个月的MSS/SaaS短期订阅方案。该…...

基于java+springboot+vue实现的在线课程管理系统(文末源码+Lw)236

摘要 本文首先介绍了在线课程管理系统的现状及开发背景,然后论述了系统的设计目标、系统需求、总体设计方案以及系统的详细设计和实现,最后对在线课程管理系统进行了系统检测并提出了还需要改进的问题。本系统能够实现教师管理,科目管理&…...

每日一更 EFK日志分析系统

需要docker和docker-compose环境 下面时docker-compose.yaml文件 [rootnode1 docker-EFK]# cat docker-compose.yaml version: 3.3services:elasticsearch:image: "docker.elastic.co/elasticsearch/elasticsearch:7.17.5"container_name: elasticsearchrestart: …...

python类继承和类变量

Python一些类继承和实例变量的使用 定义基类 class APIException:code 500msg "Sorry, error"error_code 999def __init__(self, msgNone):print("APIException init ...")def error_400(self):pass复用基类的属性值 class ClientTypeError(APIExcept…...

js 随机生成整数

随机生成一个唯一的整数 id export const randomId () > { return Date.now() Math.floor(Math.random() * 10000) } 生成随机ID的方法 // 随机生成0 - 9999 export const randomId ()> { return Math.floor(Math.random() * 10000).toString() } // 随机生成0-999之…...

深入Django(七)

Django的数据库迁移系统 引言 在前六天的教程中,我们介绍了Django的基本概念、模型、视图、模板、URL路由和表单系统。今天,我们将讨论Django的数据库迁移系统,它是管理和跟踪数据库变化的关键组件。 Django数据库迁移概述 Django的数据库…...

【区分vue2和vue3下的element UI Steps 步骤条组件,分别详细介绍属性,事件,方法如何使用,并举例】

在 Vue 2 和 Vue 3 中,Element UI(针对 Vue 2)和 Element Plus(针对 Vue 3)提供了 Steps 步骤条组件,用于展示当前操作的进度步骤。虽然这两个库都提供了步骤条组件,但它们在属性、事件和方法的…...

uni-app x 跨平台开发框架

目录 uni-app x 是什么 和Flutter对比 uts语言 uvue渲染引擎 组合式API的写法 选项式API写法 页面生命周期 API pages.json全局配置文件 总结 uni-app x 是什么 uni-app x,是下一代 uni-app,是一个跨平台应用开发引擎。 uni-app x 是一个庞…...

YOLOv8模型调参---数据增强

目录 1.数据预处理 2.数据增强 2.1 数据增强的作用 2.2 数据增强方式与适用场景 2.2.1离线增强(Offline Augmentation) 2.2.2 在线增强(Online Augmentation) 3. 数据增强的具体方法 4. YOLOv8的数据增强 4.1 YOLOv8默认…...

【Nginx】docker运行Nginx及配置

Nginx镜像的获取 直接从Docker Hub拉取Nginx镜像通过Dockerfile构建Nginx镜像后拉取 二者区别 主要区别在于定制化程度和构建过程的控制: 直接拉取Nginx镜像: 简便性:直接使用docker pull nginx命令可以快速拉取官方的Nginx镜像。这个过程…...

tensorflow和numpy的版本

查看cuda版本 dpkg -l | grep cuda i libcudart11.0:amd64 11.5.117~11.5.1-1ubuntu1 amd64 NVIDIA CUDA Runtime Library ii nvidia-cuda-dev:amd64 11.5.1-1ubuntu1 …...

二维Gamma分布的激光点云去噪

目录 1、Gamma 分布简介2、实现步骤 1、Gamma 分布简介 Gamma 分布在合成孔径雷达( Synthetic Aperture Radar,SAR) 图像分割中具有广泛应用,较好的解决了SAR 图像中相干斑噪声对图像分割的影响。采用二维Gamma 分布对…...

鸿蒙笔记导航栏,路由,还有axios

1.导航组件 导航栏位置可以调整,导航栏位置 Entry Component struct t1 {build() {Tabs(){TabContent() {Text(qwer)}.tabBar("首页")TabContent() {Text(发现内容)}.tabBar(发现)TabContent() {Text(我的内容)}.tabBar("我的")}// 做平板适配…...

Spring 框架中都用到了哪些设计模式:单例模式、策略模式、代理模式

Spring 框架是一个功能强大的企业级应用开发框架,它使用了多种设计模式来提高代码的可维护性、可扩展性和可重用性。以下是 Spring 框架中常见的几个设计模式,并简要说明它们的应用场景: 1. 单例模式(Singleton Pattern) 定义:确保一个类只有一个实例,并提供全局访问点…...

阶段总结——基于深度学习的三叶青图像识别

阶段总结——基于深度学习的三叶青图像识别 文章目录 一、计算机视觉图像分类系统设计二、训练模型2.1. 构建数据集2.2. 网络模型选择2.3. 图像数据增强与调参2.4. 部署模型到web端2.5. 开发图像识别小程序 三、实验结果3.1. 模型训练3.2. 模型部署 四、讨论五、参考文献&#…...

深度解析Java世界中的对象镜像:浅拷贝与深拷贝的奥秘与应用

在Java编程的浩瀚宇宙中,对象拷贝是一项既基础又至关重要的技术。它直接关系到程序的性能、资源管理及数据安全性。然而,提及对象拷贝,不得不深入探讨其两大核心类型:浅拷贝(Shallow Copy)与深拷贝&#xf…...

Python | Leetcode Python题解之第218题天际线问题

题目: 题解: class Solution:def getSkyline(self, buildings: List[List[int]]) -> List[List[int]]:buildings.sort(keylambda bu:(bu[0],-bu[2],bu[1]))buildings.append([inf,inf,inf])heap [[-inf,-inf,-inf]]ans []for l,r,h in buildings:i…...

使用Spring Boot构建RESTful API

使用Spring Boot构建RESTful API 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,我们将深入探讨如何使用Spring Boot构建RESTful API。通过这篇…...

Spark快速大数据分析PDF下载读书分享推荐

《Spark 快速大数据分析》是一本为 Spark 初学者准备的书,它没有过多深入实现细节,而是更多关注上层用户的具体用法。不过,本书绝不仅仅限于 Spark 的用法,它对 Spark 的核心概念和基本原理也有较为全面的介绍,让读者能…...

Centos7离线安装mysql-5.7.44bundle包

在 CentOS 7 上安装 mysql-5.7.44-1.el7.x86_64.rpm-bundle.tar(这里假设这是一个包含多个 RPM 包的 tar 归档文件)的步骤通常涉及解压归档文件、安装 RPM 包以及配置 MySQL 服务。以下是一个详细的步骤指南: 1. 下载和解压 RPM 包 首先&am…...

ROS melodic版本卸载---Ubuntu18.04

sudo apt-get remove ros-melodic-desktop-fullsudo apt-get remove gazebo* 删除依赖关系 sudo apt autoremove删除与ros关联的所有文件 sudo apt-get purge ros-* sudo rm -rf /etc/ros找到.bashrc文件删除含ros的环境配置语句 全部删除完毕,可以去计算机下的…...

Java面试之Java多线程常见面试题

1、什么是线程? 定义:线程是程序中的执行路径,是操作系统进行调度的基本单位。它允许程序并发执行多个任务,提高程序的响应速度和资源利用率。 2、为什么需要线程? 1、提高并发性:线程允许程序同时执行多…...

Java [ 基础 ] Java面向对象编程 (OOP) ✨

目录 ✨探索Java基础 Java面向对象编程 (OOP) ✨ 引言 1. 类和对象 2. 封装 3. 继承 4. 多态 5. 抽象 结论 ✨探索Java基础 Java面向对象编程 (OOP) ✨ 引言 Java是一门以面向对象编程(OOP)为基础的编程语言。OOP的核心概念包括类和对象、封装…...

敏捷开发笔记(第9章节)--开放-封闭原则(OCP)

目录 1:PDF上传链接 9.1 开放-封闭原则(OCP) 9.2 描述 9.3 关键是抽象 9.3.1 shape应用程序 9.3.2 违反OCP 糟糕的设计 9.3.3 遵循OCP 9.3.4 是的,我说谎了 9.3.5 预测变化和“贴切的”结构 9.3.6 放置吊钩 1.只受一次…...

苹果电脑清理app垃圾高效清理,无需专业知识

在我们的日常使用中,苹果电脑以其优雅的设计和强大的功能赢得了广泛的喜爱。然而,即便是最高效的设备,也无法免俗地积累各种不必要的文件和垃圾,特别是app垃圾。所以,苹果电脑清理app垃圾高效清理,对于大多…...

【算法】(C语言):快速排序(递归)、归并排序(递归)、希尔排序

快速排序(递归) 左指针指向第一个数据,右指针指向最后一个数据。取第一个数据作为中间值。右指针指向的数据 循环与中间值比对,若大于中间值,右指针往左移动一位,若小于中间值,右指针停住。右…...

模型驱动开发(Model-Driven Development,MDD):提高软件开发效率与一致性的利器

目录 前言1. 模型驱动开发的原理1.1 什么是模型驱动开发1.2 MDD的核心思想 2. 模型驱动开发的优势2.1 提高开发效率2.2 确保代码一致性2.3 促进沟通和协作2.4 方便维护和扩展 3. 实现模型驱动开发的方法3.1 选择合适的建模工具3.1.1 UML3.1.2 BPMN3.1.3 SysML 3.2 建模方法3.2.…...

记录discuz修改用户的主题出售价格

大家好,我是网创有方的站长,今天遇到了需要修改discuz的主题出售价格。特此记录下 方法很简单: 进入用于组-》选择论坛-》批量修改...