Hướng dẫn Java Design Pattern – Proxy

Khi cần điều khiển truy nhập tới một đối tượng được thực hiện từ công cuộc khởi tạo nó cho tới khi thực sự cần sử dụng nó. Hoặc cần bảo vệ quyền truy xuất vào các cách thức của object thực. Trong trường hợp như thế, ta nên dùng mẫu kiến trúc Proxy.

Proxy Pattern là gì?

Provide α surrogate or placeholder for another object to control access to it.

Proxy Pattern là một trong những Pattern thuộc nhóm kết cấu (Structural Pattern).

Proxy có nghĩa là “ủy quyền” hay “đại diện”. Mục đích xây dựng Proxy pattern cũng cũng chính vì mong muốn tạo thành một đối tượng sẽ ủy quyền, thay thế cho một đối tượng khác.

Proxy Pattern là mẫu kiến trúc mà ở đó toàn bộ các truy cập trực tiếp đó một đối tượng nào đó sẽ được chuyển hướng về phía một đối tượng trung gian (Proxy Class). Mẫu Proxy (người đại diện) đại diện cho một đối tượng khác thực thi các cách thức, cách thức đó có thể được khái niệm lại cho phù phù hợp với múc đích sử dụng.

Để dễ dàng hơn bạn có thể nghĩ đến định nghĩa HTTP proxy trong mạng laptop, nó là một gateway giữa trình duyệt (client) & máy chủ (server). HTTP proxy giúp chuyên sâu thử nghiệm người dùng, tăng tốc với lưu đệm các dữ liệu, loại bỏ các trang tiếp thị, hạn chế các vùng thông tin được xem… Proxy Pattern cũng có chung một mục đích như với HTTP proxy.

Proxy Pattern có cách gọi khác là Surrogate (thay thế) hoặc Placeholder (trình giữ chỗ).

Phân loại Proxy

  • Virtual Proxy : Virtual Proxy tạo thành một đối tượng trung gian mỗi khi có yêu cầu tại thời điểm thực thi áp dụng, nhờ đó làm tăng năng suất của áp dụng.
  • Protection Proxy : Phạm vi truy cập của các client khác nhau sẽ khác nhau. Protection proxy sẽ kiểm soát các quyền truy cập của client khi có một dịch vụ được yêu cầu.
  • Remote Proxy : Client truy cập qua Remote Proxy để chiếu tới một đối tượng được bảo về nằm bên ngoài áp dụng (trên cùng máy hoặc máy khác).
  • Monitor Proxy : Monitor Proxy sẽ seting các bảo mật trên đối tượng cần bảo vệ, ngăn không cho client truy cập một số trường trọng yếu của đối tượng. Có thể theo dõi, giám sát, ghi log việc truy cập, sử dụng đối tượng.
  • Firewall Proxy : bảo vệ đối tượng chối từ các yêu cầu xuất xứ từ các client không tin tưởng.
  • Cache Proxy : Phân phối không gian lưu trữ tạm thời cho các kết quả trả về từ đối tượng nào đó, kết quả này sẽ được tái sử dụng cho các client chia sẻ chung một yêu cầu gửi đến. Loại Proxy này hoạt động cũng giống như Flyweight Pattern.
  • Smart Reference Proxy : Là nơi kiểm tra các hoạt động bổ sung mỗi khi đối tượng được tham chiếu.
  • Synchronization Proxy : Bảo đảm nhiều client có thể truy cập vào cùng một đối tượng mà không gây ra xung đột. Khi một client nào đó chiếm hữu khóa khá lâu làm cho số lượng các client trong danh mục hàng đợi cứ tăng trưởng, & do vậy hoạt động của hệ thống bị ngừng trệ, có thể dẫn đến hiện tượng “tắc nghẽn”.
  • Sao chép-On-Write Proxy : Loại này bảo đảm rằng sẽ không có client nào phải chờ vô thời hạn. Sao chép-On-Write Proxy là một kiến trúc rất cầu kỳ.
