装饰器模式简介

概念

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许您在不改变现有对象结构的情况下,动态地将新功能附加到对象上。通过创建一个包装器类来扩展原始类的功能。这个包装器类具有与原始类相同的接口,并在内部持有一个指向原始对象的引用。通过将多个包装器链接在一起,可以递归地添加任意数量的功能。

特点

  1. 装饰器和被装饰对象实现相同接口,使得客户端无需关心具体类型。
  2. 可以动态地添加、删除或组合多个装饰器。
  3. 不需要修改已存在代码即可扩展功能。

优点

  1. 提供了灵活性和可扩展性,可以根据需要逐步增加或移除功能。
  2. 遵循开闭原则,不需要修改现有代码即可实现新功能。
  3. 具备了继承关系所缺乏的灵活性。

缺点

  1. 增加了系统中类的数量,在某些情况下可能会增加复杂度和理解难度。
  2. 如果使用不当,可能会导致过多嵌套或产生大量小粒度对象。

适用场景

  1. 当希望为一个对象动态地添加额外的功能时,可以使用装饰器模式。
  2. 当不适合使用继承来扩展对象功能时,可以考虑使用该模式。

实现方式

使用抽象类作为基础组件

实现原理:

  1. 定义一个抽象类,该抽象类定义了需要被包装和扩展功能的方法。
  2. 创建一个抽象装饰器类,它继承自该抽象类。这个抽象装饰器持有一个指向基础组件对象(即被包装对象)的引用。
  3. 在具体子类中,通过调用父级构造函数传入被包装对象,并重写相关方法,在其中添加额外功能并调用父级方法以保留原始行为。

实现代码:

// 抽象基础组件
abstract class Component {public abstract void operation();
}// 具体基础组件
class ConcreteComponent extends Component {public void operation() {System.out.println("执行具体操作");}
}// 抽象装饰器
abstract class Decorator extends Component {protected Component component;public Decorator(Component component) {this.component = component;}public void operation() {if (component != null) {component.operation();}}
}// 具体装饰器A
class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}public void addedBehavior() {System.out.println("新增附加行为 A");}public void operation(){super.operation();addedBehavior();}
}// 具体装饰器B
class ConcreteDecoratorB extends Decorator{public ConcreteDecoratorB(Component comp){super(comp);}public void addedBehavior(){System.out.println("新增附加行为 B");}public void operation(){super.operation();addedBehavior();}
}
// 使用示例
public class Main {public static void main(String[] args) {
// 使用示例Component component = new ConcreteComponent();component = new ConcreteDecoratorA(component);component.operation();component = new ConcreteDecoratorB(component);component.operation();}
}

在上述示例中,我们定义了一个抽象基础组件类Component,它包含一个抽象方法operation()。然后创建了具体的基础组件类ConcreteComponent,它实现了这个抽象方法。

接下来,我们创建了一个抽象装饰器类Decorator,它也继承自基础组件类,并持有一个指向基础组件对象的引用。在装饰器类中,我们通过调用基础组件对象的operation()方法来实现具体功能。

然后,我们创建了两个具体装饰器类ConcreteDecoratorAConcreteDecoratorB,它们分别扩展了抽象装饰器类并添加了额外的功能。在这些具体装饰器中,我们首先调用父类的operation()方法来执行原始操作,然后再执行附加行为。

最后,在客户端代码中,我们创建一个具体基础组件对象,并使用多个具体装饰器对象进行包裹和扩展。通过调用最外层装饰器对象的operation()方法来触发整个装饰过程上所有操作的执行。

需要注意,在这种方式下,每次增加新类型或结构变化时都需要修改相关子类。这可能会导致代码的脆弱性。

使用接口作为基础组件

实现原理:

  1. 定义一个接口作为基础组件,该接口定义了需要被装饰的方法。
  2. 创建一个具体的基础组件类来实现该接口,并实现具体的业务逻辑。
  3. 创建一个抽象装饰器类,它也实现了相同的接口。这个装饰器持有一个指向基础组件对象的引用,并在其内部调用基础组件对象对应方法。
  4. 创建具体装饰器类来扩展功能。这些具体装饰器也是通过实现抽象装饰器并添加额外功能来实现。

实现代码:

// 基础组件
interface Component {void operation();
}// 具体基础组件
class ConcreteComponent implements Component {public void operation() {System.out.println("执行具体操作");}
}// 抽象装饰器
abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}public void operation() {if (component != null) {component.operation();}}
}// 具体装饰器A
class ConcreteDecoratorA implements Component {private Component component;public ConcreteDecoratorA(Component comp){this.component = comp;}public void addedBehavior(){System.out.println("新增附加行为 A");}public void operation(){if(component != null){component.operation();}addedBehavior();}
}// 具体装饰器B
class ConcreteDecoratorB implements Component {private Component component;public ConcreteDecoratorB(Component comp){this.component = comp;}public void addedBehavior(){System.out.println("新增附加行为 B");}public void operation(){if(component != null){component.operation();}addedBehavior();}
}public class Main {public static void main(String[] args) {// 使用示例Component component = new ConcreteComponent();component = new ConcreteDecoratorA(component);component = new ConcreteDecoratorB(component);component.operation();}
}

在上述示例中,我们定义了一个基础组件接口Component,它包含一个方法operation()

然后创建了具体的基础组件类ConcreteComponent,它实现了该接口并实现具体的业务逻辑。

接下来,我们创建了抽象装饰器类 Decorator ,它也实现了相同的接口。这个装饰器持有一个指向基础组件对象的引用,并在其内部调用基础组件对象对应方法。

然后,我们创建了两个具体装饰器类 ConcreteDecoratorA  ConcreteDecoratorB ,它们分别实现了该接口并扩展了功能。在这些具体装饰器中,我们首先调用基础组件对象的 operation() 方法来执行原始操作,然后再执行附加行为。

最后,在客户端代码中,我们创建一个具体基础组件对象,并使用多个具体装饰器对象进行包裹和扩展。通过调用最外层装饰器对象的 operation() 方法来触发整个装饰过程上所有操作的执行。

存在的问题:

  1. 需要在每个具体装饰器中重新声明所有方法,即使它们只是简单地委派给基础组件对象。
  2. 如果需要改变已有代码结构(例如新增一种新类型),则需要修改所有相关子类。

动态装饰器链

实现原理:

  1. 定义一个接口或抽象类作为基础组件,该接口或类定义了需要被装饰的方法。
  2. 创建具体的基础组件类来实现该接口或继承该抽象类,并实现具体的业务逻辑。
  3. 创建一个抽象装饰器类,它也实现了相同的接口或继承相同的抽象类。这个装饰器持有一个指向基础组件对象的引用,并在其内部调用基础组件对象对应方法。
  4. 在抽象装饰器中添加一个方法,用于动态地添加下一个具体装饰器到链中。
  5. 创建具体装饰器类来扩展功能。这些具体装饰器也是通过继承抽象装饰器并添加额外功能来实现。

实现代码:

// 基础组件
interface Component {void operation();
}// 具体基础组件
class ConcreteComponent implements Component {public void operation() {System.out.println("执行具体操作");}
}// 抽象装饰器
abstract class Decorator implements Component {protected Component component;protected Decorator nextDecorator;public void setNextDecorator(Decorator decorator) {this.nextDecorator = decorator;}public void operation() {if (component != null) {component.operation();}if (nextDecorator != null) {nextDecorator.operation();}}
}// 具体装饰器A
class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component comp){this.component = comp;}public void addedBehavior(){System.out.println("新增附加行为 A");}public void operation(){if(component != null){component.operation();}addedBehavior();if(nextDecorator != null){nextDecorator.operation();}}
}// 具体装饰器B
class ConcreteDecoratorB extends Decorator {public ConcreteDecoratorB(Component comp){this.component = comp;}public void addedBehavior(){System.out.println("新增附加行为 B");}public void operation(){if(component != null){component.operation();}addedBehavior();if(nextDecorator!=null){nextDecorator.operation();}}
}public class Main {public static void main(String[] args) {// 使用示例Component component = new ConcreteComponent();ConcreteDecoratorA decoratorA = new ConcreteDecoratorA(component);ConcreteDecoratorB decoratorB = new ConcreteDecoratorB(component);decoratorA.setNextDecorator(decoratorB);decoratorA.operation();}
}

存在问题
使用动态装饰器链时可能会遇到以下问题:

  1. 链中每个节点都需要知道下一个节点,增加了耦合性和复杂性。
  2. 动态修改链可能会导致不可预测行为和难以调试。

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

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

相关文章

对称二叉树(Leetcode 101)

题目 101. 对称二叉树 思路 使用层序遍历,遍历当前层的节点时,如该节点的左(右)孩子为空,在list中添加null,否则加入左(右)孩子的值。每遍历完一层则对当前list进行判断&#xff0c…

从零学算法(剑指 Offer 61)

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14…

如何做一个api商品数据接口?

在构建一个API商品数据接口的过程中,我们需要涉及前端开发、后端开发、数据库设计以及API设计等多个方面。以下是一个基本的步骤和代码示例: 第一步:数据库设计 首先,我们需要设计一个数据库来存储商品信息。在这个例子中&#…

Git常用命令用法

参考视频:真的是全能保姆 git、github 保姆级教程入门,工作和协作必备技术,github提交pr - pull request_哔哩哔哩_bilibili 1.Git初始化 首先设置名称和邮箱。然后初始化一下,然后就创建了一个空的Git仓库。 PS D:\golang\oth…

区块链面临六大安全问题 安全测试方案研究迫在眉睫

区块链面临六大安全问题 安全测试方案研究迫在眉睫 近年来,区块链技术逐渐成为热门话题,其应用前景受到各国政府、科研机构和企业公司的高度重视与广泛关注。随着技术的发展,区块链应用与项目层出不穷,但其安全问题不容忽视。近年…

node socket.io

装包: yarn add socket.io node后台: const express require(express) const http require(http) const socket require(socket.io) const { getUserInfoByToken } require(../../utils/light/tools)let app express() const server http.createS…

【C++漂流记】结构体的定义和使用、结构体数组、结构体指针、结构体做函数参数以及结构体中const的使用

结构体(struct)是C语言中一种重要的数据类型,它由一组不同类型的成员组成。结构体可以用来表示一个复杂的数据结构,比如一个学生的信息、一个员工记录或者一个矩形的尺寸等。 结构体定义后,可以声明结构体变量&#xf…

NCCoE发布“向后量子密码学迁移”项目进展情况说明书

近日,NIST下属的国家网络安全中心(NCCoE)发布了一份向后量子密码学迁移(Migration to Post-Quantum Cryptography)项目情况说明书。该文档简要概述了向后量子密码学迁移项目的背景、目标、挑战、好处和工作流程&#x…

【HTML5高级第二篇】WebWorker多线程、EventSource事件推送、History历史操作

文章目录 一、多线程1.1 概述1.2 体会多线程1.3 多线程中数据传递和接收 二、事件推送2.1 概述2.2 onmessage 事件 三、history 一、多线程 1.1 概述 前端JS默认按照单线程去执行,一段时间内只能执行一件事情。举个栗子:比方说古代攻城游戏&#xff0c…

基于LinuxC语言实现的TCP多线程/进程服务器

多进程并发服务器 设计流程 框架一(使用信号回收僵尸进程) void handler(int sig) {while(waitpid(-1, NULL, WNOHANG) > 0); }int main() {//回收僵尸进程siganl(17, handler);//创建服务器监听套接字 serverserver socket();//给服务器地址信息…

Jenkins自动构建(Gitee)

Gitee简介安装JenkinsCLI https://blog.csdn.net/tongxin_tongmeng/article/details/132632743 安装Gitee jenkins-cli install-plugin gitee:1.2.7 # https://plugins.jenkins.io/gitee/releases获取安装命令(稍作变更) JenkinsURL Dashboard-->配置-->Jenkins Locatio…

ARTS第五周:A - 最大公约数

数字 function gcd(int $x, int $y): int {while($y^$x^$y^$x%$y);return $x; }位运算&#xff1a;异或&#xff1a;gcd(a,b) gcd(b,a mod b) 字符串 <?phpclass Solution {/*** param String $str1* param String $str2* return String*/function gcdOfStrings($str1, …

MySQL 8.0 OCP (1Z0-908) 考点精析-安装与配置考点1:设置系统变量

文章目录 MySQL 8.0 OCP (1Z0-908) 考点精析-安装与配置考点1&#xff1a;设置系统变量系统变量的确认设置系统变量的方法SET命令设置系统变量SET命令语法动态系统变量&#xff08;Dynamic System Variables&#xff09;全局级别变量的设置方法会话级别变量的设置方法系统变量的…

鸿蒙系列-如何使用好 ArkUI 的 @Reusable?

如何使用好 ArkUI 的 Reusable&#xff1f; OpenHarmony 组件复用机制 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为 系统组件&#xff0c;由开发者定义的称为 自定义组件。 在进行 UI 界面开发时&#xff0c;通常不是简单的将系统组件进行组合…

SpringBoot的测试方案

写完代码后&#xff0c;测试是必不可少的步骤&#xff0c;现在来介绍一下基于SpringBoot的测试方法。 基于SpringBoot框架写完相应功能的Controller之后&#xff0c;然后就可以测试功能是否正常&#xff0c;本博客列举MockMvc和RestTemplate两种方式来测试。 准备代码 实体类…

NIO原理浅析(三)

epoll 首先认识一下epoll的几个基础函数 int s socket(AF_INET, SOCK_STREAM, 0); bind(s, ...); listen(s, ...);int epfd epoll_create(...) epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中while(1) {int n epoll_wait(...);for(接受到数据的socket) {//处…

Kotlin 环境下解决属性初始化问题

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

react使用hook封装一个tab组件

目录 react使用hook封装一个tab组件Tabbar.jsx使用组件效果 react使用hook封装一个tab组件 Tabbar.jsx import PropsTypes from "prop-types"; import React, { useEffect, useState } from react; export default function Tabbar(props) {const { tabData , cur…

使用pip下载第三方软件包报错超时处理方法

报错如下&#xff1a; WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, statusNone)) after connection broken by ‘ReadTimeoutEr ror(“HTTPSConnectionPool(host‘files.pythonhosted.org’, port443): Read timed out. (read timeout15)”)’: /p…

Spring Boot常用的参数验证技巧和使用方法

简介 Spring Boot是一个使用Java编写的开源框架&#xff0c;用于快速构建基于Spring的应用程序。在实际开发中&#xff0c;经常需要对输入参数进行验证&#xff0c;以确保数据的完整性和准确性。Spring Boot提供了多种方式来进行参数验证&#xff0c;并且可以很方便地集成到应…