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

重学设计模式,【结构型】装饰器模式

在这里插入图片描述

目录

    • 一、装饰器模式的结构
    • 二、不使用装饰器模式时
    • 三、使用装饰器模式优化
      • 1、基础电脑组件
      • 2、抽象组件
      • 3、抽象装饰器
      • 4、具体装饰器
    • 四、使用装饰器模式有哪些优势
      • 1、动态扩展对象功能
      • 2、更好的代码复用性
      • 3、遵循单一职责原则
      • 4、减少子类的数量
      • 5、运行时灵活性
    • 五、哪些场景可以使用装饰器模式优化?
      • 1、增强对象功能时
      • 2、跨领域关注点(如日志、权限验证、性能监控)
      • 3、需要多个功能组合时
      • 4、避免类爆炸(class explosion)
      • 5、与策略模式配合使用
    • 六、优化场景举例
      • 场景 1:Web 应用中的权限验证和日志记录
      • 场景 2:电子商务系统中的商品定价策略
    • 七、典型案例:Java I/O 流中的装饰器模式
      • 1、BufferedInputStream 是 InputStream 的装饰器
      • 2、DataInputStream 是 InputStream 的装饰器
      • 3、PrintStream 和 PrintWriter 也是装饰器
      • 4、除了I/O流之外,Java中的装饰器模式也被应用在其他场景中:
    • 八、总结
      • 在这里,直接用 OpenAI o1草莓大模型

在日常开发中,我们往往忽视了设计模式的重要性。这可能是因为项目时间紧迫,或者对设计模式理解不深。其实,很多时候我们可能在不经意间已经使用了某些模式。

重要的是要有意识地学习和应用,让代码更加优雅和高效。也许是时候重新审视我们的编程实践,将设计模式融入其中了。

今天由浅入深,重学【装饰器模式】,让我们一起“重学设计模式”。

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许通过一种灵活的方式动态地为对象添加新的行为或职责,而无需修改其原始代码。这种模式提供了比继承更有弹性的替代方案,通过将对象包装在一系列装饰器类中,可以在运行时组合出各种行为。

一、装饰器模式的结构

  1. Component(组件):定义一个对象接口,装饰器和被装饰对象必须实现的公共接口。
  2. ConcreteComponent(具体组件):实现Component接口的类,是可以被装饰的类。
  3. Decorator(装饰器):实现Component接口,同时持有Component类型的成员变量(可以是原始对象或其他装饰器),通过组合将功能动态扩展到该对象。
  4. ConcreteDecorator(具体装饰器):实现装饰器具体行为扩展的类,通过调用被装饰对象的行为,并在此基础上添加新的功能。

二、不使用装饰器模式时

假设我们有一个基本的电脑类,并想为它动态添加不同的配置(比如CPU、内存、GPU)。

当需要新增配置时,在Computer类中新增即可,清晰明了,没有一个ifelse解决不了的。

