Menu
CoddyTech

装饰器模式

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 96 课,共 110 课。

装饰器模式让你可以通过用称为装饰器的特殊对象包装对象,动态地为对象添加新行为。与在编译时添加行为的继承不同,装饰器可以在运行时附加额外的职责,而无需修改原始类。

关键在于,原始对象和装饰器都实现了相同的接口。

装饰器包装原始对象,将操作委托给它,并在之前或之后添加自己的行为:

abstract class Coffee {
  String getDescription();
  double getCost();
}

class SimpleCoffee implements Coffee {
  @override
  String getDescription() => 'Simple Coffee';

  @override
  double getCost() => 2.0;
}

class MilkDecorator implements Coffee {
  final Coffee _coffee;
  MilkDecorator(this._coffee);

  @override
  String getDescription() => '${_coffee.getDescription()}, Milk';

  @override
  double getCost() => _coffee.getCost() + 0.5;
}

class SugarDecorator implements Coffee {
  final Coffee _coffee;
  SugarDecorator(this._coffee);

  @override
  String getDescription() => '${_coffee.getDescription()}, Sugar';

  @override
  double getCost() => _coffee.getCost() + 0.2;
}

void main() {
  Coffee coffee = SimpleCoffee();
  coffee = MilkDecorator(coffee);
  coffee = SugarDecorator(coffee);

  print(coffee.getDescription());  // 普通咖啡、牛奶、糖
  print(coffee.getCost());         // 2.7
}

每个装饰器都会包装前一个对象并增强它。你可以按任意组合叠加多个装饰器,无需为每种可能的组合都创建一个子类,就能实现灵活的行为。当你需要为对象添加功能,同时不影响同类的其他对象时,这种模式非常适用。

challenge icon

挑战

简单

让我们使用装饰器模式来构建一个披萨点单系统!你将创建一个基础披萨,并用各种配料对它进行扩展。每种配料都会包装前一个对象,并增加描述内容和价格。

你将把代码分成两个文件:

  • pizza.dart:这个文件包含披萨接口和所有装饰器类。先创建一个抽象类 Pizza,定义两个方法:String getDescription() 和 double getPrice()。然后创建一个实现 Pizza 的 PlainPizza 类,它的描述是 Plain Pizza,价格为 8.0。接着创建三个装饰器类,每个类都会包装一个 Pizza 并对它进行扩展:
    • CheeseDecorator:在描述中添加 , Cheese,并在价格上增加 1.5
    • PepperoniDecorator:在描述中添加 , Pepperoni,并在价格上增加 2.0
    • MushroomDecorator:在描述中添加 , Mushrooms,并在价格上增加 1.25
    每个装饰器都应实现 Pizza,在构造函数中接收一个 Pizza,并先委托给被包装的披萨,再添加自己的内容。
  • main.dart:导入披萨文件,演示如何叠加装饰器来构建定制订单。先创建一个 PlainPizza,然后依次用 CheeseDecorator、PepperoniDecorator 和 MushroomDecorator 进行包装。打印最终披萨的描述,然后打印 Total: $,后面跟上价格。

注意每个装饰器都是在前一个装饰器的基础上进行扩展的。通过叠加职责单一的装饰器,你就能创建一个配料丰富的披萨!

预期输出:

Plain Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.75

亲手试试

import 'pizza.dart';

void main() {
  // TODO:创建一个 PlainPizza 对象
  
  // TODO:用 CheeseDecorator 包装它
  
  // TODO:再用 PepperoniDecorator 包装它
  
  // TODO:最后用 MushroomDecorator 包装整个披萨
  
  // TODO:打印最终披萨的描述
  
  // TODO:打印 "Total: $",后面接价格
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器