代理模式(Proxy Pattern) -(最通俗易懂的案例)

1.什么是代理

Proxy(代理)模式是常见设计模式之一,“代理”顾名思义就是“替代”的意思,很容易理解。根据GoF(《Design Patterns: Elements of Reusable Object-Oriented Software》的四位作者)的代理设计模式意图是:Provide a surrogate or placeholder for another object to control access to it.就是说为另一个对象提供代理或占位符,以控制对其的访问。定义本身非常清晰,当我们要提供功能的受控访问时,将使用代理设计模式。

2.静态代理

先举个例子:
比如你现在拥有一套多余的房子想卖掉,但是你不知道谁要买,你不可能在大街上见到一个人就问“兄弟要买房吗?”;我们肯定想到了卖房中介,是的,我们把我们房子的一些信息告诉中介公司然后让他帮我们卖,然后想买房的人肯定不会直接找到你而是去了中介公司,为啥?因为中介的房子多啊有的挑,况且想买房的人根本就不认识你。于是他通过中介公司心意你的房子然后买下。
分析:在上面这个例子中,要卖房子的是你,而执行的人确实中介公司,于是中介公司就充当了“代理”的身份,客户(买房子的人)就不需要直接跟你对接。
下面画一张UML图:
在这里插入图片描述
分析:我们定义了一个House接口里面有一个sale()方法,有两个实现类,一个是实体类(真实卖房子的人)RealManOfSaleHouse与代理类SaleHouseProxy,其中代理类SaleHouseProxy还有一个额外的宣传装修房子的方法decorate(),而且代理类保存一个引用使得代理可访问实体,这样代理就可以来代替实体来卖房。下面上代码:

House接口:

package com.proxy.learn;

/**
 * Created on 2020/2/28
 * Package com.proxy.learn
 *
 * @author dsy
 */
public interface House {

    void sale();
}

RealManOfSaleHouse类:

package com.proxy.learn;

/**
 * Created on 2020/2/28
 * Package com.proxy.learn
 *
 * @author dsy
 */
public class RealManOfSaleHouse implements House {

    @Override
    public void sale() {
        System.out.println("售房啦,售房啦,130平米冬暖夏凉首付仅需20W,瞧一瞧看一看啦");
    }
}

SaleHouseProxy类:

package com.proxy.learn;

/**
 * Created on 2020/2/28
 * Package com.proxy.learn
 *
 * @author dsy
 */
public class SaleHouseProxy implements House{

    private RealManOfSaleHouse owner ;

    public SaleHouseProxy(RealManOfSaleHouse owner){
        this.owner = owner;
    }

    @Override
    public void sale() {
        owner.sale();
        decorate();
    }

    public void decorate(){
        System.out.println("装修房子喽,专业设计师保你冬暖夏凉,24期免息,瞧一瞧看一看啦");
    }

}

测试类:

package com.proxy.learn;

/**
 * Created on 2020/2/28
 * Package com.proxy.learn
 *
 * @author dsy
 */
public class SaleHouseTest {
    public static void main(String[] args) {
        RealManOfSaleHouse owner = new RealManOfSaleHouse();
        SaleHouseProxy proxy = new SaleHouseProxy(owner);
        proxy.sale();
    }
}

打印结果:

售房啦,售房啦,130平米冬暖夏凉首付仅需20W,瞧一瞧看一看啦
装修房子喽,专业设计师保你冬暖夏凉,24期免息,瞧一瞧看一看啦

以上就是一个静态代理的例子,在这里代理类对接口实现了扩展,加了一个decorate()方法,这表明静态代理有一个优点:可以进行一些功能的附加与增强。但也有缺点:

  • 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,就要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  • 假如接口中加了一个方法,除了所有实现类要实现这个方法外,所有代理类也需要实现这个方法,维护成本太高。

3.动态代理

静态代理与动态代理有什么区别?从字面上看就是“静态”与“动态”的区别。以上面的例子来说,我们使用SaleHouseProxy proxy = new SaleHouseProxy(owner);这行代码手动new了一个代理对象然后调用业务方法,在动态代理中就不需要我们手动去写而是由Proxy.newProxyInstance()这个方法在程序运行时期来动态创建。

