【问题标题】:Need to derive the decorator from component in decorator design pattern需要从装饰器设计模式中的组件派生装饰器
【发布时间】:2020-11-24 21:25:42
【问题描述】:

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

我看到的例子如下:

//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 继承,你会怎么写...int price = new AlloyWheels(new LeatherSeats(newCar)).GetPrice();?
  • 如何“不遵循 is-a 关系”?添加合金轮毂不会让汽车不再是汽车,没有必要添加合金轮毂......
  • 我认为您的代码中有错字,ActualCar 不应该继承自 Car 吗?
  • @quaabaam,谢谢你的错字,我更新了我的代码。
  • @AlexeiLevenkov 从 Car 派生 AlloyWheels 看起来好像 AlloyWheels 是一种汽车,这是没有意义的,因为合金轮毂可以很好地与 Car 分开。 Car 的属性,例如。 GasType 将不适用于 AlloyWheels。

标签: c# design-patterns decorator


【解决方案1】:

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

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

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

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

【讨论】:

  • 哦,是的,我没想到。
  • 由于装饰器构造函数将 Car 类型作为输入,因此链接装饰器将是一个问题。
  • 您在示例中毫无问题地链接了它们。
  • 是的,它适用于我的示例,因为 CarDecorator 是从 Car 派生的,但如果我们要删除它,它将失败。
【解决方案2】:

装饰器模式的目的是提供一种无需子类化即可扩展类的方法。

"将附加职责附加到对象上,动态保持 同一个界面。装饰器提供了一种灵活的替代方案 用于扩展功能的子类化。”(四人组)

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

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

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

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

您通常通过继承类型和向子类添加新功能来扩展类型。超类(祖先)被扩展,因为子类(后代)仍然是与其超类相同的类型(即继承 - 相同的继承树)。
装饰器只是实现相同结果的另一种策略,但不需要子类化您希望扩展的类型。

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

【讨论】:

    猜你喜欢
    • 2010-09-21
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 2013-11-28
    相关资源
    最近更新 更多