public class Computer {private boolean hasRAMUpgrade;private boolean hasGPUUpgrade;private boolean hasStorageUpgrade;// 基础电脑的价格private double baseCost = 500;public Computer(boolean hasRAMUpgrade, boolean hasGPUUpgrade, boolean hasStorageUpgrade) {this.hasRAMUpgrade = hasRAMUpgrade;this.hasGPUUpgrade = hasGPUUpgrade;this.hasStorageUpgrade = hasStorageUpgrade;}public String getDescription() {String description = "Basic Computer";if (hasRAMUpgrade) {description += ", with 16GB RAM";}if (hasGPUUpgrade) {description += ", with NVIDIA GPU";}if (hasStorageUpgrade) {description += ", with 1TB SSD";}return description;}public double cost() {double totalCost = baseCost;if (hasRAMUpgrade) {totalCost += 150;}if (hasGPUUpgrade) {totalCost += 300;}if (hasStorageUpgrade) {totalCost += 200;}return totalCost;}
}package com.guor.designPattern.decorator.no;public class Test {public static void main(String[] args) {// 用户选择了升级内存和显卡Computer myComputer = new Computer(true, true, true);System.out.println(myComputer.getDescription() + " costs $" + myComputer.cost());}
}

三、使用装饰器模式优化

当需要新增配置时,直接实现ComputerDecorator,重写getDescription、cost方法即可,实现了高扩展,解决了ifelse low爆炸的问题。

1、基础电脑组件

/*** 基础电脑*/
public class BasicComputer extends Computer {@Overridepublic String getDescription() {return "Basic Computer";}@Overridepublic double cost() {return 500;  // 基础电脑的价格}
}

2、抽象组件

package com.guor.designPattern.decorator;/*** 抽象组件*/
public abstract class Computer {// 描述public abstract String getDescription();// 成本public abstract double cost();
}

3、抽象装饰器

/*** 抽象装饰器*/
public abstract class ComputerDecorator extends Computer {protected Computer computer;public ComputerDecorator(Computer computer) {this.computer = computer;}public abstract String getDescription();
}

4、具体装饰器

public class StorageUpgrade extends ComputerDecorator {public StorageUpgrade(Computer computer) {super(computer);}@Overridepublic String getDescription() {return computer.getDescription() + ", with 1TB SSD";}@Overridepublic double cost() {return computer.cost() + 200;}
}public class RAMUpgrade extends ComputerDecorator {public RAMUpgrade(Computer computer) {super(computer);}@Overridepublic String getDescription() {return computer.getDescription() + ", with 16GB RAM";}@Overridepublic double cost() {return computer.cost() + 150;  // 额外内存的价格}
}public class GPUUpgrade extends ComputerDecorator {public GPUUpgrade(Computer computer) {super(computer);}@Overridepublic String getDescription() {return computer.getDescription() + ", with NVIDIA GPU";}@Overridepublic double cost() {return computer.cost() + 300;  // 额外显卡的价格}
}

四、使用装饰器模式有哪些优势

1、动态扩展对象功能

装饰器模式允许在运行时动态地为对象添加功能,而不需要修改对象的结构。相比于继承,它提供了更为灵活的扩展方式,因为继承在编译时就固定了对象的行为,而装饰器则可以通过组合多个装饰器动态改变对象的行为。

2、更好的代码复用性

装饰器可以封装可复用的功能,不同的对象可以共享这些功能而不需要重复编写代码。这种方式提高了代码的可维护性和复用性。

3、遵循单一职责原则

装饰器将对象的核心职责和其他辅助功能(如日志、缓存、权限控制)分离开来,每个装饰器只负责一个特定的功能,避免了类的职责过多。

4、减少子类的数量

继承机制常常会导致大量的子类,因为每个新的功能都需要通过子类来实现。而装饰器模式可以通过组合多个装饰器来实现相同的功能扩展,减少类的数量和复杂度。

5、运行时灵活性

装饰器可以在运行时动态地添加或移除功能,而不需要修改已有的代码。这在开发过程中非常实用,尤其是在需要频繁调整功能的场景中。

五、哪些场景可以使用装饰器模式优化?

1、增强对象功能时

当需要为现有对象增加新功能,且不希望修改对象本身或者使用继承时,可以考虑使用装饰器模式。比如在 UI 组件系统中,基础组件可以通过装饰器动态添加滚动、边框等特性,而不需要修改原始组件的代码。

2、跨领域关注点(如日志、权限验证、性能监控)

在处理一些横切关注点(cross-cutting concerns)时,装饰器模式非常有用。比如日志记录、权限控制、事务管理、性能监控等功能,它们常常不属于对象的核心业务逻辑,通过装饰器模式可以将这些功能单独提取出来,并动态添加到业务逻辑中,而不污染原始代码。

示例:

  1. 日志记录:为方法调用自动添加日志功能,记录方法的输入、输出和执行时间。
  2. 权限控制:在执行某些业务方法前,动态检查用户权限。
  3. 事务管理:在数据库操作前后动态添加事务处理。

3、需要多个功能组合时

当对象的功能可以通过不同的方式组合时,装饰器模式是一个理想的选择。它允许通过多个装饰器的组合形成不同的功能,比如装饰器可以为对象依次添加不同的行为,从而形成灵活的功能组合。

示例:在咖啡订单系统中,咖啡基础类可以通过装饰器动态添加牛奶、糖、巧克力等配料,而不用创建大量的子类来表示每一种咖啡组合。

4、避免类爆炸(class explosion)

通过装饰器模式可以避免通过继承产生大量的子类。特别是在当类的功能是通过多个维度扩展时,比如一种基础功能的类可能同时需要加日志、加缓存、加权限控制,使用继承将导致很多子类,而使用装饰器可以有效减少类的数量。

5、与策略模式配合使用

装饰器模式可以与策略模式结合起来,为策略类提供更丰富的功能扩展。比如在电商网站中,可以为订单结算策略类动态添加打折、积分计算等功能,而不必为每种结算方式都创建新的子类。

六、优化场景举例

场景 1:Web 应用中的权限验证和日志记录

在 Web 应用中,通常需要在处理请求时添加权限验证和日志记录。假设系统已经有一个基本的请求处理类,通过装饰器可以实现动态添加权限验证和日志记录功能。

在这个场景中,通过装饰器,我们可以动态地为请求处理类添加权限验证和日志记录功能,而不必修改现有的 RequestHandler 类。

// 定义一个处理请求的接口
interface RequestHandler {void handle(String request);
}// 具体的请求处理类
class BasicRequestHandler implements RequestHandler {@Overridepublic void handle(String request) {System.out.println("Handling request: " + request);}
}// 抽象装饰器类
abstract class RequestHandlerDecorator implements RequestHandler {protected RequestHandler handler;public RequestHandlerDecorator(RequestHandler handler) {this.handler = handler;}@Overridepublic void handle(String request) {handler.handle(request);}
}// 日志记录装饰器
class LoggingDecorator extends RequestHandlerDecorator {public LoggingDecorator(RequestHandler handler) {super(handler);}@Overridepublic void handle(String request) {System.out.println("Logging request: " + request);super.handle(request);}
}// 权限验证装饰器
class AuthDecorator extends RequestHandlerDecorator {public AuthDecorator(RequestHandler handler) {super(handler);}@Overridepublic void handle(String request) {if (checkPermission(request)) {System.out.println("Permission granted");super.handle(request);} else {System.out.println("Permission denied");}}private boolean checkPermission(String request) {// 简单模拟权限验证逻辑return request.contains("admin");}
}// 测试客户端
public class DecoratorExample {public static void main(String[] args) {RequestHandler handler = new BasicRequestHandler();RequestHandler authHandler = new AuthDecorator(handler);RequestHandler loggingAuthHandler = new LoggingDecorator(authHandler);// 测试通过权限验证和日志记录的请求处理loggingAuthHandler.handle("admin: important data");}
}

场景 2:电子商务系统中的商品定价策略

假设一个电子商务系统中,需要对商品的价格进行灵活调整。可以通过装饰器模式为商品价格添加不同的优惠策略,例如打折、满减、会员优惠等。

在此例中,通过装饰器可以灵活组合打折和满减的策略,而不需要为每种优惠策略创建新的子类,简化了系统结构。

// 商品接口
interface Product {double getPrice();
}// 基础商品类
class BasicProduct implements Product {private double price;public BasicProduct(double price) {this.price = price;}@Overridepublic double getPrice() {return price;}
}// 抽象装饰器类
abstract class ProductDecorator implements Product {protected Product product;public ProductDecorator(Product product) {this.product = product;}@Overridepublic double getPrice() {return product.getPrice();}
}// 打折装饰器
class DiscountDecorator extends ProductDecorator {public DiscountDecorator(Product product) {super(product);}@Overridepublic double getPrice() {return product.getPrice() * 0.9; // 10% 折扣}
}// 满减装饰器
class FullReductionDecorator extends ProductDecorator {public FullReductionDecorator(Product product) {super(product);}@Overridepublic double getPrice() {double price = product.getPrice();if (price > 200) {return price - 20; // 满200减20}return price;}
}// 测试客户端
public class EcommerceExample {public static void main(String[] args) {Product product = new BasicProduct(250);Product discountProduct = new DiscountDecorator(product);Product finalProduct = new FullReductionDecorator(discountProduct);System.out.println("Final price: " + finalProduct.getPrice());}
}

装饰器模式在需要动态扩展对象功能、复用功能模块、组合不同行为以及避免类爆炸的场景中非常有用。它可以通过装饰器类灵活地为对象添加职责,从而提升代码的可维护性、复用性和扩展性。

七、典型案例:Java I/O 流中的装饰器模式

在JDK源码中,装饰器模式有很多应用,特别是在I/O类库中,装饰器模式被广泛使用。通过装饰器模式,Java的I/O类库可以动态地为输入输出流添加缓冲、数据处理等功能,而不改变原始的输入输出流的实现。

在Java中,java.io包的输入输出流(InputStream、OutputStream)就是装饰器模式的一个典型应用。InputStream和OutputStream类是抽象基类,具体的实现类如FileInputStream、BufferedInputStream等都是具体组件,而BufferedInputStream、DataInputStream等类则是装饰器,它们通过封装InputStream实现了额外的功能,比如缓冲和数据类型处理。

1、BufferedInputStream 是 InputStream 的装饰器

// java.io.BufferedInputStream 源码片段
public class BufferedInputStream extends FilterInputStream {// Buffer sizeprotected volatile byte[] buf;public BufferedInputStream(InputStream in) {this(in, DEFAULT_BUFFER_SIZE);}public BufferedInputStream(InputStream in, int size) {super(in); // 调用父类 FilterInputStream 的构造方法,传入被装饰的 InputStreamif (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];}@Overridepublic int read() throws IOException {// 通过缓存读取流数据,添加缓冲功能}// 其他相关的重写方法
}

FilterInputStream:抽象装饰器类,它继承了InputStream,并且持有一个InputStream的引用,装饰器类通过持有的这个引用来增强对象的功能。

BufferedInputStream:具体的装饰器类,通过为InputStream添加缓冲功能提高读取效率。

public class FileInputStream extends InputStream {// 打开文件并创建文件输入流public FileInputStream(String name) throws FileNotFoundException {this(name != null ? new File(name) : null);}// 其他相关代码...
}// 客户端使用
public class DecoratorTest {public static void main(String[] args) throws IOException {InputStream fileStream = new FileInputStream("data.txt");// 用 BufferedInputStream 装饰 FileInputStream,增加缓冲功能InputStream bufferedStream = new BufferedInputStream(fileStream);int data = bufferedStream.read();  // 读取数据while (data != -1) {System.out.print((char) data);data = bufferedStream.read();}bufferedStream.close();}
}

在这个例子中,FileInputStream是一个具体的输入流,而BufferedInputStream是装饰器,增加了缓冲功能。客户端可以通过组合来增强输入流的功能,而不必修改原始的FileInputStream类。

2、DataInputStream 是 InputStream 的装饰器

DataInputStream 是另一个经典的装饰器,它允许从输入流中读取基本数据类型(如int、float等)。

// java.io.DataInputStream 源码片段
public class DataInputStream extends FilterInputStream implements DataInput {public DataInputStream(InputStream in) {super(in); // 调用装饰的 InputStream}// 读取一个 int 数据public final int readInt() throws IOException {int ch1 = in.read();int ch2 = in.read();int ch3 = in.read();int ch4 = in.read();if ((ch1 | ch2 | ch3 | ch4) < 0)throw new EOFException();return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));}// 其他相关的读取方法
}

在这个例子中,DataInputStream装饰了一个InputStream,并且扩展了功能,支持读取基本数据类型。

public class DecoratorTest {public static void main(String[] args) throws IOException {InputStream fileStream = new FileInputStream("data.bin");// 使用 BufferedInputStream 增加缓冲功能InputStream bufferedStream = new BufferedInputStream(fileStream);// 使用 DataInputStream 增加读取基本数据类型的功能DataInputStream dataStream = new DataInputStream(bufferedStream);int number = dataStream.readInt();  // 从文件中读取一个 int 值System.out.println("Read integer: " + number);dataStream.close();}
}

在这个例子中,DataInputStream为InputStream增加了读取基本数据类型的功能。通过使用BufferedInputStream和DataInputStream的组合,客户端可以获得既有缓冲功能,又可以读取基本数据类型的输入流。

3、PrintStream 和 PrintWriter 也是装饰器

PrintStream 和 PrintWriter 是对输出流的装饰器,它们添加了打印方法,比如print()和println(),使得输出流可以方便地输出字符串、对象等数据。

public class PrintStream extends FilterOutputStream implements Appendable, Closeable {// 构造函数接受 OutputStream 作为参数public PrintStream(OutputStream out) {super(out); // 调用 FilterOutputStream 构造方法}public void println(String x) {synchronized (this) {print(x);newLine();}}// 其他相关代码...
}

在Java I/O类库中,装饰器模式通过扩展InputStream和OutputStream的功能而被广泛使用。

BufferedInputStream、DataInputStream、PrintStream等类都是装饰器,它们在不改变底层流的情况下增加了缓冲、数据类型处理和打印等功能。

通过装饰器模式,Java I/O类库实现了灵活的功能扩展,并且使用者可以根据需求动态组合这些装饰器。

4、除了I/O流之外,Java中的装饰器模式也被应用在其他场景中:

(1)Java的集合类包装器

Collections.synchronizedList()、Collections.unmodifiableList()等方法实际上是对集合进行包装,动态为集合对象增加线程安全或不可修改的特性。

(2)java.util.logging.Logger

日志框架中的Logger类允许通过装饰器为日志添加各种处理行为,例如格式化输出、发送到不同的目的地(控制台、文件、远程服务器等)。

八、总结

装饰器模式在JDK源码中的典型应用主要体现在I/O流的设计中。通过装饰器模式,Java的InputStream和OutputStream类可以在不修改原始类的情况下动态扩展功能。例如,BufferedInputStream和DataInputStream分别为基础流增加了缓冲读取和读取基本数据类型的能力,而PrintStream为输出流提供了打印字符串、对象等的便捷方法。

装饰器模式允许开发者通过组合多个装饰器类,为对象添加不同的功能,避免了创建大量子类的复杂性。例如,BufferedInputStream可以与FileInputStream组合使用,为文件输入流增加缓冲,提升读取效率。这种模式不仅灵活,还使代码复用性和扩展性大幅提升。

此外,装饰器模式还广泛应用于Java集合类和日志系统等场景,像Collections.synchronizedList()、Collections.unmodifiableList()等都通过装饰器动态增加线程安全或不可修改的特性。装饰器模式通过其灵活的结构设计,实现了功能增强与解耦的目的,是Java开发中的常见设计模式。

在这里,直接用 OpenAI o1草莓大模型

谷歌浏览器访问:https://www.nezhasoft.cn

👉 GPT功能:

  1. GPT-4o知识问答:支持1000+token上下文记忆功能
  2. 最强代码大模型Code Copilot:代码自动补全、代码优化建议、代码重构等
  3. DALL-E AI绘画:AI绘画 + 剪辑 = 自媒体新时代
  4. 私信哪吒,直接使用GPT-4o


🏆文章收录于:100天精通Java从入门到就业

哪吒数年工作总结之结晶。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

华为OD机试 2023B卷题库疯狂收录中,刷题点这里

刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。

点击下方名片,回复1024,获取《10万字208道Java经典面试题总结(2024修订版).pdf 》

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Springboot小区物业服务平台—计算机毕业设计源码35514
  • 第十五章:使用html、css、js编程制作一个网页版的下雪场景动画
  • 模拟电路工程师面试题
  • Writeset
  • vue选项式写法项目案例(购物车)
  • 操作系统week3
  • vue源码分析(九)—— 合并配置
  • 打印机问题故障处理_十大打印机故障大全及处理方法
  • 大模型各版本Base, Chat, Instruction 之间的区别
  • Leetcode 3298. Count Substrings That Can Be Rearranged to Contain a String II
  • Pandas Series 概述与使用指南
  • [SDX35+WCN6856]SDX35 + WCN6856 默认增加打包wifi配置hostapd_24g.conf和hostapd_5g.conf操作方法
  • linux中vim编辑器的应用实例
  • Python画笔案例-058 绘制单击画酷炫彩盘
  • 第三篇 第16章 工程量清单计价
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • CSS实用技巧
  • Git的一些常用操作
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • HTTP中的ETag在移动客户端的应用
  • Intervention/image 图片处理扩展包的安装和使用
  • Iterator 和 for...of 循环
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • PAT A1120
  • React Native移动开发实战-3-实现页面间的数据传递
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 编写符合Python风格的对象
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 基于web的全景—— Pannellum小试
  • 悄悄地说一个bug
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 微信小程序--------语音识别(前端自己也能玩)
  • 详解移动APP与web APP的区别
  • 译自由幺半群
  • 应用生命周期终极 DevOps 工具包
  • 自动记录MySQL慢查询快照脚本
  • FaaS 的简单实践
  • #控制台大学课堂点名问题_课堂随机点名
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (007)XHTML文档之标题——h1~h6
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (160)时序收敛--->(10)时序收敛十
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (二)hibernate配置管理
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (十八)Flink CEP 详解
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • (转)我也是一只IT小小鸟
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池