设计模式 结构型 装饰器模式(Decorator Pattern)与 常见技术框架应用 解析

在这里插入图片描述

装饰器模式(Decorator Pattern),又称为包装器模式(Wrapper Pattern),是一种结构型设计模式。它允许在不改变原有对象结构的基础上,动态地给对象添加一些新的职责(即增加其额外功能)。

一、核心思想

装饰器模式的核心思想是通过组合而非继承来实现功能的扩展。它提供了一种比使用子类更加灵活的替代方案,使得对象可以在运行时根据需要动态地添加或移除装饰器,从而实现功能的动态组合。

二、定义与结构

定义:装饰器模式动态地给一个对象添加一些额外的职责。就扩展功能而言,装饰器模式提供了一种比使用子类更加灵活的替代方案。

结构

  1. 抽象组件(Component)角色:定义了一个对象接口,可以给这些对象动态地添加一些职责。
  2. 具体组件(Concrete Component)角色:实现了抽象组件接口,并定义了一个具体的对象,也可以给这个对象添加一些职责。
  3. 装饰器(Decorator)角色:持有一个组件(Component)对象的引用,并定义一个与抽象组件一致的接口。它本身是一个抽象类,通常不会直接实例化。
  4. 具体装饰器(Concrete Decorator)角色:负责给组件添加新的职责。

角色

在装饰器模式中,各个角色之间的关系和职责如下:

  • 抽象构件(Component):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法。它使得客户端能够以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
  • 具体构件(Concrete Component):它是抽象构件类的子类,用于定义具体的构建对象,实现了在抽象构件中声明的方法。装饰类可以给它增加额外的职责。
  • 抽象装饰(Decorator):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护了一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
  • 具体装饰(Concrete Decorator):它是抽象装饰类的子类,负责向构件添加新的职责。它定义了新的方法,并可以增加新的方法用于扩充对象的行为。

三、实现步骤与代码示例

以Java为例,展示装饰器模式的具体实现步骤和代码示例。

步骤

  1. 定义抽象组件接口。
  2. 实现具体组件类。
  3. 定义装饰器抽象类,它实现了抽象组件接口并持有一个抽象组件对象的引用。
  4. 实现具体装饰器类,它继承了装饰器抽象类并添加了新的职责。

代码示例

// 抽象组件接口
public interface Component {void operation();
}// 具体组件类
public class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("ConcreteComponent: Basic operation.");}
}// 装饰器抽象类
public abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}// 具体装饰器A
public class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedBehaviorA();}private void addedBehaviorA() {System.out.println("ConcreteDecoratorA: Added behavior A.");}
}// 具体装饰器B
public class ConcreteDecoratorB extends Decorator {public ConcreteDecoratorB(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedBehaviorB();}private void addedBehaviorB() {System.out.println("ConcreteDecoratorB: Added behavior B.");}
}// 客户端代码
public class Main {public static void main(String[] args) {Component component = new ConcreteComponent();Component decoratorA = new ConcreteDecoratorA(component);Component decoratorB = new ConcreteDecoratorB(decoratorA);decoratorB.operation();}
}

输出

ConcreteComponent: Basic operation.
ConcreteDecoratorA: Added behavior A.
ConcreteDecoratorB: Added behavior B.

在这个例子中,ConcreteComponent 执行了基础操作,之后通过 ConcreteDecoratorAConcreteDecoratorB 为该组件对象动态添加了新的行为。最终的输出显示了基础操作以及两个装饰器的新增行为。

四、常见技术框架应用

虽然装饰器模式在不同的技术框架中可能有不同的实现方式,但其核心思想是一致的。以下是一些在其他技术框架中应用装饰器模式应用:

