组合模式
本课属于 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() 时,它会递归计算其中所有内容的总大小。客户端代码不需要知道自己处理的是单个文件还是整个文件夹层级,两者都能响应相同的方法。
挑战
简单我们来使用组合模式构建一个组织架构系统吧!你将创建一种结构,让单个员工和部门(部门中包含员工和其他部门)都能以统一的方式处理,非常适合表示公司的层级结构。
你需要将代码组织到两个文件中:
organization.dart:这个文件包含组件接口,以及叶子类和组合类。创建一个抽象类OrganizationUnit,其中包含一个namegetter、一个返回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: $",后面接工资预算总额
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Dart 在线编译器