下面结合一个例子来说明:

我们模拟一个信息发送的过程:假设Bob要向Alice发送信息,Bob与Alice相隔千里,于是Bob打开了微信发送给Alice,见图:
在这里插入图片描述
我们先写一个核心业务接口IMessage类提供发送信息的功能

package com.dynamicProxy.learn;

/**
 * Created on 2020/2/28
 * Package com.dynamicProxy.learn
 *
 * @author dsy
 */
public interface IMessage {//核心业务接口

    public void message(String message);

}

接口实现类MessageImpl :

package com.dynamicProxy.learn;

/**
 * Created on 2020/2/28
 * Package com.dynamicProxy.learn
 *
 * @author dsy
 */
public class MessageImpl implements IMessage {

    @Override
    public void message(String message) {
        System.out.println("发送内容:"+message);
    }

}

以上两个和静态代理类中的代码没什么区别,接下来看代理类ServerProxy :

package com.dynamicProxy.learn;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created on 2020/2/28
 * Package com.dynamicProxy.learn
 *
 * @author dsy
 */
public class ServerProxy implements InvocationHandler {

    private Object object; //核心业务对象

    public  Object bind(Object object){

        //保存真实业务对象
        this.object = object;

        //获取真实业务主题所在类对应的类加载器,因为需要分析真实业务主题接口中所拥有的方法,才可以构建动态子类
        //所有的动态代理都是基于接口的设计应用,那么此时就要获取全部的接口信息
        //当前的类为InvocationHandler接口子类,所以使用this描述的是本类的实例化对象
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=null;
        if (before()){
            //此时代理方法中需要进业务方法的反射调用,需要提供实例化对象(this.object),Method对象(method),参数(args)
            result = method.invoke(this.object,args);
            after();
        }
        return result;
    }

    public boolean before(){
        System.out.println("建立连接*****连接成功");
        return true;
    }

    public void after(){
        System.out.println("接收成功*****关闭连接");
    }

}

测试类:

package com.dynamicProxy.learn;

/**
 * Created on 2020/2/28
 * Package com.dynamicProxy.learn
 *
 * @author dsy
 */
public class ServerProxyTest {

    public static void main(String[] args) {
        IMessage iMessage = (IMessage) new ServerProxy().bind(new MessageImpl());
        iMessage.message("Hi ,Alice");
    }

}

打印输出:

建立连接*****连接成功
发送内容:Hi ,Alice
接收成功*****关闭连接

这就实现了一个动态代理,接下来我们分析这个代理类ServerProxy 中用到了哪些新的东西:

1. 首先这个类实现了InvocationHandler 这个接口,那这个InvocationHandler 接口是干什么的呢,我们打开源码:

package java.lang.reflect;

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

发现接口中只有一个方法invoke()与反射有关,正是他来处理代理实例上的方法调用并返回结果,三个参数为:

  • proxy :代理实例
  • method :代理实例调用的方法
  • args :参数

2. Proxy.newProxyInstance方法

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException

该方法返回指定接口的代理类的实例,也就是创建并返回动态代理的对象,有三个参数:

  • ClassLoader loader:类加载器用于定义代理类
  • Class<?>[] interfaces:代理类所实现的接口
  • InvocationHandler h:一个InvocationHandler实例对象

总结

  • 所谓代理就是一个人代替另一个人去做一些事,以控制对这些事的访问
  • Java中代理有两种模式:静态代理与动态代理
  • 静态代理需要我们自己写代理类的实例化对象
  • 动态代理是通过Proxy.newProxyInstance方法来创建代理类的实例化对象

这只是对代理模式的简单论述,里面有很多知识有待我去考察,毕竟我还是个小白。
本篇动态代理的例子来源于李兴华老师的讲解,本篇没看懂的朋友们强烈建议点进去看看,或许对你有帮助!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值