observer-pattern-nhat-tru-kinh-thien

Observer pattern là gì? – Nhất trụ kình thiên

Hôm rồi có đứa em đi phỏng vấn, câu hỏi về Observer Pattern. Không có gì để nói nếu tới cái “main content” của pattern này nó cũng không nắm được. BỨC XÚC QUÁ!

Ngồi viết bài này chia sẻ với các “chư vị đồng môn”. Thứ nhất là về Observer pattern, hai nữa là cách học một pattern, áp dụng thực tiễn. Một số ông vừa mới nghĩ tới Design Pattern thôi đã toát mồ hôi hột, đi phỏng vấn được hỏi câu hỏi về Design Pattern run cầm cập, trả lời ú ớ.

Nhưng thực chất người phỏng vấn không hề yêu cầu cao tới mức lên bảng cầm bút implement một design pattern hoàn chỉnh, cái người interview cần là cái concept của design pattern đó. Nắm được để biết trong tình huống (situatation) nào thì dùng pattern nào cho phù hợp. Còn lúc implement thì research, chả sao cả, gõ đúng keyword thôi.

Cùng là copy, cùng là research, nhưng ông Software Engineer hiểu được mình đang đi copy cái gì thì lương tới 100k đô một năm.

Tại sao một Software Engineer tầm cỡ lại cần biết hết Design Pattern?. Vì nó là những mẫu thiết kế chuẩn mực đã được đúc kết từ trước, qua rất nhiều lần implement sai, những hệ thống “banh xa lông”, người ta mới rút ra được pattern đó.

1. Bắt đầu từ cái tên

Observer pattern – tách ra thành hai cái, observer. Ob ở đây là object (đối tượng). Còn server?, server ở đây là hướng về cái đối tượng trung tâm, ví như server. Mấy cái rìa rìa là client bên ngoài

Đây, như cái thiết kế của thành phố tương lai. Mọi thứ hướng về trung tâm – nên mới gọi là Nhất trụ kình thiên
Nguồn ảnh / Source: resourcebasedeconomy.org

Cũng là Behavior Pattern, nhắc tới là nhớ ngay cái pattern đó làm gì?, điển hình là cái Iterator Pattern – Đôi điều thú vị từ Kieblog. Nhìn cái chữ Iterator thôi là nhớ cái main content là loop, cái pattern này chắc chắn design ra để hỗ trỡ cái việc loop.

Đấy, chỉ cần nhớ cái concept vậy thôi là chả bao giờ sợ quên design pattern.

2. Định nghĩa về Observer pattern

Nhớ mãi không quên cái tên pattern rồi. Giờ đi vào detail, chứ nhớ không vậy thì cũng không ổn. Phải nắm rõ 2 thứ:

  • Khi nào sử dụng?, sử dụng như thế nào?
  • Điểm mạnh, điểm yếu

Observer Pattern là dạng pattern hành vi (Behavior Pattern). Định nghĩa:

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

Observer là pattern hành vi, giúp chúng ta xác định cơ chế đăng kí để thông báo tới nhiều object về bất kì sự kiện nào xảy ra với đối tượng mà chúng đang đăng kí

Hơn nữa, do là Behavior Pattern nên Observer là mối quan hệ one to many relationship. Một ông ở giữa trụ, một trụ chống trời!

2.1 Bài toán thực tế

Bài toán thực tế. Có 10 người trong một khu dân cứ muốn mua iPhone 12 ở cửa hàng trung tâm khu dân cư.

Nếu cửa hàng không có cơ chế gì thông báo qua phone, qua mail, mỗi người sẽ tới hỏi cửa hàng một vài lần. Câu trả lời là có, chưa có, có chưa có. Không ổn!

Nếu cửa hàng gửi thông báo cho tất cả 10 người về việc có iPhone mới?. Ổn, nhưng gửi thông báo iPhone 11 cho cả 10 người thì người ta chửi sml. Ông X giàu tổ bố, nên chỉ thích 12, 11 đ’ đủ tuổi. Ông Y mẹ bán vàng, gia cảnh nghèo hơn, chỉ thích 8 Plus, nên thông báo về 12 ổng cũng la bm.

Từ bài toán thực tế này (chém thế chứ méo biết có phải không), Observer Pattern ra đời. Chỉ gửi notification cho những người đăng kí quan sát đối tượng (Objects) đó, ngoài ra không gửi, không mang tiếng spam.

2.2 Hiện thực ý tưởng

Đã có ý tưởng thế, pattern này chia thành hai thành phần chính là Publisher Subscribers

  • Publisher: quản lý sự kiện mới (có notifi, có object mới). Sẽ publish thông tin đó ra cho subscribler.
  • Subscribers: ông này thì dễ hiểu rồi, như subscribers Youtube (ấn chuông), khi nào có video mới thì nó bắn notification lên. Chuẩn bài dễ hiểu!

