设计模式——代理模式

定义

  为其他对象提供一种代理以控制对这个对象的访问。

  代理模式也叫委托模式,日常生活中很常见。帮别人做某某事情时候,自己就是别人的代理;让别人帮自己做什么事情时候,别人就是自己的代理。

通用类图

Subject抽象主题角色

  抽象主题类是一个普通的业务类型的定义,规定要做什么事情。可以是一个抽象类也可以是一个接口。

RealSubject具体主题角色

  被委托(被代理)的角色,是真正事情处理的执行者。

Proxy代理主题角色

  委托类(代理类),负责对真实角色的调用,把所有主题限定的方法委托给具体主题角色,也可以在真实主题角色处理前做一些预处理,或在真实主题角色处理后做一些善后处理(比如Spring的AOP)。

普通代理

小时候在家都是妈妈做饭,做饭需要洗菜,切菜,炒菜……有时候妈妈有事忙没按时回家,姐姐就偷偷代替妈妈给大家做饭。如果将做饭简单理解为洗菜、切菜、炒菜三个步骤,从程序员的角度来记录做饭这件事就很简单了。妈妈和姐姐都需要洗菜、切菜然后再炒菜,所以可以规定炒菜的三个步骤,然后分别实现这三个步骤,所以可以简单表示为:

ICook定义了炒菜必须要经历的流程:

public interface ICook {
    /**洗菜*/
    void washFood();
    /**切菜*/
    void cutFood();
    /**炒菜*/
    void stirFood();
}

妈妈(对于一家人来说,天天吃妈妈做的饭,妈妈就是一个Cooker)做饭的流程:

public class Cooker implements ICook {
    @Override
    public void washFood() {
        System.out.println("妈妈在洗菜……");
    }

    @Override
    public void cutFood() {
        System.out.println("妈妈在切菜……");
    }

    @Override
    public void stirFood() {
        System.out.println("妈妈在炒菜……");
    }
}

一个能代理妈妈做饭(实现了ICook接口)的角色

public class CookerProxy implements ICook {
    private ICook cooker;

    public CookerProxy(ICook cooker){
        this.cooker = cooker;
    }

    @Override
    public void washFood() {
        this.cooker.washFood();
    }

    @Override
    public void cutFood() {
        this.cooker.cutFood();
    }

    @Override
    public void stirFood() {
        this.cooker.stirFood();
    }
}

姐姐代替妈妈做饭

    public void test() {
        Cooker cooker = new Cooker();
        ICook cook = new CookerProxy(cooker);
        cook.washFood();
        cook.cutFood();
        cook.stirFood();
    }

姐姐学会了妈妈的手艺,如果不是亲眼看到,还不知道是姐姐做的饭,这就是真实生活中一个简单的代理例子。

强制代理

普通代理是通过代理找到真实的对象,由真实对象去执行动作;但是强制代理却是要通过真实角色查找代理,否则不能访问。也就是不管是通过代理类还是直接new一个真实对象都不能访问,只有通过真实对象委托的代理才能访问。

还是上面的那个例子,后来妈妈上班去了,中午不能回家做饭了,拿普通代理方式来说,只要是一个会做菜的人,就可以趁着妈妈不在冒充妈妈的身份替妈妈做菜,万一做的很难吃,岂不是把妈妈的招牌给毁了吗?

于是,妈妈想到了个办法,必须由她指定谁可以代替她做菜,不是由她亲自指定的人无法替她做菜:

定义了一个getProxy方法,由Cooker来指定自己的代理,并且代理自己做事之前,先验证下身份:

public class Cooker implements ICook {
    private ICook cook;

    @Override
    public void washFood() {
        if (this.isProxy()) {
            System.out.println("妈妈在洗菜……");
        } else {
            System.out.println("请妈妈指定的人来炒菜……");
        }
    }

    @Override
    public void cutFood() {
        if (this.isProxy()) {
            System.out.println("妈妈在切菜……");
        } else {
            System.out.println("请妈妈指定的人来炒菜……");
        }
    }

    @Override
    public void stirFood() {
        if (this.isProxy()) {
            System.out.println("妈妈在炒菜……");
        } else {
            System.out.println("请妈妈指定的人来炒菜……");
        }
    }

    @Override
    public ICook getProxy() {
        this.cook = new CookerProxy(this);
        return this.cook;
    }

    private Boolean isProxy() {
        if (this.cook == null) {
            return false;
        }
        return true;
    }
}

只有通过Cooker对象获取(getProxy)代理才能做菜。

代理扩展

姐姐做了几顿饭之后也开始不乐意了,凭什么我要占用玩的时间来给大家做饭?妈妈想了一个好主意,姐姐每做一顿饭可以从妈妈那里领到1块钱,于是姐姐又高高兴兴地给大家做饭了。做饭和付钱是两个单独的功能,是不能混在一起的,可以将上面类图稍作修改:

这样,在每次做好饭之后就可以记账应该拿到1元钱

public class CookerProxy implements ICook, IPay {
    private ICook cooker;

    public CookerProxy(ICook cooker){
        this.cooker = cooker;
    }

    @Override
    public void washFood() {
        this.cooker.washFood();
    }

    @Override
    public void cutFood() {
        this.cooker.cutFood();
    }

    @Override
    public void stirFood() {
        this.cooker.stirFood();
        this.pay();
    }

    @Override
    public void pay() {
        System.out.println("做完饭拿到了1元钱");
    }

    @Override
    public ICook getProxy() {
        /** 可以指定代理的代理,暂时没有就是自己 */
        return this;
    }
}

代理不仅可以实现主题的接口,还可以实现其他的接口,在目标行为基础之上做个性化处理。

动态代理

后来姐姐长大了,也出门在外了,我们也不能每次都等着吃饭,于是各自都学会了做饭,只要妈妈不在,谁方便了谁就做饭。也就是说,只有饭已经在被做了才知道是谁在做,更灵活了,这就不能提前指定谁来做饭了。对应程序中是:只有在运行时才知道真实类型是谁。

将类图稍作修改,如下所示。

InvocationHandler是java提供的动态代理接口,CookerHandler实现了InvocationHandler接口,这样可以动态指定代理,运行时才知道类型。

public class CookerHandler implements InvocationHandler {
    Class clazz = null;

    Object obj = null;

    public CookerHandler(Object _obj) {
        this.obj = _obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }
}

动态代理客户端使用示例:

    public void dynamicProxy(){
        proxy.dynamicproxy.ICook cook = new proxy.dynamicproxy.Cooker();

        ClassLoader classLoader = cook.getClass().getClassLoader();
        Class<?>[] interfaces = cook.getClass().getInterfaces();
        CookerHandler handler = new CookerHandler(cook);

        proxy.dynamicproxy.ICook proxyInstance = (proxy.dynamicproxy.ICook) Proxy.newProxyInstance(classLoader, interfaces, handler);
        proxyInstance.washFood();
        proxyInstance.cutFood();
        proxyInstance.stirFood();
    }

 

参考

动态代理可以参考:Java动态代理之JDK实现和CGlib实现(简单易懂)

本文示例代码:Proxy Demo

posted @ 2018-10-13 23:17 Mr.yang.localhost 阅读(...) 评论(...) 编辑 收藏