首页 > 其他分享 >装饰器模式

装饰器模式

时间:2024-02-28 14:45:09浏览次数:25  
标签:功能 中间件 模式 next public 添加 装饰

简介

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态地添加新功能,同时又不改变其结构。该模式通过创建一个包装对象,也就是装饰器,来包裹原始对象,并在包裹的过程中添加新的行为或责任。

结构

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

案例

假设你有一个简单的客厅(SimpleLivingRoom),里面只有一些基本的家具和装饰。然后你可以添加不同的装饰器来增添节日氛围,比如圣诞节你会加上圣诞树、彩灯等等,或者在生日派对时你会添加气球、彩带等装饰品。

这些装饰器(Decorator)可以动态地叠加在客厅上,每一个装饰器负责添加一个特定的装饰。而客厅本身仍然保持不变,你可以根据需要添加或移除装饰器,使客厅适应不同的场合,而不需要修改客厅的原始结构。

下面是一个用 C# 实现装饰器模式的简单示例,展示了如何在客厅中动态添加装饰物:

using System;

// Component
interface ILivingRoom
{
    void Display();
}

// ConcreteComponent
class SimpleLivingRoom : ILivingRoom
{
    public void Display()
    {
        Console.WriteLine("This is a simple living room.");
    }
}

// Decorator
abstract class LivingRoomDecorator : ILivingRoom
{
    protected ILivingRoom livingRoom;

    public LivingRoomDecorator(ILivingRoom livingRoom)
    {
        this.livingRoom = livingRoom;
    }

    public virtual void Display()
    {
        livingRoom.Display();
    }
}

// ConcreteDecorator
class ChristmasDecorator : LivingRoomDecorator
{
    public ChristmasDecorator(ILivingRoom livingRoom) : base(livingRoom) { }

    public override void Display()
    {
        base.Display();
        Console.WriteLine("Adding Christmas decorations: Christmas tree, lights, etc.");
    }
}

// ConcreteDecorator
class BirthdayDecorator : LivingRoomDecorator
{
    public BirthdayDecorator(ILivingRoom livingRoom) : base(livingRoom) { }

    public override void Display()
    {
        base.Display();
        Console.WriteLine("Adding birthday decorations: balloons, ribbons, etc.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // 创建一个简单的客厅
        ILivingRoom livingRoom = new SimpleLivingRoom();
        Console.WriteLine("Simple Living Room:");
        livingRoom.Display();
        Console.WriteLine();

        // 添加圣诞装饰
        ILivingRoom christmasLivingRoom = new ChristmasDecorator(livingRoom);
        Console.WriteLine("Living Room Decorated for Christmas:");
        christmasLivingRoom.Display();
        Console.WriteLine();

        // 添加生日装饰
        ILivingRoom birthdayLivingRoom = new BirthdayDecorator(livingRoom);
        Console.WriteLine("Living Room Decorated for Birthday:");
        birthdayLivingRoom.Display();
    }
}

这段代码首先定义了一个客厅接口 ILivingRoom 以及一个简单的客厅实现 SimpleLivingRoom。然后定义了装饰器抽象类 LivingRoomDecorator,以及具体的装饰器类 ChristmasDecoratorBirthdayDecorator。在 Main 方法中,首先创建了一个简单的客厅对象,然后分别使用圣诞和生日装饰器来动态添加装饰物,并显示客厅的状态。

实际案例

可以使用装饰器模式来构建中间件管道,以实现灵活的功能组合。通过这种方式,你可以将多个中间件组合起来,每个中间件负责一个特定的功能,从而实现复杂的请求处理逻辑。

以下是一个简单的示例,演示了如何使用装饰器模式来构建中间件管道:

假设有三个中间件,分别是记录请求处理时间的中间件、记录请求日志的中间件和处理请求的中间件。你可以按照以下方式组合这些中间件:

  1. 记录请求处理时间的中间件(Decorator 1)
  2. 记录请求日志的中间件(Decorator 2)
  3. 处理请求的中间件(ConcreteComponent)

下面是一个简化的示例代码:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;

// ConcreteComponent: 处理请求的中间件
public class RequestHandlerMiddleware
{
    private readonly RequestDelegate _next;

    public RequestHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // 处理请求
        await _next(context);
    }
}

