设计模式之代理模式

栏目: 后端 · 发布时间: 5年前

内容简介:代理(Proxy)模式是设计模式中结构型模式的一种,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上,对目标对象进行扩展,比如增加日志打印等。现实中有很多代理模式的例子。比如过年的时候买不到火车票怎么办?找黄牛啊,这个时候黄牛就是一个代理,我们只需要和这个代理去打交道,剩下买票工作交给黄牛去做。代理模式的通用UML图如下:

定义

代理(Proxy)模式是 设计模式 中结构型模式的一种,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上,对目标对象进行扩展,比如增加日志打印等。

现实中有很多代理模式的例子。比如过年的时候买不到火车票怎么办?找黄牛啊,这个时候黄牛就是一个代理,我们只需要和这个代理去打交道,剩下买票工作交给黄牛去做。

代理模式的通用UML图如下:

设计模式之代理模式

涉及到以下几个角色

  • Subject抽象角色:定义被代理角色和代理角色要实现接口。
  • RealSubject目标角色:实现抽象角色,即具体业务逻辑。
  • Proxy代理角色:实现抽象角色,持有目标角色的引用。

接下来看一下具体的代码实现:

静态代理实现

Subject抽象角色Subject.java

public interface Subject {
    void operation();
    void operation2();
}

RealSubject目标角色RealSubject.java

public class RealSubject implements Subject {
    @Override
    public void operation() {
        System.out.println("我是真实操作");
    }

    @Override
    public void operation2() {
        System.out.println("我是真实操作2");
    }
}

Proxy代理角色StaticProxy.java

public class StaticProxy implements Subject {

    private Subject realSubject;

    public StaticProxy(Subject subject) {
        this.realSubject = subject;
    }

    @Override
    public void operation() {
        System.out.println("我是代理操作");
        realSubject.operation();
    }

    @Override
    public void operation2() {
        System.out.println("我是代理操作2");
        realSubject.operation();
    }
}

测试类Test.java

public class Test {

    public static void main(String args[]) {

        /**
         * 静态代理
         */
        Subject subject = new RealSubject();
        Subject proxy = new StaticProxy(subject);

        proxy.operation();
        proxy.operation2();

    }
}

打印结果

我是代理操作
我是真实操作
我是代理操作2
我是真实操作

可以看到,在真实要操作的对象前,可以输出一些我们定义好的逻辑,这就是代理模式的实现。代理模式实现的方式有几种。以上是静态代理,还有一种是动态代理,在运行时生成代理对象。接下来看一下动态代理如何实现。

动态代理实现

Java 中要想实现动态代理机制,需要用到java.lang.reflect.InvocationHandler和 java.lang.reflect.Proxy。

代理类要实现InvocationHandler接口,重写invoke,可以在这里对目标角色进行增强,比如这里打印了一条日志。

public class DynamicProxy implements InvocationHandler {

    private Object obj;

    public DynamicProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("我是代理操作" + method.getName());
        Object result = method.invoke(obj, args);

        return result;

    }
}

测试类Test.java

Subject subject1 = new RealSubject();
DynamicProxy handler = new DynamicProxy(subject1);
Subject proxy1 = (Subject) Proxy.newProxyInstance(subject1.getClass().getClassLoader(), subject1.getClass().getInterfaces(), handler);
proxy1.operation();
proxy1.operation2();

可以看到,通过反射得到了一个代理对象,执行结果的静态代理类似,如下:

打印结果

我是代理操作operation
我是真实操作
我是代理操作operation2
我是真实操作2

小结

无论是静态代理还是动态代理,都是对被代理对象进一步的包装。

静态代理

优点:目标类无需关系代理类实现逻辑,只需知道有代理类即可。 缺点:目标类和代理类实现相同的接口,代码量变大。如果接口增加了一个方法,那么目标类和代理类都需要增加这个方法的实现。

动态代理

优点:代码量少 缺点:只能代理实现接口的类,没有实现接口的类就不能实现动态代理。通过反射会有性能消耗。

one more thing

在Android性能监控开发中,我们可能对网络库的请求进行监控,在请求之前或之后做一些统计,接下来我们就以OkHttp为例,对网络请求进行代理(当然OkHttp自己有一套拦截器机制也可以实现对请求拦截,不在我们讨论范围内)。以下例子没什么难度,但是会在接下来的文章中用到。

静态代理

一般情况下,OkHttp的请求是这样的

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().get().url("https://democome.com/").build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

        Log.d(TAG, response.body().string());
    }
});

我们要做的就是代理Callback,在返回之前打一些日志。按照代理模式的定义实现如下:

代理类CallbackProxy

public class CallbackProxy implements Callback {

    public static final String TAG = CallbackProxy.class.getSimpleName();

    private Callback realCallback;

    public CallbackProxy(Callback realCallback) {
        this.realCallback = realCallback;
    }

    @Override
    public void onFailure(Call call, IOException e) {

        Log.d(TAG, "请求失败先搞点事情" + e.toString());

        realCallback.onFailure(call, e);
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

        Log.d(TAG, "请求成功先搞点事情" + response.toString());

        realCallback.onResponse(call, response);
    }
}

调用 工具

public class StaticUtil {
    public static void proxyRequest(Call call, Callback callback) {
        CallbackProxy callbackProxy = new CallbackProxy(callback);
        call.enqueue(callbackProxy);
    }
}

调用

StaticUtil.proxyRequest(call, new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
    }

    @Override
    public void onResponse(Call call, Response response) {

        try {
            Log.d(TAG, response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
});

动态代理

调用工具类

public class DynamicUtil {

    public static final String TAG = DynamicUtil.class.getSimpleName();

    public static class ResponseHandler implements InvocationHandler {

        private Object obj;

        public ResponseHandler(Object obj) {
            this.obj = obj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {

            Log.d(TAG, "要请求了 搞事搞事搞事" + method.toString());

            Object result = method.invoke(obj, args);

            return result;
        }
    }

    /**
     * @param call
     * @param callback
     */
    public static void proxyRequest(Call call, Callback callback) {

        ResponseHandler handler = new ResponseHandler(callback);
        Callback proxy = (Callback) Proxy.newProxyInstance(callback.getClass().getClassLoader(), callback.getClass().getInterfaces(), handler);
        call.enqueue(proxy);
    }

}

调用

DynamicUtil.proxyRequest(call, new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

        try {
            Log.d(TAG, response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
});

demo地址:https://github.com/77Y/ProxySample

本文由 snow 创作,采用 知识共享署名4.0 国际许可协议进行许可

本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名

最后编辑时间为: 2019/01/04 10:04


以上所述就是小编给大家介绍的《设计模式之代理模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

爆款:如何打造超级IP

爆款:如何打造超级IP

【美】安妮塔•埃尔伯斯 / 杨雨 / 中信出版社 / 2016-1-10 / 49

哈佛商学院IP运营与产品管理方法论第一书,翻转长尾理论的重要著作! 电影大片、当红炸子鸡、百万畅销书背后的运营逻辑是什么? 《五十度灰》、Lady Gaga、维多利亚的秘密有何共同秘密? 漫威如何将蜘蛛侠、X战警、绿巨人打造成金矿? 皇马如何打造体育IP,一跃成为全球收 入最高的足球俱乐部? 爆款策略如何运用于电影、电视、音乐、出版、体育与商业各领域? ----......一起来看看 《爆款:如何打造超级IP》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换