Hướng dẫn Proxy Pattern trong Java
1. Giới thiệu Proxy Pattern
Proxy Pattern là một mẫu thiết kế thuộc nhóm Structural Patterns, cung cấp một đối tượng thay thế hoặc một proxy để kiểm soát truy cập đến đối tượng ban đầu. Proxy có thể kiểm soát việc truy cập, bảo vệ đối tượng thực, giảm thiểu chi phí khởi tạo đối tượng hoặc thêm các tính năng bổ sung mà không thay đổi đối tượng thực.
2. Các trường hợp ứng dụng Proxy Pattern
Kiểm soát truy cập: Khi cần kiểm soát việc truy cập đến đối tượng, ví dụ như bảo vệ đối tượng hoặc giới hạn quyền truy cập.
Lazy Initialization: Khi cần trì hoãn việc khởi tạo đối tượng cho đến khi thực sự cần thiết.
Logging và Caching: Khi cần thêm chức năng ghi log hoặc caching mà không làm thay đổi đối tượng thực.
Remote Proxy: Khi cần đại diện cho một đối tượng nằm trên một không gian địa chỉ khác (ví dụ như trên một máy chủ từ xa).
3. Triển khai Proxy Pattern
3.1. Ví dụ triển khai đơn giản
Giả sử chúng ta có một đối tượng Image với các phương thức display. Chúng ta sẽ sử dụng Proxy Pattern để kiểm soát truy cập đến đối tượng này.
// Bước 1: Tạo interface Image
public interface Image {
void display();
}
// Bước 2: Tạo lớp RealImage
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
}
// Bước 3: Tạo lớp ProxyImage
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
// Bước 4: Sử dụng Proxy Pattern
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_image.jpg");
// Image sẽ được tải lần đầu tiên
image.display();
System.out.println("");
// Image sẽ không cần tải lại lần thứ hai
image.display();
}
}
3.2. Ví dụ triển khai phức tạp
Giả sử chúng ta có một hệ thống truy cập cơ sở dữ liệu và chúng ta muốn thêm chức năng kiểm soát truy cập và caching.
// Bước 1: Tạo interface Database
public interface Database {
String executeQuery(String query);
}
// Bước 2: Tạo lớp RealDatabase
public class RealDatabase implements Database {
@Override
public String executeQuery(String query) {
return "Executing query: " + query;
}
}
// Bước 3: Tạo lớp ProxyDatabase
public class ProxyDatabase implements Database {
private RealDatabase realDatabase;
private static final Set<String> authorizedUsers = new HashSet<>(Arrays.asList("admin", "user"));
private Map<String, String> cache = new HashMap<>();
private String currentUser;
public ProxyDatabase(String currentUser) {
this.currentUser = currentUser;
}
@Override
public String executeQuery(String query) {
if (!authorizedUsers.contains(currentUser)) {
return "Access denied for user: " + currentUser;
}
if (cache.containsKey(query)) {
return "Returning cached result for query: " + query;
}
if (realDatabase == null) {
realDatabase = new RealDatabase();
}
String result = realDatabase.executeQuery(query);
cache.put(query, result);
return result;
}
}
// Bước 4: Sử dụng Proxy Pattern
public class ProxyPatternDemo {
public static void main(String[] args) {
Database dbAdmin = new ProxyDatabase("admin");
Database dbUser = new ProxyDatabase("user");
Database dbGuest = new ProxyDatabase("guest");
System.out.println(dbAdmin.executeQuery("SELECT * FROM users"));
System.out.println(dbAdmin.executeQuery("SELECT * FROM users"));
System.out.println(dbUser.executeQuery("SELECT * FROM orders"));
System.out.println(dbUser.executeQuery("SELECT * FROM orders"));
System.out.println(dbGuest.executeQuery("SELECT * FROM products"));
}
}
4. Phân tích các trường hợp ứng dụng Proxy Pattern
4.1. Kiểm soát truy cập
Ưu điểm: Giúp kiểm soát việc truy cập đến đối tượng, bảo vệ đối tượng khỏi truy cập trái phép.
Nhược điểm: Cần đảm bảo rằng proxy hoạt động chính xác và không gây ảnh hưởng đến hiệu năng hệ thống.
4.2. Lazy Initialization
Ưu điểm: Giúp trì hoãn việc khởi tạo đối tượng cho đến khi thực sự cần thiết, tiết kiệm tài nguyên hệ thống.
Nhược điểm: Cần quản lý trạng thái khởi tạo của đối tượng một cách cẩn thận.
4.3. Logging và Caching
Ưu điểm: Thêm chức năng ghi log hoặc caching mà không làm thay đổi đối tượng thực, giúp quản lý log và cache hiệu quả.
Nhược điểm: Cần đảm bảo rằng proxy không làm ảnh hưởng đến hiệu năng của hệ thống và quản lý cache một cách hiệu quả.
4.4. Remote Proxy
Ưu điểm: Giúp đại diện cho một đối tượng nằm trên một không gian địa chỉ khác, giúp truy cập đối tượng từ xa một cách dễ dàng.
Nhược điểm: Cần quản lý kết nối mạng và đảm bảo rằng proxy hoạt động chính xác trong môi trường phân tán.
5. Kết luận
Proxy Pattern là một mẫu thiết kế mạnh mẽ và linh hoạt, giúp kiểm soát truy cập, trì hoãn khởi tạo, ghi log, caching và đại diện cho các đối tượng từ xa trong Java. Việc hiểu và áp dụng đúng Proxy Pattern sẽ giúp lập trình viên xây dựng các hệ thống dễ bảo trì, dễ mở rộng và hiệu quả hơn.