// Decorator 1: 记录请求处理时间的中间件
public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;

    public RequestTimingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // 记录请求处理开始时间
        var stopwatch = Stopwatch.StartNew();

        // 调用下一个中间件
        await _next(context);

        // 请求处理完成,计算处理时间并输出
        stopwatch.Stop();
        var elapsedTime = stopwatch.ElapsedMilliseconds;

        Console.WriteLine($"Request completed in {elapsedTime} ms");
    }
}

// Decorator 2: 记录请求日志的中间件
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public RequestLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // 记录请求日志
        Console.WriteLine($"Request {context.Request.Path} received at {DateTime.Now}");

        // 调用下一个中间件
        await _next(context);
    }
}

// 中间件扩展方法
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestTimingMiddleware>();
    }

    public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggingMiddleware>();
    }
}

// Startup 类
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        // 构建中间件管道
        app.UseRequestTiming();
        app.UseRequestLogging();
        app.UseMiddleware<RequestHandlerMiddleware>();

        // 添加其他中间件和路由处理等
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        });
    }
}

在这个示例中,RequestTimingMiddlewareRequestLoggingMiddleware 分别充当了装饰器的角色,用于添加请求处理时间记录和请求日志记录功能。RequestHandlerMiddleware 是处理请求的具体组件。

Startup 类的 Configure 方法中,我们通过调用 UseRequestTimingUseRequestLogging 扩展方法来注册这两个中间件,然后再注册处理请求的中间件。这样,每个请求都会依次经过这三个中间件,从而实现请求处理时间记录和请求日志记录的功能。

其他案例

  1. IO流处理:C# 中的 Stream 类就是一个典型的使用装饰器模式的例子。例如,BufferedStreamCryptoStreamFileStream 等都是 Stream 的装饰器,它们分别添加了缓冲、加密和文件操作等额外功能。

  2. WCF 中的消息处理器:Windows Communication Foundation(WCF)中的消息处理器也是使用装饰器模式的典型例子。消息处理器可以在传输和消息协议之上提供额外的功能,比如安全性、日志记录等。

  3. 数据库访问层:在数据访问层中,可以使用装饰器模式来动态地添加日志记录、性能监控、缓存等功能,而不需要修改原始的数据访问逻辑。

  4. 日志记录:日志记录是应用程序中常见的功能,可以使用装饰器模式来动态地给对象添加日志记录功能,而不需要修改原始的业务逻辑。

优点

  1. 灵活性:装饰器模式允许动态地给对象添加功能,而无需修改其原始类。你可以根据需要选择添加哪些装饰器,以及以何种顺序来添加,从而实现灵活的功能组合。

  2. 遵循开闭原则:通过装饰器模式,可以轻松地扩展对象的功能,而不需要修改现有的代码。这使得系统更容易维护和扩展,符合开闭原则。

  3. 单一职责原则:每个装饰器类通常只负责给对象添加一个特定的功能,这有助于保持类的单一职责,提高代码的可读性和可维护性。

  4. 组合功能:装饰器模式允许组合多个装饰器,从而实现复杂的功能组合,而不会造成类层次结构的爆炸性增长。

缺点

  1. 复杂性:如果过度使用装饰器模式,可能会导致系统中出现大量的装饰器类,增加了代码的复杂性和理解难度。

  2. 顺序依赖性:装饰器模式的功能依赖于装饰器的添加顺序,如果顺序不正确,可能会影响到最终的功能组合。这需要开发人员仔细考虑装饰器的顺序。

  3. 性能开销:每个装饰器都会增加额外的处理逻辑,可能会带来一定的性能开销。在性能要求较高的场景下,需要谨慎使用装饰器模式。

综上所述,虽然装饰器模式具有灵活性和扩展性等优点,但在设计时需要权衡好其复杂性和性能开销,避免过度使用。

