Menu
CoddyTech

Mẫu tổng hợp

Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 99 trên 110.

Mẫu Composite cho phép bạn xử lý các đối tượng riêng lẻ và nhóm đối tượng theo cùng một cách. Mẫu này kết hợp các đối tượng thành cấu trúc cây, trong đó cả phần tử đơn lẻ và tập hợp phần tử đều dùng chung một giao diện. Mẫu này rất phù hợp để biểu diễn các cấu trúc phân cấp như hệ thống tệp, sơ đồ tổ chức hoặc thành phần giao diện người dùng.

Mẫu này có hai loại thành phần tham gia: các nút Leaf (đối tượng riêng lẻ không có nút con) và các nút Composite (vùng chứa các thành phần khác). Cả hai đều triển khai giao diện Component chung:

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');
}

Lớp Folder có thể chứa cả các đối tượng File lẫn các đối tượng Folder khác. Khi bạn gọi getSize() trên một thư mục, hàm này tính đệ quy tổng kích thước của toàn bộ nội dung bên trong. Code phía client không cần biết nó đang làm việc với một tệp riêng lẻ hay cả một hệ thống thư mục phân cấp, cả hai đều phản hồi cùng các phương thức.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một hệ thống sơ đồ tổ chức bằng mẫu Composite! Bạn sẽ tạo một cấu trúc trong đó nhân viên riêng lẻ và các phòng ban (có nhân viên và phòng ban khác bên trong) đều có thể được xử lý thống nhất, rất phù hợp để biểu diễn hệ thống phân cấp của công ty.

Bạn sẽ tổ chức code thành hai tệp:

  • organization.dart: Tệp này chứa giao diện thành phần cùng với các lớp lá và lớp tổ hợp. Tạo một lớp trừu tượng OrganizationUnit có getter name, phương thức getSalaryBudget() trả về kiểu int và phương thức display([String indent = '']) để hiển thị hệ thống phân cấp. Xây dựng lớp Employee (lớp lá) triển khai OrganizationUnit. Lớp này cần có tên và mức lương, trong đó getSalaryBudget() trả về mức lương của nhân viên. display() cần in ra [indent]- [name] ($[salary]). Tiếp theo, tạo lớp Department (lớp tổ hợp), cũng triển khai OrganizationUnit. Lớp này cần có tên và duy trì một danh sách phần tử con thuộc kiểu OrganizationUnit. Thêm phương thức add() để đưa nhân viên hoặc phòng ban phụ vào. getSalaryBudget() cần tính tổng ngân sách của tất cả phần tử con bằng đệ quy. display() cần in ra [indent]+ [name]/, sau đó hiển thị từng phần tử con với mức thụt lề tăng lên (thêm hai dấu cách).
  • main.dart: Nhập tệp tổ chức của bạn và xây dựng cấu trúc công ty. Tạo phòng ban Engineering gồm hai nhân viên: Alice có mức lương 80000 và Bob có mức lương 75000. Tạo phòng ban QA với một nhân viên: Carol có mức lương 65000. Tiếp theo, tạo phòng ban Technology và thêm cả Engineering lẫn QA làm phòng ban phụ. Gọi display() trên phòng ban Technology, sau đó in ra Total Budget: $ theo sau là tổng ngân sách tiền lương.

Điểm hay của mẫu Composite là Technology.getSalaryBudget() tự động tính tổng của tất cả phòng ban lồng nhau và nhân viên, code phía gọi không cần biết cấu trúc bên trong!

Đầu ra dự kiến:

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

Tự làm thử

import 'organization.dart';

void main() {
  // TODO: Tạo một phòng ban Engineering
  // TODO: Thêm Alice với mức lương 80000
  // TODO: Thêm Bob với mức lương 75000
  
  // TODO: Tạo một phòng ban QA
  // TODO: Thêm Carol với mức lương 65000
  
  // TODO: Tạo một phòng ban Technology
  // TODO: Thêm Engineering và QA làm các phòng ban trực thuộc
  
  // TODO: Gọi display() trên phòng ban Technology
  
  // TODO: In ra "Total Budget: $", sau đó là tổng ngân sách lương
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Dart online