GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Design pattern Hướng dẫn Java Design Pattern – Proxy

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

Đăng vào 30/11/2018 . Được đăng bởi GP Coder . 32062 Lượt xem . Toàn màn hình

Khi cần điều khiển truy nhập tới một đối tượng được thực hiện từ quá trình 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 phương thức của object thực. Trong trường hợp như vậy, ta nên dùng mẫu thiết kế Proxy.

Nội dung

  • 1 Proxy Pattern là gì?
  • 2 Phân loại Proxy
  • 3 Cài đặt Proxy Pattern như thế nào?
  • 4 Lợi ích của Proxy Pattern là gì?
  • 5 Sử dụng Proxy Pattern khi nào?
  • 6 So sánh Proxy Pattern với Decorator Pattern

Proxy Pattern là gì?

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

Proxy Pattern là một trong những Pattern thuộc nhóm cấu trúc (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 chính vì muốn tạo ra một đối tượng sẽ ủy quyền, thay thế cho một đối tượng khác.

Proxy Pattern là mẫu thiết kế mà ở đó tất cả các truy cập trực tiếp đến một đối tượng nào đó sẽ được chuyển hướng vào 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 phương thức, phương thức đó có thể được định nghĩa lại cho phù hợp với múc đích sử dụng.

Để đơn giản hơn bạn có thể nghĩ đến khái niệm HTTP proxy trong mạng máy tính, nó là một gateway giữa trình duyệt (client) và máy chủ (server). HTTP proxy giúp nâng cao trải 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 quảng cáo, giới hạn 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òn được gọi là Surrogate (thay thế) hoặc Placeholder (trình giữ chỗ).

Phân loại Proxy

  • Virtual Proxy : Virtual Proxy tạo ra một đối tượng trung gian mỗi khi có yêu cầu tại thời điểm thực thi ứng dụng, nhờ đó làm tăng hiệu suất của ứng 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 tra 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 ứng dụng (trên cùng máy hoặc máy khác).
  • Monitor Proxy : Monitor Proxy sẽ thiết lập 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 quan trọng 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 từ chối các yêu cầu xuất xứ từ các client không tín nhiệm.
  • Cache Proxy : Cung cấp 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 tương tự như Flyweight Pattern.
  • Smart Reference Proxy : Là nơi kiểm soát các hoạt động bổ sung mỗi khi đối tượng được tham chiếu.
  • Synchronization Proxy : Đảm bảo 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 dụng khóa khá lâu khiến cho số lượng các client trong danh sách hàng đợi cứ tăng lên, và do đó 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”.
  • Copy-On-Write Proxy : Loại này đảm bảo rằng sẽ không có client nào phải chờ vô thời hạn. Copy-On-Write Proxy là một thiết kế rất phức tạp.

Cài đặt Proxy Pattern như thế nào?

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

  • Cung cấp 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 và chuyển tiếp các yêu cầu đến đối tượng đó.
  • Cả Proxy và đố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 cụ thể và do đó có thể giảm được thời gian tải dữ liệu giữa server và client.

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

  • Subject : là một interface định nghĩa các phương thực để giao tiếp với client. Đối tượng này xác định giao diện chung cho RealSubject và Proxy để Proxy có thể được sử dụng bất cứ nơi nào mà RealSubject mong đợi.
  • Proxy : là một class sẽ thực hiện các bước kiểm tra và gọi tới đối tượng của class service thật để thực hiện các thao tác sau khi kiểm tra. 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 tương tự 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 và 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.

Ví dụ Virtual Proxy

Trì hoãn việc tạo ra 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 cơ chế Lazy Loading.

Lazy Loading là một khái niệm mà ứng 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 đơn giản 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 cần thiết. Lợi ích của việc này là giảm thiểu số lượng yêu cầu, giảm thiểu 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ự. Chúng ta đã thấy điều này qua cơ chế Lazy load của Hibernate.

Nó giải quyết vấn đề rất lớn về hiệu suất, lý do là vì proxy class có chi phí 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 khi đó Real class thường rất tốn chi phí , vì thế với virtual proxies, chỉ khi nào cần thiết, real class mới được khởi tạo.

Ví dụ một website 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 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 website truyền thống là load hình ngay khi load website, nó rất tốn tài nguyên và đôi khi không cần thiết 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.

Ví dụ Protection proxy

Yêu cầu người gọi proxy class phải chứng thực trước khi proxy class truy xuất vào real class. Cái này rất hữu dụng khi bạn bạn viết library cho một bên khác sử dụng và 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 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ó.

Ví dụ Remote Proxy

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

Chúng ta có thể thấy remote Proxy khi implements Java RMI hoặc thông dụng nhất là ở WebService. Bên phía client sẽ có 1 proxy class, client sẽ khởi tạo proxy class và gọi tới real class nằm ở 1 địa chỉ khác.

Ví dụ Smart Proxy

Proxy Class sẽ thay đổ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 lại thông tin của real class vào cache và hữu dụng cho lần tái sử dụng sau. Ta có thể thấy việc xuất hiện ở lazy-load ở các connection xuống database.
  • Trước khi gọi real class, ta có thể lock real class lại và không cho các thread khác phải chờ cho tới khi thread hiện tại release real class.
  • Đếm số lượng reference tới real class.

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

Lợi ích 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ó cung cấp sự bảo vệ cho đối tượng thực từ thế giới bên ngoài.
  • 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.
  • Dễ nâng cấp, bảo trì.

Sử dụng Proxy Pattern khi nào?

  • Khi muốn bảo vệ quyền truy xuất vào các phương 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 phương 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 nhất định (lazy loading).
  • Khi có nhiều truy cập vào đối tượng có chi phí 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 muốn theo dõi trạng thái và vòng đời đối tượng.

So sánh Proxy Pattern với Decorator Pattern

Cấu trúc của Proxy Pattern và Decorator Pattern là tương tự 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ó. Tuy nhiên, khác nhau thật sự giữa Proxy Pattern và Decorator Pattern nằm ở mục đích sử dụng. Với Decorator Pattern, người sử dụng sẽ nhắm tới mục tiêu là có thể thêm tính năng động vào một đối tượng có trước, trong khi đó Proxy Pattern cho phép ta tạo ra một đại diện cho một đối tượng khác.

Tài liệu tham khảo:

  • 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 Software – GOF
4.9
25
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Design pattern, Structuaral Pattern Được gắn thẻ: Design pattern, Structuaral Pattern

Hướng dẫn Java Design Pattern – Flyweight
Hướng dẫn Java Design Pattern – Chain of Responsibility

Có thể bạn muốn xem:

  • Giới thiệu Design Patterns (30/08/2018)
  • Hướng dẫn Java Design Pattern – Prototype (22/09/2018)
  • Hướng dẫn Java Design Pattern – MVC (19/02/2019)
  • Hướng dẫn Java Design Pattern – Singleton (08/09/2018)
  • Hướng dẫn Java Design Pattern – Interpreter (10/12/2018)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (98056 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97695 lượt xem)
  • Giới thiệu Design Patterns (87758 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86427 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83830 lượt xem)

Nội dung bài viết

  • 1 Proxy Pattern là gì?
  • 2 Phân loại Proxy
  • 3 Cài đặt Proxy Pattern như thế nào?
  • 4 Lợi ích của Proxy Pattern là gì?
  • 5 Sử dụng Proxy Pattern khi nào?
  • 6 So sánh Proxy Pattern với Decorator Pattern

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link