-
Design Pattern
- Singleton Design Pattern
- Factory Design Pattern
- Factory Method Design Pattern
- Abstract Factory Design Pattern
- Builder Design Pattern
- Prototype Design Pattern
- Object Pool Design Pattern
- Chain of Responsibility Design Pattern
- Command Design Pattern
- Interpreter Design Pattern
- Iterator Design Pattern
- Mediator Design Pattern
- Memento Design Pattern
- Observer Design Pattern
- Observer Design Pattern - Xử Lý Exception
- Strategy Design Pattern
- Template Method Design Pattern
- Visitor Design Pattern
- Null Object Design Pattern
- Adapter Design Pattern
- Bridge Design Pattern
- Composite Design Pattern
- Decorator Design Pattern
- Flyweight Design Pattern
- Proxy Design Pattern
- S.O.L.I.D
- Clean code
- Lập trình socket
- Java Core
- Multi-Thread
- Spring
- Java Web
- Memory Caching
- Message Queue
- DevOps
- Xây dựng một nền tảng
- MongoDB
- MySQL timestamp
- Properties vs yaml
- Kotlin
- Lập Trình Machine Learning với PyTorch
- Mã Nguồn Mở
- Ezy HTTP
- Free Chat
- Một số kinh nghiệm với Git
- Review cho đồng nghiệp!
- Setup Dev Environment
- Hello World
- Create a Server Project
- Handle Client Requests
- Using ezyfox-server-csharp-client
- Using ezyfox-es6-client
- Client React.js Interaction
- Build And Deploy In Local

Chuỗi phản ứng linh hoạt!
Có bao giờ bạn tự hỏi chỉ đúng bằng một câu lệnh logger.error
mà lại có thể vừa hiện trên màn hình console, vừa ghi được vào file lại vừa bắn ầm ầm trên slack? đó chính là một trong những ứng dụng quan trọng của chain of responsibility design pattern.
Vấn đề trong thực tế
Để làm 3 việc ghi ra console, ghi file và thông báo đến slack, có lẽ chúng ta sẽ viết lớp logger thế này:
public class Logger {
public void error(String message) {
writeToConsole(message);
writeToFile(message);
sendToSlack(message);
}
private void writeToConsole(String message) {
System.out.println(message);
}
private void writeToFile(String message) {
fileWriter.write(message);
}
private void sendToSlack(String message) {
slackClient.send(message);
}
}
Nhìn code cũng khá clean
đúng không? Nhưng bây giờ lại phát sinh vấn đề. Công ty của chúng ta vừa xây dựng xong hệ thống lưu log tên là LogStore, và yêu cầu đặt ra là chúng ta phải thêm code để gửi log sang hệ thống LogStore vào lớp Logger kiểu này:
public void error(String message) {
writeToConsole(message);
writeToFile(message);
sendToSlack(message);
sendToLogStore(message);
}
private void sendToLogStore(String message) {
logStoreClient.send(message);
}
Ồ không, chúng ta đang vi phạm quy tắc Open/Close (đóng với thay đổi, mở với mở rộng). Việc chúng ta đổi một lớp được sử dụng nhiều nơi trong chương trình như Logger sẽ yêu cầu chúng ta phải đưa ra bản đánh giá mức độ ảnh hưởng và test lại toàn bộ.
Mục tiêu ra đời
Rõ ràng giải pháp thay đổi lớp Logger như trên là không ổn, nên chúng ta cần một design pattern với mục tiêu:
- Cho phép xử lý một hành động hay yêu cầu qua nhiều bước, từ đó có thể chia nhỏ thành nhiều phần xử lý độc lập
- Tránh việc thay đổi lớp xử lý đã chạy ổn định để tránh rủi ro
- Dễ dàng thêm mới hoặc bỏ đi một phần xử lý trong chuỗi xử lý
Đó chính là mục tiêu ra đời của Chain of Responsibility Design Pattern
Giải quyết vấn đề
Bây giờ hãy qua trở lại với bài toán log, chúng ta sẽ cần thiết kế lớp một chút cho dễ hiểu

