结构型设计模式之 - 装饰模式

138 阅读3分钟

何为装饰模式? ☀️☀️☀️

  • 本文已参与「新人创作礼」活动,一起开启掘金创作之路。

装饰模式 是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为,也可以理解为:动态的给一个对象添加一些额外的职责。也被称为装饰者模式、装饰器模式。

分析场景 📖

🌻 大家都知道台式电脑是由很多的硬件组装而成的,如主板、硬盘(固态|机械)、CPU(i5|i7)、电源(500w|750w)、内存条、机箱等等,这些配置都是灵活组合的,而且组装也有一定的顺序,我们应该怎么来写呢。

// 组装电脑类
public class AssembleComputer
{
    private string Type;
    public AssembleComputer(string Type) => this.Type = Type;
    public void Mainboard() => Console.WriteLine("组装主板中");
    public void I5CPU() => Console.WriteLine("组装I5-CPU中");
    public void SolidStateDisk() => Console.WriteLine("固态硬盘组装");
    public void Show() => Console.WriteLine($"{Type}电脑组装中---:");
}
static void Main(string[] args)
{
    // 初始化组装类
    AssembleComputer leave = new AssembleComputer("A");
    leave.Show();
    leave.Mainboard();
    leave.I5CPU();
    leave.SolidStateDisk();
    Console.ReadLine();
}

❌ 如果新增显卡,我们需要修改组装类新增GraphicsCard()方法,违背了开闭原则!
❌ 我们可以将硬件抽象,每一个配件实现硬件类,新增配件直接创建新类实现抽象类,遵循了开闭原则,但是顺序控制不灵活,并且组合流程裸露。
✔️ 使用装饰模式:替代继承,无须定义子类即可给对象动态增加职责。

装饰模式结构 🍭

  • 部件 (Component) 声明封装器和被封装对象的公用接口。
  • 具体部件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。
  • 基础装饰 (Base Decorator) 类拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。
  • 具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。

使用装饰模式 🍉

  • 新增AssembleComputer抽象类,里面包含一个Combination()虚方法
  • 新增ConcreteComponent具体部件类继承部件。
// 组装电脑类即部件
public abstract class AssembleComputer
{
    public virtual void Combination() 
    {
    }
}
// 具体部件
public class ConcreteComponent : AssembleComputer
{
    public override void Combination()
    {
        Console.WriteLine("组装电脑");
    }
}
  • 添加基础装饰类继承AssembleComputer,并且提供了SetDecorator设置AssembleComputer,重写了父类的Combination()方法,调用AssembleComputer设置值的Combination()

也可以使用构造函数来设定

// 基础装饰类
public abstract class ComputerDecorator : AssembleComputer
{
    private AssembleComputer _component;
    //SetDecorator用来设置AssembleComputer
    public void SetDecorator(AssembleComputer component) 
    {
        _component = component;
    }
    public override void Combination()
    {
        // 调用AssembleComputer类的Combination()方法
        _component.Combination();
    }
}
  • 新增具体的装饰类
public class Mainboard : ComputerDecorator
{
    public override void Combination()
    {
        // 可以在执行逻辑之前或者之后调用原来的Combination()
        base.Combination();
        Console.WriteLine("组装主板中-------");
    }
}
public class I5Cpu : ComputerDecorator
{
    public override void Combination()
    {
        base.Combination();
        Console.WriteLine("组装I5-CPU中-------");
    }
}
  • 执行
static void Main(string[] args)
{
    // 实例化具体部件
    AssembleComputer concreteComponent = new ConcreteComponent();
    Mainboard mainboard = new Mainboard();
    I5Cpu i5cpu = new I5Cpu();
    SolidStateDisk stateDisk = new SolidStateDisk();
    // 使用主板类包装具体部件
    mainboard.SetDecorator(concreteComponent);
    // 使用cpu类包装主板类
    i5cpu.SetDecorator(mainboard);
    // 使用硬盘类包装cpu
    stateDisk.SetDecorator(i5cpu);
    // 最终执行Combination
    stateDisk.Combination();

    I7Cpu i7cpu = new I7Cpu();
    mainboard.SetDecorator(concreteComponent);
    i7cpu.SetDecorator(mainboard);
    stateDisk.SetDecorator(i7cpu);
    stateDisk.Combination();

    Console.ReadLine();
}

image.png

总结 🍑

不难看出来这样写的好处在于,功能的核心与装饰分离,在我们新增功能的时候,客户端可以在运行的时候根据需要自由的组合,更加灵活了,但是在实现多层装饰的时候会使系统更加复杂。