Menu
CoddyTech

Mẫu trang trí

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 96 trên 110.

Mẫu thiết kế Decorator cho phép bạn thêm hành vi mới vào đối tượng một cách linh hoạt bằng cách bọc chúng trong các đối tượng đặc biệt gọi là decorator. Khác với kế thừa, vốn bổ sung hành vi trong quá trình biên dịch, decorator gắn thêm trách nhiệm vào thời điểm chạy mà không sửa đổi lớp ban đầu.

Ý tưởng chính là cả đối tượng ban đầu lẫn các decorator đều triển khai cùng một giao diện.

Một decorator bao bọc đối tượng ban đầu, ủy quyền xử lý cho đối tượng đó và bổ sung hành vi riêng trước hoặc sau:

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());  // Simple Coffee, Milk, Sugar
  print(coffee.getCost());         // 2.7
}

Mỗi decorator bọc đối tượng trước đó và bổ sung chức năng cho đối tượng. Bạn có thể kết hợp nhiều decorator theo bất kỳ cách nào để tạo ra hành vi linh hoạt mà không cần tạo lớp con cho mọi tổ hợp có thể. Mẫu này rất phù hợp khi bạn cần thêm tính năng cho đối tượng mà không ảnh hưởng đến các đối tượng khác cùng lớp.

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống đặt pizza bằng mẫu Decorator! Bạn sẽ tạo một chiếc pizza cơ bản có thể thêm nhiều loại topping, trong đó mỗi topping bọc phần trước đó và bổ sung vào cả mô tả lẫn giá tiền.

Bạn sẽ sắp xếp code thành hai tệp:

  • pizza.dart: Tệp này chứa giao diện pizza và tất cả các lớp decorator. Bắt đầu bằng lớp trừu tượng Pizza, định nghĩa hai phương thức: String getDescription() và double getPrice(). Sau đó, tạo lớp PlainPizza triển khai Pizza. Lớp này trả về Plain Pizza làm mô tả và có giá 8.0. Tiếp theo, tạo ba lớp decorator, mỗi lớp bọc một Pizza và thêm phần bổ sung:
    • CheeseDecorator: thêm , Cheese vào mô tả và 1.5 vào giá
    • PepperoniDecorator: thêm , Pepperoni vào mô tả và 2.0 vào giá
    • MushroomDecorator: thêm , Mushrooms vào mô tả và 1.25 vào giá
    Mỗi decorator cần triển khai Pizza, nhận một Pizza trong hàm khởi tạo và gọi đến pizza được bọc trước khi thêm phần bổ sung của riêng nó.
  • main.dart: Nhập tệp pizza và minh họa cách xếp chồng các decorator để tạo một đơn hàng tùy chỉnh. Bắt đầu bằng PlainPizza, sau đó bọc nó bằng CheeseDecorator, tiếp theo bọc phần đó bằng PepperoniDecorator, cuối cùng bọc tất cả bằng MushroomDecorator. In mô tả của chiếc pizza hoàn chỉnh, sau đó in Total: $ kèm theo giá tiền.

Hãy chú ý cách mỗi decorator được xây dựng dựa trên phần trước đó. Bạn đang tạo một chiếc pizza đầy đủ topping bằng cách xếp chồng các decorator đơn giản, mỗi decorator chỉ đảm nhiệm một việc!

Kết quả mong đợi:

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

Tự làm thử

import 'pizza.dart';

void main() {
  // TODO: Tạo một PlainPizza
  
  // TODO: Bọc nó bằng CheeseDecorator
  
  // TODO: Bọc tiếp bằng PepperoniDecorator
  
  // TODO: Bọc toàn bộ bằng MushroomDecorator
  
  // TODO: In ra mô tả của chiếc pizza cuối cùng
  
  // TODO: In ra "Total: $", sau đó là giá tiền
}
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