Proxy Pattern là một mẫu thiết kế thuộc nhóm Structural Patterns (mẫu cấu trúc), được sử dụng để cung cấp một đại diện hoặc thay thế cho một đối tượng khác nhằm kiểm soát truy cập đến đối tượng đó. Proxy giúp quản lý quyền truy cập, thêm chức năng bổ sung hoặc thực hiện các hành động trước và sau khi tương tác với đối tượng thật.

Cơ bản về Proxy Pattern

Trong Proxy Pattern, có hai đối tượng chính:

  • Real Subject (Đối tượng thực sự): Đối tượng mà Proxy đại diện và thực hiện các tác vụ chính.
  • Proxy: Đối tượng đóng vai trò trung gian giữa client (người sử dụng) và Real Subject. Nó kiểm soát cách mà client tương tác với đối tượng thật bằng cách kiểm tra, quản lý hoặc điều chỉnh trước khi gửi yêu cầu đến Real Subject.

Proxy có thể thực hiện các nhiệm vụ như:

  • Kiểm soát truy cập: Proxy có thể kiểm soát quyền truy cập vào đối tượng thật, ví dụ như chỉ cho phép một số người dùng cụ thể truy cập vào hệ thống.
  • Tối ưu hóa tài nguyên: Proxy có thể trì hoãn việc khởi tạo đối tượng thật cho đến khi nó thực sự cần thiết (được gọi là Lazy Initialization).
  • Cung cấp chức năng bổ sung: Proxy có thể thêm các chức năng bổ sung như lưu log, đo lường hiệu suất hoặc thực hiện xác thực trước khi gọi đến đối tượng thật.

Khi nào nên sử dụng Proxy Pattern?

Bạn nên sử dụng Proxy Pattern trong các trường hợp sau:

  • Khi bạn cần kiểm soát quyền truy cập vào một đối tượng nhất định, ví dụ như chỉ cho phép một số người dùng được truy cập tài nguyên.
  • Khi bạn muốn tối ưu hóa việc sử dụng tài nguyên, ví dụ như chỉ khởi tạo đối tượng thật khi cần thiết hoặc bảo vệ các tài nguyên đắt đỏ (như kết nối mạng, cơ sở dữ liệu).
  • Khi bạn muốn thêm chức năng bổ sung vào các hoạt động của đối tượng thật mà không làm thay đổi mã nguồn của nó, ví dụ như ghi log, kiểm tra hiệu năng hoặc bảo mật.

Các loại Proxy phổ biến

  1. Virtual Proxy: Trì hoãn việc khởi tạo đối tượng thật cho đến khi cần thiết, giúp giảm thiểu việc sử dụng tài nguyên.
  2. Remote Proxy: Cung cấp một proxy để tương tác với đối tượng ở một máy khác trong hệ thống phân tán.
  3. Protection Proxy: Kiểm soát quyền truy cập vào đối tượng, thường dùng trong các hệ thống bảo mật.
  4. Smart Proxy: Thực hiện các hành động bổ sung như lưu log, kiểm tra quyền hoặc lưu cache khi client truy cập đối tượng thật.

Ví dụ về Proxy Pattern

1. Ví dụ đơn giản

// Interface Subject
public interface Image {
    void display();
}

// Real Subject: Đối tượng thực sự
public class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image from disk: " + fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + fileName);
    }
}

// Proxy: Đối tượng đại diện
public class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        // Chỉ khởi tạo đối tượng thật khi cần thiết (lazy initialization)
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

// Sử dụng Proxy Pattern
public class Main {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_image.jpg");

        // Tải ảnh từ đĩa chỉ khi cần hiển thị
        System.out.println("Image will be displayed now.");
        image.display();  // Lần đầu tiên, ảnh được tải từ đĩa

        System.out.println("Image will be displayed again.");
        image.display();  // Lần sau, ảnh không cần phải tải lại
    }
}

