Last week passed smoothly, today we continue the series of two design patterns a week. This week we focus on structural models, and today we introduce the proxy pattern of structural models
Definition:
The proxy object is used to access the target object, so that additional functions such as adding permissions, access control and auditing can be enhanced on top of the target object. In reality, the example is the intermediary, for example: buying tickets does not necessarily go to the station, this time the agent will come out, 12306 and other software and agencies
The main advantages of the proxy model are:
Proxy mode plays an intermediary role and protects the target object between the client and the target object. Proxy objects extend the functionality of target objects; The proxy mode can separate the client from the target object, reduce the coupling degree of the system to a certain extent, and increase the scalability of the programCopy the code
There are two proxy modes: static proxy mode and dynamic proxy mode. The dynamic proxy mode is implemented by JDK and CGLIB.
Static proxy mode
Structure of proxy pattern:
Abstract Subject class: Business methods that declare real topics and proxy object implementations through interfaces or abstract classes. Real Subject class: Implements the concrete business in an abstract topic, is the Real object represented by the proxy object, and is the object ultimately referenced. Proxy class: Provides the same interface as a real topic, with internal references to real topics that can access, control, or extend the functionality of real topics.Copy the code
The following shows the static proxy pattern using code
IUserDao {fun save()}Copy the code
Concrete implementation class
Class UserDao:IUserDao {override fun save() {log.v ("======"," real operation ")}}Copy the code
The proxy class
class ProxyUser:IUserDao { private lateinit var userDao:UserDao override fun save() { if(! this::userDao.isInitialized){ userDao = UserDao() } saveBefore() userDao.save() saveAfter() } private fun saveBefore(){ The v (" = = = = = = ", "before")} private fun saveAfter () {the v (" = = = = = = ", "operation")}}Copy the code
Usage:
var proxyUser = ProxyUser()
proxyUser.save()
Copy the code
Advantages:
The function of the target object can be extended in accordance with the open and close principleCopy the code
So that’s the simple static proxy approach. Let’s take a look at its disadvantages:
When you need to delegate multiple classes, there are two ways to implement the same interface as the target object: Only maintain a proxy class, represented by this class implements multiple interfaces, but this will lead to the proxy class is too big New multiple proxy classes, each target object corresponding to a proxy class, but this will generate too many proxy class 2, when the interface need to add, delete, modify method when the target object and the proxy class to change at the same time, not easy to maintain.Copy the code
In order to solve the disadvantages of static proxy, the dynamic proxy method is extended
Dynamic proxy mode
Definition:
We learned earlier that static proxies mean that the proxy class exists before the program runs. On the contrary, the proxy class of dynamic proxy does not exist before the program runs, that is, the proxy mode created by the proxy class when the program runs becomes dynamic proxy. In this case, the proxy classes are not defined in Java code, but are dynamically generated at runtime based on our "instructions" in Java codeCopy the code
Dynamic proxies are ways to compute the bytecode of the proxy class, based on the interface or target object, and then load it into the JVM for use.Copy the code
To keep the generated proxy classes consistent with the target object (real subject roles), the two most common methods will be introduced from now on: by implementing interfaces -> JDK dynamic proxies by inheriting classes -> CGLIB dynamic proxiesCopy the code
Let’s look at the implementation of the dynamic design pattern
In addition to the above User class we are creating a new real estate agent class, of course, the main is to buy a house.
public interface BuyHouse {
void buyHosue();
void buyEnd();
}
Copy the code
Concrete implementation class:
Public class BuyHouseImpl implements BuyHouse {@override public void buyHosue() {log.v ("=========","======= "); // system.out.println (" I want to buy a house "); {} @ Override public void buyEnd () the v (" = = = = = = = = = ", "= = = = = = = buy out"); }}Copy the code
Notice the dynamic proxy
class DynamicProxy(any:Any):InvocationHandler { private var any:Any ? = null init { this.any = any } override fun invoke(proxy: Any? , method: Method? , args: Array<out Any>?) : Any? {log. v("==========="," save before ") return method!! .invoke(any,*(args ? : arrayOfNulls<Any>(0))) } }Copy the code
Usage:
val userDao: IUserDao = UserDao()
val proxyUser = Proxy.newProxyInstance(
IUserDao::class.java.classLoader, UserDao::class.java.interfaces,
DynamicProxy(userDao)
) as IUserDao
proxyUser.save()
val buyHouseImpl: BuyHouse = BuyHouseImpl()
val proxyBuy = Proxy.newProxyInstance(
BuyHouse::class.java.classLoader, BuyHouseImpl::class.java.interfaces,
DynamicProxy(buyHouseImpl)
) as BuyHouse
proxyBuy.buyHosue()
proxyBuy.buyEnd()
Copy the code
In this way, it is not necessary to abstract the interface and concrete implementation, and do not need to write a separate proxy class, but through reflection to get the required method for operation.
The proxy.newproxyinstance () method takes three arguments: ClassLoader loader: specifies the Class loader used by the current target object. The method for obtaining the loader is fixed Class<? >[] interfaces: specifies the type of the interface implemented by the target object. InvocationHandler: specifies the dynamic handler that triggers the event handler when executing methods on the target object. JDK dynamic proxies require the protreated class to implement an interfaceCopy the code