Mẫu thiết kế Observer định nghĩa một phụ thuộc một-nhiều, trong đó, nếu đối tượng một (Subject, còn gọi là Observable) thay đổi trạng thái, tất cả các đối tượng nhiều (Observer) phụ thuộc đối tượng một sẽ được thông báo và tự động cập nhật.
Phía một thường là dữ liệu, phía nhiều thường là giao diện người dùng.
Mẫu thiết kế này còn gọi là Dependents, Publish/Subcribe hoặc Source/Listener
1. Cài đặt
-Subject: giao diện cho đối tượng dữ liệu, khai báo các phương thức chính:
- addObserver(): thêm các Observer vào danh sách đăng ký các đối tượng cần phải thông báo về sự thay đổi.
- deleteObserver(): xóa Observer chỉ định ra khỏi danh sách đăng ký các đối tượng cần phải thông báo về sự thay đổi.
- notifyObserver(): thông báo cho các Observer đã đăng ký về những thay đổi trên Subject.
-ConcreteSubject: cài đặt giao diện Subject. Vì thường là đối tượng dữ liệu, nó lưu giữ trạng thái mà các đối tượng Observer quan tâm. Khi trạng thái này thay đổi, các Observer đăng ký với nó sẽ được thông báo.
-Observer: Khai báo giao diện với phương thức chính update(). Phương thức này có thể truy cập đối tượng Subject mà nó đăng ký, cập nhật Observer với trạng thái thay đổi của Subject.
-ConcreteSubject: cài đặt giao diện của Observer. Constructor của nó thường yêu cầu phải đăng ký nó cho đối tượng Subject mà nó theo dõi. Khi được thông báo, nó sẽ thực thi một tác vụ gì đó, ví dụ thay đổi giao diện, cập nhật biểu đồ.
package demoobserverpattern; import java.util.ArrayList; import java.util.List; import java.util.Random; public class DemoObserverPattern { public static void main(String[] args) { System.out.println("... Demo Observer Pattern ..."); ConcreteSubject subject = new ConcreteSubject(); Observer ob1 = new ConcreteObserver(subject); Observer ob2 = new ConcreteObserver(subject); System.out.println("Doing something in the subject over time ..."); System.out.println(" Observable Observer1 Observer2"); System.out.println("Iteration change? notified? notified?"); for (int i = 0; i < 10; i++) { System.out.print(i + ": "); subject.operation(); System.out.println(); } System.out.println("Removing observer1 from the subject ... Repeating ..."); System.out.println(" Observable Observer2"); System.out.println("Iteration change? notified?"); subject.deleteObserver(ob1); for (int i = 0; i < 10; i++) { System.out.print(i + ": "); subject.operation(); System.out.println(); } } } interface Observer { public void update(); } interface Subject { public void addObserver(Observer observer); public void deleteObserver(Observer observer); public void notifyObservers(); } class ConcreteObserver implements Observer { private ConcreteSubject subject; public ConcreteObserver(ConcreteSubject subject) { this.subject = subject; this.subject.addObserver(ConcreteObserver.this); } @Override public void update() { System.out.printf(" [%.2f]", subject.d); } } class ConcreteSubject implements Subject { double d; List observers = new ArrayList(); @Override public void addObserver(Observer observer) { observers.add(observer); } @Override public void deleteObserver(Observer observer) { observers.remove(observer); } @Override public void notifyObservers() { for (Observer o : observers) { o.update(); } } public void operation() { Random rand = new Random(); d = rand.nextDouble(); if (d < 0.25 || d > 0.75) { System.out.print("YES"); notifyObservers(); } else { System.out.print("NO"); } } }
2. Liên quan
Mediator: bằng cách đóng gói những cập nhật ngữ cảnh phức tạp, Observable hoạt động như đối tượng Mediator giữa các đối tượng và các Observer.
Singleton: các Observable có thể là Singleton để nó trở nên duy nhất và được truy cập toàn cục.
3. Java API
Mẫu thiết kế Observer đã được tích hợp vào package java.util trong Java API. Để áp dụng mẫu thiết kế này:
-ConcreteSubject cần thừa kế từ lớp Observable. Trong phương thức operation(), sau khi thay đổi dữ liệu, gọi các phương thức setChanged() và notifyObservers() của giao diện Observable để tự động cập nhật cho các đối tượng Observer có đăng ký nhận cảnh báo thay đổi với nó.
-ConcreteObserver, phần hiển thị của ứng dụng (giao diện người dùng GUI, report, sơ đồ, bảng biểu) thường cài đặt giao diện Observer. Trong constructor, nó tự đăng ký để nhận cảnh báo thay đổi diễn ra trên đối tượng Observable mà nó quan tâm. Đồng thời cài đặt phương thức update(Observable, Object), trong đó nó nhận dữ liệu thay đổi từ đối tượng Observable để cập nhật phần hiển thị của mình.
Ví dụ sau đây sẽ sử dụng mẫu thiết kế Observer được tích hợp trong Java API
package javaobserverpattern; import java.util.Observable; import java.util.Observer; import java.util.Random; public class JavaObserverPattern { public static void main(String[] args) { System.out.println("... Demo Observer Pattern ..."); ConcreteSubject subject = new ConcreteSubject(); Observer ob1 = new ConcreteObserver(subject); Observer ob2 = new ConcreteObserver(subject); System.out.println("Doing something in the subject over time ..."); System.out.println(" Observable Observer1 Observer2"); System.out.println("Iteration change? notified? notified?"); for (int i = 0; i < 10; i++) { System.out.print(i + ": "); subject.operation(); System.out.println(); } System.out.println("Removing observer1 from the subject ... Repeating ..."); System.out.println(" Observable Observer2"); System.out.println("Iteration change? notified?"); subject.deleteObserver(ob1); for (int i = 0; i < 10; i++) { System.out.print(i + ": "); subject.operation(); System.out.println(); } } } class ConcreteObserver implements Observer {</p><pre> <code>public ConcreteObserver(Observable observable) { observable.addObserver(ConcreteObserver.this); } @Override public void update(Observable o, Object arg) { if (o instanceof ConcreteSubject) { ConcreteSubject t = (ConcreteSubject) o; System.out.printf("  [%.2f]", t.d); } } </code> </pre><p>} class ConcreteSubject extends Observable { double d; public void operation() { Random rn = new Random(); d = rn.nextDouble(); if (d < 0.25 || d > 0.75) { System.out.print("Yes"); this.setChanged(); this.notifyObservers(); } else { System.out.print("No"); } } }
4. Sử dụng
Ta muốn:
- Cập nhật trên một đối tượng (thường là dữ liệu) sẽ thay đổi một số đối tượng được lựa chọn khác (thường là giao diện người dùng), không xác định được số đối tượng thay đổi kéo theo.
- Một đối tượng cần thông báo cho một số các đối tượng khác mà không cần biết thông tin về các đối tượng được thông báo.