Chi tiết hơn nha.

Đầu tiên, thằng Publisher. Trong Observer Pattern thì thằng Thằng này có 3 method: đăng kí (subscribe), bỏ đăng kí (unsubscribe) và thông báo (notify).

// Source code: refactoring.guru - observer pattern
public class EventManager {
    Map<String, List<EventListener>> listeners = new HashMap<>();

    public EventManager(String... operations) {
        for (String operation : operations) {
            this.listeners.put(operation, new ArrayList<>());
        }
    }

    public void subscribe(String eventType, EventListener listener) {
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    public void unsubscribe(String eventType, EventListener listener) {
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    public void notify(String eventType, File file) {
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.update(eventType, file);
        }
    }
}

Thằng Subscriber chỉ là một interface, tất cả những thằng nào đăng kí với Publisher đều implement cái interface này.

// Source code: refactoring.guru - observer pattern
package refactoring_guru.observer.example.listeners;

import java.io.File;

// Chỉ là interface, ông nào đăng kí thì implement interface này
public interface EventListener {
    void update(String eventType, File file);
}

The Subscriber interface declares the notification interface. In most cases, it consists of a single update method.

Subscriber interface sẽ định nghĩa notification interface. Trong hầu hết các trường hợp, nó chỉ có duy nhất một method update.

Cuối cùng là ông Concrete Subscribers – ông này là người đăng kí cuối cùng. Ông này thì implement interface Subscribers. Ngoài ra còn override method update, tùy cơ ứng biến cho từng trường hợp. Khá linh động

// Source code: refactoring.guru - observer pattern
public class EmailNotificationListener implements EventListener {
    private String email;

    public EmailNotificationListener(String email) {
        this.email = email;
    }
	// Override lại method update, tùy cơ ứng biến khi có thay đổi từ Publisher
    @Override
    public void update(String eventType, File file) {
        System.out.println("Email to " + email + ": Someone has performed " + eventType + " operation with the following file: " + file.getName());
    }
}

Ta cũng có ông Editor để implement các method sẽ gọi notify(), thông báo tới tất cả Subscriber

// Source code: refactoring.guru - observer pattern
package refactoring_guru.observer.example.editor;

import refactoring_guru.observer.example.publisher.EventManager;

import java.io.File;

public class Editor {
    public EventManager events;
    private File file;

    public Editor() {
        this.events = new EventManager("open", "save");
    }
    // Actions method đăng kí gọi notify
    public void openFile(String filePath) {
        this.file = new File(filePath);
        events.notify("open", file);
    }
   // Actions method đăng kí gọi notify
    public void saveFile() throws Exception {
        if (this.file != null) {
            events.notify("save", file);
        } else {
            throw new Exception("Please open a file first.");
        }
    }
}

Cuối cùng, main method chỉ việc đăng kí các event và gọi actions.

public class Demo {
    public static void main(String[] args) {
        Editor editor = new Editor();
      
        // Đăng kí loại event và các actions muốn làm với event đó
        editor.events.subscribe("open", new LogOpenListener("/path/to/log/file.txt"));
        editor.events.subscribe("save", new EmailNotificationListener("[email protected]"));

        try {
            // Gọi tới notify để thông báo tới các subscriber
            editor.openFile("test.txt");
            editor.saveFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. Ưu nhược điểm

Ưu điểm lớn nhất ở Observer Pattern là khi có thay đổi ở Subscribler sẽ không ảnh hưởng tới Publisher. Cái này vô cùng quan trọng, mỗi channel trên Youtube đôi khi tới cả triệu lượt theo dõi.

Nếu một thay đổi ở Subscriber đều cần update tới Publisher thì toang, toang thật sự.

Open/Closed Principle. You can introduce new subscriber classes without having to change the publisher’s code.

Có thể thêm mới một class subscriber mà không cần phải thay đổi gì code ở publisher.

Ngoài ra

You can establish relations between objects at runtime.

Với pattern này, ta còn có thể khởi tạo mối liên hệ giữa các object trong lúc runtime

4. Tham khảo

Đối với Java, trên Java 9 có thể sử dụng java.util.Observer. Trên Python thì sử dụng thông qua Pip pip install pattern-observer.

Nốt công có thể tìm hiểu thêm các pattern hay ho khác ở Kieblog nha.

Cảm ơn vì đã đọc bài, nhớ like page để không bỏ lỡ bài viết hay nha. Happy coding!

Có gì thắc mắc cứ comment đây nha! - Please feel free to comment here!
Chia sẻ bài viết


Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *