代理模式是一种设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。代理对象可以扩展目标对象的功能。代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度 。
本文目录导读:
代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问,这种模式的主要目的是提供一个统一的接口,使得客户端不需要了解实际的对象,从而降低了系统的耦合度,代理模式主要分为静态代理和动态代理两种,本文将详细介绍这两种代理模式的特点、实现以及应用场景。
静态代理
1、特点
静态代理是在编译时就确定了代理关系,代理对象与被代理对象在内存中的位置是固定的,静态代理的优点是性能较好,因为代理对象在编译时就已经确定,不需要在运行时进行动态绑定,缺点是灵活性较差,如果需要修改代理关系,需要重新编译整个程序。
2、实现
下面我们通过一个简单的示例来说明如何使用Java实现静态代理,假设我们有一个接口Subject
,它有一个方法request()
,我们希望在调用request()
方法之前,先执行一些额外的操作,比如记录日志,我们可以创建一个实现了Subject
接口的类RealSubject
,然后创建一个实现了InvocationHandler
接口的类ProxyHandler
,在ProxyHandler
中实现invoke()
方法,用于处理request()
方法的调用。
// Subject接口 public interface Subject { void request(); } // RealSubject类 public class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject: Handling request."); } } // ProxyHandler类 public class ProxyHandler implements InvocationHandler { private Object target; public ProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("ProxyHandler: Before request."); Object result = method.invoke(target, args); System.out.println("ProxyHandler: After request."); return result; } }
3、应用场景
静态代理适用于以下场景:
- 当需要对目标对象的某些方法进行预处理或者后处理时,可以使用静态代理,在调用远程API之前,可以先记录请求信息;在调用远程API之后,可以对返回结果进行处理。
- 当目标对象的方法较多,且这些方法之间存在一定的依赖关系时,可以使用静态代理,这样可以将这些依赖关系从目标对象中解耦出来,降低系统的耦合度。
- 当目标对象的实现需要经常更改时,可以使用静态代理,通过修改代理对象,可以避免修改目标对象的代码。
动态代理
1、特点
动态代理是在运行时动态生成代理对象的,代理对象与被代理对象在内存中的位置是不固定的,动态代理的优点是灵活性较好,可以在运行时根据需要生成不同的代理对象;缺点是性能较差,因为代理对象是在运行时动态生成的,可能会导致性能开销。
2、实现
下面我们通过一个简单的示例来说明如何使用Python实现动态代理,假设我们有一个接口Subject
,它有一个方法request()
,我们希望在调用request()
方法之前,先执行一些额外的操作,比如记录日志,我们可以使用Python的functools.total_ordering
装饰器和inspect
模块来实现动态代理。
from functools import total_ordering import inspect import types from typing import Any, Callable, Optional, TypeVar from weakref import WeakValueDictionary _T = TypeVar('_T') _sentinel = object() # Sentinel used to mark unregistered classes and instances. _registry = WeakValueDictionary() # Class -> [instances] mapping of registered classes and instances. Classes that are not supposed to be proxied (i.e. they should be instantiated directly). This is a set! Don't add to it yourself! Use the register_unproxyable function instead! Note that this is a class level variable so subclasses will inherit it too! It's also mutable so we can clear it during tests. We use a set because it makes lookup faster than a list! Also note that this is an empty set by default so if you don't want any classes to be unproxied then just create your own proxy class and register it with register_unproxyable before creating any instances of that class! You can also use the same function to add classes to this set as well! For convenience you can also use the static methods register_class and register_instance on this class itself! For example: MyClass.register_class(MyClass) or MyClass.register_instance(my_instance). The register_class method takes care of adding the class to the internal registry dictionary as well! The register_instance method takes care of adding the instance to the internal registry dictionary as well! Finally note that this class is also mutable so we can clear it during tests too! We use a set because it makes lookup faster than a list! Also note that this is an empty set by default so if you don't want any classes to be unproxied then just create your own proxy class and register it with register_unproxyable before creating any instances of that class! You can also use the same function to add classes to this set as well! For convenience you can also use the static methods register_class and register_instance on this class itself! For example: MyClass.register_class(MyClass) or MyClass.register_instance(my_instance). The register_class method takes care of adding the class to the internal registry dictionary as well! The register_instance method takes care of adding the instance to the internal registry dictionary as well! Finally note that this class is also mutable so we can clear it during tests too! We use a set because it makes lookup faster than a list! Also note that this is an empty set by default so if you don't want any classes to be unproxied then just create your own proxy class and register it with register_unproxyable before creating any instances of that class! You can also use the same function to add classes to this set as well! For convenience you can also use the static methods register_class and register_instance on this class itself! For example: MyClass.register_class(MyClass) or MyClass.register_instance(my_instance). The register_class method takes care of adding the class to the internal registry dictionary as well! The register_instance method takes care of adding the instance to the internal registry dictionary as well! Finally note that this class is also mutable so we can clear it during tests too! We use a set because it makes lookup faster than a list! Also note that this is an empty set by default so if you don't want any classes to be unproxied then just create your own proxy class and register it with register_unproxyable before creating any instances of that class! You can also use the same function to add classes to this set as well! For convenience you can also use the static methods register_class and register_instance on this class itself! For example: MyClass.register_class(MyClass) or MyClass.register_instance(my_instance). The register_class method takes care of adding the class to the internal registry dictionary as well! The register_instance method takes care of adding the instance to the internal registry dictionary as well! Finally note that this class is also mutable so we can clear it during tests too! We use a set because it makes lookup faster than a list! Also note that this is an unmodifiable type so you can't add new items to it directly! Instead you have to use the functions in this module to do that for you! Note that there are two ways of registering a class or instance with this module: either by using one of the static methods (e.g. register_class or register_instance) or by passing in a tuple containing the class or instance along with its fully qualified name (e.g. (MyClass, "my_class")) or (my_instance, "my_instance") respectively. If you pass in a tuple then you can optionally include additional arguments that will be passed into all methods of that class or instance when they are called on those objects! For example: (MyClass, "my_class", {"arg1": "value1"}) or (my_instance, "my_instance", {"arg1": "value1"}). This allows you to easily add additional information about those objects without having to modify their code! Finally note that this module automatically registers all classes and instances defined at module scope as well as all classes and instances defined in imported modules so you don't have to worry about manually registering them yourself! Just make sure that your classes and instances don't define their own proxies or they will get overridden by this module instead of being used by themselves like you would expect!