23种设计模式-代理模式

1.产生背景

  • 为什么需要代理模式
    这里写图片描述

假如你是一个业主,有一套500万的房子需要出售;你希望找一个合适的客户,签个合同,收了钱,然后把房子过户给客户就可以了;

但是,有太多的客户天天找你需要看房,询问,而且大部分都是没有诚意的客户,让你不胜其烦;

业主: 只需要签字收钱就好了,剩下的事情,如果有人能帮我处理就发了;
这里写图片描述

2.概念

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

本质: 引入一个中介类, 这个类实现了被代理类相同的接口或继承自被代理类,对外假装成被代理对象,帮被代理对象处理一些非必要或不能做的事情;但核心功能,仍然通过调用被代理对象来实现;

3.目的

不修改原来代码的情况下,动态地给一个对象添加一些额外的职责和功能,或者添加一些控制功能,以限制对原对象的访问

本质:动态增加功能;把需要新增加的功能以代理的方式动态的添加到原对象上;

4.解决方案

本质:引入一个第三方中介类,这个类实现被代理类的接口或直接继承被代理,以方便代理类可以假装成原类;并通过依赖方式引用原类;这个代理类处于调用方和被代理之间,以便对被代理类进行访问控制和增加新功能;

  • 引入中介对象
  • 中介对象实现被装饰类的接口或直接继承(对外假装成被代理类)
  • 中介对象内部引用被代理对象(并把真实功能委托给被代理对象);
  • 中介对象在调用被代理对象前后:增加特殊功能或访问控制

5. 类图

图片转自:runoob
这里写图片描述

6.优缺点

优点:

  • 松耦合
    在不修改原来代码的情况下,动态的为原类增加新功能

  • 扩展性高
    只需要增加新的代理功能类,就可以对原类不断增加新功能

  • 灵活
    通过动态代理,不修改调用方和被调用方的情况下动态增加新功能

缺点:

  • 代理实现起来可能会比较复杂
  • 增加了中介类,可能会对性能有影响

最核心的目的:在不修改原代码的情况下,动态的为对象增加或减少某些功能,以及控制对被代理类的访问

7.应用场景

  • A、远程代理。
  • B、虚拟代理。
  • C、Copy-on-Write 代理。
  • D、保护(Protect or Access)代理。
  • E、Cache代理。
  • F、防火墙(Firewall)代理。
  • G、同步化(Synchronization)代理。
  • H、智能引用(Smart Reference)代理。

“与装饰器的区别”
在学习代理模式时,会发现它与装饰器模式无论从实现结构,还是功能目的都非常接近;

装饰器模式侧重的是对功能的增强,不改变原功能;
装饰器模式使用方明确知道自己需要什么的增强功能,硬编码使用;

代理模式侧重于对原功能的改变(特别是访问权限的控制)
代理模式分为静态和动态代理,动态代理是在调用方和被调用都不知情的情况下使用,代理功能增强由第三方进行维护(如:spring 框加维护代理);

如果代理模式使用静态代理实现,而且也是侧重对功能的增强,那么他们之间没有任何区别(如:spring中通过动态代理实现缓存或日志)

因此:代理在java中,代理更多的应用在动态代理上(如:spring aop);

8.代理种类

  • 远程代理
    如:微服务,通过对远程接口创建一个代理对象,代理内部使用RPC实现功能委托给远程服务;

  • 虚拟代理
    如:hibernet 中,lazy加载的对象,可以先创建一个代理对象,等到实际使用时,再加载实际对象;

  • 保护代理
    如:通过代理实现认证控制,如果未登录,则不允许对服务的访问;

  • 缓存代理
    如:spring-cache,对原对象进行代理,在访问原对象时,先从缓存中取数据,如果缓存中存在则直接返回,否则,再调用原对象的功能,并把返回值放入缓存;

  • 等等

9.现实案例

spring @Aspect aop对应的实现

10. 注意事项

java中代理分为:静态代理和动态代理;
-静态代理

-动态代理
jdk动态代理

//被代理接口
public interface Owner {
    void sellHouse(String customerName);
}

//被代理类(真正的业务对象)
public class OwnerImpl implements Owner {
    @Override
    public void sellHouse(String customerName) {
        System.out.println("我是业主,我把房子卖给了:" + customerName);
    }
}

//代理后的:增强功能
public class AgentInvocationHandler implements InvocationHandler {
    //被代理的原对象:引用了实际的业务对象,以方便把真正的业务功能委托给它
    Object srcObject;
    //绑定原对象
    public AgentInvocationHandler(Object srcObject) {
        this.srcObject = srcObject;
    }

    /**
     *调用代理对象方法时,全部委托给此方法
     *@Param proxy :代理对象,即假装成原对象的对象
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //访问控制:在调用
        if(args == null || args[0] == null || args[0].toString().startsWith("杨")){
            System.out.println("不卖给没有名字的人,也不卖给姓杨的人");
            return null;
        }

        //真正的业务功能,委托给原对象
        return method.invoke(this.srcObject, args);
    }
}

//执行
public class ProxyMain {
    public static void main(String args[]){
        //被代理对象
        Owner owner = new OwnerImpl();
        //代理功能(增强的功能)
        AgentInvocationHandler agentInvocationHandler = new AgentInvocationHandler(owner);
        //根据被代理对象的接口、增强功能、原对象一起:生成一个新的对象:代理对象
        Owner agent = (Owner) Proxy.newProxyInstance(owner.getClass().getClassLoader(), new Class[]{Owner.class},agentInvocationHandler);

        agent.sellHouse("杨先生");
    }
}
  • 创建流程:
    • 编写业务接口
    • 编写业务类
    • 编写增强功能类:InvocationHandler
    • 创建业务对象
    • 创建增强功能对象,并引用业务对象
    • 创建代理对象(实现了业务接口),并引用增强功能对象
  • 执行流程:
    • 通过创建代理对象,获得代理对象,并假装成业务对象(实现了业务接口)
    • 调用代理对象
    • 代理对象的任何方法调用,都委托给增强功能对象(invoke)
    • 根据invoke方法中的method,判断代理对象调用了哪个方法,进行不同的控制;
    • InvocationHandler.invoke中,根据各种判断增强或控制原对象访问
    • 调用原对象实际方法,把核心功能委托给原对象
    • InvocationHandler.invoke中,在调用原对象方法后,进行二次处理;
    • 返回结果

猜你喜欢

转载自blog.csdn.net/yangspgao/article/details/80574188