- Lớp
Logger
của chúng ta bây giờ sẽ không chứa các hàm xử lý nghiệp vụ nữa mà sẽ chỉ chứa một danh sách các lớpAppender
- Các lớp
LoggerAppender
sẽ chịu trách nhiệm xử lý nghiệp vụ tương ứng với chức năng của mình
Còn bây giờ là source code:
public interface LoggerAppender {
void append(String message);
}
public class ConsoleAppender implements LoggerAppender {
@Override
public void append(String message) {
System.out.println("Console: " + message);
}
}
public class FileAppender implements LoggerAppender {
@Override
public void append(String message) {
System.out.println("File: " + message);
}
}
public class SlackAppender implements LoggerAppender {
@Override
public void append(String message) {
System.out.println("Slack: " + message);
}
}
public class LogStoreAppender implements LoggerAppender {
@Override
public void append(String message) {
System.out.println("LogStore: " + message);
}
}
public class Logger {
private final List<LoggerAppender> appenders;
public Logger(List<LoggerAppender> appenders) {
this.appenders = appenders;
}
public void info(String message) {
for(LoggerAppender appender : appenders) {
appender.append(message);
}
}
}
public final class LoggerFactory {
private final static Map<Object, Logger> loggers = new HashMap<>();
private final static List<LoggerAppender> appenders = new ArrayList<>();
static {
appenders.add(new ConsoleAppender());
appenders.add(new FileAppender());
appenders.add(new SlackAppender());
appenders.add(new LogStoreAppender());
}
public static Logger getLogger(Object name) {
return loggers.computeIfAbsent(name, k -> new Logger(appenders));
}
}
Và khi sử dụng sẽ đơn giản là thế này:
Logger logger = LoggerFactory.getLogger(LoggerDemo.class);
logger.info("Hello World");
Khi cần phải thêm một nghiệp vụ xử lý log mới, chúng ta sẽ chỉ đơn giản là tạo thêm một lớp Appender
và đăng ký vào lớp LoggerFactory
, như hiện nay thì logback
hay log4j12
cho phép chúng ta đăng ký qua file xml
hoặc file .properties
. Lớp Logger
của chúng ta sẽ được giữ nguyên mà không phải thay đổi gì cả. Việc thêm mới hoặc bỏ đi một Appender
là cực kì dễ dàng
Tham khảo
-
Design Pattern
- Singleton Design Pattern
- Factory Design Pattern
- Factory Method Design Pattern
- Abstract Factory Design Pattern
- Builder Design Pattern
- Prototype Design Pattern
- Object Pool Design Pattern
- Chain of Responsibility Design Pattern
- Command Design Pattern
- Interpreter Design Pattern
- Iterator Design Pattern
- Mediator Design Pattern
- Memento Design Pattern
- Observer Design Pattern
- Observer Design Pattern - Xử Lý Exception
- Strategy Design Pattern
- Template Method Design Pattern
- Visitor Design Pattern
- Null Object Design Pattern
- Adapter Design Pattern
- Bridge Design Pattern
- Composite Design Pattern
- Decorator Design Pattern
- Flyweight Design Pattern
- Proxy Design Pattern
- S.O.L.I.D
- Clean code
- Lập trình socket
- Java Core
- Multi-Thread
- Spring
- Java Web
- Memory Caching
- Message Queue
- DevOps
- Xây dựng một nền tảng
- MongoDB
- MySQL timestamp
- Properties vs yaml
- Kotlin
- Lập Trình Machine Learning với PyTorch
- Mã Nguồn Mở
- Ezy HTTP
- Free Chat
- Một số kinh nghiệm với Git
- Review cho đồng nghiệp!
- Setup Dev Environment
- Hello World
- Create a Server Project
- Handle Client Requests
- Using ezyfox-server-csharp-client
- Using ezyfox-es6-client
- Client React.js Interaction
- Build And Deploy In Local