定义
代理模式:给某一个对象提供一个代理,并由代理对象控制原对象的引用。代理模式是一种对象结构型模式。
代理模式根据其目的和实现不同可分为很多种类,其中常用的几种代理模式如下:
- 远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中。
- 虚拟代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
- 保护代理:控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。
- 缓冲代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 智能引用代理:当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来。
适用场景
- 当客户端对象需要访问远程主机中的对象时,可以使用远程代理。
- 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时,可以使用虚拟代理。
- 当需要控制一个对象的访问,为不同用户提供不同级别的访问权限时,可以使用保护代理。
- 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时,可以使用缓冲代理。
- 当需要为一个对象的访问提供一些额外的操作时,可以使用智能引用代理。
代理模式概述
结构图
静态代理
通常情况下,每一个代理类编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称为静态代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public interface Subject(){ void request(String message); }
public class RealSubject() implements Subject{ @Override public void request(String message){ System.out.println("send the " + messagejkj); } }
public class Proxy implements Subject{ private RealSubject realSubject = new RealSubject(); public void preRequest(){ System.out.println("prepare the request"); } public void postRequest(){ System.out.println("send the request success"); } @Override public void request(String message){ preRequest(); realSubject.request(message); postRequest(); } }
public class Client{ public static void main(String[] args){ Subject subject = new Proxy(); subject.request("proxy pattern"); } }
|
动态代理
在传统的代理模式中,客户端通过Proxy类调用RealSubject类的reuqest()方法,同时还可以在代理类中封装其他方法。如果按照这种方法使用代理模式,那么代理类和真实主题类都应该是事先已经存在的,代理类的接口和说代理方法都已明确指定,如果需要为不同的真实主题类提供代理类或者代理一个真实主题类中的不同方法,都需要增加新的代理类,这将导致系统中的类个数急剧增加。
动态代理可以让系统根据实际需求来动态创建代理类,让同一个代理类能够处理多个不同的真实主题类,而且可以代理不同的方法。
Java实现动态代理需要用到位于java.lang.reflect包中的一些类。
- Proxy
Proxy
类提供了用于创建动态代理类和胜利对象的方法,它是所创建的动态代理类的父类,最常用的方法:
public static Class<?> getProxyClass(Classloader loader, Class<?>...interfaces)
public static Object newProxyInstance(ClassLoader loader, Class[]<?> interfaces, InvocationHandler h)
- InvocationHanlder
InvocationHandler
接口是代理处理程序类的实现接口,该接口作为代理实例的调用者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者
public Object invoke(Object proxy, Method method, Object[] args)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| public interface AbstractUserDAO{ boolean findUserById(String userId); }
public interface AbstractDocumentDAO{ boolean deleteDocumentById(String documentId); }
public class UserDAO implements AbstractUserDAO{ @Override public boolean findUserById(String userId){ if(userId.equalsIgnoreCase("Sam")){ System.out.println("select ID =" + userId + "information successfully!") return true; } else { System.out.println("select ID ="+ userId + "information unsuccessfully!"); return false; } }
public class DocumentDAO implements AbstractDocumentDAO{ @Override public boolean deleteDocumentById(String documentId){ if(userId.equalsIgnoreCase("Sam"){ System.out.println("delete ID =" + documentId + " document successfully!"); return true; } else { System.out.println("delete ID =" + documentId + " document unsuccessfully!"); return false; } } }
public class DAOLogHandler implements InvocationHandler{ private Object object; public DAOLogHandler(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ beforeInvoke(); Object result = method.invoke(object,args); afterInvoke(); return result; } public void beforeInvoke(){ System.out.println("invoke time=" + System.currentTimeMillis()); } public void afterInvoke(){ System.out.println("invoke finish!") } }
public class Client{ public static void main(String[] args){ InvoationHandler handler = null; AbstractUserDAO userDAO = new UserDAO(); handler = new DAOLogHandler(userDAO); AbstactUserDAO proxy = null; proxy = (AbstractUserDAO) proxy.newInstance(AbstractUserDAO.class.getClassLoader(),new Class[]{AbstractUserDAO.class},handler); proxy.findUserById("Sam"); System.out.println("-------------------"); AbstractDocumentDAO documentDAO = new DocumentDAO(); handler = new DAOLogHandler(documentDAO); AbstractDocument proxy_doc = null; proxy_doc = (AbstractDocumentDAO) proxy_doc.newInstance(AbstractDocument.class.getClassLoader(),new Class[]{AbstractDocumentDAO.class},handler); proxy_doc.deleteDocumentById("Sam"); } }
|
代理模式总结
优点
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度,满足迪米特法则。
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有良好的灵活性和可拓展性。
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式会造成请求的处理速度变慢,例如保护代理。