Menu
CoddyTech

组合模式

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

组合模式让你可以统一处理单个对象和对象组。它将对象组合成树形结构,使单个元素和元素集合共用同一接口。这非常适合表示文件系统、组织结构图或 UI 组件等层级结构。

这种模式有两种类型的参与者:叶节点(没有子节点的独立对象)和 组合节点(包含其他组件的容器)。两者都实现了通用的 组件接口:

abstract class FileSystemItem {
  String get name;
  int getSize();
  void display([String indent = '']);
}

class File implements FileSystemItem {
  @override
  final String name;
  final int size;

  File(this.name, this.size);

  @override
  int getSize() => size;

  @override
  void display([String indent = '']) {
    print('$indent- $name ($size KB)');
  }
}

class Folder implements FileSystemItem {
  @override
  final String name;
  final List<FileSystemItem> _children = [];

  Folder(this.name);

  void add(FileSystemItem item) => _children.add(item);

  @override
  int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());

  @override
  void display([String indent = '']) {
    print('$indent+ $name/');
    for (var child in _children) {
      child.display('$indent  ');
    }
  }
}

void main() {
  var docs = Folder('Documents');
  docs.add(File('resume.pdf', 150));
  docs.add(File('photo.jpg', 2400));

  var projects = Folder('Projects');
  projects.add(File('main.dart', 25));
  docs.add(projects);

  docs.display();
  print('Total: ${docs.getSize()} KB');
}

Folder 类可以包含 File 对象和其他 Folder 对象。对文件夹调用 getSize() 时,它会递归计算其中所有内容的总大小。客户端代码不需要知道自己处理的是单个文件还是整个文件夹层级,两者都能响应相同的方法。

challenge icon

挑战

简单

我们来使用组合模式构建一个组织架构系统吧!你将创建一种结构,让单个员工和部门(部门中包含员工和其他部门)都能以统一的方式处理,非常适合表示公司的层级结构。

你需要将代码组织到两个文件中:

  • organization.dart:这个文件包含组件接口,以及叶子类和组合类。创建一个抽象类 OrganizationUnit,其中包含一个 name getter、一个返回 int 的 getSalaryBudget() 方法,以及一个用于显示层级结构的 display([String indent = '']) 方法。 创建一个实现 OrganizationUnit 的 Employee 类(叶子)。它应包含姓名和薪水,且 getSalaryBudget() 返回该员工的薪水。它的 display() 应打印 [indent]- [name] ($[salary])。 接下来,创建一个同样实现 OrganizationUnit 的 Department 类(组合)。它应包含名称,并维护一个由 OrganizationUnit 子项组成的列表。添加一个 add() 方法,用于添加员工或子部门。它的 getSalaryBudget() 应递归地累加所有子项的预算。它的 display() 应打印 [indent]+ [name]/,然后显示每个子项,并增加缩进(添加两个空格)。
  • main.dart:导入组织架构文件,并构建公司结构。创建一个 Engineering 部门,其中包含两名员工:薪水为 80000 的 Alice 和薪水为 75000 的 Bob。创建一个 QA 部门,其中有一名员工:薪水为 65000 的 Carol。然后创建一个 Technology 部门,并将 Engineering 和 QA 都添加为子部门。在 Technology 部门上调用 display(),然后打印 Total Budget: $,后面跟上薪水预算总额。

组合模式的优势在于,Technology.getSalaryBudget() 会自动计算所有嵌套部门和员工的总额,客户端代码无需了解内部结构!

预期输出:

+ Technology/
  + Engineering/
    - Alice ($80000)
    - Bob ($75000)
  + QA/
    - Carol ($65000)
Total Budget: $220000

亲手试试

import 'organization.dart';

void main() {
  // TODO: 创建 Engineering 部门
  // TODO: 添加 Alice,工资为 80000
  // TODO: 添加 Bob,工资为 75000
  
  // TODO: 创建 QA 部门
  // TODO: 添加 Carol,工资为 65000
  
  // TODO: 创建 Technology 部门
  // TODO: 将 Engineering 和 QA 添加为子部门
  
  // TODO: 调用 Technology 部门的 display() 方法
  
  // TODO: 打印 "Total Budget: $",后面接工资预算总额
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: Dart 在线编译器