Android设计模式之代理模式 Proxy

一.概述

       代理模式也是平时比較经常使用的设计模式之中的一个,代理模式事实上就是提供了一个新的对象,实现了对真实对象的操作,或成为真实对象的替身.在日常生活中也是非经常见的.比如A要租房,为了省麻烦A会去找中介,中介会替代A去筛选房子,A坐享中介筛选的结果,而且交房租也是交给中介,这就是一个典型的日常生活中代理模式的应用.平时打开网页,最先开到的一般都是文字,而图片等一些大的资源都会延迟载入,这里也是使用了代理模式.

       代理模式的组成:

       Abstract Subject:抽象主题-声明真实主题和代理主题共同的接口

       Real Subject:真实主题-真实的对象,须要被代理主题引用

       Proxy Subject:代理主题-由于ProxySubject引用了RealSubject,而且实现了跟RealSubject一样的接口,所以ProxySubject能够操作RealSubject,还能够提供一些附加操作,比如before & after




       代理模式经常使用基于场景的分类:

       1.Virtual Proxy:虚拟代理事实上就是通过代理的模式对消耗资源比較大的对象做了一个延迟载入,就是什么时候用到这个对象才去创建它.

       2.Remote Proxy:远程代理是比較经典的应用了,类似于C/S模式(主要拦截并控制远程方法的调用,做代理防火墙之类的).

       3.Smart Reference Proxy:智能引用代理能够给引用的对象提供一些额外的操作,比如实现里面中介Searching和Prepare contract的动作.

       4.Access Proxy;保护代理能够控制一个对象的訪问,必要时候提供一系列的权限管理.

       5.Copy-on-write Proxy:写时拷贝(克隆)代理事实上是Virtual Proxy的分支,提供了拷贝大对象的时候仅仅有在对象真正变化后才会进行拷贝(克隆)的操作(延迟拷贝).


       代理模式的优缺点:

       长处:

       1.代理作为调用着和真实对象的中间层,减少了模块间和系统的耦合性

       2.能够以一个小对象代理一个大对象,达到优化系统提高执行速度的目的

       3.提供RealSubject的权限管理

       4.easy扩展,RealSubject和ProxySubject都接口化了,RealSubject更改业务后仅仅要接口不变,ProxySubject能够不做不论什么改动.

       缺点:

       1.同长处1,由于调用者和真实对象多了一个中间层,所以会添加调用响应的时间

二.实现

       这里就拿A找中介租房为Demo来构建代理模式.

1.普通代理

       依据场景先定义一个抽象主题,IHouse,提供三个方法,各自是获取房屋信息,签合同和付租金.

/**
 * Created by jesse on 15-7-24.
 */
public interface IHouse {
    void getHouseInfo();
    void signContract();
    void payFees();
}
       接下来定义真实主题,并实现IHouse接口.添加房屋名称和价格两个属性,填充借口方法,在获取房屋信息的时候就把房屋名称和价格log出来;签合同的时候log出签合同的时间,付租金的时候log出价格.

public class House implements IHouse{
    private final String TAG = House.class.getSimpleName();
    private String name;
    private double price;

    public House(String name, double price){
        this.name = name;
        this.price = price;
    }

    @Override
    public void getHouseInfo() {
        Log.i(TAG, "House Info- name:" + name + "  ¥:" + price);
    }

    @Override
    public void signContract() {
        Log.i(TAG, "Contract:" + name + "  signed at" +
               new SimpleDateFormat("HH:mm:ss").format(SystemClock.uptimeMillis()));
    }

    @Override
    public void payFees() {
        Log.i(TAG, "Bill: name-" + name + "  $-" + price);
    }
}
       定义房屋代理,相同须要实现IHouse接口,并持有House的引用.能够看到代理类事实上就像有封装House,提供了一些附加操作,比如客户要看房子的时候代理会先检索自己库存的房屋信息,签合同之前要准备合同之类的.

public class ProxyHouse implements IHouse{
    private final String TAG = ProxyHouse.class.getSimpleName();
    private IHouse house;
    public ProxyHouse(IHouse house){
        this.house = house;
    }
    @Override
    public void getHouseInfo() {
        Log.i(TAG, "searching");
        house.getHouseInfo();
        Log.i(TAG, "search finished");
    }

    @Override
    public void signContract() {
        Log.i(TAG, "prepare contract");
        house.signContract();
    }

    @Override
    public void payFees() {
        house.payFees();
    }
}
       对于客户来说,全然不用跟House进行直接交互,这里先定义一个房子叫唐顿庄园,租金5k,建立一个房屋代理,把唐顿庄园托付给代理.客户要找房子,签合同,付租金直接找代理即可了.

        IHouse house = new House("Downton Abbey", 5000);
        IHouse proxyHouse = new ProxyHouse(house);
        Log.i(TAG, "looking for a perfect house");
        proxyHouse.getHouseInfo();
        Log.i(TAG, "thinking");
        proxyHouse.signContract();
        proxyHouse.payFees();
        Log.i(TAG, "so easy");

       整个代理模式的流程能够从以下的时序图展示出来.Client仅仅跟代理进行交互.

2.虚拟代理

       虚拟代理前面有介绍,就是基于代理模式又做了延迟载入来节省内存,可是假设某个对象要在多个没有固定时序地方使用的时候就要进行判空,也会一定程度上牺牲性能(有点像代理模式+懒汉模式).这里还是拿租房的样例来展示.

       这里就如果House是一个非常庞大的对象,在创建的时候非常耗费资源,那我们就更改成当Custom须要用它的时候才去初始化.这里就在ProxyHouse构造的时候先判House的引用是否为空,然后才会初始化House,当然如果这里有多线程并发的话能够依据不同的场景进行加锁或者双检锁来保证线程安全.

    public ProxyHouse(){
        if (null == house)
            house = new House("Downton Abbey", 5000);
    }
        IHouse proxyHouse = new ProxyHouse();
        Log.i(TAG, "looking for a perfect house");
        proxyHouse.getHouseInfo();
        Log.i(TAG, "thinking");
        proxyHouse.signContract();
        proxyHouse.payFees();
        Log.i(TAG, "so easy");
3.强制代理

       强制代理是反其道而行之的代理模式,普通情况下代理模式都是通过代理来找到真实的对象,而强制代理则是通过真实对象才干找到代理也就是说由真实对象指定代理,当然终于訪问还是通过代理模式訪问的.从名字还能看出它跟其它代理的一个不同,就是强制用代理.拿上面普通代理的样例来说,Custom看不到实体的House的时候它仅仅能通过代理来訪问,可是因为没有限制,Custom也能够直接绕过ProxyHouse来訪问House,可是强制代理就多了一个限制,Custom必须通过ProxyHouse才干訪问House.就像一些房东嫌麻烦,有房客直接电话过来说要看房,房东给出一个中介的电话说你跟中介联系吧.

       首先须要在接口里面加入一个获代替理的接口

public interface IHouse {
    void getHouseInfo();
    void signContract();
    void payFees();
    IHouse getProxy();
}
       真实对象实现接口,并在getProxy中实例化代理,同一时候在其它方法里面做代理推断,仅仅有使用自身自定的代理才会正常进行.

public class House implements IHouse{
    private final String TAG = House.class.getSimpleName();
    private String name;
    private double price;
    private IHouse proxy;

    public House(String name, double price){
        this.name = name;
        this.price = price;
    }

    @Override
    public void getHouseInfo() {
        if (isProxy())
            Log.i(TAG, "House Info- name:" + name + "  ¥:" + price);
        else
            Log.i(TAG, "Please use correct proxy");
    }

    @Override
    public void signContract() {
        if (isProxy())
            Log.i(TAG, "Contract:" + name + "  signed at" +
                    new SimpleDateFormat("HH:mm:ss").format(SystemClock.uptimeMillis()));
        else
            Log.i(TAG, "Please use correct proxy");

    }

    @Override
    public void payFees() {
        if (isProxy())
            Log.i(TAG, "Bill: name-" + name + "  $-" + price);
        else
            Log.i(TAG, "Please use correct proxy");
    }

    @Override
    public IHouse getProxy() {
        if (null == proxy)
            proxy = new ProxyHouse(this);
        return proxy;
    }

    private boolean isProxy(){
        if (null == proxy)
            return false;
        else
            return true;
    }
}
       假设这个时候直接操作House对象,或者通过Custom构建的代理来訪问都会返回下面结果


       所以我们必须使用由真实对象指定的代理才干够正常得訪问.

        IHouse house = new House("Downton Abbey", 5000);
        house = house.getProxy();
        Log.i(TAG, "looking for a perfect house");
        house.getHouseInfo();
        Log.i(TAG, "thinking");
        house.signContract();
        house.payFees();

       可是这里的强制代理有个Bug,强制代理事实上并没有生效,Custom还是能够直接訪问House,比如我通过以下的方式来进行訪问,仅仅是通过getProxy创建并获代替理,可是我不用代理还是直接用House的实例进行訪问,这个时候还是能够正常訪问的.兴许会想办法解了这个Bug而且更新上来的.

        IHouse house = new House("Downton Abbey", 5000);
        house.getProxy();//这里仅仅是通过getProxy创建出代理
        Log.i(TAG, "looking for a perfect house");
        house.getHouseInfo();
        Log.i(TAG, "thinking");
        house.signContract();
        house.payFees();

4.动态代理

       上面介绍的都是自己先写好的代理类,这样代理关系都是固定的,当代理多个真实对象的时候就要写多个代理类,而且会产生冗余的代码,扩展性和可维护性都不高,而动态代理是基于反射实现了在程序执行的过程中才决定代理什么对象.像AOP的核心思想就是动态代理.(这里使用的是Java的动态代理)

       既然是动态代理就不须要ProxyHouse也不须要实现IHouse接口了,这里写一个ProxyHandler实现InvocationHandler的invoke接口,而且提供一个依据Proxy构建出来的代理实例给Custom.在通过反射调用真实对象详细的方法之前打印出该方法的名字.

public class ProxyHandler implements InvocationHandler{
    private final String TAG = ProxyHandler.class.getSimpleName();
    Object targetObj;

    public Object newProxyInstance(Object targetObj){
        this.targetObj = targetObj;
        return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),
                    targetObj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret;
        Log.i(TAG, "method name:" + method.getName());
        ret = method.invoke(targetObj, args);
        return ret;
    }
}
        ProxyHandler proxy = new ProxyHandler();
        IHouse house = (IHouse) proxy.newProxyInstance(new House("Downton Abbey", 5000));
        Log.i(TAG, "looking for a perfect house");
        house.getHouseInfo();
        Log.i(TAG, "thinking");
        house.signContract();
        house.payFees();
        Log.i(TAG, "so easy");
       从结果能够看出在真正invoke真实对象的方法之前都会打印出方法名,也能够在这里做一些其它的对象控制.


       这个时候整个过程的时序图就变成以下的样子了,通过JDK的Proxy对象和反射的机制来支撑起来动态代理的核心功能.


三.总结

       代理模式的使用场景还是挺多的,能够减少对象的复杂度,对项目进行解耦(特别是动态代理的AOP)等,学习设计模式事实上最适合的方法就是拿来用,在适用于该模式的场景下灵活得去运用它才算是真正的掌握一种模式.



posted @ 2017-08-17 15:24  jzdwajue  阅读(456)  评论(0编辑  收藏  举报