代理设计模式的UML图:
我将首先介绍Java中的各种代理实现方法
这个例子非常简单,只有一个方法 wirteCode
的接口 IDeveloper
:
public interface IDeveloper { public void writeCode(); } // 实现这个接口的类: public class Developer implements IDeveloper{ private String name; public Developer(String name){ this.name = name; } @Override public void writeCode() { System.out.println("Developer " + name + " writes code"); } }
测试代码:
public class DeveloperTest { public static void main(String[] args) { IDeveloper jerry = new Developer("Jerry"); jerry.writeCode(); } }
测试输出:
Developer Jerry writes code
现在麻烦的是,Jerry的领导因为团队中的开发者像Jerry一样没有编写技术文档,所以并不满意。经过讨论后,整个团队达成协议,相关文档必须与代码一起提供。
为了迫使开发人员编写文档而不直接对现有的实现类Developer进行修改,现在就可以使用静态代理来实现:
// 创建一个和Developer类实现同样接口的类 public class DeveloperProxy implements IDeveloper{ private IDeveloper developer; // 引用Developer类对象 public DeveloperProxy(IDeveloper developer){ this.developer = developer; } @Override public void writeCode() { System.out.println("Write documentation..."); this.developer.writeCode(); } }
测试代码:
public class DeveloperTest { public static void main(String[] args) { Developer jerry = new Developer("jerry"); DeveloperProxy jerryproxy = new DeveloperProxy(jerry); jerryproxy.writeCode(); } }
测试输出:
Write documentation... Developer jerry writes code
假设你希望在不修改原始类代码的情况下增强现有的稳定实现,你可以创建一个代理类,并将原始实现封装为代理中的私有属性。增强的功能是在代理类中完成的,对现有的代码是完全透明的。回到上面的示例,客户端代码并不关心它用来调用 writeCode()
方法的变量是否指向真正的开发人员或开发人员代码。
优点:
我们仍然使用这个例子来说明。
假设现在缺失文档的问题在QA同事中仍然存在。如果我们想通过静态代理来解决这个问题,那么必须引入另一个代理类。
这是测试人员的接口:
public interface ITester { public void doTesting(); } // ITester 接口的实现类: public class Tester implements ITester { private String name; public Tester(String name){ this.name = name; } @Override public void doTesting() { System.out.println("Tester " + name + " is testing code"); } }
测试人员代理:
public class TesterProxy implements ITester{ private ITester tester; public TesterProxy(ITester tester){ this.tester = tester; } @Override public void doTesting() { System.out.println("Tester is preparing test documentation..."); tester.doTesting(); } }
测试代码和输出:
从Tester代理的源代码中我们可以很容易的观察到它与开发人员具有完全相同的逻辑。如果又过了一段时间,我们必须为软件交付过程中的其他同事建立文档,我们必须一次又一次的引入新的静态代理类,这会导致静态代理类变得十分庞大。
现在我通过代理类 EnginnerProxy
来为所有的具体角色提供代理服务,而不是单独为每个原始实现类设置专用的静态代理类。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class EnginnerProxy implements InvocationHandler { Object obj; public Object bind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Enginner writes document"); Object res = method.invoke(obj, args); return res; } }
InvocationHandler
。 Object
变量。 InvocationHandler
中定义的 invoke
方法拦截,其中由应用程序开发人员声明的增强逻辑与由 Java Reflection
调用的原始逻辑一起调用。 下面是如何使用InvocationHandler设计的动态代理和测试输出:
虽然这个变体成功的避免了静态代理中的重复缺陷,但是它仍然有一个局限性,它无法使用不是从接口继承的实现类,就是说,使用动态代理类,原始类必须先要实现一个或多个接口,这个接口也就是代理接口。
考虑下面的例子,产品所有者没有实现任何接口:
public class ProductOwner { private String name; public ProductOwner(String name){ this.name = name; } public void defineBackLog(){ System.out.println("PO: " + name + " defines Backlog."); } }
以下代码在IDE中没有任何语法错误:
ProductOwner po = new ProductOwner("Ross"); ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po); poProxy.defineBackLog();
不幸的是编译时报出了以下错误:
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新链接地址: https://www.linuxidc.com/Linux/2019-06/158978.htm