Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.

Từ “proxy” có nghĩa là ủy quyền. Mục đích của design pattern proxy là chuyển hướng tất cả những truy cập trực tiếp đến một đối tượng cho một đối tượng được ủy quyền.

Problem

Giả sử chúng ta có một object sử dụng rất nhiều tài nguyên của hệ thống và thỉnh thoảng ta mới dùng nó, chẳng hạn object cho phép truy vấn dữ liệu (service object hay real object).

Ta có thể nghĩ đến việc sử dụng lazy initialization (khởi tạo chỉ khi nào sử dụng). Tuy nhiên, cách làm này khiến cho tất cả các client object đều phải chạy qua đoạn code khởi tạo và điều này dẫn đến vấn đề trùng lặp code.

Solution

Để giải quyết vấn đề trên, ta có thể tạo ra một proxy object có cùng interface với real object. Khi nhận request từ client object, proxy object sẽ chịu trách nhiệm tạo ra và gọi thực hiện các phương thức của real object.

Lợi thế của cách làm này là ta có thể thực thi thêm những tác vụ trước và sau các logic chính của real object. Ngoài ra, do có cùng interface với real object nên proxy object có thể được sử dụng bởi các client object.

Hơn thế nữa, proxy object còn giúp bảo vệ việc truy cập đến các phương thức của real object. Ví dụ: proxy class yêu cầu người dùng phải được xác thực trước thì mới có thể thực thi các phương thức truy cập database.

Analogy

Một sự tương tự trong thế giới thực của proxy object đó là thẻ tín dụng. Thẻ tín dụng sẽ được ủy quyền bởi tiền mặt và cả hai đều là phương thức thanh toán.

Người mua và người bán sẽ rất thuận lợi trong việc mua bán bởi vì họ không phải lo đến việc mang nhiều tiền và đếm thiếu tiền.

Structure

Cấu trúc của proxy pattern gồm những thành phần sau:

  1. Service interface: định nghĩa interface mà proxy sẽ implement.
  2. Service: là lớp cung cấp các logic chính.
  3. Proxy: là một lớp implement service interface và có một thuộc tính giúp lưu tham chiếu đến một real object.

Minh họa:

Implementation

Chúng ta sẽ triển khai chức năng cache hình ảnh của trang web bằng cách dùng proxy pattern. Cụ thể, nếu hình ảnh đã được khởi tạo thì ta sẽ hiển thị ra luôn thay vì khởi tạo lại.

Sơ đồ lớp:

Service Interface

Tạo ra một interface dùng chung cho real object và proxy object:

class Image {
public:
	virtual void show() = 0;
};

Real Subject

Tạo ra service class như sau:

class RealImage : public Image {
private:
	std::string _url;
 
public:
	RealImage(std::string url) {
		_url = url;
		std::cout << "Image loaded: " << _url << "\n";
	}
 
	void show() {
		std::cout << "Image showed: " << _url << "\n";
	}
};

Proxy

Proxy class sẽ kiểm tra sự tồn tại của tham chiếu. Nếu không tồn tại thì nó sẽ khởi tạo một real object.

class ProxyImage : public Image {
private:
	std::string _url;
	Image* _realImage = nullptr;
 
public:
	ProxyImage(std::string url) {
		_url = url;
		std::cout << "Image loaded: " << _url << "\n";
	}
 
	void show() {
		if (_realImage == nullptr)
			_realImage = new RealImage(_url);
		else
			std::cout << "Image is already existed: " << _url << "\n";
 
		_realImage->show();
	}
};

Usage

Gọi sử dụng như sau:

void TestProxy() {
	std::cout << "Init proxy image: \n";
	ProxyImage* proxyImage = new ProxyImage("https://refactoring.guru/favicon.ico");
 
	std::cout << "---\n";
	std::cout << "Call real service 1st: \n";
	proxyImage->show();
 
	std::cout << "---\n";
	std::cout << "Call real service 2st: \n";
	proxyImage->show();
}

Output sẽ là:

Init proxy image:
Image loaded: https://refactoring.guru/favicon.ico
---
Call real service 1st:
Image loaded: https://refactoring.guru/favicon.ico
Image showed: https://refactoring.guru/favicon.ico
---
Call real service 2st:
Image is already existed: https://refactoring.guru/favicon.ico
Image showed: https://refactoring.guru/favicon.ico

Applicability

Sử dụng khi cần:

  • Triển khai lazy initialization (virtual proxy) cho một đối tượng sử dụng nhiều tài nguyên.
  • Áp dụng access control (protection proxy)
  • Local execution của một remote service (remote proxy)
  • Ghi chú các request (logging proxy)
  • Cache kết quả request (caching proxy)

Pros and Cons

ProsCons
Client code không cần phải kiểm soát service objectCode trở nên phức tạp hơn
Kiểm soát được life-cycle của service objectPhản hồi từ service có thể có delay
Proxy chạy được kể cả khi service object chưa sẵn sàng
Open/closed: có thể thêm vào các proxy mới mà không làm thay đổi service hay client

Resources