Design Pattern Structural Pattern---Agent Pattern

1 Overview

The proxy pattern is a type of structural pattern. The structural pattern describes how to combine classes and objects into larger structures according to a certain layout. It is divided into class structural type and object structural type. Class structural type mainly involves inheritance. Mechanisms are used to organize interfaces and classes, while object structural types mainly use composition and aggregation to combine objects. There are two types of proxy modes, namely static proxy and dynamic proxy. The proxy mode mainly provides a proxy for other objects to control access to this object, as shown below:

Insert image description here
As shown in the figure above, assuming that our client A wants to access target C, but cannot access it directly, we can use a proxy B to access target C on our behalf. For example, if we want to buy a foreign product, this product cannot be purchased domestically. , and we don’t have time to go abroad, or we don’t understand the language, then we will ask a purchasing agent to help us buy it. The client A here refers to ourselves, the agent is the purchasing agent, and the target C is the product we want to buy.

为了更加浅显地展示代理模式的原理和实现,参考B站上一个博主的买U盘的例子来介绍本文,博主名字具体忘了,大家想看视频教学的可以到B站搜索“代理模式”关键词学习。

2. Agent pattern class diagram

Insert image description here


Abstract theme class: The interface interface in the above figure defines what we want to do in this interface. We use doSomeThing() to express what we want to do. The official point here is to declare the business methods implemented by the real subject and proxy objects through interfaces or abstract classes.
Real subject class: RealSubject in the picture above implements the specific business in the abstract subject class.
Proxy class: Proxy in the picture above implements the same interface as the real theme class. It contains references to the real theme class and can control access or extend the functions of the real theme.

3. Application scenarios

3.1 Function enhancement

The proxy mode has many application scenarios. For a while, Android's plug-in function required the use of the proxy mode. Simply put, it is to inherit a certain class of the system, and then rewrite the method we want to operate, using a dynamic proxy. Let the system call the classes we implement to complete the functions we want to add, so the proxy mode can be used for function enhancement, which means that additional functions can be added on the basis of existing functions. As shown in the figure below:
Insert image description here
The meaning of functional enhancement is as shown in the figure above. For example, there is an original functional class that defines that coders can only move bricks (write code), but later coders are upgraded and can deliver a few takeaway orders before going to work. You can go to Didi after work. If programmers directly use the original functional classes, then adding these functions will require modifying the original functional classes, which does not comply with the opening and closing principle; but using proxy classes can add new functions on the basis of the previous original functional classes, which is proxy Mode enhancements

3.2 Controlling access

Controlling access means that we can use proxy classes to control client classes’ access to targets and prevent client classes from causing dangerous requests to cause damage to target classes, as shown in the following figure:

Insert image description here

4. Implementation

There are two ways to implement the proxy mode, namely static proxy and dynamic proxy. Here we use the example of selling a USB flash drive at the beginning of the article. We simulate the user's behavior of buying a USB flash drive: first, the user is a client class and purchases a USB flash drive
. Regarding the behavior of the USB flash drive, the merchant is an agent, representing a certain brand of USB flash drives, while the manufacturer is a target category, representing the behavior of selling USB flash drives. The relationship between the three is as follows:

Insert image description here
As shown in the figure above, the purpose of both merchants and manufacturers is to sell USB flash drives. The functions they complete are the same, which is the act of selling USB flash drives. Let's take a look at the implementation of the static proxy and dynamic proxy modes.

4.1 Implementation of static proxy

The proxy class of the static proxy is implemented manually by ourselves. We create a Java class ourselves to represent the proxy class. At the same time, the target of the proxy is determined. The characteristics of the static proxy are that it is simple to implement and easy to understand.

4.1.1 Steps to implement static proxy mode

(1) Create an interface, define the method of selling USB flash drives, and indicate what manufacturers and merchants have to do, that is, sell USB flash drives.

public interface IUSBShop {
    
    
     int sellUSB();
}

(2) Create a manufacturer class and implement the interface of step (1)

public class USBFactory implements IUSBShop {
    
    
    @Override
    public int sellUSB() {
    
    
        return 100;//返回100表示厂家的U盘定价
    }
}

(3) Create a merchant class, that is, an agent class, implement the interface in step (1), implement the method of selling USB flash drives, and hold a reference to the manufacturer class

public class USBProxy implements IUSBShop {
    
    
    private IUSBShop mIUSBShop;

    public USBProxy(IUSBShop iusbShop){
    
    
        this.mIUSBShop = iusbShop;
    }
    @Override
    public int sellUSB() {
    
    
        int price = mIUSBShop.sellUSB();//厂家建议零售价
        System.out.println("厂家建议的零售价是:" + price);
        //功能增强,商家的定价
        int finalPrice = price + 40;
        return finalPrice;
    }
}

As shown in the code above, after getting the manufacturer's pricing, the agent class extends its own pricing definition.

(4) Create a client class and call the merchant’s method to purchase the USB flash drive

public class Client {
    
    
    public static void main(String[] args) {
    
    
        IUSBShop iusbShop = new USBFactory();
        USBProxy usbProxy = new USBProxy(iusbShop);
        System.out.println("商家不接受厂家建议,最终卖一个USB的价格是:"+usbProxy.sellUSB());
    }
}

4.1.2 Disadvantages of static proxy

When using a static proxy, if there are many target classes that need to be proxied in the project, the proxy classes may need to be doubled. That is to say, to add a target class, you have to manually add a corresponding proxy class, and assuming that the interface The addition or modification of functions will affect many implementation classes, that is, the manufacturer class and agent classes all need to be modified. The solution to these problems is to use dynamic proxies

4.2 Implementation of dynamic proxy

Dynamic proxy refers to using JDK's reflection mechanism to create objects of proxy classes during program execution, and dynamically specifying the target class to be proxied. There are two ways to implement dynamic proxy. One is to use JDK's dynamic proxy (this article Implementation method) That is to use the classes and interfaces in the Java launch package to implement the function of dynamic proxy. In Java's reflection package java.lang,reflect, InvocationHandler,Method,Proxythree classes are used to implement dynamic proxy. The second one is to use to CgLibimplement dynamic proxy.

When there are many target classes in a static proxy, you can use a dynamic proxy, because even if there are many target classes in a dynamic proxy, the number of proxy classes can be very small, and when the methods in the interface are modified, the proxy classes will not be affected.

注释:CgLib(Code Generation Librart)是实现动态代理的第三方工具库,它的原理是继承,CbLib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改,因为CgLib是继承重写方法,所以要求目标类不能是final的,如Mybatis Spring中都有使用导CgLib

4.2.1 Implementation of Java JDK dynamic proxy

Before using JDK's dynamic proxy, we need to understand three classes, namely Method, InvocationHandler, and Proxy.

Method

Represents the method in the class. There is an invoke() method in method, which represents the invocation of the method. The invoke method requires 2 parameters: 1.object:
represents the object to which the method needs to be executed.
2.object args: the parameters required when executing the method. value

InvocationHandler

There is only one invoke method in this class. This method represents the function code to be executed by the proxy object. The functions to be completed by the proxy class are written in this invoke method. As we said earlier, the main function of the proxy class is to call the target method and To expand and enhance functions, these logics are written in the invoke() method of this class.

The prototype of the invoke method is as follows:

invoke(Object proxy, Method method, Object[] args)

Parameter explanation:
(1) proxy: proxy object created by Jdk, no assignment is required
(2) method: method of the target class, provided by JDK
(3) args: parameters in the target class method

Proxy

The function of this class is relatively simple. Its function is to create proxy objects. In this class, there is a method for creating proxy class objects, newProxyInstance(). The prototype of the method is as follows:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

Parameter explanation:
(1) ClassLoader: the class loader of the target object, obtained using reflection
(2) interface: the interface implemented by the target object, obtained by reflection
(3) InvocationHandler: implemented by ourselves, the function to be completed by the proxy class

The return value of this method is a proxy object

4.2.2 How to use JDK dynamic proxy

(1) Create a class to implement Invocationhandler, override the invoke() method, and write the function to be completed in this method. (
2) There is a Method type parameter in the invoke() method we override, which represents the target. For methods in a class, we can execute the method in a certain target class through Method, use method.invoke(目标对象,方法参数)
(3) Use the Proxy class to create a proxy object, and call the method of the target class to complete the defined function.

4.2.3 Implementation steps of JDK dynamic proxy

Let’s take merchants selling U disks as an example to introduce the implementation steps of JDK dynamic proxy
(1) Create an interface to define the functions to be completed by the target class

public interface IUSBShop {
    
    
     int sellUSB();
}

(2) Create a target class to implement the interface created in step (1)

public class USBFactory implements IUSBShop {
    
    
    @Override
    public int sellUSB() {
    
    
        return 100;
    }
}

(3) Create a class to implement the InvocationHandler interface, and complete the function of the proxy class in the invoke() method of the interface, that is, call the target method to enhance the function

public class MyInvocationHandler implements InvocationHandler {
    
    
    private Object usbFactory;

    public void setUSBFactory(Object usbFactory) {
    
    
        this.usbFactory = usbFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        int price = (int) method.invoke(usbFactory, args);
        System.out.println("厂家零售价: " + price);
        int finalprice = price + 40;
        System.out.println("最终价格: " + finalprice);

        return finalprice;
    }
}

(4) Use the static method of the Proxy class to create a proxy object, convert the return value to the interface type, call the target class method, and complete the behavior of buying a USB flash drive.

public class Client {
    
    
    public static void main(String[] args) {
    
    
        IUSBShop iusbShop = new USBFactory();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        myInvocationHandler.setUSBFactory(iusbShop);
        IUSBShop proxy = (IUSBShop) Proxy.newProxyInstance(USBFactory.class.getClassLoader(),
                iusbShop.getClass().getInterfaces(), myInvocationHandler
        );

        System.out.println("FinalResult :" + proxy.sellUSB());
    }
}

动态代理可以在不改变原来目标方法功能的前提下,在代理中增强扩展自己的代码

Guess you like

Origin blog.csdn.net/zxj2589/article/details/131543272