Xem Thêm  Cách tạo bảng trong studio quản lý máy chủ sql (Hướng dẫn chi tiết) - tạo bảng trong studio quản lý sql

Setup Proxy Pattern như vậy nào?

Proxy Pattern có những đặc điểm giống nhau sau đây:

  • Phân phối mức truy cập gián tiếp vào một đối tượng.
  • Tham chiếu vào đối tượng đích & chuyển tiếp các yêu cầu đến đối tượng đó.
  • Cả Proxy & đối tượng đích đều kế thừa hoặc thực thi chung một lớp giao diện. Mã máy dịch cho lớp giao diện thường “nhẹ” hơn các lớp rõ ràng & do vậy có thể giảm được thời gian tải dữ liệu giữa server & client.

Các thành phần gia nhập vào mẫu Proxy Pattern:

  • Subject : là một interface khái niệm các phương thực để giao tiếp với client. Đối tượng này xác nhận giao diện chung cho RealSubject & Proxy để Proxy có thể được sử dụng bất cứ đâu mà RealSubject đợi mong.
  • Proxy : là một class sẽ thực hiện các bước kiểm soát & gọi tới đối tượng của class service thật để thực hiện các thao tác sau khoảng thời gian kiểm soát. Nó duy trì một tham chiếu đến RealSubject để Proxy có thể truy cập nó. Nó cũng thực hiện các giao diện cũng giống như RealSubject để Proxy có thể được sử dụng thay cho RealSubject. Proxy cũng điều khiển truy cập vào RealSubject & có thể tạo hoặc xóa đối tượng này.
  • RealSubject : là một class service sẽ thực hiện các thao tác thực sự. Đây là đối tượng chính mà proxy đại diện.
  • Client : Đối tượng cần sử dụng RealSubject nhưng thông qua Proxy.

Chẳng hạn Virtual Proxy

Trì hoãn việc tạo thành real subject bên trong proxy class. Chỉ đến khi cần, proxy class mới thật sự khởi tạo real class. Loại Proxy này làm việc theo chế độ Lazy Loading.

Lazy Loading là một định nghĩa mà áp dụng trì hoãn việc tải các đối tượng cho đến thời điểm mà người dùng cần nó. Nói một cách dễ dàng là tải theo yêu cầu của người dùng chứ không phải tải đối tượng không thiết yếu. Lợi nhuận của việc đó là giảm bớt số lượng yêu cầu, giảm bớt số lượng tài nguyên thừa cần tải cho tới khi người dùng cần đến chúng thực sự. Tất cả chúng ta đã thấy điều này qua chế độ Lazy load của Hibernate.

Nó khắc phục vấn đề rất lớn về năng suất, nguyên nhân là vì proxy class có ngân sách khởi tạo rất ít, việc duy trì nó không mất nhiều tài nguyên hệ thống. Trong lúc đó Real class thường rất tốn ngân sách , chính vì thế với virtual proxies, chỉ bao giờ thiết yếu, real class mới được khởi tạo.

Xem Thêm  BẢNG TRUNCATE (Transact-SQL) - Máy chủ SQL - làm trống một bảng sql

Chẳng hạn một trang web hiển thị ảnh, có thể có rất nhều ảnh trên một trang hay một ảnh được hiển thị nhiều lần. Trường hợp này tất cả chúng ta chỉ cần load ảnh khi nó cần hiển thị (khi ta scroll tới nơi đặt image) hoặc là nó chưa được load (không như các trang web truyền thống là load hình ngay khi load trang web, nó rất tốn tài nguyên & thỉnh thoảng không thiết yếu do người dùng không scroll tới nơi đặt image, có thể load 1 ảnh nhiều lần).

Image.java


package com.gpcoder.patterns.structural.proxy.virtual;

public interface Image {

	void showImage();

}

RealImage.java


package com.gpcoder.patterns.structural.proxy.virtual;

public class RealImage implements Image {
	private String url;

	public RealImage(String url) {
		this.url = url;
		System.out.println("Image loaded: " + this.url);
	}

	@Override
	public void showImage() {
		System.out.println("Image showed: " + this.url);
	}
}

ProxyImage.java


package com.gpcoder.patterns.structural.proxy.virtual;

public class ProxyImage implements Image {
	private Image realImage;
	private String url;

	public ProxyImage(String url) {
		this.url = url;
		System.out.println("Image unloaded: " + this.url);
	}

	@Override
	public void showImage() {
		if (realImage == null) {
			realImage = new RealImage(this.url);
		} else {
			System.out.println("Image already existed: " + this.url);
		}
		realImage.showImage();
	}
}

Client.java


package com.gpcoder.patterns.structural.proxy.virtual;

public class Client {
	public static void main(String[] args) {
		System.out.println("Init proxy image: ");
		ProxyImage proxyImage = new ProxyImage("http://gpcoder.com/favicon.ico");
		
		System.out.println("---");
		System.out.println("Call real service 1st: ");
		proxyImage.showImage();
		
		System.out.println("---");
		System.out.println("Call real service 2nd: ");
		proxyImage.showImage();
	}
}

Output của chương trình:


Init proxy image: 
Image unloaded: http://gpcoder.com/favicon.ico
---
Call real service 1st: 
Image loaded: http://gpcoder.com/favicon.ico
Image showed: http://gpcoder.com/favicon.ico
---
Call real service 2nd: 
Image already existed: http://gpcoder.com/favicon.ico
Image showed: http://gpcoder.com/favicon.ico

Như bạn thấy image chỉ thật sử được load lên khi proxy class gọi hàm.

Chẳng hạn Protection proxy

Yêu cầu người gọi proxy class phải chứng nhận trước khi proxy class truy xuất vào real class. Cái này rất hữu ích khi bạn bạn viết library cho một bên khác sử dụng & yêu cầu họ xác thực trước khi gọi hàm.

UserService.java


package com.gpcoder.patterns.structural.proxy.protection;

public interface UserService {

	void load();

	void insert();
}

UserServiceImpl.java


package com.gpcoder.patterns.structural.proxy.protection;

public class UserServiceImpl implements UserService {

	private String name;

	public UserServiceImpl(String name) {
		this.name = name;
	}

	@Override
	public void load() {
		System.out.println(name + " loaded");
	}

	@Override
	public void insert() {
		System.out.println(name + " inserted");
	}

}

UserServiceProxy.java


package com.gpcoder.patterns.structural.proxy.protection;

public class UserServiceProxy implements UserService {

	private String role;
	private UserService userService;

	public UserServiceProxy(String name, String role) {
		this.role = role;
		userService = new UserServiceImpl(name);
	}

	@Override
	public void load() {
		userService.load();
	}

	@Override
	public void insert() {
		if (isAdmin()) {
			userService.insert();
		} else {
			throw new IllegalAccessError("Access denied");
		}
	}

	private boolean isAdmin() {
		return "admin".equalsIgnoreCase(this.role);
	}
}

Client.java


package com.gpcoder.patterns.structural.proxy.protection;

public class Client {
	public static void main(String[] args) {
		UserService admin = new UserServiceProxy("gpcoder", "admin");
		admin.load();
		admin.insert();

		UserService customer = new UserServiceProxy("customer", "guest");
		customer.load();
		customer.insert();
	}
}

Output của chương trình:


gpcoder loaded
gpcoder inserted
customer loaded
Exception in thread "main" 
java.lang.IllegalAccessError: Access denied
	at com.gpcoder.patterns.structural.proxy.rotection.UserServiceProxy.insert(UserServiceProxy.java:23)
	at com.gpcoder.patterns.structural.proxy.rotection.Client.main(Client.java:11)


Các bạn thấy khi Client mong muốn gọi hàm insert(), trong Proxy luôn xác thực quyền của user trước khi thực hiện nó.

Chẳng hạn Remote Proxy

Với Remote Proxy, proxy class & real class nằm ở 2 địa chỉ khác nhau. Thông qua network, proxy class sẽ encode & gửi request tới real class để khởi tạo, truy xuất, …

Tất cả chúng ta có thể thấy remote Proxy khi implements Java RMI hoặc phổ biến đặc biệt là ở WebService. Bên phía client sẽ có 1 proxy class, client sẽ khởi tạo proxy class & gọi tới real class nằm ở 1 địa chỉ khác.

Xem Thêm  Câu lệnh CASE & Trường hợp lồng nhau trong SQL Server: Ví dụ về T-SQL - sql chọn bên trong trường hợp

Chẳng hạn Smart Proxy

Proxy Class sẽ biến đổi hoặc thêm 1 số thao tác trước khi gọi tới real class. Một số trường hợp thường thấy là:

  • Ở lần đâu tiên khởi tạo real class, Proxy class sẽ lưu giữ thông tin của real class vào cache & hữu ích cho lần tái sử dụng sau. Ta có thể thấy việc hiện ra ở lazy-load ở các connection xuống database.
  • Trước khi gọi real class, ta có thể lock real class lại & không cho các thread khác phải chờ cho tới khi thread hiện giờ release real class.
  • Đếm số lượng reference tới real class.

Ở Virtual Proxy, mình cũng có lồng 1 chẳng hạn của Smart Proxy vào. Khi ta khởi tạo RealImage, mình đã lưu cache RealImage lại & lần sau gọi hàm ShowImage(), ta không phải khởi tạo lại RealImage.

Lợi nhuận của Proxy Pattern là gì?

  • Cãi thiện Performance thông qua lazy loading, chỉ tải các tài nguyên khi chúng được yêu cầu.
  • Nó phân phối sự bảo vệ cho đối tượng

    thực từ 

    toàn cầu bên ngoài.

  • Giảm ngân sách khi có nhiều truy cập vào đối tượng có ngân sách khởi tạo ban đầu lớn.
  • Dễ cải tiến, bảo dưỡng.

Sử dụng Proxy Pattern bao giờ?

  • Khi mong muốn bảo vệ quyền truy xuất vào các cách thức của object thực.
  • Khi cần một số thao tác bổ sung trước khi thực hiện cách thức của object

    thực

    .

  • Khi tạo đối tượng ban đầu là theo yêu cầu hoặc hệ thống yêu cầu sự chậm trễ khi tải một số tài nguyên khẳng định (lazy loading).
  • Khi có nhiều truy cập vào đối tượng có ngân sách khởi tạo ban đầu lớn.
  • Khi đối tượng gốc tồn tại trong môi trường từ xa (remote).
  • Khi đối tượng gốc nằm trong một hệ thống cũ hoặc thư viện của bên thứ ba.
  • Khi mong muốn theo dõi hiện trạng & vòng đời đối tượng.

So sánh Proxy Pattern với Decorator Pattern

Kết cấu của Proxy Pattern & Decorator Pattern là tương đương nhau (bạn có thể coi ở class diagram). Hai Pattern này đều Wrap một đối tượng thực bên trong nó. Ngoài ra, khác nhau thật sự giữa Proxy Pattern & Decorator Pattern nằm ở mục đích sử dụng. Với Decorator Pattern, người tiêu dùng sẽ hướng tới mục tiêu là có thể thêm chức năng động vào một đối tượng có trước, trong lúc đó Proxy Pattern cho phép ta tạo thành một đại diện cho một đối tượng khác.

Ebook đọc qua:

  • https://sourcemaking.com/design_patterns/proxy
  • https://refactoring.guru/design-patterns/proxy
  • https://www.javatpoint.com/proxy-pattern
  • https://www.tutorialspoint.com/design_pattern/proxy_pattern.htm
  • https://appstechviet.wordpress.com/2016/08/23/design-pattern-the-proxy-pattern/
  • Design Patterns: Elements of Reusable Object-Oriented Phần mềm – GOF

4.9

Nếu bạn thấy hay thì hãy chia sẻ nội dung cho mọi người nhé!

Shares

Phản hồi

phản hồi

Viết một bình luận