代理设计模式

1、什么是代理设计模式?

        即Proxy Pattern,23种常用的面向对象软件的设计模式之一。为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象或者为了某种简洁方便,而代理对象可以在客户端和目标对象之间起到中介的作用。

        在生活中常见的代理有:如抢票软件、各种中介还有代理服务器,这些和我们今天说的代理设计模式原理是一样的。中介可以帮我们做很多事,让我们省心、省事,只要把我们要做的是委托给中介就可以了,但是中介也是需要费用或者好处的。一些代理或者说中介可能会做一些我们不知道的事,比如偷工减料、增加我们不想要的或者价值很低的东西等等,同理代理设计模式也会有这种问题。


2、代理模式的优缺点及使用场景

  • 优点:

    1. 代理模式能将代理对象与真实被调用的目标对象分离。

    2. 一定程度上降低了系统的耦合度,扩展性好。

    3. 可以起到保护目标对象的作用。

    4. 可以对目标对象的功能增强

  • 缺点:

    1. 代理模式会造成系统设计中类的数量增加。

    2. 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。

    3. 增加了系统的复杂度。

  • 应用场景:

    • 安全代理:屏蔽对真实角色的直接访问。

    • 远程代理:通过代理类处理远程方法调用(RMI)

    • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

    比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

    • 分类

      • 静态代理(静态定义代理类)

      • 动态代理(动态生成代理类)


3、静态代理模式

  1. 静态代理模式的代码实现

    //定义一个接口对代理类与被代理类进行约束
    interface NetWork{
    ​
        public void browse();
    ​
    }
    //被代理类
    class Server implements NetWork{
    ​
        @Override
        public void browse() {
            System.out.println("真实服务器的访问网络");
        }
    }
    //代理类
    class ProxyServer implements NetWork{
    ​
        private NetWork work;
    ​
        public ProxyServer(NetWork work) {
            this.work = work;
        }
    ​
        public void check(){
            System.out.println("联网之前的检查工作");
        }
    ​
        @Override
        public void browse() {
            check();
    ​
            work.browse();
    ​
        }
    }
    ​
    //对静态代理模式进行测试
    public class NetWorkTest {
        public static void main(String[] args) {
            Server server = new Server();
            ProxyServer proxyServer = new ProxyServer(server);
    ​
            proxyServer.browse();
        }
    ​
    }

  2. 通过上面的代码我们可以看出静态代理的优缺点:

    1. 优点:代理类可以接受一个已经实现了Subject接口的对象,任何实现了Subject接口的对象都可以通过代理类进行代理,实现了通用型。

    2. 缺点:当接口增删改方法,那么代理类已得要跟着修改;代理类的每个接口对象对应一个委托对象,如果委托对象很多,代理类就会变得异常臃肿。


4、动态代理模式

动态代理有别用静态代理,它是通过要代理的类,动态的生成代理类。这样可以避免静态代理中代理类接口过多的问题。

动态代理的实现方式是借助java.lang.Reflect.Proxy进行反射实现的,有如下4个步骤:

  1. 编写一个委托类接口

  2. 编写一个委托类接口的实现类

  3. 创建动态代理类方法调用处理程序,实现InvocationHandler接口,并重写invoke方法

  4. 在测试类中生成动态代理对象

1、编写委托类接口

interface Human{
​
    String getBelief();
​
    void eat(String food);
}

2、编写一个委托类接口的实现类(被代理类)

//被代理类
class SuperMan implements Human{
​
    @Override
    public String getBelief() {
        return "I believe I can fly! ";
    }
​
    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

3、创建动态代理类方法调用处理程序,实现InvocationHandler接口,并重写invoke方法

  • 首先,在编写动态代理类时,需要解决两个问题

    1. 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象?

    2. 当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a?

//生成代理类的工厂
class ProxyFactory{
​
    //调用此方法返回一个代理类的对象。解决问题1
    public static Object getProxyInstance(Object obj){ //obj:被代理类的对象
​
        MyInvocationHandler handler = new MyInvocationHandler();
​
        handler.bind(obj);//被代理类赋值,避免出现空指针问题
​
        //参数1:获取被代理类的加载器; 参数2:获取被代理类的接口; 参数3:InvocationHandler对象的实例
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                                      obj.getClass().getInterfaces(),
                                      handler);
    }
​
}
​
//解决问题2
//自定义接口的实现类
class MyInvocationHandler implements InvocationHandler{
​
    private Object obj;//需要使用被代理类的对象进行赋值
    //用一个方法(或构造器)进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
​
    //当我们通过代理类的对象,调用方法a时,就会自动调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    //参数1(proxy):代理类的对象;  参数2(method):代理类的对象调用的方法; 参数3(args):同名方法的参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
​
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnVal = method.invoke(obj, args);
        //代理类对象调用的方法(上述方法)的返回值就作为当前类中invoke()的返回值
        return returnVal;
​
​
    }
}

4、在测试类中生成动态代理对象

public class ProxyTest {
    public static void main(String[] args) {
        //创建被代理类的对象
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
​
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("火锅!");
​
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒙太奇_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值