饮料售卖机,需要有人监视它,如果饮料卖完了,需要往里面放饮料。
对饮料售卖机进行监视,通过把饮料售卖机对象传到监视器对象中以获取饮料售卖机的库存量以及当前状态。
饮料售卖机上进行监视,还是需要有人跑到售卖机前(很不方便)。
如果要远程监视饮料售卖机,我们可以通过饮料售卖机的代理,无需修改我们的代码。
代理假装它是真正的对象,但是其实一切的动作是它利用网络和真正的对象沟通。
代理模式:为另一个对象提供一个替身,以控制对这个对象的访问。
代理控制访问的方式:
远程代理控制访问远程对象
虚拟代理控制访问创建开销大的资源
保护代理基于权限控制对资源的访问
财务系统中,财务报表需要增加权限控制,普通用户无权限查看财务报表,管理员可以。
使用代理模式:创建财务报表的代理对象(代理对象包含真实的财务报表对象),用户通过查看代理对象就可以增加权限控制,无需修改代码。
package com.ez.impl; import com.ez.FinancialStatements; /** * 代理财务报表 * 管理员才可以检查财务报表。 * @author 窗外赏雪(EZ编程网) */ public class Test { public static void main(String[] args) { FinancialStatements realFinancialStatements=new RealFinancialStatements(); boolean isAdmin=true; FinancialStatements fs=new ProxyFinancialStatements(realFinancialStatements, isAdmin); fs.check(); isAdmin=false; fs=new ProxyFinancialStatements(realFinancialStatements, isAdmin); fs.check(); } }
package com.ez; /** * 通过实现同一接口,代理能在任何财务系统出现的地方取代它。 * @author 窗外赏雪(EZ编程网) */ public interface FinancialStatements { void check(); }
package com.ez.impl; import com.ez.FinancialStatements; /** * 真正做事的对象,它是被proxy代理和控制访问的对象。 * @author 窗外赏雪(EZ编程网) */ public class RealFinancialStatements implements FinancialStatements{ @Override public void check() { System.out.println("====欢迎查看年度财务报表===="); System.out.print("总支出:$99999"); System.out.println("总收入:$100000"); System.out.println("盈利:$1"); System.out.println("==========================="); } }
package com.ez.impl; import com.ez.FinancialStatements; /** * 客户和真正财务系统的交互都必须通过它。 * 因为代理和真正财务系统实现相同的接口,所以任何用到财务系统的地方,都可以用该代理取代。 * 代理还可以控制对这个对象的访问。has-a关系。 * @author 窗外赏雪(EZ编程网) */ public class ProxyFinancialStatements implements FinancialStatements{ private FinancialStatements realFinancialStatements; private boolean isAdmin; public ProxyFinancialStatements(FinancialStatements realFinancialStatements,boolean isAdmin) { this.realFinancialStatements=realFinancialStatements; this.isAdmin=isAdmin; } @Override public void check() { if(isAdmin){ realFinancialStatements.check(); }else{ System.out.println("sorry no permission"); } } }
使用Java API的代理
Java在java.lang.reflect包中有自己的代理支持,利用这个包可以在运行时动态地创建一个代理类,实现一个或者多个接口并将方法的调用转发到你所指定的类。
因为实际的代理类是在运行时创建的,所以我们称这个为:动态代理。
因为Java已经为你创建Proxy类,不能像以前一样把控制访问的代码放在Proxy类中,控制访问的代码需要放在InvocationHandler中。
InvocationHandler的工作是,响应代理的任何调用,你可以把InvocationHandler想象成是代理收到方法调用后,请求做实际工作的对象。
代理模式,就是为一个对象找个替身,控制该对象的访问。
Java帮我们创建了代理对象,把响应代理的任何调用的业务逻辑抽离出来,单独放到InvocationHandler的实现类中。
这样可以让开发把精力更多的放在有价值的业务实现上面。
之前的财务系统代理模式,使用动态代理实现如下:
1、创建InvocationHandler
InvocationHandler实现了代理的行为,正如你将看到,Java负责创建真实代理类和对象。我们只需要提供在方法调用时知道做什么的handler。
2、写代码创建动态代理
需要写代码产生代理类,并实例化它。
3、利用适当的代理包装任何财务系统对象。
package com.ez.impl; import java.lang.reflect.Proxy; import com.ez.FinancialStatements; /** * 代理财务报表 管理员才可以检查财务报表。 * @author 窗外赏雪(EZ编程网) */ public class Test { /** * 动态创建Proxy类并实例化Proxy对象 * 我们创建一个代理,将它的方法调用转发给InvocationHandler。 * @param realFinancialStatements * @return */ private static FinancialStatements getProxyFinancialStatements(FinancialStatements realFinancialStatements) { return (FinancialStatements) Proxy.newProxyInstance( realFinancialStatements.getClass().getClassLoader(), realFinancialStatements.getClass().getInterfaces(), new PermissionInvocationHandler(realFinancialStatements, true)); } public static void main(String[] args) { FinancialStatements realFinancialStatements=new RealFinancialStatements(); FinancialStatements proxyFinancialStatements = getProxyFinancialStatements(realFinancialStatements); proxyFinancialStatements.check(); } }
package com.ez; /** * 通过实现同一接口,代理能在任何财务系统出现的地方取代它。 * @author 窗外赏雪(EZ编程网) */ public interface FinancialStatements { void check(); }
package com.ez.impl; import com.ez.FinancialStatements; /** * 真正做事的对象,它是被proxy代理和控制访问的对象。 * @author 窗外赏雪(EZ编程网) */ public class RealFinancialStatements implements FinancialStatements{ @Override public void check() { System.out.println("====欢迎查看年度财务报表===="); System.out.print("总支出:$99999"); System.out.println("总收入:$100000"); System.out.println("盈利:$1"); System.out.println("==========================="); } }
package com.ez.impl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import com.ez.FinancialStatements; /** * InvocationHandler实现了代理的行为,正如你将看到,Java负责创建真实代理类和对象。 * 我们只需要提供在方法调用时知道做什么的handler。 * Java帮我们创建了代理对象,把控制访问的业务逻辑抽离出来,单独放到InvocationHandler的实现类中。 * 这样可以让开发把精力更多的放在有价值的业务实现上面。 * @author 窗外赏雪(EZ编程网) */ public class PermissionInvocationHandler implements InvocationHandler{ private FinancialStatements realFinancialStatements; private boolean isAdmin; public PermissionInvocationHandler(FinancialStatements realFinancialStatements,boolean isAdmin) { this.realFinancialStatements=realFinancialStatements; this.isAdmin=isAdmin; } /** * 每次proxy的方法被调用,就会导致proxy调用此方法。 * method就是proxy被调用的方法,args就是方法的参数。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(isAdmin){ method.invoke(realFinancialStatements, args); }else{ System.out.print("sorry no permission"); } return null; } }