Menu
CoddyTech

Mẫu Observer

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

Observer Pattern là một mẫu thiết kế hành vi, tạo mối quan hệ một-nhiều giữa các đối tượng. Khi một đối tượng (chủ thể) thay đổi trạng thái, tất cả đối tượng phụ thuộc (observer) sẽ tự động nhận thông báo và cập nhật.

Hãy hình dung việc đăng ký nhận bản tin. Bản tin là đối tượng, còn người đăng ký là các bên quan sát. Khi có số mới, tất cả người đăng ký đều tự động nhận được mà bản tin không cần biết thông tin chi tiết về từng người.

Đây là cách triển khai cơ bản bằng PHP:

<?php
interface Observer {
    public function update(string $message): void;
}

class Newsletter {
    private array $subscribers = [];
    
    public function subscribe(Observer $observer): void {
        $this->subscribers[] = $observer;
    }
    
    public function publish(string $content): void {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->update($content);
        }
    }
}

class EmailSubscriber implements Observer {
    public function __construct(private string $email) {}
    
    public function update(string $message): void {
        echo "Sending to {$this->email}: $message\n";
    }
}

$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");

Đầu ra:

Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!

Mẫu này tách đối tượng được theo dõi khỏi các đối tượng quan sát. Lớp Newsletter không cần biết mình đang thông báo cho người đăng ký nhận email, người đăng ký nhận SMS hay dịch vụ thông báo đẩy. Lớp chỉ cần gọi update() trên bất kỳ đối tượng nào triển khai giao diện Observer. Nhờ vậy, bạn có thể dễ dàng thêm loại đối tượng quan sát mới mà không cần sửa đổi code hiện có.

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống cảnh báo giá cổ phiếu bằng mẫu Observer Pattern. Khi giá cổ phiếu thay đổi, hệ thống sẽ tự động thông báo cho tất cả nhà đầu tư đã đăng ký: đây là một ứng dụng thực tế hoàn hảo của mẫu này.

Bạn sẽ sắp xếp code trong bốn tệp:

  • Observer.php: Định nghĩa giao diện Observer với một phương thức duy nhất là update(string $stockName, float $price): void. Đây là quy ước mà tất cả observer phải tuân theo để nhận thông báo.
  • Investors.php: Thêm tệp Observer và tạo hai lớp triển khai giao diện:
    • EmailInvestor: Nhận địa chỉ email trong hàm khởi tạo (dùng cách khai báo promotion trong hàm khởi tạo). Phương thức update() in ra "Email to [email]: [stockName] is now $[price]"
    • SmsInvestor: Nhận số điện thoại trong hàm khởi tạo. Phương thức update() in ra "SMS to [phone]: [stockName] is now $[price]"
  • Stock.php: Thêm tệp Observer và tạo lớp Stock đóng vai trò chủ thể. Cổ phiếu cần:
    • Lưu tên cổ phiếu và giá hiện tại (dùng cách khai báo promotion trong hàm khởi tạo cho tên, khởi tạo giá bằng 0.0)
    • Duy trì một mảng observer riêng tư
    • Có phương thức subscribe(Observer $observer): void để thêm observer
    • Có phương thức setPrice(float $price): void để cập nhật giá và thông báo cho tất cả observer
    Khi thông báo cho observer, hãy gọi phương thức update() của từng observer và truyền tên cổ phiếu cùng giá mới.
  • main.php: Thêm các tệp Investors và Stock. Bạn sẽ nhận ba đầu vào: tên cổ phiếu, địa chỉ email và số điện thoại.

    Tạo một Stock với tên đã cho. Đăng ký một EmailInvestor với địa chỉ email và một SmsInvestor với số điện thoại.

    Đặt giá cổ phiếu thành 150.50 để gửi thông báo cho tất cả người đăng ký.

Mẫu Observer Pattern giúp lớp Stock không phụ thuộc chặt chẽ vào các phương thức thông báo cụ thể. Sau này, bạn có thể dễ dàng thêm PushInvestor hoặc SlackInvestor mà không cần sửa đổi lớp Stock.

Tự làm thử

<?php
require_once 'Investors.php';
require_once 'Stock.php';

// Đọc dữ liệu đầu vào
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));

// TODO: Tạo Stock với tên đã cho

// TODO: Đăng ký EmailInvestor bằng địa chỉ email

// TODO: Đăng ký SmsInvestor bằng số điện thoại

// TODO: Đặt giá cổ phiếu thành 150.50 để kích hoạt thông báo
?>
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 PHP online