Proxy Pattern là một mẫu thiết kế cấu trúc, nó cung cấp một đối tượng đại diện hoặc thay thế cho một đối tượng khác để kiểm soát truy cập vào đối tượng đó.
Proxy Pattern giới thiệu một cấp độ trung gian để truy cập đối tượng.
Một proxy từ xa có thể ẩn chi tiết của một đối tượng ở không gian địa chỉ khác.
Một proxy ảo có thể tối ưu hóa việc tạo đối tượng theo nhu cầu.
Một proxy bảo mật và hướng dẫn thông minh cho phép xử lý các nhiệm vụ khác khi truy cập vào đối tượng.
Ứng dụng
Khởi tạo chậm (Proxy ảo): Nếu bạn có một đối tượng dịch vụ nặng mà chỉ sử dụng đôi khi sẽ tiêu tốn tài nguyên hệ thống, bạn có thể sử dụng Proxy Pattern.
Kiểm soát truy cập (Proxy bảo mật): Nếu bạn chỉ muốn cho phép một số khách hàng cụ thể sử dụng đối tượng dịch vụ, trong đó đối tượng có thể là một phần quan trọng của hệ thống hoạt động, bạn có thể sử dụng Proxy Pattern.
Triển khai dịch vụ từ xa (Proxy từ xa): Áp dụng khi đối tượng dịch vụ nằm trên một máy chủ từ xa.
Cấu trúc
Giải thích cấu trúc
Giao diện dịch vụ (Service Interface) khai báo giao diện dịch vụ. Proxy phải tuân thủ giao diện này để giả mạo thành đối tượng dịch vụ.
Lớp Dịch vụ (Service) cung cấp một số logic kinh doanh hữu ích.
Lớp Proxy chứa một biến thành viên tham chiếu đến đối tượng dịch vụ. Proxy hoàn thành nhiệm vụ của nó (như trì hoãn khởi tạo, ghi nhật ký, kiểm soát truy cập và bộ nhớ cache) và sau đó chuyển yêu cầu cho đối tượng dịch vụ. Thông thường, Proxy quản lý toàn bộ vòng đời của đối tượng dịch vụ.
Khách hàng (Client) có thể tương tác với dịch vụ hoặc proxy thông qua cùng một giao diện, vì vậy bạn có thể sử dụng proxy trong bất kỳ mã nào cần đối tượng dịch vụ.
Mẫu code cấu trúc
Subject: Định nghĩa giao diện chung cho RealSubject và Proxy, điều này cho phép sử dụng Proxy ở bất kỳ nơi nào sử dụng RealSubject.
RealSubject: Định nghĩa đối tượng thực sự mà Proxy đại diện.
Proxy: Lưu trữ một tham chiếu để cho phép Proxy truy cập vào đối tượng và cung cấp một giao diện giống như giao diện của Subject, điều này cho phép Proxy được sử dụng để thay thế đối tượng.
Pseudocode
Ví dụ này mô tả cách sử dụng Proxy Pattern để thêm việc khởi tạo chậm và bộ nhớ cache vào thư viện bên thứ ba Netflix (được gọi là TV trong mã giả). Thư viện này cung cấp một lớp tải xuống video. Tuy nhiên, lớp này có hiệu suất rất thấp. Nếu chương trình khách hàng yêu cầu cùng một video nhiều lần, thư viện sẽ tải lại video đó mỗi lần, không lưu trữ file đã tải lần đầu để sử dụng lại.
Lớp Proxy triển khai cùng một giao diện với tải xuống ban đầu và chuyển tiếp tất cả công việc cho tải xuống ban đầu. Tuy nhiên, lớp Proxy sẽ lưu trữ tất cả các bản ghi tải xuống và nếu chương trình yêu cầu cùng một tệp tin nhiều lần, nó sẽ trả về tệp tin được lưu trữ trong bộ nhớ cache.
Ví dụ
Ví dụ sử dụng: Mặc dù Proxy Pattern không phổ biến trong hầu hết các chương trình Java, nhưng nó vẫn rất hữu ích trong một số trường hợp đặc biệt. Khi bạn muốn thêm hành vi bổ sung cho một đối tượng hiện có mà không cần sửa đổi mã khách hàng, mẫu này là không thể thay thế.
Dưới đây là một số ví dụ về Proxy Pattern trong thư viện chuẩn Java:
Cách nhận biết: Proxy Pattern sẽ chuyển giao tất cả công việc thực tế cho một số đối tượng khác. Trừ khi Proxy là một lớp con của một dịch vụ, mỗi phương thức Proxy cuối cùng sẽ tham chiếu đến một đối tượng dịch vụ.
Ví dụ: Sử dụng annotation, reflect và proxy để loại bỏ mã lặp lại
Giả sử ngân hàng cung cấp một số giao diện API, yêu cầu chúng ta không sử dụng JSON để tuần tự hóa tham số, mà là ghép các tham số lại thành một chuỗi lớn.
Theo thứ tự được cung cấp trong tài liệu API của ngân hàng, chúng ta ghép tất cả các tham số thành dữ liệu có độ dài cố định, sau đó ghép chúng lại thành một chuỗi hoàn chỉnh. Vì mỗi loại tham số có độ dài cố định, nếu không đạt được độ dài, chúng ta cần thực hiện xử lý điền đầy:
Tham số kiểu chuỗi không đạt đến độ dài yêu cầu sẽ được điền bằng gạch dưới bên phải, tức là nội dung chuỗi sẽ nằm bên trái;
Tham số kiểu số không đạt đến độ dài yêu cầu sẽ được điền bằng số 0 bên trái, tức là số thực tế sẽ nằm bên phải;
Biểu diễn số tiền cần làm tròn xuống 2 chữ số thập phân, tính theo đơn vị xuống còn phần xuống, và cũng được điền bằng số 0 bên trái như kiểu số.
Tất cả các tham số đều được thực hiện phép MD5 để tạo chữ ký (trong ví dụ này, chúng ta không xem xét việc thêm salt).
Phiên bản có vấn đề
Trong phiên bản mã trên, chúng ta gặp các vấn đề sau:
Có sự lặp lại trong xử lý logic cho ba loại dữ liệu chuẩn, chỉ cần một chút không cẩn thận là có thể gây ra lỗi;
Quá trình xử lý bao gồm việc nối chuỗi, tạo chữ ký và gửi yêu cầu được lặp lại trong tất cả các phương thức;
Các loại tham số thực tế của phương thức và thứ tự không nhất thiết phải giống với yêu cầu giao diện, dễ gây lỗi;
Mã cứng cho từng tham số, không thể kiểm tra một cách rõ ràng. Nếu có hàng chục hoặc hàng trăm tham số, khả năng gây lỗi rất cao.
Phiên bản tối ưu
Để giải quyết các vấn đề trong phiên bản mã trên, chúng ta có thể sử dụng Proxy Pattern kết hợp với annotation, refect và proxy để loại bỏ mã lặp lại và tăng tính linh hoạt của mã. Dưới đây là một phiên bản tối ưu hơn:
【Annotation 1】
【Annotation 2】
【Lớp trừu tượng】
【Lớp dịch dịch vụ】
【Annotation cho giao diện API 1】
【Annotation cho giao diện API 2】
Mối quan hệ với các mẫu thiết kế khác
Adapter Pattern cung cấp các giao diện khác nhau cho các đối tượng được đóng gói, Proxy Pattern cung cấp cùng một giao diện cho đối tượng, và Decorator Pattern cung cấp giao diện được tăng cường cho đối tượng.
Facade Pattern và Proxy Pattern có điểm tương đồng trong việc lưu trữ một thực thể phức tạp và tự khởi tạo nó. Tuy nhiên, Proxy và đối tượng dịch vụ của nó tuân theo cùng một giao diện, cho phép nó và đối tượng dịch vụ có thể hoán đổi, điều này khác với Facade.
Decorator Pattern và Proxy Pattern có cấu trúc tương tự nhau, nhưng mục đích của chúng rất khác nhau. Cả hai mẫu này đều dựa trên nguyên tắc tổ hợp, tức là một đối tượng nên giao phần công việc cho một đối tượng khác. Sự khác biệt giữa hai mẫu này là Proxy thường tự quản lý vòng đời của đối tượng dịch vụ của nó, trong khi Decorator luôn được khách hàng kiểm soát quá trình tạo ra nó.