首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在装饰器设计模式中需要从组件中派生出装饰器

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

Stack Overflow用户
提问于 2020-08-04 18:04:03
回答 2查看 79关注 0票数 2

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

我看到的例子如下所示:

代码语言:javascript
运行
复制
//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;
}

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

代码语言:javascript
运行
复制
Car newCar = new ActualCar();
int price = new AlloyWheels(new LeatherSeats(newCar)).GetPrice();

现在我觉得奇怪的是,CarDecorator是从Car继承来的,不管你怎么看,它不遵循is--一种关系类型。因此,我看了很少的例子,并意识到这是如何设计的装饰模式。

我不想质疑为什么装饰器模式是这样设计的,我只想知道为什么没有装饰器模式是从组件包装中派生出来的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-08-05 11:39:53

Decorator模式的目的是提供一种不用子类来扩展类的方法。

“将额外的责任附加到动态保持相同接口的对象上,装饰器为扩展功能提供了一种灵活的子类选择。(四人集团)

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

若要使类型可替换,它们必须具有相同的基类型:

代码语言:javascript
运行
复制
interface IVehicle {}

class Car : IVehicle {}

class Bus : IVehicle {}

现在,每个IVehicle都是可替换的:

代码语言:javascript
运行
复制
IVehicle car = new Car();
IVehicle bus = new Bus();

IVehicle vehicle = car;
vehicle = bus;

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

代码语言:javascript
运行
复制
// 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();
}

使用

代码语言:javascript
运行
复制
// 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

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

通常通过继承类型和向子类添加新特性来扩展类型。超类(祖先)被扩展,因为子类(后代)仍然具有与其超类(即继承-相同继承树)相同的类型。

装饰师只是实现相同结果的另一种策略,但不对您希望扩展的类型进行子类划分。

装饰类型(后代)实际上是装饰类型(祖先)的扩展版本。就像装饰器是修饰类型的子类(扩展名)一样。

要在类型层次结构中实现这一点,装饰师必须与超类的类型相同,才能成为真正的后代。基本上,装饰器是使用组合来伪造继承。或者,Decorator是一种更灵活的继承形式,因为由单个装饰器包装的每个类型实际上都是扩展的,而不需要编写包装类型的新派生类。

票数 1
EN

Stack Overflow用户

发布于 2020-08-04 18:25:37

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

如果CarDecorator不继承Car,那么您就不能这样做:

代码语言:javascript
运行
复制
Car newCar = new ActualCar();
int price = new CarDecorator(newCar).GetPrice();

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63252693

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档