Nhà máy không công nhân!

Chắc hẳn chúng ta ai cũng đã từng sử dụng đối tượng LoggerFactory để lấy đối tượng Logger rồi nhỉ?

Logger logger = LoggerFactory.getLogger(getClass());

Đây chính là một ví dụ điển hình của Factory pattern.

Giới thiệu

Hãy xem interface Logger có những gì nhé:

interface Logger {
    public void trace(...);
    public void debug(...);
    public void info(...);
    public void warn(...);
    public void error(...);
}

Đây là những hàm mà Logger cung cấp cho chúng ta, chúng ta sẽ sử dụng các hàm này để log thông tin ra console, file hoặc gửi vào hệ thống ELK, slack vân vân và mây mây, để làm được những việc này, chúng ta cần 1 lớp cài đặt cho lớp Logger như này:

class LoggerImpl implements Logger {
    private List<Appender> appenders;

    @Override
    public void trace(message) {
        appenders.forEach(appender -> appender.log(LogLevel.TRACE, message));
    }
}

Bạn có thấy hàm trace không? nó đang sử dụng ObserverPattern (mình sẽ nói ở bài viết khác) mỗi Appender này tương ứng với mỗi nới mà chúng ta muốn gửi log đi (ví dụ: ConsoleAppend cho console, FileAppender cho file ...), để tạo được các Appender này không hề đơn giản, mà cũng không thể nào cứ mỗi lần tạo đối tượng Logger lại đi tạo lại các Appender được. Đây chính là lúc chúng ta cần FactoryPattern.

Mục tiêu ra đời

Factory pattern ra đời với mục tiêu:

  1. Che giấu các logic phức tạp để tạo ra một đối tượng, giúp lập trình viên chỉ cần quan tâm đến đối tượng được tạo ra mà thôi
  2. Tạo ra các đối tượng implement chung các interface giúp chúng ta không cần quan tâm đến lớp cài đặt là gì, đảm bảo sau này cần sửa đổi hoặc nâng cấp, chúng ta chỉ cần sửa hàm cài đặt của lớp Factory mà không ảnh hưởng đến những nơi đang sử dụng (ví dụ: chúng ta có thể chuyển từ slf4j12 sang logback mà không cần sửa source code)

Ví dụ

Bây giờ hãy cài đặt lớp LoggerFactory

class LoggerFactory {

    private static final List<Appender> appenders = loadAppenders();
    private static final Map<Object, Logger> loggers = new ConcurrentHashMap<>();

    public Logger getLogger(Object type) {
        return loggers.computIfAbsent(type, k -> new LoggerImpl(appenders));
    }
}

Bạn thấy không, để tạo 1 đối tượng Logger thật phức tạp, nhưng may mắn thay, với Factory Pattern, chúng ta đã được giải phóng, cheer!!

Kết luận

Factory design pattern rất quan trọng với chúng ta, đặc bệt là trong những team đông người, nó đảm bảo rằng tất cả mọi người sẽ phải tuân thủ theo một quy tắc để tạo đối tượng, điều này giúp giảm thiểu được việc trùng lặp mã nguồn từ đó sẽ giảm thiểu được thời gian và chi phí phát triển cho dự án, nó cũng đồng thời giúp chúng ta tránh được những lỗi giống nhau trong một tập thể đông người.