Skip to main content

Design Patterns: Proxy Pattern (Supplement)

Free2015-03-07#Design_Pattern#代理模式#Proxy Pattern

The previous blog post on the Proxy Pattern detailed the Remote Proxy. This post will supplement other types of proxies (Virtual Proxy, Protection Proxy, Firewall Proxy...).

no_mkd

Foreword:

The internal principles, roles, and implementation of the Remote Proxy in the Proxy Pattern were explained in detail in the previous blog post. This article serves as a supplement, introducing other types of proxies.

1. Virtual Proxy

First, clarify the role of a virtual proxy: it acts as a surrogate for a massive object before it is actually created (ensuring that the massive object is created only when needed, thus avoiding resource waste). We might use virtual proxies frequently without recognizing them, for example:

Our programs may contain time-consuming and resource-intensive operations such as intensive computations or retrieving large amounts of data from a server. Generally, default information or an animation is displayed first to indicate that the program is working hard, and the retrieved information is displayed after the operation is complete. This uses the idea of a virtual proxy, except the virtual proxy separates and encapsulates all the work during the wait period into a virtual proxy object. The client interacts directly with the virtual proxy rather than the object that actually does the work.

The virtual proxy performs several tasks upon receiving a client request:

  1. If the actual object does not exist (not yet created or still being created), it records the client's request and displays default information, passing the request to the actual object once it is fully created.
  2. If the actual object already exists, it delegates the client's request directly to the actual object.
  3. Before the actual object returns the operation result, the proxy object is responsible for displaying default information (or performing default processing).
  4. Once the result is obtained, it is passed directly back to the client (indeed, the proxy's task is "handing over" and "receiving").

Since it is a proxy, it must masquerade as the actual object so the client does not perceive its presence. Therefore, the class diagram for a virtual proxy looks like this:

The Client holds a virtual proxy object `Proxy`, and `Proxy` holds a concrete service object `MyService`.

More importantly: both the virtual proxy `Proxy` and the concrete service `MyService` extend from the `Service` interface. Thus, the proxy and the actual object exhibit the same behavior, and the client remains unaware of the `Proxy`'s existence.

2. Protection Proxy

A protection proxy plays the role of an agent (not everyone can contact the star behind me; you can only contact me, and I will only patch you through after I've determined you're not a bad person...).

In other words, a protection proxy provides access control for the actual object, ensuring that only legitimate callers receive service.

Implementation-wise, the class diagram is identical to the virtual proxy above, except `Proxy` gains the responsibility of judging the caller's permissions. (Notice something? We can combine multiple proxies. While such a proxy no longer satisfies the Single Responsibility Principle, we can simply create a proxy of proxies to manage a series of them...)

In fact, the Java API provides support for the proxy pattern (dynamic proxy). The "dynamic" nature of Java's dynamic proxy lies in the fact that the `Proxy` class (yes, a class, not an object) is created at runtime. We cannot directly modify the internal implementation of `Proxy`, but we can tell `Proxy` what to do via an `InvocationHandler`.

The basic idea of using dynamic proxies to implement protection proxies is: create multiple proxies, where different proxies represent services corresponding to callers with different permissions.

Let's design a scenario to try implementing a protection proxy with dynamic proxies: a blog post "like" mechanism where you can like others' posts but not your own, though both the author and visitors can see the number of likes.

The scenario is simple but sufficient to illustrate the point. Let's begin the simulation. First, we need a `BlogUser`:

package ProxyPattern.ProtectionProxy;

/**

  • 定义博客用户接口
  • @author ayqy */ public interface BlogUser { public void support(); public int getSupportCount(); }

We also need a `ConcreteUser`, whose implementation is simple and omitted here. Now, let's create the dynamic proxies. Since there are two permissions—author and visitor—we need at least two different dynamic proxies.

AuthorHandler:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;

/**

  • 实现为作者服务的InvocationHandler

  • @author ayqy */ public class AuthorHandler implements InvocationHandler{ BlogUser user;

    public AuthorHandler(BlogUser user){ this.user = user; }

    @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {//声明非法访问异常 try { /如果博主想要查看被赞数目,则委托给调用者user实现具体操作/ if(method.getName().equals("getSupportCount")){ return method.invoke(user, args); } /如果博主要给自己点赞,则抛出异常表示拒绝/ if(method.getName().equals("support")){ throw new IllegalAccessException(); } } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); }

     	return null;//如果调用其它方法,直接无视
    

    }

}

Similarly, we need to implement a `VisitorHandler`. With multiple proxies, we also need to implement the proxy of proxies—the protection proxy (which returns a corresponding dynamic proxy based on the caller's permissions).

ProtectionProxy:

package ProxyPattern.ProtectionProxy;

import java.lang.reflect.Proxy;

/**

  • 实现保护代理

  • @author ayqy / public class ProtectionProxy { /*

    • @param user 调用者
    • @return 作者对应的代理 */ public BlogUser getAuthorProxy(BlogUser user){ return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader() , user.getClass().getInterfaces() , new AuthorHandler(user)); }

    /**

    • @param user 调用者
    • @return 游客对应的代理 */ public BlogUser getVisitorProxy(BlogUser user){ return (BlogUser)Proxy.newProxyInstance(user.getClass().getClassLoader() , user.getClass().getInterfaces() , new VisitorHandler(user)); }

    /在此处插入生成其它代理的方法/ }

This is sufficient. Let's do a simple test:

package ProxyPattern.ProtectionProxy;

/**

  • 实现测试类

  • @author ayqy */ public class Test { public static void main(String[] args){ //创建保护代理 ProtectionProxy proxy = new ProtectionProxy();

     System.out.println("Author Section:");
     //创建博主对应的代理
     BlogUser authorProxy = proxy.getAuthorProxy(new ConcreteUser());
     System.out.println("Author checking total likes");
     System.out.println("Liked " + authorProxy.getSupportCount() + " times");
     //博主要给自己点赞
     System.out.println("Author trying to like their own post");
     try{
     authorProxy.support();
     }catch(Exception e){
     	System.out.println("Like failed: cannot like your own post");
     }
     System.out.println("After self-like attempt, likes: " + authorProxy.getSupportCount() + " times");
    
     
     System.out.println("\nVisitor Section:");
     //创建游客对应的代理
     BlogUser vistorProxy = proxy.getVisitorProxy(new ConcreteUser());
     System.out.println("Visitor checking total likes");
     System.out.println("Liked " + vistorProxy.getSupportCount() + " times");
     //博主要给自己点赞
     System.out.println("Visitor liking author's post");
     try{
     vistorProxy.support();
     }catch(Exception e){
     	System.out.println("Like failed for unknown reason");
     }
     System.out.println("After like, likes: " + vistorProxy.getSupportCount() + " times");
    

    } }

Test result:

3. Other Proxies

The Proxy Pattern is widely used, and it's impossible to list all types. Here are a few common ones:

Proxy NameRole
Firewall ProxyControls access to network resources, protecting the subject from "bad clients."
Smart Reference ProxyPerforms additional actions when the subject is referenced, such as counting the number of references to an object.
Cache ProxyProvides temporary storage for expensive computation results and allows multiple clients to share results to reduce computational or network latency.
Synchronization ProxyProvides safe access to the subject in multi-threaded scenarios.
Complex Hiding Proxy (Facade Proxy)Used to hide the complexity of a complex set of classes and perform access control (more powerful than the Facade Pattern).
Copy-on-Write ProxyControls the copying of objects by delaying the copy until it is actually needed (similar to a virtual proxy).

Of course, once you understand the original Remote Proxy, these other proxies are quite easy to grasp...

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment