代理模式

参考
代理模式深入(一)——静态到动态
代理模式深入(二)——动态代理实现机制
JAVA与模式-代理模式
Android插件化原理解析——Hook机制之动态代理

周末放假,小孙睡到12点才告别周公醒来,顿时饥肠辘辘。舍长小王正准备去食堂买饭,作为一个好舍长小王主动要帮小孙带饭。小孙点了米饭、宫保鸡丁、芬达。小孙起床洗漱,然后静待舍长。

一、静态代理
/**
 * 宿舍成员接口
 * 开发时间:2014-8-12 下午9:17:19
 */
public interface DormitoryMember {
    //买主食
    public boolean buyStapleFood(String stapleFoodName);
    //买菜
    public boolean buyDish(String dishName);
    //买饮料
    public boolean buyDrink(String drinkName);   
}

//代理类宿舍长小王
public class DormitoryMemberProxy implements DormitoryMember {
    //持有一个被代理的对象
    private DormitoryMember dormitoryMember;
    //构造时传入被代理对象
    public DormitoryMemberProxy(DormitoryMember dormitoryMember){
        this.dormitoryMember=dormitoryMember;
    }
    //代理买主食
    public boolean buyStapleFood(String stapleFoodName) {
        return dormitoryMember.buyStapleFood(stapleFoodName);
    }
    //代理买菜
    public boolean buyDish(String dishName) {
        return dormitoryMember.buyDish(dishName);
    }
    //代理买饮料
    public boolean buyDrink(String drinkName) {
        return dormitoryMember.buyDrink(drinkName);
    }
}

//实际的类
public class DormitoryMemberImpl implements DormitoryMember {
    //买主食
    public boolean buyStapleFood(String stapleFoodName) {
        try{
            System.out.println("买到主食" + stapleFoodName);
        }catch(Exception e){
            return false;
        }
        return true;
    }
    //买菜
    public boolean buyDish(String dishName) {
        try{
            System.out.println("买到菜:" + dishName);
        }catch(Exception e){
            return false;
        }
        return true;
    }
    //买饮料
    public boolean buyDrink(String drinkName) {
        try{
            System.out.println("买到饮料:" + drinkName);
        }catch(Exception e){
            return false;
        }
        return true;
    }
}

故事继续

宿舍长去到食堂买饭,结果小孙要的芬达断货了。于是宿舍长久提着米饭和菜就回去了,小孙吃着米饭和菜没有饮料难以下咽。但是,也不好说宿舍长,舍长是好舍长啊。将就的吃了午饭。时间来的晚饭时间,小孙在英雄联盟里正杀的起劲,那又闲工夫去买饭于是这工作有落到了好舍长身上。这次小孙学聪明了交代舍长说,如果有他点的东西没有了就打个电话回来在决定买什么替代。

问题来了

这里需求已经改变了,如果小孙要的东西没有了的话,要给小孙打电话以决定是不是买其他的或者不买。这个电话自然是宿舍长来打。所以我们要对代理类进行修改!这也就是使用代理模式的好处,我们关闭了对实际类的修改。代理类修改后的代码如下:

//代理类
public class DormitoryMemberProxy implements DormitoryMember {
    //持有一个被代理的对象
    private DormitoryMember dormitoryMember;
    //构造时传入被代理对象
    public DormitoryMemberProxy(DormitoryMember dormitoryMember){
        this.dormitoryMember=dormitoryMember;
    }
    //代理买主食
    public boolean buyStapleFood(String stapleFoodName) {
        boolean buySuccess=dormitoryMember.buyStapleFood(stapleFoodName);
        if( buySuccess=false){
            System.out.println("给小孙打电话");
            return false;
        }else{
        return true;
        }
    }
    //代理买菜
    public boolean buyDish(String dishName) {
        boolean buySuccess=dormitoryMember.buyDish(dishName);
        if( buySuccess=false){
            System.out.println("给小孙打电话");
            return false;
        }else{
        return true;
        }
    }
    //代理买饮料
    public boolean buyDrink(String drinkName) {
        boolean buySuccess=dormitoryMember.buyDrink(drinkName);;
        if( buySuccess=false){
            System.out.println("给小孙打电话");
            return false;
        }else{
        return true;
        }
    }
}

问题(1)
看代码可以知道,我们要对代理类中的每一个方法都进行同样的修改。极限假设我们实现的这个接口有1000个方法,哭死都不冤枉。

问题(2)
极限假设,伟大的宿舍长不止帮小孙买饭,还要帮小李买衣服,还要帮小赵买电脑。换句话说宿舍长要代理不同的接口。但是这是不可能实现的,因为在代理类所持有的实际类是确定的,也就是说每一个代理类只能代理一个借口。

显然很不科学,这么多的代码重复出现。有不科学的情况出现,自然就会有办法因对。最怕的是没有发现问题,没有问题就没有改进。下面我们上动态代理。

二、动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//继承了InvocationHandler接口的handler类
public class ProxyHandler implements InvocationHandler {
    private Object target;
    public ProxyHandler(Object target){
        this.target=target;
        }
    //所有代理方法的公共处理方法
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result =null;
        result=method.invoke(target, args);
        //要买的东西断货时
        if(result.toString().equals("false")){
            System.out.println("给小孙打电话");
            return result;
        }else{
        return result;
        }
    }
}

import java.lang.reflect.Proxy;
//代理工厂类
public class ProxyFactory {
    //实际类
     private Object target;
     public ProxyFactory(Object target){
         this.target=target;
     }
     //根据传入的实际类生成代理
     public Object getInstance(){
         //实例化一个继承了InvocationHandler接口的预处理对象
         ProxyHandler handler=new ProxyHandler(target);
        //反射得到代理类
         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(),
                 handler);//handler即集中处理在动态代理类对象上的方法调用     
     }
}

public class Client {
   public static void main(String[] args){
       //真正得到的代理类在客户端强转
       DormitoryMember dormitoryMember=(DormitoryMember)
       new ProxyFactory(new DormitoryMemberImpl()).getInstance();
       dormitoryMember.buyStapleFood("米饭");
   }
}

InvocationHandler接口只有一个Invoke方法。这个方法就是对代理类的方法的加强。它实际是对代理方法的一个拦截处理,也就是说当调用代理类中的方法时,不论哪一个都相当于调用了Invoke方法。而在Invoke方法中对代理的方法做了一个包装加强。Invoke方法有三个参数如下:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        //代码略
    }

proxy :在其上调用方法的代理实例
method:对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args: 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。

Proxy类
这个类是动态的产生代理类的关键,先看看下表对该类的方法有些了解。这其中目前我觉得比较重要的是NewProxyInstance方法。所以这个介绍一下,其他的就查Java的api文档吧。

public static Object newProxyInstance(ClassLoader loader,
                                      Class[] interfaces,
                                      InvocationHandler h)

参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序

返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

你可能感兴趣的:(代理模式)