适用场景

  1. 动态添加功能:当需要动态地给对象添加额外的功能或责任,并且希望这些功能能够灵活组合和扩展时,装饰器模式是一个很好的选择。它允许你在运行时动态地添加或删除功能,而不需要修改现有对象的代码。

  2. 避免类爆炸:当类的功能组合方式会导致类的数量呈指数级增长时,使用装饰器模式可以避免这种类爆炸的问题。通过装饰器模式,你可以将不同的功能拆分成独立的装饰器类,并根据需要组合这些装饰器类,从而减少类的数量。

  3. 单一职责原则:当需要遵循单一职责原则,确保每个类只负责一个功能时,装饰器模式可以帮助你实现这一目标。每个装饰器类通常只关注一个特定的功能,从而保持了类的单一职责。

  4. 不影响现有代码:当需要对现有代码进行功能扩展,但又不想修改现有代码时,装饰器模式可以派上用场。通过装饰器模式,你可以在不修改原始类的情况下,为对象动态添加新的功能,从而实现功能扩展。

  5. 保持接口一致性:当希望扩展对象的功能时,但又不想破坏对象原始接口时,装饰器模式可以很好地保持接口的一致性。由于装饰器类和被装饰类具有相同的接口,因此可以无缝地替换被装饰类。

 

标签:功能,中间件,模式,next,public,添加,装饰
From: https://www.cnblogs.com/mchao/p/18040337

相关文章

  • 组合模式
    简介组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表现"部分-整体"的层次结构。这种模式使得用户对单个对象和组合对象的使用具有一致性。在组合模式中,有两种主要类型的对象:叶节点和组合节点。叶节点代表了树结构中的最终对象,而组合节点则表示了可以包含其他组合......
  • 桥接模式
    简介桥接模式(BridgePattern)是一种结构型设计模式,它主要用于将抽象部分与实现部分分离,从而使它们可以独立变化。桥接模式通过将继承关系转化为组合关系,使得抽象部分和实现部分可以独立地变化,不会相互影响。在桥接模式中,抽象部分通常指的是一个抽象类或接口,它定义了对外的接口或......
  • KISS 原则 (设计模式)
    KISS定义kiss原则的英文描述又几个版本,计较下边的这几个keepitsimpleandstupid.keepitshortandsimplekeepitsimpleandstraightforward不过仔细看你会发现,他们表达的意思其实差不多,尽量保持简单。它是一个万金油的设计原则,可以在很多场景中,它不仅仅经常用来指......
  • 适配器模式
    简介适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于解决两个不兼容接口之间的兼容性问题,使得原本由于接口不匹配而无法一起工作的类能够协同工作。适配器模式主要由三个角色组成:目标接口(TargetInterface):客户端所期......
  • 原型模式
    定义:原型模式(PrototypeDesignPattern)用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象原型模式主要解决的问题: 如果创建对象的成本较大,比如对象中的数据是经过复杂计算才能得到的,或者需要从RPC接口或者数据库等比较慢的IO中获取,这种情况我们......
  • 原型模式
    简介原型模式(PrototypePattern)是一种创建型设计模式,它允许创建新对象的同时,通过复制现有对象的方式来初始化新对象。这意味着在原型模式中,新对象的创建过程不再依赖于直接实例化类,而是通过复制已有对象的方式来进行。在原型模式中,存在一个原型对象(Prototype),它是需要被复制的对......
  • 建造者模式
    简介建造者模式(BuilderPattern)是一种创建型设计模式,用于构建复杂对象。它的主要目的是将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示形式。这种模式通常适用于创建对象的参数较多,且某些参数之间存在复杂的依赖关系的情况。在建造者模式中,通常会有一个建造......
  • C++特殊类的设计与单例模式
    //1.设计一个不能被拷贝的类/*解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。C++98;将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即......
  • 详解在 centos 中引导到救援模式
    详解在centos中引导到救援模式Linux系统CentOS进入单用户模式和救援模式详解一、概述目前在运维日常工作中,经常会遇到服务器异常断电、忘记root密码、系统引导文件损坏无法进入系统等等操作系统层面的问题,给运维带来诸多不便,现将上述现象的解决方法和大家分享一下,本次主要以C......
  • Ubuntu如何进救援模式
    linux的救援模式-1详解在Ubuntu中引导到救援模式或紧急模式这篇教程将介绍如何在Ubuntu22.04、20.04和18.04LTS版本中引导到救援Rescue模式或紧急Emergency模式。你可能已经知道,在RHEL7、RHEL8、Ubuntu16.04LTS及其更新的版本的Linux发行版中运行等级R......