1、以Java的IO流为例

  • 在Java的java.io包中广泛使用了装饰器模式。
  • 抽象构件是InputStream(字节输入流)和OutputStream(字节输出流)等接口。
  • 具体构件例如FileInputStream(从文件读取字节流)和FileOutputStream(向文件写入字节流)。
  • 抽象装饰类可以是FilterInputStreamFilterOutputStream,它们都实现了对应的InputStreamOutputStream接口,并且包含一个对InputStreamOutputStream的引用。
  • 具体装饰类有很多,比如BufferedInputStream(为输入流添加缓冲功能)和DataInputStream(可以从输入流读取基本数据类型)。以下是简单的代码示例:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class Main {public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("test.txt");// 使用装饰器BufferedInputStream为输入流添加缓冲功能InputStream bufferedInputStream = new BufferedInputStream(inputStream);int data;while ((data = bufferedInputStream.read())!= -1) {System.out.print((char) data);}bufferedInputStream.close();inputStream.close();}
}

2、ES6 装饰器

在ES6(ECMAScript 2015)及之后的版本中,JavaScript引入了装饰器作为实验性特性。装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。它们使用@expression这种形式,在编译时执行,并且可以用来修改类的行为。

装饰器本质上是一个函数,它接收目标对象(类构造函数或其原型)、属性名(对于属性和方法),以及描述符(对于方法)。装饰器可以返回一个新的属性描述符来替换旧的描述符,或者返回一个全新的构造函数来替代旧的构造函数。

下面我们将通过几个例子来详细解释如何在JavaScript中使用装饰器模式。

类装饰器

类装饰器应用于类构造函数,通常用于观察、修改或替换类定义。

function sealed(constructor) {console.log('Sealed decorator called');Object.seal(constructor);Object.seal(constructor.prototype);
}@sealed
class Person {constructor(name) {this.name = name;}
}

在这个例子中,当定义Person类时,sealed装饰器会立即执行,并将Person构造函数及其原型都密封起来,防止进一步扩展。

方法装饰器

方法装饰器用于修饰类的方法,可以改变方法的行为。

function enumerable(value) {return function (target, propertyKey, descriptor) {descriptor.enumerable = value;return descriptor;};
}class Example {@enumerable(false)method() {}
}

这里我们定义了一个enumerable装饰器,它可以控制类方法是否可以在遍历中出现。当我们在Example类中使用这个装饰器时,我们可以设置method方法是否可枚举。

属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

  • 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • 成员的名字。
function format(formatString) {return function(target, key) {let _value = target[key];const getter = () => _value;const setter = (value) => {console.log(`Setting ${key} to ${formatString.replace('{}', value)}`);_value = value;};delete target[key];Object.defineProperty(target, key, {get: getter,set: setter,enumerable: true,configurable: true});};
}class Greeting {@format('Hello, {}!')name = '';
}const greeting = new Greeting();
greeting.name = 'Alice';
console.log(greeting.name); // Output: Hello, Alice!

在这个例子中,format装饰器用来格式化name属性的值,当设置name属性时,它会输出一条格式化的消息。

注意事项

不同的框架(如TypeScript、Angular)对装饰器的支持程度也不同。在实际项目中使用装饰器之前,请确认你的开发环境支持这一特性。

五、应用场景

装饰器模式适用于以下场景:

  1. 当不能采用继承的方式扩展系统,或采用继承的方式扩展系统不利于系统维护时。
  2. 当对象的功能要求可以动态增加、撤销时。
  3. 需要灵活扩展对象的功能,而不想增加大量子类时。

具体应用场景包括:

  1. I/O流处理:如Java的I/O库中的BufferedReaderBufferedWriter等类都是对基本的ReaderWriter进行装饰,增加了缓冲功能。
  2. GUI组件定制:如按钮、文本框等组件的定制。
  3. 报表生成:如分页、标题、水印等格式的动态添加。
  4. 权限控制:如为不同的资源或操作添加权限检查。
  5. 日志记录:如为不同的对象或方法添加日志记录功能。

六、优缺点

