NỘI DUNG BÀI VIẾT
Định nghĩa
* Đưa ra một đại diện, thay thế cho một đối tượng để kiếm soát quyền truy cập vào nó.
* Sử dụng mức độ gián tiếp để kiểm soát, phân tán hoặc truy cập tốt hơn.
* Thêm một wrapper (gói gọn các chi tiết xử lý) và delegation (cung cấp hàm để gọi đến các xử lý đó) để bảo mật các thành phần thực sự khỏi sự phức tạp
Vấn đề
Bạn cần phải hỗ trợ việc thiếu tài nguyên các object, bạn không muốn phải khởi tạọ các object cho đến khi chúng thực sự được yêu cầu từ phía client.
Thảo luận
Thiết kế một đại diện, hoặc ủy quyền đối tượng đó: khởi tạo đối tượng thực sự cần ở lần đầu tiên client yêu cầu, ghi nhớ nhận dạng của đối tượng đấy, và chuyển tiếp yêu cầu khởi tạo đến đối tượng thực sự này. Sau đó, tát cả các yêu cầu tiếp theo chỉ đơn giản là chuyển tiếp trục tiếp đến đối tượng đã được đóng gói
Proxy Pattern được áp dụng ở 4 trường hợp phổ biến sau:
1. Một proxy ảo được thay thế cho các đối tượng không dễ để tạo ra. Đối tượng thực sự chỉ được tạo khi client lần đầu yêu cầu/ truy cập đối tượng.
2. Một proxy từ xa cung cấp đại diện local cho một đối tượng ở trong một không gian địa chỉ khác nhau. Đây là những gì mà mã “stub” trong RPC và CORBA đưa ra.
3. Một proxy bảo vệ kiểm soát truy cập tới một đối tượng nhạy cảm chính. Đối tượng được đại diện kiểm tra người gọi có được quyền truy cập được yêu cầu trước khi chuyển tiếp yêu cầu.
4. Một proxy khôn ngoan can thiệp các hành động được thêm vào khi một đối tượng được phép truy cập. Điển hình như:
* Đếm số tham chiếu tới đối tượng thực vì vậy nó có thể giải phóng tự động khi không có nhiều tham chiếu (aka smart pointer).
* Nạp một đối tượng liên tục vào bộ nhớ khi nó được tham chiếu lần đầu tiên
* Kiểm tra xem đối tượng thực có bị khóa trước khi nó được truy cập để đảm bảo không có bất kỳ đối tượng nào có thể thay đổi nó.
Cấu trúc
Bằng cách định nghĩa một giao diện Subject, đối tượng Proxy đứng ở vị trí của RealSubject là minh bạch tới client.
Ví dụ
Đưa ra một đại diện, thay thế cho một đối tượng để kiếm soát quyền truy cập tới một đối tượng. Séc hoặc tờ phiếu ngân hàng là một proxy cho các khoản tiền trong tài khoản. Séc có thể được sử dụng để thay thế cho tiền để mua hàng và cuối cùng kiểm soát việc truy cập vào tiền mặt trong tài khoản của người dùng.
Kiểm tra
- Xác định thế mạnh hoặc các khía cạnh được thực hiện một cách tốt nhất dưới dạng wrapper hay surrogate (đại diện)
- Định nghĩa một giao diện sẽ làm cho proxy và thành phần ban đầu có thể hoán đổi cho nhau.
- Xem xét xác định một Factory có thể được đóng gói các giải pháp cho dù proxy hoặc các đối tượng gốc thực sự là kỳ vọng.
- Lớp wrapper giữ con trỏ tới lớp thật và thực hiện giao diện
- Con trỏ có thể được khởi tạo ở hàm tạo hoặc khi sử dụng lần đầu tiên
- Mỗi phương thức wrapper đóng góp thế mạnh, và ủy quyền tới các đối tượng wrappee
Kinh nghiệm thực tiễn
- Adapter cung cấp một giao diện khác cho subject của nó. Proxy cung cấp cùng giao diện đó. Decorator cung cấp một giao diện nâng cao hơn
- Decorator và Proxy có các mục đích khác nhau nhưng có cùng cấu trúc tương tự nhau. Chúng mô tả làm thế nào để đưa ra mức độ gián tiếp cho một đối tượng khác, và thực thi nắm giữ một tham chiếu đến đối tượng mà chúng chuyển tiếp yêu cầu.
Ví dụ áp dụng Template Proxy Design Pattern trong Java
Proxy design pattern
- Tạo một “wrapper” cho mục tiêu từ xa, hoặc xa xỉ, hoặc nhạy cảm
- Đóng gói sự phức tạp/ chi phí của mục tiêu trong wrapper
- Client xử lý, giao tiếp với wrapper.
- Ủy quyền tới muc tiêu.
- Hỗ trợ khả năng tương thích giữa wrapper và target, tạo một giao diện.
interface SocketInterface {
String readLine();
void writeLine(String str);
void dispose();
}
class SocketProxy implements SocketInterface {
// 1. Tạo một “wrapper” cho mục tiêu từ xa,
// hoặc xa xỉ, hoặc nhạy cảm
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public SocketProxy(String host, int port, boolean wait) {
try {
if (wait) {
// 2. Đóng gói sự phức tạp/ chi phí của mục tiêu trong wrapper
ServerSocket server = new ServerSocket(port);
socket = server.accept();
} else {
socket = new Socket(host, port);
}
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
} catch(IOException e) {
e.printStackTrace();
}
}
public String readLine() {
String str = null;
try {
str = in.readLine();
} catch( IOException e ) {
e.printStackTrace();
}
return str;
}
public void writeLine(String str) {
// 4. Ủy quyền tới muc tiêu
out.println(str);
}
public void dispose() {
try {
socket.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
public class ProxyDemo {
public static void main( String[] args ) {
// 3. Client xử lý, giao tiếp với wrapper
SocketInterface socket = new SocketProxy( "127.0.0.1", 8080, args[0].equals("first") ? true : false );
String str;
boolean skip = true;
while (true) {
if (args[0].equals("second") && skip) {
skip = !skip;
} else {
str = socket.readLine();
System.out.println("Receive - " + str);
if (str.equals(null)) {
break;
}
}
System.out.print( "Send ---- " );
str = new Scanner(System.in).nextLine();
socket.writeLine( str );
if (str.equals("quit")) {
break;
}
}
socket.dispose();
}
}
Đầu ra
Receive - GET / HTTP/1.1
Send ----
Receive - Host: localhost:8080
Send ----
Receive - Connection: keep-alive
Send ----
Receive - Cache-Control: max-age=0
Send ----
Receive - Upgrade-Insecure-Requests: 1
Send ----
Receive - User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36
Send ----
Receive - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Send ----
Receive - DNT: 1
Send ----
Receive - Accept-Encoding: gzip, deflate, sdch, br
Send ----
Receive - Accept-Language: ru,en-US;q=0.8,en;q=0.6,uk;q=0.4
Send ----
Receive - Cookie: JSESSIONID=32E01EAB6C4F723716A8E4A80BC5A381
Send ---- quit