Mẫu Thiết Kế Proxy: Điều Khiển Truy Cập Đối Tượng

Mẫu thiết kế Proxy là một trong những mẫu cấu trúc (Structural Pattern) trong lập trình hướng đối tượng. Mục đích chính của nó là cung cấp một đối tượng đại diện hoặc "placeholder" cho một đối tượng khác, nhằm kiểm soát hoặc bổ sung các hành vi trước/sau khi truy cập đối tượng gốc. Ví dụ, khi cần đo thời gian thực thi của một phương thức, proxy có thể ghi nhận thời điểm bắt đầu và kết thúc mà không cần sửa đổi mã nguồn của đối tượng thực.

Mẫu Proxy cho phép thực hiện các tác vụ như quản lý quyền truy cập, tải trễ (lazy loading) tài nguyên, hay đơn giản hóa giao diện tương tác.

Các ứng dụng phổ biến của mẫu Proxy bao gồm: proxy từ xa (remote proxy), proxy ảo (virtual proxy), proxy bảo vệ (protection proxy), proxy bộ đệm (cache proxy), ghi nhật ký hệ thống, tối ưu hóa tải ảnh, quản lý kết nối cơ sở dữ liệu, kiểm soát quyền hạn, và proxy động (dynamic proxy).

Cấu Trúc Mẫu

Mẫu Proxy thường được cấu thành từ ba thành phần chính:

  • Giao diện Chủ thể (Subject Interface): Đây là một giao diện hoặc lớp trừu tượng định nghĩa các phương thức chung mà cả đối tượng thực và đối tượng proxy sẽ triển khai. Khách hàng tương tác với đối tượng thông qua giao diện này.
  • Chủ thể Thực tế (Real Subject): Đây là đối tượng "gốc" mà proxy đại diện. Nó chứa logic nghiệp vụ chính và là đối tượng mà client thực sự muốn sử dụng. Nó phải triển khai giao diện Chủ thể Trừu tượng.
  • Đối tượng Proxy (Proxy Object): Đối tượng này chứa một tham chiếu đến Chủ thể Thực tế. Nó cũng triển khai cùng giao diện với Chủ thể Thực tế. Proxy có thể chặn các yêu cầu từ client, thực hiện các thao tác bổ sung (như kiểm tra quyền, ghi nhật ký, tải trễ) trước hoặc sau khi chuyển tiếp yêu cầu đến Chủ thể Thực tế.

Nguyên Lý Hoạt Động

Cách thức hoạt động của mẫu Proxy có thể được mô tả như sau:

  • Truy cập gián tiếp: Thay vì tương tác trực tiếp với đối tượng thực, client sẽ gửi yêu cầu đến đối tượng proxy thông qua giao diện chung.
  • Kiểm soát và bổ sung: Đối tượng proxy sẽ chặn yêu cầu này. Tại đây, nó có thể thực hiện các hành động bổ sung như xác thực người dùng, ghi nhật ký hoạt động, hoặc kiểm tra điều kiện trước khi quyết định có chuyển tiếp yêu cầu tới đối tượng thực hay không.
  • Tải trễ (Virtual Proxy): Trong trường hợp proxy ảo, đối tượng thực chỉ được khởi tạo khi nó thực sự cần thiết, giúp tiết kiệm tài nguyên hệ thống.
  • Các tính năng mở rộng: Proxy cũng có thể được dùng để triển khai các chức năng khác như quản lý bộ nhớ đệm (caching), xử lý lỗi, hoặc nén dữ liệu mà không làm thay đổi đối tượng thực.

Ví Dụ Mã Nguồn (Java)

Giao diện Chủ thể: MessageWriter.java

interface MessageWriter {
    void write(String message);
}

Chủ thể Thực tế: SimpleMessageWriter.java

class SimpleMessageWriter implements MessageWriter {
    @Override
    public void write(String message) {
        System.out.println(message);
    }
}

Đối tượng Proxy: BorderedMessageWriterProxy.java

class BorderedMessageWriterProxy implements MessageWriter {
    private SimpleMessageWriter realWriter;

    public BorderedMessageWriterProxy() {
        // Proxy tạo và quản lý đối tượng thực
        this.realWriter = new SimpleMessageWriter();
    }

    @Override
    public void write(String message) {
        System.out.println("====== BẮT ĐẦU NỘI DUNG ======");
        realWriter.write(message); // Ủy quyền cho đối tượng thực
        System.out.println("====== KẾT THÚC NỘI DUNG ======");
    }
}

Chương trình kiểm tra: ProxyDemo.java

public class ProxyDemo {
    public static void main(String[] args) {
        System.out.println("--- Sử dụng đối tượng proxy ---");
        MessageWriter proxyWriter = new BorderedMessageWriterProxy();
        proxyWriter.write("Đây là một đoạn văn bản được ghi thông qua proxy.");

        System.out.println("\n--- Sử dụng đối tượng thực (để so sánh) ---");
        MessageWriter simpleWriter = new SimpleMessageWriter();
        simpleWriter.write("Đây là đoạn văn bản được ghi trực tiếp, không qua proxy.");
    }
}

Giải Thích Mã Nguồn

Trong ví dụ minh họa trên, chúng ta đã xây dựng một cơ chế ghi thông báo ra console, trong đó mỗi thông báo được bao quanh bởi các đường viền phân tách. Chức năng này được thêm vào thông qua việc sử dụng mẫu Proxy.

Cụ thể, giao diện MessageWriter định nghĩa phương thức write. Lớp SimpleMessageWriter là đối tượng thực thi chức năng ghi thông báo cơ bản. Tuy nhiên, thay vì sửa đổi trực tiếp SimpleMessageWriter để thêm các đường viền, chúng ta tạo BorderedMessageWriterProxy. Lớp proxy này cũng triển khai MessageWriter và giữ một tham chiếu đến SimpleMessageWriter. Khi phương thức write của proxy được gọi, nó sẽ thực hiện các thao tác bổ sung (ghi đường viền "BẮT ĐẦU" và "KẾT THÚC") trước và sau khi gọi phương thức write của đối tượng thực.

Cách tiếp cận này cho phép mở rộng chức năng ghi nhật ký mà không cần thay đổi mã nguồn của lớp SimpleMessageWriter gốc. Điều này đặc biệt hữu ích khi đối tượng thực có cấu trúc phức tạp, chứa nhiều logic nghiệp vụ quan trọng. Việc trực tiếp sửa đổi mã nguồn có thể dẫn đến rủi ro phát sinh lỗi và tăng chi phí bảo trì. Mẫu Proxy cung cấp một giải pháp linh hoạt để kiểm soát và mở rộng hành vi của một đối tượng mà không can thiệp vào mã nguồn ban đầu.

Thẻ: Java design patterns Proxy Pattern structural patterns Object-Oriented Programming

Đăng vào ngày 30 tháng 8 lúc 13:41