99 Ngày Java — Ngày 92: Singleton và Factory
Các mẫu khởi tạo trả lời câu hỏi ai tạo đối tượng và tạo thế nào. Singleton bảo đảm chỉ có một instance; các mẫu Factory giấu việc chọn lớp cụ thể sau một phương thức. Cả hai rất phổ biến, và cả hai đều dễ bị lạm dụng, nên bài này nói cả cách viết đúng lẫn lúc không nên dùng.

1. Singleton thread-safe
Cách đơn giản nhất là khởi tạo sớm trong một trường static final: JVM bảo đảm việc khởi tạo lớp là thread-safe. Nếu cần khởi tạo trễ, dùng holder idiom: instance nằm trong một lớp lồng static và chỉ được tạo khi lớp đó được nạp lần đầu. Double-checked locking cũng đúng nhưng bắt buộc trường phải là volatile và dễ viết sai, nên hiếm khi đáng dùng.
final class AppClock {
private AppClock() {}
private static final class Holder {
static final AppClock INSTANCE = new AppClock();
}
static AppClock instance() { return Holder.INSTANCE; } // lazy, thread-safe
}2. Enum singleton và mặt trái của singleton
Enum một phần tử là singleton gọn nhất: JVM bảo đảm chỉ có một instance, kể cả khi deserialize hay bị tấn công bằng reflection. Nhưng mọi singleton đều là trạng thái toàn cục: lớp dùng nó giấu phụ thuộc bên trong, test khó thay thế và các test ảnh hưởng lẫn nhau. Trong ứng dụng có dependency injection, hãy để container giữ một instance và truyền nó qua constructor.
enum IdGenerator {
INSTANCE;
private final AtomicLong next = new AtomicLong(1);
long nextId() { return next.getAndIncrement(); }
}
long id = IdGenerator.INSTANCE.nextId();3. Static factory và Factory Method
Static factory method là phương thức tĩnh trả về instance, ví dụ List.of hay Optional.empty. So với constructor, nó có tên, có thể trả về kiểu con và có thể tái sử dụng instance. Mẫu Factory Method đi xa hơn một bước: lớp cha định nghĩa quy trình và gọi một phương thức trừu tượng để tạo đối tượng, lớp con quyết định tạo loại nào.
sealed interface Notifier permits EmailNotifier, SmsNotifier {
void send(String to, String message);
static Notifier of(Channel channel) { // static factory
return switch (channel) {
case EMAIL -> new EmailNotifier();
case SMS -> new SmsNotifier();
};
}
}4. Abstract Factory: tạo cả một họ đối tượng
Abstract Factory là một interface có nhiều phương thức tạo, mỗi cài đặt cho ra một bộ đối tượng ăn khớp với nhau. Ví dụ một factory cho AWS tạo storage và queue của AWS, factory cho môi trường local tạo bản chạy trong bộ nhớ. Code nghiệp vụ chỉ biết interface, nên đổi cả họ chỉ cần đổi một factory và không thể vô tình trộn hai họ.
Bắt đầu bằng constructor; thêm factory khi việc chọn lớp cụ thể trở nên phức tạp hoặc lặp lại.
Factory là nơi duy nhất biết các lớp cụ thể.
Trong Spring, container chính là factory: @Bean là factory method.
interface CloudFactory {
BlobStorage storage();
MessageQueue queue();
}
final class LocalFactory implements CloudFactory {
public BlobStorage storage() { return new InMemoryStorage(); }
public MessageQueue queue() { return new InMemoryQueue(); }
}Singleton giới hạn số instance, factory giới hạn chỗ biết lớp cụ thể. Ngày 93 xem hai mẫu khởi tạo còn lại: Builder và Prototype.
Tài liệu đối chiếu
https://refactoring.guru/design-patterns/singleton
https://refactoring.guru/design-patterns/abstract-factory
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


