GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Design pattern Hướng dẫn Java Design Pattern – Observer

Hướng dẫn Java Design Pattern – Observer

Đăng vào 28/12/2018 . Được đăng bởi GP Coder . 50411 Lượt xem . Toàn màn hình

Chúng ta không thể nói về Lập trình hướng đối tượng mà không xem xét trạng thái của các đối tượng. Tất cả các chương trình hướng đối tượng là về các đối tượng và sự tương tác của chúng. Trong trường hợp khi một số đối tượng nhất định cần được thông báo thường xuyên về những thay đổi xảy ra trong các đối tượng khác. Để có một thiết kế tốt có nghĩa là tách rời càng nhiều càng tốt và giảm sự phụ thuộc. Mẫu thiết kế Observer (quan sát) có thể được sử dụng bất cứ khi nào mà một đối tượng có sự thay đổi trạng thái, tất các thành phần phụ thuộc của nó sẽ được thông báo và cập nhật một cách tự động.

Nội dung

  • 1 Observer Pattern là gì?
  • 2 Cài đặt Observer Pattern như thế nào?
  • 3 Lợi ích của Observer Pattern là gì?
  • 4 Sử dụng Observer Pattern khi nào?

Observer Pattern là gì?

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Observer Pattern là một trong những Pattern thuộc nhóm hành vi (Behavior Pattern). Nó định nghĩa mối phụ thuộc một – nhiều giữa các đối tượng để khi mà một đối tượng có sự thay đổi trạng thái, tất các thành phần phụ thuộc của nó sẽ được thông báo và cập nhật một cách tự động.

Observer có thể đăng ký với hệ thống. Khi hệ thống có sự thay đổi, hệ thống sẽ thông báo cho Observer biết. Khi không cần nữa, mẫu Observer sẽ được gỡ khỏi hệ thống.

  • Hình 1-8, cho phép observer thứ 1 đăng ký với hệ thống.
  • Hình 1-9, cho phép observer thứ 2 đăng ký với hệ thống.
  • Hiện tại hệ thống đang liên lạc với 2 observer: Observer 1 và Observer 2. Khi hệ thống phát sinh một sự kiện cụ thể nào đó, nó sẽ thông báo (notification) với cả 2 observer như hình số 1-10.

Observer Pattern còn gọi là Dependents, Publish/Subcribe hoặc Source/Listener.

Cài đặt Observer Pattern như thế nào?

Các thành phần tham gia Observer Pattern:

  • Subject : chứa danh sách các observer,  cung cấp phương thức để có thể thêm và loại bỏ observer.
  • Observer : định nghĩa một phương thức update() cho các đối tượng sẽ được subject thông báo đến khi có sự thay đổi trạng thái.
  • ConcreteSubject : cài đặt các phương thức của Subject, lưu trữ trạng thái danh sách các ConcreateObserver, gửi thông báo đến các observer của nó khi có sự thay đổi trạng thái.
  • ConcreteObserver : cài đặt các phương thức của Observer, lưu trữ trạng thái của subject, thực thi việc cập nhật để giữ cho trạng thái đồng nhất với subject gửi thông báo đến.

Sự tương tác giữa subject và các observer như sau: mỗi khi subject có sự thay đổi trạng thái, nó sẽ duyệt qua danh sách các observer của nó và gọi phương thức cập nhật trạng thái ở từng observer, có thể truyền chính nó vào phương thức để các observer có thể lấy ra trạng thái của nó và xử lý.

Ví dụ Observer Pattern với ứng dụng Tracking thao tác một Account

Giả sử hệ thống của chúng ta cần theo dõi về tài khoản của người dùng. Mọi thao tác của người dùng đều cần được ghi log lại, sẽ thực hiện gửi mail thông báo khi tài khoản hết hạn, thực hiện chặn người dùng nếu truy cập không hợp lệ, …

Chương trình của chúng ta như sau:

  • Subject : cung cấp các phương thức để thêm, loại bỏ, thông báo observer.
  • AccountService : đóng vai trò là ConcreteSubject, sẽ thông báo tới tất cả các observers bất cứ khi nào có thao tác của người dùng liên quan đến đăng nhập, tài khoản hết hạn.
  • Observer : định nghĩa một phương thức update() cho các đối tượng sẽ được subject thông báo đến khi có sự thay đổi trạng thái. Phương thức này chấp nhận đối số là SubjectState, cho phép các ConcreteObserver sử dụng dữ liệu của nó.
  • Logger, Mailer và Protector là các ConcreteObserver. Sau khi nhận được thông báo rằng có thao tác với user và gọi tới phương thức update(), các ConcreteObserver sẽ sử dụng dữ liệu SubjectState để xử lý.

Subject.java


package com.gpcoder.patterns.behavioral.observer.account;

public interface Subject {

	void attach(Observer observer);

	void detach(Observer observer);

	void notifyAllObserver();
}

AccountService.java


package com.gpcoder.patterns.behavioral.observer.account;

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

import lombok.Data;

enum LoginStatus {
	SUCCESS, FAILURE, INVALID, EXPIRED
}

@Data
class User {
	private String email;
	private String ip;
	private LoginStatus status;
}

public class AccountService implements Subject {

	private User user;
	private List<Observer> observers = new ArrayList<>();

	public AccountService(String email, String ip) {
		user = new User();
		user.setEmail(email);
		user.setIp(ip);
	}

	@Override
	public void attach(Observer observer) {
		if (!observers.contains(observer))
			observers.add(observer);
	}

	@Override
	public void detach(Observer observer) {
		if (observers.contains(observer)) {
			observers.remove(observer);
		}
	}

	@Override
	public void notifyAllObserver() {
		for (Observer observer : observers) {
			observer.update(user);
		}
	}

	public void changeStatus(LoginStatus status) {
		user.setStatus(status);
		System.out.println("Status is changed");
		this.notifyAllObserver();
	}

	public void login() {

		if (!this.isValidIP()) {
			user.setStatus(LoginStatus.INVALID);
		} else if (this.isValidEmail()) {
			user.setStatus(LoginStatus.SUCCESS);
		} else {
			user.setStatus(LoginStatus.FAILURE);
		}

		System.out.println("Login is handled");
		this.notifyAllObserver();
	}

	private boolean isValidIP() {
		return "127.0.0.1".equals(user.getIp());
	}

	private boolean isValidEmail() {
		return "contact@gpcoder.com".equalsIgnoreCase(user.getEmail());
	}
}

Observer.java


package com.gpcoder.patterns.behavioral.observer.account;

public interface Observer {
	void update(User user);
}

Logger.java


package com.gpcoder.patterns.behavioral.observer.account;

public class Logger implements Observer {

	@Override
	public void update(User user) {
		System.out.println("Logger: " + user);
	}
}

Mailer.java


package com.gpcoder.patterns.behavioral.observer.account;

public class Mailer implements Observer {

	@Override
	public void update(User user) {
		if (user.getStatus() == LoginStatus.EXPIRED) {
			System.out.println("Mailer: User " + user.getEmail() + " is expired. An email was sent!");
		}
	}
}

Protector.java


package com.gpcoder.patterns.behavioral.observer.account;

public class Protector implements Observer {

	@Override
	public void update(User user) {
		if (user.getStatus() == LoginStatus.INVALID) {
			System.out.println("Protector: User " + user.getEmail() + " is invalid. " 
					+ "IP " + user.getIp() + " is blocked");
		}
	}
}

ObserverPatternExample.java


package com.gpcoder.patterns.behavioral.observer.account;

public class ObserverPatternExample {

	public static void main(String[] args) {
		AccountService account1 = createAccount("contact@gpcoder.com", "127.0.0.1");
		account1.login();
		account1.changeStatus(LoginStatus.EXPIRED);

		System.out.println("---");
		AccountService account2 = createAccount("contact@gpcoder.com", "116.108.77.231");
		account2.login();
	}

