装饰器设计模式中需要从组件派生装饰器

Need to derive the decorator from component in decorator design pattern

我正在学习设计模式教程并遇到了装饰器模式。我知道如何以及何时使用装饰器模式,但是,我对为什么需要从组件派生装饰器感到有点困惑。

我看到的例子如下:

//Component classes
public abstract class Car
{
   public abstract int GetPrice();
   //Other properties and methods
}

public class ActualCar : Car
{
   public int GetPrice(){return 1000;}
}

//Decorators classes
public class CarDecorator : Car //No idea why this is necessary
{
   protected Car car;
   public CarDecorator(Car car)
   {
      this.car = car;
   }

   public override int GetPrice() => this.car.GetPrice();
}

public class LeatherSeats : CarDecorator
{
   public LeatherSeats(Car car) : base(car){}
   public override int GetPrice() => this.car.GetPrice() + 200;
}

public class AlloyWheels : CarDecorator
{
   public AlloyWheels(Car car) : base(car) {}
   public override int GetPrice() => this.car.GetPrice() + 150;
}

现在,当使用组件及其装饰器时,我们将其用作:

Car newCar = new ActualCar();
int price = new AlloyWheels(new LeatherSeats(newCar)).GetPrice();

现在我觉得 CarDecorator 继承自 Car 很奇怪,因为无论你怎么看它都不遵循 is-a 类型的关系。所以我又看了几个例子,意识到装饰器模式就是这样设计的。

我不想质疑这样设计装饰器模式的原因,只是想知道不从它包装的组件派生装饰器模式的缺点是什么。

你的 CarDecorator 是一个基础装饰器,它应该继承 Car 接口(这里是一个抽象 class)以确保 CarDecorator 已经实现(应该覆盖)GetPrice方法。

如果CarDecorator不继承自Car,那么你不能这样做:

Car newCar = new ActualCar();
int price = new CarDecorator(newCar).GetPrice();

Car 在这里很像一个接口,或者一个明确告诉所有具体装饰器都应该实现 GetPrice 方法的契约。

Decorator 模式的目的是提供一种无需子class 即可扩展class 的方法。

"Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality." (Gang of Four)

这意味着对于客户端来说,装饰器类型的行为必须与(模仿)装饰器类型完全一样。更重要的是,装饰器类型必须可以替换为装饰器类型。

要使类型可替换,它们必须是相同的基本类型:

interface IVehicle {}

class Car : IVehicle {}

class Bus : IVehicle {}

现在每个 IVehicle 都可以替换:

IVehicle car = new Car();
IVehicle bus = new Bus();

IVehicle vehicle = car;
vehicle = bus;

要允许装饰器类型可以被装饰器类型替换,两者都必须继承相同的基类型。这意味着装饰器实际上将扩展装饰基类型:

// Common base class. Will be extended by a decorator.
public interface ICar
{
  ISound Honk();

  //Other properties and methods
}

public class Beagle : ICar 
{
  // Implementation of ICar
  public ISound Honk() => new BeagleHornSound(); // BeagleHornSound implements ISound
}

// Abstract decorator base class.
// Since the base class implements ICar, every CarDecorator "is-a" ICar.
// This makes CarDecorator and every ICar replaceable and allows the CarDecorator to mimic the decorated/wrapped ICar. 
// Therefore every feature of this class or subclass is actually extending every ICar.
public abstract class CarDecorator : ICar
{
  private ICar Car { get; set; }
  private CarDecorator(ICar car)
  {
    this.Car = car;
  }

  // Implementation of ICar
  public ISound Honk() => this.Car.Honk();
}

// Concrete decorator class
public abstract class Lowrider : CarDecorator
{
  private Hydraulics Hydraulics { get; set; }
  
  public CarDecorator(ICar car) : base (car)
  {
    this.Hydraulics = new Hydraulics();
  }

  public void EnableHydraulic() => LetCarHop();
}

用法

// Extends the ICar by wrapping it into a decorator
public CarDecorator PimpCar(ICar car) 
{
  return new Lowrider(car);  
}

ICar beagle = new Beagle(); // Beagle implements ICar

CarDecorator lowrider = PimpCar(beagle); // Returns a Lowrider which extends CarDecorator

// Since the decorator also implements ICar, every CarDecorator is replaceable with every other ICar
ICar pimpedBeagle = lowrider; // Lowrider implements ICar

// Since the decorator "is-a" ICar, it can mimic every ICar
ISound hornSound = lowrider.Honk();

// Since the Lowrider is decorating/wrapping currently a Beagle, it actually mimics a Beagle
bool isSameType = lowrider.Honk() is BeagleHornSound; // true

现在您可以看到,如果装饰器不会实现它正在装饰的相同类型,那么您就不会扩展装饰类型,而是创建一个新类型。

您通常通过继承类型来扩展类型并向子类添加新功能class。 Superclass(祖先)被扩展了,因为 subclass(后代)仍然是与其 superclass 相同的类型(即继承 - 相同的继承树)。
Decorator 只是实现相同结果的另一种策略,但无需 subclassing 您希望扩展的类型。

装饰器类型(后代)实际上是装饰器类型(祖先)的扩展版本。就像装饰器是装饰类型的子class(扩展)。
要在类型层次结构方面实现这一点,装饰器必须也是与 superclass 相同的类型才能成为真正的后代。基本上,装饰器是使用组合来伪造继承。或者 Decorator 是一种更灵活的继承形式,因为每个由单个装饰器包装的类型实际上都被扩展而无需编写包装类型的新派生 class。