STDIO
Tìm kiếm gần đây
    • Nội dung
    • QR Code
    • 0
    • 0
    • Sao chép

    Design Pattern: Proxy Pattern

    Tổng quan về Proxy Pattern và hiện thực Proxy Pattern dựa trên ngôn ngữ C/C++.
    18/10/2017
    27/08/2020
    5 phút đọc
    Design Pattern: Proxy Pattern

    Bài viết cung cấp cái nhìn tổng quan về Proxy Pattern, các trường hợp thường sử dụng và hiện thực Proxy Pattern dựa trên ngôn ngữ C/C++.

    Proxy Design Pattern là gì?

    Proxy Pattern là một những mẫu thiết kế thuộc nhóm Structural design patterns.

    Proxy là một đối tượng sẽ đại diện cho một đối tượng khác.

    Tùy theo yêu cầu bài toán mà áp dụng Proxy một cách linh hoạt, với mục đích:

    • Kiểm soát quyền truy xuất các phương thức của đối tượng.
    • Bổ sung thêm chức năng trước khi thực thi phương thức.
    • Tạo ra đối tượng mới có chức năng nâng cao hơn đối tượng ban đầu.
    • Giảm chi phí khi có nhiều truy cập vào đối tượng có chi phí khởi tạo ban đầu lớn.

    Kiến trúc Proxy Pattern

    Kiến trúc Proxy Pattern

    Proxy Pattern gồm 3 phần chính:

    • Interface (hoặc abstract class) ISubject: gọi thực hiện các phương thức từ Client. 
    • Lớp CRealSubject: định nghĩa các phương thức để thực thi.
    • Lớp CSubjectProxy: thực hiện các thao tác như kiểm tra, thêm tính năng trước khi gọi lớp CRealSubject để thực thi phương thức.

    Các loại Proxy Pattern và trường hợp áp dụng

    • Virtual Proxy: khi có nhiều lượt truy xuất vào một đối tượng cấu trúc phức tạp, chứa dữ liệu lớn (hình ảnh, video,..), cần tạo ra một Proxy để đại diện cho đối tượng đó. Đối tượng chỉ được tạo ở lần truy xuất đầu tiên, những lần tiếp theo chỉ cần tái sử dụng lại mà không cần khởi tạo, tránh trường hợp sao chép ra nhiều đối tượng, tiết kiệm tài nguyên.
    • Protection Proxy: muốn kiểm tra một yêu cầu có quyền truy cập vào một nội dung hay không.
    • Remote Proxy: muốn thực thi một phương thức của đối tượng đang tồn tại khác vùng địa chỉ hoặc ở máy tính khác.

    Virtual Proxy: Giảm chi phí khởi tạo nhiều đối tượng

    Virtual Proxy

    Hiện thực

    #include <stdio.h>
    
    // abstract class 
    class Image
    {
    public:
    	virtual void displayImage() = 0;
    };
    
    // real subject
    class CRealImage : public Image
    {
    private:
    	char* m_path;
    
    public:
    	CRealImage(char* path)
    	{
    		m_path = path;
    		loadImage();
    	}
    
    	~CRealImage()
    	{
    		delete m_path;
    	}
    
    	void displayImage()
    	{
    		printf("Display image! %s\n", m_path);
    	}
    
    	void loadImage()
    	{
    		printf("Load image! %s\n", m_path);
    	}
    
    };
    
    // subject proxy
    class CImageVirtualProxy : public Image
    {
    private:
    	CRealImage* m_realImage;
    	char* m_path;
    
    public:
    	CImageVirtualProxy(char* path)
    	{
    		m_path = path;
    		m_realImage = NULL;
    	}
    
    	~CImageVirtualProxy()
    	{
    		delete m_path, m_realImage;
    	};
    
    	void displayImage()
    	{
    		if (m_realImage == NULL)
    			m_realImage = new CRealImage(m_path);
    
    		m_realImage->displayImage();
    	}
    };
    
    int main()
    {
    	// không sử dụng proxy
    	Image* img1 = new CRealImage("sample/veryLargeImage1.png");
    	Image* img2 = new CRealImage("sample/veryLargeImage2.png");
    	Image* img3 = new CRealImage("sample/veryLargeImage3.png");
    
    	img1->displayImage();
    
    	printf("\n");
        
    	// sử dụng proxy tiết kiệm chi phí
    	Image* imgProxy1 = new CImageVirtualProxy("sample/veryLargeImage1.png");
    	Image* imgProxy2 = new CImageVirtualProxy("sample/veryLargeImage2.png");
    	Image* imgProxy3 = new CImageVirtualProxy("sample/veryLargeImage3.png");
    
    	imgProxy1->displayImage();
    	imgProxy1->displayImage();
    
    	return 0;
    }

    Output:

    Load image! sample/veryLargeImage1.png
    Load image! sample/veryLargeImage2.png
    Load image! sample/veryLargeImage3.png
    Display image! sample/veryLargeImage1.png

    Load image! sample/veryLargeImage1.png
    Display image! sample/veryLargeImage1.png
    Display image! sample/veryLargeImage1.png

    Theo cách không áp dụng Proxy, đối tượng sẽ tự động gọi phương thức loadImage() khi tạo mới một đối tượng, tiêu tốn tài nguyên không cần thiết cho dù sau này có thực thi phương thức displayImage() hay không.

    Áp dụng Virtual Proxy để tiết kiệm tài nguyên, khi thực hiện displayImage() sẽ tự động thực thi phương thức loadImage() trước và chỉ load một lần cho lời gọi displayImage() đầu tiên. 

    Có thể định nghĩa một phương thức loadImage() tách biệt, khi khởi tạo đối tượng không cần thực hiện loadImage(), phương thức loadImage() khi định nhĩa có quyền truy cập là public để chúng ta gọi bất kỳ nơi đâu trước khi thực thi phương thức displayImage(). Nhưng có điều bất lợi là phải cố gắng nhớ thực hiện việc loadImage() trước khi thực thi phương thức displayImage(), gây ra nhiều sai sót khi thao tác trên nhiều đối tượng ở nhiều file khác nhau.

    Trong trường hợp này, việc áp dụng Virtual Proxy an toàn và dễ kiểm soát hơn.

    Protection Proxy: Quản lý quyền tải tập tin

    Hiện thực

    #include <stdio.h>
    
    enum MEMBERSHIP_TYPE {
    	VIP,
    	FREE,
    	FREEDOM
    };
    
    // abstract class
    class ActionAccount
    {
    public:
    	virtual void downMaxSpeed() = 0;
    	virtual void downLimitSpeed() = 0;
    };
    
    // real subject
    class CFileAccount : public ActionAccount
    {
    private:
    	char*			m_userName;
    	MEMBERSHIP_TYPE		m_membership_type;
    
    public:
    	CFileAccount(char* userName, MEMBERSHIP_TYPE membership_type = FREEDOM)
    	{
    		m_userName = userName;
    		m_membership_type = membership_type;
    	}
    
    	~CFileAccount()
    	{
    		delete m_userName;
    	}
    
    	MEMBERSHIP_TYPE getMembershipType()
    	{
    		return m_membership_type;
    	}
    
    	void downMaxSpeed()
    	{
    		printf("%s :: download file MAX SPEED success!\n", m_userName);
    	}
    
    	void downLimitSpeed()
    	{
    		printf("%s :: download file LIMIT SPEED success!\n", m_userName);
    	}
    
    };
    
    // subject proxy
    class CFileAccountProxy : public ActionAccount
    {
    private:
    	CFileAccount*		m_fileAccount;
    	MEMBERSHIP_TYPE		m_membership_type;
    	char*				m_userName;
    
    public:
    	CFileAccountProxy(char* userName, MEMBERSHIP_TYPE membership_type)
    	{
    		m_fileAccount = NULL;
    		m_membership_type = membership_type;
    		m_userName = userName;
    	}
    
    	~CFileAccountProxy()
    	{
    		delete m_fileAccount, m_userName;
    	}
    
    	void downMaxSpeed()
    	{
    		if (m_fileAccount == NULL)
    			m_fileAccount = new CFileAccount(m_userName, m_membership_type);
    
    		if (m_fileAccount->getMembershipType() == VIP)
    			m_fileAccount->downMaxSpeed();
    		else
    			printf("%s :: download fail!\n", m_userName);
    	}
    
    	void downLimitSpeed()
    	{
    		if (m_fileAccount == NULL)
    			m_fileAccount = new CFileAccount(m_userName, m_membership_type);
    
    		// VIP member is king
    		if (m_fileAccount->getMembershipType() == VIP)
    		{
    			m_fileAccount->downMaxSpeed();
    			return;
    		}
    
    		if (m_fileAccount->getMembershipType() == FREE)
    			m_fileAccount->downLimitSpeed();
    		else
    			printf("%s :: download fail!\n", m_userName);
    	}
    };
    
    int main()
    {
    	// không áp dụng proxy
    	ActionAccount* actionAccount = new CFileAccount("FREEDOM");
    	actionAccount->downMaxSpeed();
    
    	// sử dụng proxy giới hạn quyền truy cập
    	ActionAccount* actionAccountProxyVip = new CFileAccountProxy("STDIO Training", VIP);
    	actionAccountProxyVip->downMaxSpeed();
    
    	ActionAccount* actionAccountProxyFree = new CFileAccountProxy("OTHER Training", FREE);
    	actionAccountProxyFree->downMaxSpeed();
    
    	return 0;
    }

    Output:

    FREEDOM :: download file MAX SPEED success!
    STDIO Training :: download file MAX SPEED success!
    OTHER Training :: download fail!

    Áp dụng Protection Proxy giúp dễ dàng kiểm soát và tùy biến lại quyền truy xuất để thực thi phương thức của một đối tượng.

    Thông thường, có thể đưa công đoạn kiểm tra vào chính lớp CFileAccount nhưng điều đó không hiệu quả cho việc quản lý và tái sử dụng khi phát triển phần mềm quy mô lớn gồm nhiều đối tượng và chức năng.

    Việc đưa các mã kiểm tra vào Proxy giúp trực quan, mỗi lớp đều đảm nhận một nhiệm vụ riêng biệt.

    • Lớp CFileAccount chỉ lưu thông tin và định nghĩa các hành động của một tài khoản.
    • Lớp CFileAccountProxy đảm nhận công việc quản lý quyền tải file của một tài khoản.
    0 Bình luận

    Đề xuất

      Khi bạn nhấn vào sản phẩm do chúng tôi đề xuất và mua hàng, chúng tôi sẽ nhận được hoa hồng. Điều này hỗ trợ chúng tôi có thêm kinh phí tạo nhiều nội dung hữu ích. Tìm hiểu thêm.
      STDIO

      Trang chính

      Công ty TNHH STDIO

      30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
      +84 28.36205514 - +84 942.111912
      developer@stdio.vn

      383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
      Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

      Đã thông báo Bộ Công Thương
      ©STDIO, 2013 - 2020