设计模式:学习笔记(12)——代理模式
代理模式
挟天子以令诸侯,曹操与汉献帝的关系,映射了代理模式的。所谓代理者是指一个类别可以作为其他东西的接口。代理者使用代理对象完成用户请求,屏蔽用户对真实对象的访问。在软件设计中,使用代理模式的意图有很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节(如RMI),也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。
延迟加载
以一个简单的示例来阐述使用代理模式实现延迟加载的方法及其意义。假设某客户端软件有根据用户请求去数据库查询数据的功能。在查询数据前,需要获得数据库连接,软件开启时初始化系统的所有类,此时尝试获得数据库连接。当系统有大量的类似操作存在时 (比如 XML 解析等),所有这些初始化操作的叠加会使得系统的启动速度变得非常缓慢。为此,使用代理模式的代理类封装对数据库查询中的初始化操作,当系统启动时,初始化这个代理类,而非真实的数据库查询类,而代理类什么都没有做。因此,它的构造是相当迅速的。
在系统启动时,将消耗资源最多的方法都是用代理模式分离,可以加快系统的启动速度,减少用户的等待时间。而在用户真正做查询时再由代理类取加载真实的数据库查询类,完成用户请求。这个过程就是使用代理模式实现了延迟加载。
延迟加载的核心思想:如果当前并没有使用这个组件,则不需要真正地初始化它,使用一个代理对象替代它原有的位置,只要在真正需要的时候才对他进行加载。
下面是一个延迟加载代理示例:
public interface IDBQuery { String request(); } public class DBQuery implements IDBQuery{ public DBQuery(){ try{ Thread.sleep(1000);//假设数据库连接等耗时操作 }catch(InterruptedException ex){ ex.printStackTrace(); } } @Override public String request() { // TODO Auto-generated method stub return "request string"; } } public class DBQueryProxy implements IDBQuery{ private DBQuery real = null; @Override public String request() { // TODO Auto-generated method stub //在真正需要的时候才能创建真实对象,创建过程可能很慢 if(real==null){ real = new DBQuery(); }//在多线程环境下,这里返回一个虚假类,类似于 Future 模式 return real.request(); } } public class Main { public static void main(String[] args){ IDBQuery q = new DBQueryProxy(); //使用代里 q.request(); //在真正使用时才创建真实对象 } }
动态代理
动态代理指在运行时动态生成代理类。即,代理类的自己吗将在运行时生成并载入当前代理的ClassLoader。与静态处理类相比,动态类不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护。使用一些动态代理的生成方法可以在 运行时定制代理类的执行逻辑,从而大大提高系统的灵活性。
public class DBQueryHandler implements InvocationHandler { IDBQuery realQuery = null ; //定义主题接口 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(realQuery == null) realQuery = new DBQuery(); return realQuery.request(); } /** * 返回代理类的实例 * @return */ public static IDBQuery createProxy(){ IDBQuery proxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new DBQueryHandler()); return proxy; } }
以上代码生成了一个实现了IDBQuery接口的代理类,代理类的内部逻辑由DBQueryHandle决定。
生成代理类后,由newProxyInstance()方法返回该代理类的一个实例。