GoF之代理模式详解

代理(ProxyPattern)模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

优点
1、职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
2、代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
3、高扩展性。按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。动态:是在程序运行时通过反射机制动态创建的。

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

静态具体实现:通过浏览器上网,可以看新闻、学习。
//定义上网行为接口
publicinterface Internet {
public void news();//上网可以看新闻
public void study();//上网可以学习
}

//用真实的浏览器实现上网接口
publicclass BrowserPeal implements Internet{
public void news() {
System.out.println(“上网看新闻”);
}
public void study() {
System.out.println(“上网学习”);
}
}

//用代理浏览器实现上网接口,并持有真实浏览器的对象,便于调用。
publicclass BrowserProxy implements Internet{
BrowserPeal browserPeal;
public BrowserProxy(Internet internet) {
this.browserPeal=internet;
}
public void news() {
browserPeal.news();//调用真实浏览器的方法
}
public void study() {
browserPeal.strdy();//调用真实浏览器的方法
}
}

//客户端
publicclass Client {
public static void main(String[] args) {
BrowserPeal br=new BrowserPeal();
Internet bp=newBrowserProxy(br);//生成代理对象
bp.news();//看新闻
bp.study();//学习
}
}
运用简单工厂
由于对象的生成放在了客户端,我们可以轻易看出运用了代理模式,现在用简单工厂封装一下对象的创建。
publicclass InternetFactory {
Internet internet;
public Internet getInstance(){
internet = new BrowserProxy(new BrowserPeal());//建立代理对象
return internet;
}
}

publicclass Client {
public static void main(String[] args) {
InternetFactory itfa = new InternetFactory();
Internet internet = itfa.getInstance();
internet.news();
internet.study();
}
}

静态代理类
优点:
代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)。

缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。出现大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态具体实现:JDK 自带的动态代理。

java.lang.reflect.Proxy:生成动态代理类和对象;
java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现
对真实角色的代理访问。
每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。
//定位上网的接口
publicinterface Internet {
public void news();//上网可以看新闻
public void study();//上网可以学习
}

//真实的对象
publicclass BrowserPeal implements Internet{
@Override
public void news() {
System.out.println(“上网看新闻”);
}
@Override
public void study() {
System.out.println(“上网学习”);
}
}

//InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类.
//如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
publicinterface InvocationHandler {
public Object invoke(Object proxy, Methodmethod, Object[] args)
throws Throwable;
}

//处理器对象:MyInvocationHandler.java
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;

publicclass MyInvocationHandler implements InvocationHandler {
//因为需要处理真实角色,所以要把真实角色传进来
Object target;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
//nvoke接口的方法,proxy表示代理类,method表示原对象被调用的方法,args表示方法的参数
@Override
public Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable {
System.out.println(“调用代理类”);
Object obj=null;
if(“news”.equals(method.getName())){
System.out.println("++++++before " + method.getName() +"++++++");
Object obj = method.invoke(target,args);
System.out.println("++++++after " + method.getName() +"++++++")
System.out.println(“调用的是看新闻的方法”);
}else {
Object obj = method.invoke(target,args);
System.out.println(“调用的是学习的方法”);
}
return obj;
}
}
//客户端:Main.java
importjava.lang.reflect.Proxy;
publicclass Client {
public static void main(String[] args) {
//真实对象
Internet internet = new BrowserPeal();
MyInvocationHandler myInvocationHandler= new MyInvocationHandler(internet);
//代理对象.第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
Internet proxyClass =(Internet)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),newClass[]{Internet.class}, myInvocationHandler);
//只要你在newProxyInstance方法中指定代理需要实现的接口,指定用于自定义处理的InvocationHandler对象.
//整个代理的逻辑处理都在你自定义的InvocationHandler实现类中进行处理。
//至此,而我们终于可以从不断地写代理类用于实现自定义逻辑的重复工作中解放出来了,从此需要做什么,交给InvocationHandler。
proxyClass.news();
proxyClass.strdy();
}
}
如果把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,在动态生成的代理类的任意方法中都会间接调用InvocationHandler->invoke(proxy, method, args)方法.
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。在接口方法数量比较多的时候,不需要像静态代理那样每一个方法进行中转。动态代理的应用使我们的类职责更加单一,复用性更强。

欢迎关注微信公众号“专注一行代码”

https://mp.weixin.qq.com/s/MLAYSB5P4B7di605aCMPYg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值