设计模式学习-动态代理
代理模式
定义
给某一个对象提供一个代理,并由代理对象来控制对原对象的访问。
结构
- Subject抽象主题角色。声明了真实主题和代理主题的共同接口,这样可以将代理主题取代真实主题,达到取代和控制的目的。
- Proxy代理主题角色。包含了对真实主题的引用,同时控制这着对真实主题的访问,具有附加操作、控制访问的功能。
- RealSubject真实主题。实现了抽象主题角色的接口,具有真正的业务功能。
与装饰模式的区别
通过和装饰模式的类图对比可以发现,代理模式的类图和装饰模式的类图几乎完全一样,甚至代码结构都完全一样。它们最重要的区别在于
- 装饰模式的思想在于对被装饰对象功能的扩展,比如给手机增加手机壳,没有改变手机的功能,但是更加美观。代理模式在与对被代理对象的完全控制,不光可以对功能进行扩展,更加控制着功能可不可以执行,比如常见的重要信息查询功能,可以通过代理模式对访问权限进行控制,并且增加记录日志功能。
- 装饰模式中的装饰类只需要编写一个,可以接受任意构件;代理模式为每一个真实主题都要创建一个代理类,数量上要比装饰模式多很多。
示例
java中比较简单的实现代理模式的方法有两种,静态代理和jdk自带的proxy动态代理,用查询功能做示例。
package proxy.other;
public interface Searcher {
void search(String userId);
}
package proxy.other;
public class ConcreteSearcher implements Searcher {
@Override
public void search(String userId) {
System.out.println("查询订单列表");
}
}
package proxy.other;
public class Log {
public void log(String userId, Boolean success) {
String result = success ? "成功" : "失败";
System.out.println(userId + "查询订单列表" + result);
}
}
package proxy.other;
public class LoginValidator {
public boolean volidate(String userId) {
if ("zhangsan".equals(userId)) {
System.out.println("校验通过");
return true;
}
System.out.println("校验失败");
return false;
}
}
静态代理
package proxy.staticproxy;
import proxy.other.ConcreteSearcher;
import proxy.other.Log;
import proxy.other.LoginValidator;
import proxy.other.Searcher;
public class StaticProxySearcher implements Searcher {
private Searcher searcher = new ConcreteSearcher();
private LoginValidator validator = new LoginValidator();
private Log log = new Log();
@Override
public void search(String userId) {
System.out.println(userId + "登陆");
boolean success = Boolean.FALSE;
if (validator.volidate(userId)) {
searcher.search(userId);
success = Boolean.TRUE;
}
log.log(userId, success);
}
}
动态代理
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import proxy.other.Log;
import proxy.other.LoginValidator;
public class DynamicProxy implements InvocationHandler {
private Object target;
private LoginValidator validator = new LoginValidator();
private Log log = new Log();
public DynamicProxy(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(args[0] + "登陆");
Object result = null;
boolean success = Boolean.FALSE;
if (validator.volidate((String) args[0])) {
result = method.invoke(target, args);
success = Boolean.TRUE;
}
log.log((String) args[0], success);
return result;
}
}
package proxy;
import java.lang.reflect.Proxy;
import proxy.other.ConcreteSearcher;
import proxy.other.Searcher;
import proxy.staticproxy.StaticProxySearcher;
public class Test {
public static void main(String[] args) {
System.out.println("静态代理");
Searcher searcher = new StaticProxySearcher();
searcher.search("lisi");
System.out.println("----------------------------");
System.out.println("动态代理");
Searcher searcher2 = (Searcher) Proxy.newProxyInstance(Searcher.class.getClassLoader(),
new Class[] { Searcher.class }, new DynamicProxy(new ConcreteSearcher()));
searcher2.search("zhangsan");
}
}
优点
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合性,对于新增内容,无需修改源代码,只要新增代理类就可以,符合开闭原则。
- 动态代理可以同时代理大量类,不需要编写新的代理类,对于相同功能的添加十分方便。
适用场景
- 增加功能
- 权限访问控制