	private static AccountService createAccount(String email, String ip) {
		AccountService account = new AccountService(email, ip);
		account.attach(new Logger());
		account.attach(new Mailer());
		account.attach(new Protector());
		return account;
	}
}

Output của chương trình:


Login is handled
Logger: User(email=contact@gpcoder.com, ip=127.0.0.1, status=SUCCESS)
Status is changed
Logger: User(email=contact@gpcoder.com, ip=127.0.0.1, status=EXPIRED)
Mailer: User contact@gpcoder.com is expired. An email was sent!
---
Login is handled
Logger: User(email=contact@gpcoder.com, ip=116.108.77.231, status=INVALID)
Protector: User contact@gpcoder.com is invalid. IP 116.108.77.231 is blocked

Lợi ích của Observer Pattern là gì?

Lợi ích:

  • Dễ dàng mở rộng với ít sự thay đổi : mẫu này cho phép thay đổi Subject và Observer một cách độc lập. Chúng ta có thể tái sử dụng các Subject mà không cần tái sử dụng các Observer và ngược lại. Nó cho phép thêm Observer mà không sửa đổi Subject hoặc Observer khác. Vì vậy, nó đảm bảo nguyên tắc Open/Closed Principle (OCP).
  • Sự thay đổi trạng thái ở 1 đối tượng có thể được thông báo đến các đối tượng khác mà không phải giữ chúng liên kết quá chặt chẽ.
  • Một đối tượng có thể thông báo đến một số lượng không giới hạn các đối tượng khác.

Bên cạnh những lợi ích, chúng ta cần xem xét đến trường hợp cập nhật không mong muốn (Unexpected update) của Subject. Bởi vì các Observer không biết về sự hiện diện của nhau, nó có thể gây tốn nhiều chi phí của việc thay đổi Subject.

Sử dụng Observer Pattern khi nào?

  • Thường được sử dụng trong mối quan hệ 1-n giữa các object với nhau. Trong đó một đối tượng thay đổi và muốn thông báo cho tất cả các object liên quan biết về sự thay đổi đó.
  • Khi thay đổi một đối tượng, yêu cầu thay đổi đối tượng khác và chúng ta không biết có bao nhiêu đối tượng cần thay đổi, những đối tượng này là ai.
  • Sử dụng trong ứng dụng broadcast-type communication.
  • Sử dụng để quản lý sự kiện (Event management).
  • Sử dụng trong mẫu mô hình MVC (Model View Controller Pattern) : trong MVC, mẫu này được sử dụng để tách Model khỏi View. View đại diện cho Observer và Model là đối tượng Observable.

Tài liệu tham khảo:

  • https://sourcemaking.com/design_patterns/observer
  • https://refactoring.guru/design-patterns/observer
  • https://www.baeldung.com/java-observer-pattern
  • https://pawelgrzybek.com/the-observer-pattern-in-javascript-explained/
  • Design Patterns: Elements of Reusable Object-Oriented Software – GOF
  • Design Pattern for dummies
4.7
29
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Behavior Pattern, Design pattern Được gắn thẻ: Behavior Pattern, Design pattern

Hướng dẫn Java Design Pattern – Memento
Hướng dẫn Java Design Pattern – State

Có thể bạn muốn xem:

  • Hướng dẫn Java Design Pattern – Strategy (03/01/2019)
  • Hướng dẫn Java Design Pattern – Visitor (10/01/2019)
  • Hướng dẫn Java Design Pattern – Flyweight (22/11/2018)
  • Hướng dẫn Java Design Pattern – Service Locator (21/01/2019)
  • Hướng dẫn Java Design Pattern – Memento (25/12/2018)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (97657 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97343 lượt xem)
  • Giới thiệu Design Patterns (87178 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (85895 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83401 lượt xem)

Nội dung bài viết

  • 1 Observer Pattern là gì?
  • 2 Cài đặt Observer Pattern như thế nào?
  • 3 Lợi ích của Observer Pattern là gì?
  • 4 Sử dụng Observer Pattern khi nào?

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link