优点

  1. 灵活性强:装饰器和被装饰类可以独立发展,互不耦合。可以通过组合多个装饰器来灵活地组合对象的行为。
  2. 扩展性好:提供了比继承更加灵活的扩展方式。不需要创建大量的子类,就可以给对象增加新的行为或功能。
  3. 透明性好:用户可以根据需要选择性地添加装饰器,而不需要了解这些装饰器是如何实现的。这使得代码更加清晰和简洁。
  4. 复用性高:装饰器可以被重复使用在多个对象上,提高了代码的复用性。

缺点

  1. 复杂性增加:过多地使用装饰器可能会使代码变得复杂,特别是当装饰器链很长时,理解和维护起来会比较困难。
  2. 性能开销:在装饰器链中,每个方法调用都会经过多层装饰器的包装,这可能会导致性能上的开销。

总之,装饰器模式是一种非常有用的设计模式,它可以提高代码的可扩展性和灵活性。但在设计时需要慎重考虑是否使用该模式,并权衡其优缺点。

在这里插入图片描述

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

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

相关文章

<论文>聊聊初代LLaMA

一、摘要 本文介绍来自Meta的论文《LLaMA: Open and Efficient Foundation Language Models》,这篇2023年的研究发布了开源的LLaMA系列大模型,轰动一时。 译文: 我们推出了 LLaMA,一系列参数规模从 70 亿到 650 亿的基础语言模型。…

【bluedroid】A2dp Source播放流程源码分析(4)

接上集分析:【bluedroid】A2dp Source播放流程源码分析(3)-CSDN博客 蓝牙和AUDIO之间的接口 蓝牙和audio之间的通信是通过socket,管理socket中的文件是UIPC,UIPC管理两条socket。 A2DP_CTRL_PATH /data/misc/bluedroid/.a2dp_ctrl A2DP_DATA_PATH /data/misc/bluedroid…

React 中结合 antd 的 Input 组件实现防抖输入

在 React 中结合 antd 的 Input 组件实现防抖输入,可以通过以下几种方式实现: 1. 使用 useEffect 和 setTimeout 代码示例 import React, { useState, useEffect } from "react"; import { Input } from "antd";const DebouncedI…

把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用

将vue项目发布成npm库文件,第三方通过npm依赖安装使用;使用最近公司接了一个项目,这个项目需要集成到第三方页面,在第三方页面点击项目名称,页面变成我们的项目页面;要求以npm库文件提供给他们;…

STM32 NOR FLASH(SPI FLASH)驱动移植(2)

2)FLASH 读取函数 /* * brief 读取 SPI FLASH * note 在指定地址开始读取指定长度的数据 * param pbuf : 数据存储区 * param addr : 开始读取的地址(最大 32bit) * param datalen : 要读取的字节数(最大 65535) * retval 无 */ void norflash_read(uint8_t *pbuf…

自从学会Git,感觉打开了一扇新大门

“同事让我用 Git 提交代码,我居然直接把项目文件压缩发过去了……”相信很多初学者都经历过类似的窘境。而当你真正掌握 Git 时,才会发现它就像一本魔法书,轻松解决代码管理的种种难题。 为什么 Git 能成为程序员的标配工具?它究…

【重庆】《政务数字化应用费用测算规范》(T/CDCIDA 001—2023)-省市费用标准解读系列36

《政务数字化应用费用测算规范(报批稿)》于2023年11月18日实施,本文件按照GB/T 1.1-2020给出的规则起草,主要适用于重庆政务数字化应用项目的费用测算。我司基于专业第三方信息化项目造价机构角度,从标准创新点、定制软…

Python | 学习type()方法动态创建类

getattr方法的使用场景是在访问不存在的属性时,会触发该方法中的处理逻辑。尤其是在动态属性获取中结合 type()动态创建类有着良好的使用关系。 type()方法常用来判断属性的类别,而动态创建类不常使用,通过如下的几个实例来学习使用&#xff…

机器学习之逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告

逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告 目录 逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告1 逻辑回归算法1.1 概念理解1.2 算法导入1.3 算法优缺点 2 LogisticRegression理解2.1查看参数定义2.2 参数理解2.3 方法2.4基本格式 3 数据标准…

.NET在中国的就业前景:开源与跨平台带来的新机遇

随着技术的不断发展和市场需求的变化,.NET在中国的就业前景正变得愈加广阔。尤其是在开源和跨平台的推动下,越来越多的中国中小型企业选择了.NET技术作为其开发平台,进一步提升了.NET技术人才的市场需求。尽管在中国市场,.NET的市…

Linux(Ubuntu24.04)源码编译安装VTK7.1.1记录

VTK(Visualization Toolkit)是一个开源的3D可视化开发工具包,用于开发可视化和图形处理应用程序。VTK提供了一系列的算法和工具,用于创建、渲染和处理复杂的3D图形和数据。VTK由C编写,并提供了Python、Java和Tcl等语言…

Hadoop解决数据倾斜方法

数据倾斜是指在分布式数据处理过程中,数据在某些节点上的分布不均匀,导致这些节点的处理负载过重,而其他节点的资源闲置,从而影响整个系统的性能。在 Hadoop 中,以下是一些解决数据倾斜的方法: 1. 数据预处…

【人工智能】人工智能与大模型

人工智能与大模型的结合正在深刻改变多个行业和领域的格局。 1. 人工智能 (AI) 人工智能指的是使计算机或机器具备模拟人类智能的能力,包括学习、推理、问题解决、自然语言处理、视觉感知等。AI的发展可以分为几个阶段: 弱人工智能 (Narrow AI)&#…

安卓11 SysteUI添加按钮以及下拉状态栏的色温调节按钮

最近客户想要做一个台灯产品,需要实现 串口调节台灯功能 ,其中包括 亮度调节 色温调节 开关 三个功能 话不多说,贴代码 diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml old mode 100644 new …

node.js下载、安装、设置国内镜像源(永久)(Windows11)

目录 node-v20.18.0-x64 工具下载安装设置国内镜像源(永久) node-v20.18.0-x64 工具 系统:Windows 11 下载 官网https://nodejs.org/zh-cn/download/package-manager 版本我是跟着老师选的node-v20.18.0-x64如图选择 Windows、x64、v2…

如何轻松安全地销售旧 Android 手机

众所周知,手机不断更新换代。当您想要的手机终于到货时,您可能迫不及待地将旧 Android 手机更新为最新手机。在此之前,你们中的一些人可能会考虑以最多的钱卖掉旧的Android手机。 但永远不要冲动地卖掉你的旧 Android 手机!为了安…

欧科云链研究院:ChatGPT 眼中的 Web3

编辑|OKG Research 转眼间,2024年已经进入尾声,Web3 行业经历了热闹非凡的一年。今年注定也是属于AI的重要一年,OKG Research 决定拉上 ChatGPT 这位“最懂归纳的AI拍档”,尝试把一整年的研究内容浓缩成精华。我们一共…

【从零开始入门unity游戏开发之——unity篇04】unity6基础入门——场景窗口(Scene)和层级窗口(Hierarchy)介绍

文章目录 场景窗口(Scene)和层级窗口(Hierarchy)一、层级窗口(Hierarchy)1、添加新的对象(物体)2、Hierarchy层级窗口快捷键3、搜索 二、Scene场景窗口1、工具栏控制台2、操作物体位…

Jboss EnhancedQueueExecutor 使用案例及源码解读

使用案例 EnhancedQueueExecutor配置类 Configuration Slf4j public class EnhancedQueueExecutorConfig {Beanpublic EnhancedQueueExecutor enhancedQueueExecutor() {return createExecutor(5, 100,"enhancedQueueExecutor","任务处理失败 {}");}priv…

如何确保涡度通量观测数据的准确性?涡度通量光敏感性分析、温度敏感性分析、数据风浪区分析等

确保涡度通量观测数据的准确性,可以采取以下几个步骤: 1.数据预处理:在进行数据分析之前,需要对原始的高频涡度通量数据进行预处理,包括剔除异常值和进行数据缺失插补。异常值剔除可以通过设定合理的阈值来识别并剔除数…