💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
# 代理模式 **代理(Proxy)模式:** 为某对象提供一种代理以控制对该对象的访问。即客户端通过代理**间接地访问**该对象,从而限制、增强或修改该对象的一些特性。 常见的代理模式:远程代理、虚拟代理、保护代理、智能引用代理 **代理模式的主要优点有:** - 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用; - 代理对象可以扩展目标对象的功能; - 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度; **其主要缺点是:** - 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢; - 增加了系统的复杂度; ## **代理模式的应用场景** 前面分析了代理模式的结构与特点,现在来分析以下的应用场景。 - **远程代理**,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。 - **虚拟代理**,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。 - **安全代理**,这种方式通常用于控制不同种类客户对真实对象的访问权限。 - **智能指引**,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。 - **延迟加载**,指为了提高系统的性能,延迟对目标的加载。例如,[Hibernate](http://c.biancheng.net/hibernate/)中就存在属性的延迟加载和关联表的延时加载。 #### **模式的结构** 1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。 2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。 3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。 ![](https://img.kancloud.cn/20/98/2098786632a6bf2aa00b80d1da3570cd_672x355.png) ``` <pre class="calibre10">``` <span class="token1"><</span><span class="token1">?</span>php class <span class="token4">ProxyTest</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $proxy<span class="token1">=</span><span class="token5">new</span> <span class="token4">Proxy</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $proxy<span class="token1">-</span><span class="token1">></span><span class="token4">Request</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//抽象主题</span> interface <span class="token4">Subject</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">Request</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//真实主题</span> class <span class="token4">RealSubject</span> implements <span class="token4">Subject</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">Request</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">"访问真实主题方法..."</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//代理</span> class <span class="token4">Proxy</span> implements <span class="token4">Subject</span> <span class="token3">{</span> private $realSubject<span class="token1">=</span><span class="token5">null</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">Request</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>realSubject<span class="token1">==</span><span class="token5">null</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>realSubject<span class="token1">=</span><span class="token5">new</span> <span class="token4">RealSubject</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">preRequest</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>realSubject<span class="token1">-</span><span class="token1">></span><span class="token4">Request</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">postRequest</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">preRequest</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">"访问真实主题之前的预处理。"</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">postRequest</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> echo <span class="token2">"访问真实主题之后的后续处理。"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> ProxyTest<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` ``` <pre class="calibre10">``` <span class="token">//公司内部类</span> class <span class="token4">User</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token2">'张三'</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getType</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token2">'付费用户'</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//代理接口定义,例如开放平台 </span> interface <span class="token4">UserInterface</span> <span class="token3">{</span> <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//代理对象 </span> class <span class="token4">UserProxy</span> implements <span class="token4">UserInterface</span> <span class="token3">{</span> protected $_user<span class="token3">;</span> <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_user <span class="token1">=</span> <span class="token5">new</span> <span class="token4">User</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_user<span class="token1">-</span><span class="token1">></span><span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//公司内部调用 </span> $user <span class="token1">=</span> <span class="token5">new</span> <span class="token4">User</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token4">printf</span><span class="token3">(</span><span class="token2">"user name:%sn"</span><span class="token3">,</span> $user<span class="token1">-</span><span class="token1">></span><span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token4">printf</span><span class="token3">(</span><span class="token2">"user type:%sn"</span><span class="token3">,</span> $user<span class="token1">-</span><span class="token1">></span><span class="token4">getType</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//公司外部调用(开放给用户的) </span> $user <span class="token1">=</span> <span class="token5">new</span> <span class="token4">UserProxy</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token4">printf</span><span class="token3">(</span><span class="token2">"user name:%sn"</span><span class="token3">,</span> $user<span class="token1">-</span><span class="token1">></span><span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token4">printf</span><span class="token3">(</span><span class="token2">"user type:%sn"</span><span class="token3">,</span> $user<span class="token1">-</span><span class="token1">></span><span class="token4">getType</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//不能访问,及时知道内部对象有这个方法</span> ``` ``` ## 代理模式的扩展 在前面介绍的代理模式中,代理类中包含了对真实主题的引用,这种方式存在两个缺点。 1. 真实主题与代理主题一一对应,增加真实主题也要增加代理。 2. 设计代理以前真实主题必须事先存在,不太灵活。采用动态代理模式可以解决以上问题,如[Spring](http://c.biancheng.net/spring/)AOP,其结构图如图 4 所示。 ![](https://img.kancloud.cn/6e/60/6e60cbe965d2d44df823d6d37176962b_698x628.png) 变相的动态代理 ``` <pre class="calibre10">``` <span class="token">//调用B的show方法时候去调用A的show方法</span> class <span class="token4">A</span><span class="token3">{</span> <span class="token5">function</span> <span class="token4">show</span><span class="token3">(</span>$param<span class="token1">=</span><span class="token6">20</span><span class="token3">)</span><span class="token3">{</span> echo <span class="token2">"classA的show方法,参数值为{$param}"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">B</span><span class="token3">{</span> private $obj<span class="token3">;</span> <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span><span class="token3">)</span><span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>obj <span class="token1">=</span> <span class="token5">new</span> <span class="token4">A</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">function</span> <span class="token4">__call</span><span class="token3">(</span>$name<span class="token3">,</span> $arguments<span class="token3">)</span> <span class="token3">{</span> $ref <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ReflectionClass</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">)</span><span class="token3">;</span> <span class="token">//检查方法是否已定义</span> <span class="token5">if</span> <span class="token3">(</span>$ref<span class="token1">-</span><span class="token1">></span><span class="token4">hasMethod</span><span class="token3">(</span>$name<span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//获取一个类方法的 ReflectionMethod对象。相当于new ReflectionMethod('class', 'myMethod')</span> $method <span class="token1">=</span> $ref<span class="token1">-</span><span class="token1">></span><span class="token4">getMethod</span><span class="token3">(</span>$name<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isPublic</span><span class="token3">(</span><span class="token3">)</span><span class="token1">&&</span><span class="token1">!</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isAbstract</span><span class="token3">(</span><span class="token3">)</span><span class="token1">&&</span><span class="token4">count</span><span class="token3">(</span>$arguments<span class="token3">)</span><span class="token1"><</span><span class="token6">1</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isStatic</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//如果是静态方法,不需要传入调用的对象</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invoke</span><span class="token3">(</span><span class="token5">null</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invoke</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isStatic</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//如果是静态方法,不需要传入调用的对象</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invokeArgs</span><span class="token3">(</span><span class="token5">null</span><span class="token3">,</span>$arguments<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invokeArgs</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">,</span>$arguments<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> $b<span class="token1">=</span><span class="token5">new</span> <span class="token4">B</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $b<span class="token1">-</span><span class="token1">></span><span class="token4">show</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span><span class="token">//classA的show方法,参数值为20</span> $b<span class="token1">-</span><span class="token1">></span><span class="token4">show</span><span class="token3">(</span><span class="token6">21</span><span class="token3">)</span><span class="token3">;</span><span class="token">//classA的show方法,参数值为21</span> ``` ``` **动态代理:** ``` <pre class="calibre17">``` <span class="token">//真实角色接口类 AbstractSubject</span> interface <span class="token4">IGamePlayer</span> <span class="token3">{</span> <span class="token">// 登录游戏</span> public <span class="token5">function</span> <span class="token4">login</span><span class="token3">(</span>String $user<span class="token3">,</span> String $password<span class="token3">)</span><span class="token3">;</span> <span class="token">// 杀怪</span> public <span class="token5">function</span> <span class="token4">killBoss</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 升级</span> public <span class="token5">function</span> <span class="token4">upgrade</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//真实角色类:RealSubject</span> class <span class="token4">GamePlayer</span> implements <span class="token4">IGamePlayer</span> <span class="token3">{</span> private $name <span class="token1">=</span> <span class="token2">""</span><span class="token3">;</span> <span class="token">// 我的代理 IGamePlayer类型</span> private $proxy <span class="token1">=</span> <span class="token5">null</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>String $_name<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>name <span class="token1">=</span> $_name<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * @Override */</span> public <span class="token5">function</span> <span class="token4">login</span><span class="token3">(</span>String $user<span class="token3">,</span> String $password<span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"登录名为【"</span> <span class="token3">.</span> $user <span class="token3">.</span> <span class="token2">"】的用户【"</span> <span class="token3">.</span> $this<span class="token1">-</span><span class="token1">></span>name <span class="token3">.</span> <span class="token2">"】登录成功!<br>"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * @Override */</span> public <span class="token5">function</span> <span class="token4">killBoss</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>name <span class="token3">.</span> <span class="token2">"在打怪!<br>"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * @Override */</span> public <span class="token5">function</span> <span class="token4">upgrade</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>name <span class="token3">.</span> <span class="token2">" 已升了一级!<br>"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//动态代理接口 Invocationhandler</span> interface <span class="token4">InvocationHandler</span><span class="token3">{</span> public <span class="token5">function</span> <span class="token4">invoke</span><span class="token3">(</span>$proxy<span class="token3">,</span> $function_name<span class="token3">,</span> $param<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//动态代理类:DynamicProxy</span> class <span class="token4">GamePlayIH</span> implements <span class="token4">InvocationHandler</span> <span class="token3">{</span> <span class="token">// 被代理实例Object</span> private $obj <span class="token1">=</span> <span class="token5">null</span><span class="token3">;</span> <span class="token">// 我要代理谁</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$_obj<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>obj <span class="token1">=</span> $_obj<span class="token3">;</span> <span class="token3">}</span> <span class="token">/** * @Override */</span> public <span class="token5">function</span> <span class="token4">invoke</span><span class="token3">(</span>$proxy<span class="token3">,</span> $function_name<span class="token3">,</span>$param<span class="token3">)</span><span class="token3">{</span> <span class="token">//检查方法是否已定义</span> <span class="token5">if</span> <span class="token3">(</span>$proxy<span class="token1">-</span><span class="token1">></span><span class="token4">hasMethod</span><span class="token3">(</span>$function_name<span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//获取一个类方法的 ReflectionMethod对象。相当于new ReflectionMethod('class', 'myMethod')</span> $method <span class="token1">=</span> $proxy<span class="token1">-</span><span class="token1">></span><span class="token4">getMethod</span><span class="token3">(</span>$function_name<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isPublic</span><span class="token3">(</span><span class="token3">)</span><span class="token1">&&</span><span class="token1">!</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isAbstract</span><span class="token3">(</span><span class="token3">)</span><span class="token1">&&</span><span class="token4">count</span><span class="token3">(</span>$param<span class="token3">)</span><span class="token1"><</span><span class="token6">1</span><span class="token3">)</span><span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isStatic</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//如果是静态方法,不需要传入调用的对象</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invoke</span><span class="token3">(</span><span class="token5">null</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invoke</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$method<span class="token1">-</span><span class="token1">></span><span class="token4">isStatic</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">{</span> <span class="token">//如果是静态方法,不需要传入调用的对象</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invokeArgs</span><span class="token3">(</span><span class="token5">null</span><span class="token3">,</span>$param<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span><span class="token5">else</span><span class="token3">{</span> $method<span class="token1">-</span><span class="token1">></span><span class="token4">invokeArgs</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">,</span>$param<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">__call</span><span class="token3">(</span>$function_name<span class="token3">,</span>$param<span class="token3">)</span><span class="token3">{</span> $ref <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ReflectionClass</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>obj<span class="token3">)</span><span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span><span class="token4">invoke</span><span class="token3">(</span>$ref<span class="token3">,</span>$function_name<span class="token3">,</span>$param<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">Client</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">// 定义一个痴迷的玩家 IGamePlayer</span> $player <span class="token1">=</span> <span class="token5">new</span> <span class="token4">GamePlayer</span><span class="token3">(</span><span class="token2">"猿哥"</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//InvocationHandler</span> $proxy <span class="token1">=</span> <span class="token5">new</span> <span class="token4">GamePlayIH</span><span class="token3">(</span>$player<span class="token3">)</span><span class="token3">;</span> <span class="token">// 登录</span> $proxy<span class="token1">-</span><span class="token1">></span><span class="token4">login</span><span class="token3">(</span><span class="token2">"oneape15"</span><span class="token3">,</span> <span class="token2">"pwd123"</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//开始杀怪</span> $proxy<span class="token1">-</span><span class="token1">></span><span class="token4">killBoss</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">// 升级</span> $proxy<span class="token1">-</span><span class="token1">></span><span class="token4">upgrade</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> Client<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> 结果: 登录名为【oneape15】的用户【猿哥】登录成功<span class="token1">!</span> 猿哥在打怪<span class="token1">!</span> 猿哥 已升了一级<span class="token1">!</span> ``` ```