Trang chủHướng dẫnMẫu thiết kế Observer trong Java: Cách hoạt động và ứng dụng thực tế
Java

Mẫu thiết kế Observer trong Java: Cách hoạt động và ứng dụng thực tế

CyStack blog 5 phút để đọc
CyStack blog09/07/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 5 minutes

Observer là một mẫu thiết kế hành vi (behavioral design pattern). Mẫu thiết kế này đặc biệt hữu ích khi bạn cần theo dõi trạng thái của một đối tượng và nhận thông báo mỗi khi nó có bất kỳ thay đổi nào. Trong Observer pattern, đối tượng theo dõi trạng thái của một đối tượng khác được gọi là Observer, còn đối tượng được theo dõi thì ta gọi là Subject.

Mẫu thiết kế Observer trong Java

Mẫu thiết kế Observer trong Java

Theo định nghĩa của GoF, mục đích của Observer design pattern là:

Định nghĩa một quan hệ phụ thuộc một-nhiều giữa các đối tượng để khi một trong số chúng thay đổi trạng thái, tất cả các đối tượng phụ thuộc của nó sẽ được thông báo và cập nhật một cách tự động.

Subject chứa một danh sách các observer để thông báo về bất kỳ thay đổi nào trong trạng thái của nó. Vì vậy, nó nên cung cấp các phương thức để observer có thể đăng ký (register) và hủy đăng ký (unregister). Subject cũng chứa một phương thức để thông báo cho tất cả các observer về bất kỳ thay đổi nào. Nó có thể gửi thông tin cập nhật ngay khi thông báo, hoặc cung cấp một phương thức khác để observer tự lấy thông tin cập nhật.

Observer nên có một phương thức để thiết lập đối tượng cần theo dõi và một phương thức khác sẽ được Subject sử dụng để thông báo về các cập nhật. Java cung cấp nền tảng sẵn có để triển khai Observer pattern thông qua class java.util.Observable và interface java.util.Observer. Tuy nhiên, chúng không được sử dụng rộng rãi trong thực tế vì các triển khai đó khá sơ khai. Và trong hầu hết các trường hợp, chúng ta không muốn phải kế thừa một class chỉ để triển khai Observer pattern vì Java không hỗ trợ tính đa kế thừa.

Java Message Service (JMS, API giao tiếp tin nhắn) sử dụng Observer pattern cùng với Mediator pattern để cho phép các ứng dụng đăng ký (subscribe) và xuất (publish) dữ liệu đến các ứng dụng khác.

Các framework có kiến trúc MVC (Model-View-Controller, Mô hình-Giao diện-Điều khiển) cũng sử dụng Observer pattern. Trong đó Model là Subject và View là các observer có thể đăng ký để nhận thông báo về bất kỳ thay đổi nào trong model.

Ví dụ về Observer pattern trong Java

Trong ví dụ dưới đây, chúng ta sẽ triển khai một topic đơn giản và các observer có thể đăng ký vào topic này. Bất cứ khi nào có tin nhắn mới được gửi đến topic, tất cả các observer đã đăng ký sẽ được thông báo và có thể sử dụng tin nhắn đó.

Dựa trên các yêu cầu đối với Subject, dưới đây là interface Subject cơ sở định nghĩa các phương thức giao ước bắt buộc mà bất kỳ subject cụ thể nào cũng phải triển khai.

package com.journaldev.design.observer;

public interface Subject {

	//methods to register and unregister observers
	public void register(Observer obj);
	public void unregister(Observer obj);
	
	//method to notify observers of change
	public void notifyObservers();
	
	//method to get updates from subject
	public Object getUpdate(Observer obj);
	
}

Tiếp theo, ta sẽ tạo giao ước cho Observer. Sẽ có một phương thức để gắn Subject vào observer và một phương thức khác để Subject dùng để thông báo về bất kỳ thay đổi nào.

package com.journaldev.design.observer;

public interface Observer {
	
	//method to update the observer, used by subject
	public void update();
	
	//attach with subject to observe
	public void setSubject(Subject sub);
}

Giờ khi giao ước đã sẵn sàng, chúng ta sẽ tiếp tục với việc triển khai cụ thể cho topic của mình.

package com.journaldev.design.observer;

import java.util.ArrayList;
import java.util.List;

public class MyTopic implements Subject {

	private List<Observer> observers;
	private String message;
	private boolean changed;
	private final Object MUTEX= new Object();
	