Phân tích ví dụ

  1. Image là interface đại diện cho đối tượng hình ảnh mà cả RealImageProxyImage đều triển khai.
  2. RealImage là đối tượng thực sự thực hiện việc tải và hiển thị hình ảnh.
  3. ProxyImage là proxy đóng vai trò đại diện cho RealImage. Nó sẽ trì hoãn việc khởi tạo đối tượng thật cho đến khi phương thức display được gọi. Điều này giúp tối ưu hóa hiệu suất khi đối tượng thật chỉ được khởi tạo khi cần thiết.
  4. Trong hàm main, đối tượng ProxyImage sẽ kiểm soát việc khởi tạo và hiển thị hình ảnh. Khi ảnh được hiển thị lần đầu tiên, nó sẽ được tải từ đĩa. Tuy nhiên, khi hiển thị lại, đối tượng thật không cần phải tải lại ảnh, giúp tối ưu hóa tài nguyên.

2. Ví dụ về Protection Proxy

Giả sử bạn có một hệ thống bảo mật và chỉ cho phép một số người dùng truy cập vào một tài nguyên cụ thể. Bạn có thể sử dụng Proxy Pattern để kiểm soát quyền truy cập này.

// Interface Subject
public interface Database {
    void access(String userRole);
}

// Real Subject: Đối tượng thực sự
public class RealDatabase implements Database {
    @Override
    public void access(String userRole) {
        System.out.println("Accessing database...");
    }
}

// Proxy: Đối tượng đại diện
public class DatabaseProxy implements Database {
    private RealDatabase realDatabase = new RealDatabase();

    @Override
    public void access(String userRole) {
        if (userRole.equals("ADMIN")) {
            realDatabase.access(userRole);
        } else {
            System.out.println("Access denied: You do not have the required permissions.");
        }
    }
}

// Sử dụng Proxy Pattern
public class Main {
    public static void main(String[] args) {
        Database database = new DatabaseProxy();

        System.out.println("User with role USER:");
        database.access("USER");  // Quyền truy cập bị từ chối

        System.out.println("User with role ADMIN:");
        database.access("ADMIN");  // Truy cập thành công
    }
}

Phân tích ví dụ

  1. Database là interface đại diện cho đối tượng cơ sở dữ liệu.
  2. RealDatabase là đối tượng thực sự thực hiện truy cập vào cơ sở dữ liệu.
  3. DatabaseProxy là proxy bảo vệ quyền truy cập vào cơ sở dữ liệu. Proxy sẽ kiểm tra vai trò người dùng trước khi cho phép truy cập vào đối tượng thật.
  4. Trong hàm main, proxy sẽ kiểm tra vai trò của người dùng trước khi cho phép họ truy cập vào cơ sở dữ liệu.

Lợi ích của Proxy Pattern

  1. Kiểm soát truy cập: Proxy có thể kiểm soát quyền truy cập vào đối tượng thật, ví dụ như thêm chức năng xác thực hoặc kiểm tra quyền.
  2. Tối ưu hóa tài nguyên: Proxy có thể trì hoãn việc khởi tạo đối tượng thật, giúp tối ưu hóa tài nguyên, đặc biệt khi đối tượng thật có chi phí khởi tạo lớn.
  3. Bổ sung chức năng: Proxy có thể thêm các chức năng bổ sung mà không cần sửa đổi mã nguồn của đối tượng thật, ví dụ như ghi log, theo dõi hiệu suất, hoặc xử lý cache.

Kết luận

Proxy Pattern là một mẫu thiết kế mạnh mẽ giúp bạn kiểm soát truy cập và tối ưu hóa tài nguyên khi làm việc với các đối tượng phức tạp. Nó giúp tách biệt logic quản lý truy cập ra khỏi đối tượng thật và dễ dàng bổ sung các chức năng bổ sung mà không làm thay đổi cấu trúc hiện tại của đối tượng.