【愚公系列】2023年10月 二十三种设计模式(九)-装饰者模式(Decorator Pattern)
  pjqvC5BRM2Nx 2023年11月02日 48 0

🏆 作者简介,愚公搬代码 🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。 🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。 🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。 🏆🎉欢迎 👍点赞✍评论⭐收藏

(文章目录)

🚀前言

设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。

毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。

🚀一、装饰者模式(Decorator Pattern)

当涉及到优化和扩展时,装饰者模式是一种强大的结构型设计模式。它允许您在不必修改原始类文件或使用继承的情况下,动态地扩展对象的功能。

装饰者模式的核心思想是通过创建包装对象(也称为装饰器)来包裹真实的对象。这个包装对象可以添加额外的功能,同时保持原始对象的接口不变。这种方式有几个关键优势:

  1. 灵活性:您可以根据需要组合多个装饰器,以达到不同的功能组合,而无需创建大量子类。

  2. 开放-封闭原则:通过装饰者模式,您可以轻松地添加新的装饰器,而无需修改现有的代码,符合面向对象设计的开放-封闭原则。

  3. 保持接口一致性:原始对象和装饰器都实现相同的接口,这意味着客户端代码可以与它们互换使用,而不必担心对象类型的差异。

  4. 动态性:您可以在运行时动态地添加或移除装饰器,以适应不同的需求,这为系统的动态性提供了支持。

装饰者模式是一种强大的设计模式,它通过包装对象实现了功能的动态扩展,为构建灵活、可维护和可扩展

🚀二、使用步骤

🔎1.角色

🦋1.1 抽象构件(Component)

在装饰者模式(Decorator Pattern)中,抽象构件(Component)是该模式的核心概念之一,具有以下作用和特点:

  1. 定义接口:抽象构件定义了一个接口,它规定了具体构件类和装饰器类都必须实现的方法。这个接口通常包含了一组基本的操作,是所有具体构件和装饰器共享的。

  2. 作为基础组件:抽象构件代表了被装饰的对象的基础类型。它可以是一个具体的类,也可以是一个接口,取决于具体实现。

  3. 提供装饰点:抽象构件为装饰器提供了一个装饰点,即装饰器可以通过扩展或修改抽象构件的方法来添加额外的功能,而不必改变客户端与抽象构件交互的方式。

  4. 实现透明性:抽象构件的存在使得客户端无需关心装饰器的具体实现。客户端可以像处理具体构件一样处理装饰器,因为它们都共享相同的接口。

  5. 用于组合:抽象构件可以用于构建复杂的对象组合,即一个对象可以包含其他抽象构件或装饰器,从而实现了功能的递归组合。

抽象构件在装饰者模式中充当了核心角色,它定义了装饰器模式的基本框架和接口规范。通过抽象构件,可以实现对对象功能的动态扩展,同时保持客户端代码简洁和与具体构件类的解耦。这种设计模式有助于满足开放-封闭原则,使得系统更加灵活和可维护。

🦋1.2 具体构件(Concrete Component)

在装饰者模式(Decorator Pattern)中,具体构件(Concrete Component)是一种核心组件,具有以下概念和作用:

  1. 实现抽象构件:具体构件是抽象构件(Component)接口的实际实现之一。它实现了抽象构件所定义的接口,并提供了具体的功能。

  2. 基础功能:具体构件代表了被装饰的对象的基础版本,它提供了一组基本的操作或功能。这些功能通常是系统的最初版本,未经过任何装饰。

  3. 可以被装饰:具体构件是装饰器模式中的初始对象,它可以被一个或多个装饰器进行装饰。这意味着其他类可以通过装饰器来扩展或修改具体构件的功能,而不必修改具体构件本身。

  4. 客户端使用:客户端通常通过具体构件来创建对象。客户端代码与具体构件交互,而不需要了解装饰器的存在,因为装饰器和具体构件都共享相同的抽象构件接口。

  5. 作为装饰的起点:具体构件是装饰链的起点,装饰器可以依次包装具体构件,形成一条装饰链,以实现功能的递归组合。

具体构件是装饰者模式中的基本组件,它是装饰器模式的核心之一。它提供了被装饰对象的基本功能,同时允许其他类通过装饰器来扩展和修改这些功能,实现了功能的动态组合和扩展。这种模式有助于遵循开放-封闭原则,使得系统更加灵活和可维护。

🦋1.3 抽象装饰(Decorator)

在装饰者模式(Decorator Pattern)中,抽象装饰(Decorator)是一个关键的组件,具有以下概念和作用:

  1. 实现抽象构件接口:抽象装饰类实现了抽象构件(Component)接口,这使得它可以与具体构件以及其他装饰器保持一致的接口。

  2. 维护对具体构件的引用:抽象装饰类通常包含一个对抽象构件的引用,这个引用允许抽象装饰类包装(装饰)具体构件。

  3. 作为装饰器的基类:抽象装饰类是所有具体装饰器的基类。它定义了一些基本的装饰操作,以及一个用于装饰的抽象方法或接口。

  4. 添加额外的功能:抽象装饰类的主要作用是添加额外的功能或行为到被装饰对象上。这些功能可以在装饰器类中实现,通常通过调用具体构件的方法来完成,并在此基础上添加额外的行为。

  5. 实现透明装饰:抽象装饰类的存在使得客户端可以像处理具体构件一样处理装饰器。客户端无需关心具体装饰器的类型,因为它们都实现了相同的抽象装饰接口。

  6. 递归组合:多个装饰器可以按照一定的顺序组合在一起,形成装饰链。这样的装饰链可以递归地影响被装饰对象的行为,实现复杂的功能组合。

抽象装饰类是装饰者模式的核心之一,它允许动态地将额外的功能附加到对象上,同时保持接口的一致性。通过抽象装饰,可以实现功能的递归组合,而客户端代码不受具体装饰器的影响,从而实现了代码的可扩展性

🦋1.4 具体装饰(Concrete Decorator)

在装饰者模式(Decorator Pattern)中,具体装饰(Concrete Decorator)是一种实现抽象装饰(Decorator)接口的具体类,具有以下概念和作用:

  1. 继承自抽象装饰:具体装饰类继承自抽象装饰类,它们都实现了相同的接口,这意味着具体装饰类与抽象装饰类具有相同的方法签名,从而保持了接口的一致性。

  2. 包装具体构件:具体装饰类的主要作用是包装(装饰)具体构件(Concrete Component)或其他装饰器。它们在构造函数中通常接受一个具体构件或抽象装饰的引用,以便能够在运行时动态地装饰对象。

  3. 添加额外的功能:具体装饰类通过扩展或修改抽象装饰类中的方法来添加额外的功能。这些额外功能通常在调用具体构件的基本操作后执行,以确保不破坏原始功能。

  4. 可以组合多个装饰器:在一个系统中,可以有多个具体装饰类,它们可以按照一定的顺序组合在一起,形成装饰链,以实现复杂的功能组合。每个具体装饰类负责添加特定的功能。

  5. 动态扩展功能:具体装饰类使得系统能够在运行时动态地扩展对象的功能,而无需修改原始对象的代码。这有助于符合开放-封闭原则,使得系统更加灵活和可维护。

具体装饰类是装饰者模式中的关键组件之一,它们负责实现具体的功能扩展,并通过包装具体构件或其他装饰器来实现这些扩展。通过合理组合具体装饰类,可以创建出具有丰富功能的对象,同时保持客户端代码的简洁性和可扩展性。这种模式非常适合需要动态添加或修改对象功能的情况。

🔎2.示例

在这里插入图片描述

命名空间DecoratorPattern中包含汽车Car类、玛莎拉蒂Maserati类、法拉利Fabbrica类、酷汽车CoolCar类、汽车车贴Paster类和汽车遮阳挡Visor类。本示例通过使用装饰者模式为汽车进行一次简单的外观装饰。

public abstract class Car {

    public string Name { get; set; }

    public abstract void StartUp();

}

汽车基类Car,充当抽象构件。

public class Fabbrica : Car {
 
    public Fabbrica() {
        Name = "Fabbrica";
    }
 
    public override void StartUp() {
        Console.WriteLine($"{Name} is starting!");
    }
 
}

法拉利Fabbrica类,充当具体构件。

public class Maserati : Car {
 
    public Maserati() {
        Name = "Maserati";
    }
 
    public override void StartUp() {
        Console.WriteLine($"{Name} is starting!");
    }
 
}

玛莎拉蒂Maserati类,充当具体构件。

public abstract class CoolCar : Car {
 
    protected Car _car = null;
 
    public CoolCar(Car car) {
        _car = car;
    }
 
    public override void StartUp() {
        Console.WriteLine($"{_car.Name} is starting!");
    }
 
}

酷汽车CoolCar类,充当抽象装饰。

public class Paster : CoolCar {
 
    public Paster(Car car) : base(car) {
 
    }
 
    public override void StartUp() {
        SetPaster();
        base.StartUp();
    }
 
    private void SetPaster() {
        Console.WriteLine($"Set paster to {_car.Name}!");
    }
 
}

汽车车贴Paster类,通过继承CoolCar类,为对汽车进行装饰提供了可能。

public class Visor : CoolCar {
 
    public Visor(Car car) : base(car) {
 
    }
 
    public override void StartUp() {
        SetVisor();
        base.StartUp();
    }
 
    private void SetVisor() {
        Console.WriteLine($"Set visor to {_car.Name}!");
    }
 
}

汽车遮阳挡Visor 类,通过继承CoolCar类,为对汽车进行装饰提供了可能。

public class Program {

    private const string LINE_SPLIT = "-------------------------";

    private static Car _car = null;

    private static CoolCar _coolCar = null;

    public static void Main(string[] args) {
        _car = new Maserati();

        _coolCar = new Visor(_car);
        _coolCar.StartUp();
        Console.WriteLine(LINE_SPLIT);

        _car = new Fabbrica();

        _coolCar = new Paster(_car);
        _coolCar.StartUp();
        Console.WriteLine(LINE_SPLIT);

        Console.ReadKey();
    }

}

以上是调用方的代码,以下是这个案例的输出结果:

Set visor to Maserati!
Maserati is starting!
-------------------------
Set paster to Fabbrica!
Fabbrica is starting!
-------------------------

🚀总结

🔎1.优点

装饰者模式(Decorator Pattern)具有许多优点,使它成为一种有用的设计模式。以下是一些装饰者模式的主要优点:

  1. 动态扩展功能:装饰者模式允许在运行时动态地为对象添加新功能,而无需修改其原始类。这使得系统更加灵活,能够适应不断变化的需求。

  2. 遵循开放-封闭原则:使用装饰者模式,可以在不修改现有代码的情况下扩展对象的行为。这符合开放-封闭原则,即对扩展开放,对修改封闭。

  3. 保持接口一致性:具体装饰类和抽象装饰类都实现相同的接口,这保持了接口的一致性,使得客户端可以无缝地处理被装饰的对象和装饰器,而不需要了解具体实现。

  4. 可组合性:多个装饰器可以按照一定的顺序组合在一起,形成装饰链。这种组合方式可以创建出复杂的功能组合,而不会导致类爆炸问题。

  5. 单一职责原则:每个具体装饰类只负责一个特定的功能扩展,这有助于保持类的单一职责原则,使得代码更加清晰和易于维护。

  6. 分层结构:装饰者模式创建了一个分层结构,其中具体构件是基本组件,而具体装饰器是对功能的递归包装。这种结构有助于管理和维护各个层次的功能。

  7. 不影响其他对象:添加装饰器不会影响其他对象,因为它们都是通过共享相同的接口来交互的。这提高了代码的可维护性和可扩展性。

装饰者模式是一种强大的设计模式,适用于需要动态地扩展对象功能而又保持代码灵活性和可维护性的情况。它有助于将功能分解为小块,使得每个块都可以独立扩展和修改,从而提高了系统的可扩展性和可维护性。

🔎2.缺点

虽然装饰者模式(Decorator Pattern)具有许多优点,但也存在一些缺点,需要在使用时考虑:

  1. 复杂性增加:使用装饰者模式会引入许多额外的类和对象,包括具体装饰类、抽象装饰类和具体构件类。这可能增加系统的复杂性,使得代码更难理解和维护。

  2. 装饰器的顺序:装饰器的顺序很重要,因为它们按照一定的顺序进行组合。如果装饰器的顺序设置不当,可能会导致意外的行为或错误。

  3. 不适合所有情况:装饰者模式并不适用于所有情况。它主要用于动态添加功能,如果系统中的功能扩展是静态的或有限的,可能会显得过于复杂。

  4. 可能导致性能损失:由于装饰者模式引入了多层的装饰器,可能会导致一些性能损失。每个装饰器都会增加方法调用的开销。

  5. 可能引发混淆:在装饰者模式中,装饰器和具体构件具有相同的接口,这可能导致混淆,使得开发人员难以确定对象的真正类型。

  6. 增加代码量:引入多个具体装饰类可能会增加代码量,特别是在需要多次装饰对象时,代码可能会显得冗长。

  7. 不容易移除装饰:一旦装饰器被添加到对象上,要移除它们可能会比较困难,因为可能需要修改大量代码。

装饰者模式在某些情况下是非常有用的,但也需要谨慎使用,特别是在处理复杂的功能组合时。开发人员需要权衡其优点和缺点,根据具体需求和设计考虑是否使用装饰者模式。

🔎3.使用场景

装饰者模式(Decorator Pattern)适用于以下一些使用场景:

  1. 动态功能扩展:当需要在不修改现有代码的情况下动态地增加对象的功能时,装饰者模式非常有用。它允许您通过添加装饰器来实现功能扩展。

  2. 遵循开放-封闭原则:如果您希望系统能够轻松扩展而不需要修改现有代码,装饰者模式是一个很好的选择。它保持了对扩展开放,对修改封闭的原则。

  3. 组合多个功能:当需要将多个不同的功能组合在一起,形成各种组合时,装饰者模式非常适合。您可以通过组合不同的装饰器来创建复杂的功能集合。

  4. 维护单一职责原则:装饰者模式有助于保持类的单一职责原则。每个具体装饰类都负责一个特定的功能扩展,从而使代码更加清晰和易于维护。

  5. 添加可选功能:当某些功能是可选的,并且不是每个对象都需要这些功能时,装饰者模式可以用于按需添加这些可选功能。

  6. 适用于继承有限的情况:如果您的系统已经存在许多子类,而不方便再添加新的子类,装饰者模式可以用于在不创建大量子类的情况下扩展对象的功能。

  7. 保持原始对象不变:装饰者模式允许您保持原始对象不变,而只对其进行装饰,这在某些情况下非常重要,尤其是在共享对象实例时。

装饰者模式是一种灵活的设计模式,适用于需要动态扩展对象功能的情况,同时保持代码的可维护性和可扩展性。它允许您以透明的方式将功能添加到对象中,而不会影响其接口和原始功能。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
pjqvC5BRM2Nx