	public MyTopic(){
		this.observers=new ArrayList<>();
	}
	@Override
	public void register(Observer obj) {
		if(obj == null) throw new NullPointerException("Null Observer");
		synchronized (MUTEX) {
		if(!observers.contains(obj)) observers.add(obj);
		}
	}

	@Override
	public void unregister(Observer obj) {
		synchronized (MUTEX) {
		observers.remove(obj);
		}
	}

	@Override
	public void notifyObservers() {
		List<Observer> observersLocal = null;
		//synchronization is used to make sure any observer registered after message is received is not notified
		synchronized (MUTEX) {
			if (!changed)
				return;
			observersLocal = new ArrayList<>(this.observers);
			this.changed=false;
		}
		for (Observer obj : observersLocal) {
			obj.update();
		}

	}

	@Override
	public Object getUpdate(Observer obj) {
		return this.message;
	}
	
	//method to post message to the topic
	public void postMessage(String msg){
		System.out.println("Message Posted to Topic:"+msg);
		this.message=msg;
		this.changed=true;
		notifyObservers();
	}

}

Việc triển khai phương thức để đăng ký và hủy đăng ký một observer rất đơn giản. Phương thức bổ sung là postMessage() sẽ được bên client dùng để gửi tin nhắn dạng String đến topic.

Lưu ý biến boolean được dùng để theo dõi sự thay đổi trạng thái của topic và được sử dụng khi thông báo cho các observer. Biến này là cần thiết để tránh trường hợp khi không có cập nhật nào mà vẫn có người gọi phương thức notifyObservers(), nó sẽ không gửi các thông báo sai đến observer.

Cũng lưu ý việc sử dụng đồng bộ hóa trong phương thức notifyObservers(). Nó đảm bảo rằng thông báo chỉ được gửi đến các observer đã đăng ký trước khi tin nhắn được publish lên topic.

Đây là phần triển khai code của các Observer sẽ theo dõi subject.

package com.journaldev.design.observer;

public class MyTopicSubscriber implements Observer {
	
	private String name;
	private Subject topic;
	
	public MyTopicSubscriber(String nm){
		this.name=nm;
	}
	@Override
	public void update() {
		String msg = (String) topic.getUpdate(this);
		if(msg == null){
			System.out.println(name+":: No new message");
		}else
		System.out.println(name+":: Consuming message::"+msg);
	}

	@Override
	public void setSubject(Subject sub) {
		this.topic=sub;
	}

}

Trong phần triển khai phương thức update(), nó đang gọi phương thức getUpdate() của Subject để lấy tin nhắn về xử lý. Ta có thể bỏ qua lệnh gọi này bằng cách truyền tin nhắn trực tiếp như một tham số cho phương thức update().

Dưới đây là một chương trình đơn giản để sử dụng phần triển khai topic của chúng ta.

package com.journaldev.design.observer;

public class ObserverPatternTest {

	public static void main(String[] args) {
		//create subject
		MyTopic topic = new MyTopic();
		
		//create observers
		Observer obj1 = new MyTopicSubscriber("Obj1");
		Observer obj2 = new MyTopicSubscriber("Obj2");
		Observer obj3 = new MyTopicSubscriber("Obj3");
		
		//register observers to the subject
		topic.register(obj1);
		topic.register(obj2);
		topic.register(obj3);
		
		//attach observer to subject
		obj1.setSubject(topic);
		obj2.setSubject(topic);
		obj3.setSubject(topic);
		
		//check if any update is available
		obj1.update();
		
		//now send message to subject
		topic.postMessage("New Message");
	}

}

Khi chạy chương trình trên, ta nhận được kết quả như sau.

Obj1:: No new message
Message Posted to Topic:New Message
Obj1:: Consuming message::New Message
Obj2:: Consuming message::New Message
Obj3:: Consuming message::New Message

Sơ đồ các lớp của Observer pattern

Sơ đồ mẫu thiết kế Observer

Observer pattern còn được gọi là thiết kế theo mô hình publish-subscribe (xuất bản-đăng ký). Một vài ví dụ triển khai của nó bao gồm:

  • java.util.EventListener trong Swing
  • javax.servlet.http.HttpSessionBindingListener
  • javax.servlet.http.HttpSessionAttributeListener

Trên đây là toàn bộ nội dung về Observer pattern trong Java. Hy vọng bài viết sẽ giúp bạn xây dựng những hệ thống thay đổi tự động cập nhật, giảm thiểu việc kiểm tra thủ công và nâng cao hiệu suất xử lý sự kiện trong ứng dụng của mình.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất