Luôn luôn lắng nghe, luôn luôn thấu hiểu!

Chắc chắn rồi, ứng cử viên số 3 trong danh sách các designpattern hay được sử dụng nhất phải là Observer. Nếu không có Observer có lẽ chúng ta đã không có nodejs không có socket.io, không có RxJava hay các thư viện lập trình bất đồng bộ (async) khác Observer len lỏi vào trong từng ngõ ngách của lập trình với nhiều biết thể như delegate, callback hay listener. Thậm chí các kỹ sư của Microsoft yêu thích delegate đến mức cho nó trở thành một từ khoá trong CSharp và khuyến khích các lập trình viên sử dụng, tuy nhiên là từ khoá này chắc chắn gây đau đầu cho mọi lập trình viên lúc đầu làm việc với CSharp không kém gì từ khoá yield. Vì sao Observer lại thú vị đến vậy?

Với Callback

Trong thế giới của chúng ta, người nói phải có người nghe thì trong lập trình cũng vậy, khi một hàm được gọi thì có 2 cách để chúng ta có được kết quả, 1 là chờ đợi return trả về 2 là lắng nghe kết quả trả về, nghe khó hiểu nhỉ, vậy chúng ta hãy làm một ví dụ về phép tính tổng a + b nhé, chúng ta sẽ có 2 kiểu như này:

public int sumWithReturn(int a, int b) {
    return a + b;
}
public void sumWithCallback(int a, int b, Consumer<Integer> callback) {
    int sum = a + b;
    callback.accept(sum);
}

Ứng dụng đa luồng

Nhưng mà đây là 1 chương trình đơn luồng thì còn dùng kiểu return được chứ đa luồng return là hỏng, đặc biệt là một số anh em lập trình với #javascript #nodejs rất hay quên gọi callback kiểu này:

function sum(a, b, callback) {
    return new Promise(resolve => callback(a + b));
}

lúc sử dụng thì lại gọi kiểu:

console.log(sum(1, 2));

Rồi tha hồ đi debug và hỏi tại sao, trong khi đúng ra phải dùng kiểu này:

sum(1, 2, sum => console.log(sum));

Còn trong java chúng ta phải dùng kiểu này:

public static void sumAsync(int a, int b, Consumer<Integer> callback) {
    Thread newThread = new Thread(() -> callback.accept(a + b));
 newThread.start();
}
sumAsync(1, 2, it -> {
    System.out.println("sumAsync: " + it);
});

Ứng dụng Observer

Qua một ví dụ nhỏ có thể chúng ta có thể thấy được 1 ít công năng của Observer rồi nhỉ? Observer sinh ra nhằm mục tiêu:

  1. Liên kết các lớp với nhau, liên kết giữa lớp tính toán và lớp xử lý kết quả
  2. Liên kết các tầng xử lý và các tầng dữ liệu trong chương trình
  3. Lắng nghe sự thay đổi trạng thái giữa các lớp khi được thông báo
  4. Kết hợp với Queue để chuyển đổi dữ liệu giữa các Thread

Tổng kết

Một designpattern hay ho thế này thì không thể nói hết được trong 1 bài được, mình sẽ viết thêm nhiều bài nữa để nói về các trường hợp sử dụng của Observer nhé, 🙂

Tham khảo

  1. Lý thuyết
  2. Ví dụ