设计模式-代理模式

1、定义

Provide a surrogate or placeholder for another object to control access to it.

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

2、类图

3、角色

代理模式也叫委托模式,它是一项基本的设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。代理模式可以提供非常好的访问控制。

Subject :抽象主题角色

抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

RealSubject:具体主题角色

也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。

Proxy:代理主题角色

也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

4、例子

public interface ISubject {
    public void method1();
    public void method2();
    public void method3();
}
public class RealSubjectA implements ISubject {

    private String name;

    public RealSubjectA(String name) {
        this.name = name;
    }

    @Override
    public void method1() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");
    }

    @Override
    public void method2() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");
    }

    @Override
    public void method3() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");
    }
}

public class RealSubjectProxy implements ISubject {

    private ISubject mISubject;

    public RealSubjectProxy(ISubject iSubject) {
        this.mISubject = iSubject;
    }

    @Override
    public void method1() {
        this.mISubject.method1();
    }

    @Override
    public void method2() {
        this.mISubject.method2();
    }

    @Override
    public void method3() {
        this.mISubject.method3();
    }
}

使用:

ISubject iSubject = new RealSubjectA("zlm");
RealSubjectProxy proxy = new RealSubjectProxy(iSubject);
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3

5、优缺点

优点:

a、职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰

b、高扩展性。具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。

c、智能化。动态代理可以体现。

6、扩展

设计模式中的代理分为“普通代理”和“强制代理”两种。

普通代理

他的要求就是客户端只能访问代理角色,而不能访问真实角色,也就是场景类不能再直接new一个对象了,他必须由代理来模拟场景。

我们需要修改两个实现类的构造函数即可。

RealSubject:

public class RealSubjectA implements ISubject {

    private String name;


    //构造函数限制谁能创建对象,并同时传递name..当然还可以 有其他的限制,比如类名必须为Proxy类等,
    public RealSubjectA(ISubject iSubject,String name) throws Exception {
        if (iSubject == null) {
            throw new Exception("can not create RealSubject");
        } else {
            this.name = name;
        }
    }

    @Override
    public void method1() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");
    }

    @Override
    public void method2() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");
    }

    @Override
    public void method3() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");
    }
}

Proxy:

public class RealSubjectProxy implements ISubject {

    private ISubject mISubject;

    //仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,
    public RealSubjectProxy(String name) {
        try {
            this.mISubject = new RealSubjectA(this,name);
        } catch (Exception e) {
            // TODO: 2018/6/22 异常处理
        }
    }

    @Override
    public void method1() {
        this.mISubject.method1();
    }

    @Override
    public void method2() {
        this.mISubject.method2();
    }

    @Override
    public void method3() {
        this.mISubject.method3();
    }
}

使用:

ISubject proxy = new RealSubjectProxy("zlm");
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3
        运行结果完全相同。在该模式下, 调用者只知代理而不用知道真实的角色RealSubject是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个非常好的方案。


强制代理

强制代理在设计模式汇总比较另类,一般的思维是通过代理找到真实的角色,但是强制代理确实要“强制”,比必须通过真实角色查找到代理,否则你不能访问,也就是通过真实角色管理代理角色。

改造如下:

ISubject:

public interface ISubject {
    public void method1();

    public void method2();

    public void method3();
    //每个角色都可以拿到自己的代理类
    public ISubject getProxy();

}

RealSubjectProxy:

public class RealSubjectProxy implements ISubject {

    private ISubject mISubject = null;

    //仅仅修改了构造函数,传递进来一个用户
    public RealSubjectProxy(ISubject iSubject) {
        this.mISubject = iSubject;
    }

    @Override
    public void method1() {
        this.mISubject.method1();
    }

    @Override
    public void method2() {
        this.mISubject.method2();
    }

    @Override
    public void method3() {
        this.mISubject.method3();
    }

    @Override
    public ISubject getProxy() {
        return this;
    }
}

RealSubjectA:

public class RealSubjectA implements ISubject {

    private String name;
    private ISubject iSubject = null;

    public RealSubjectA(String name) {
        this.name = name;
    }

    @Override
    public void method1() {
        if (this.isProxy()) {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");
        } else {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1" + "\t" + "请使用指定代理访问!");
        }
    }

    @Override
    public void method2() {
        if (this.isProxy()) {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");
        } else {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2" + "\t" + "请使用指定代理访问!");
        }
    }

    @Override
    public void method3() {
        if (this.isProxy()) {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");
        } else {
            System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3" + "\t" + "请使用指定代理访问!");
        }
    }

    @Override
    public ISubject getProxy() {
        this.iSubject = new RealSubjectProxy(this);
        return this.iSubject;
    }

    //校验是否是代理访问
    private boolean isProxy() {
        if (this.iSubject == null) {
            return false;
        } else {
            return true;
        }
    }
}

运行:

RealSubjectA realSubjectA = new RealSubjectA("zlm");
ISubject proxy = realSubjectA.getProxy();
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3

7、代理个性化

一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。

增加一个接口,IProxy:

public interface IProxy {
    public void methodIProxy();
}

RealSubjectProxy:

public class RealSubjectProxy implements ISubject,IProxy {

    private ISubject mISubject = null;

    //仅仅修改了构造函数,传递进来一个用户
    public RealSubjectProxy(ISubject iSubject) {
        this.mISubject = iSubject;
    }

    @Override
    public void method1() {
        this.mISubject.method1();
    }

    @Override
    public void method2() {
        this.mISubject.method2();
    }

    @Override
    public void method3() {
        this.mISubject.method3();
    }

    @Override
    public ISubject getProxy() {
        return this;
    }

    @Override
    public void methodIProxy() {
        System.out.println("新加了一个接口,实现了其中的方法!");
    }
}

就只用修改这一个类,实现接口,其他了类不用变!

使用:

RealSubjectA realSubjectA = new RealSubjectA("zlm");
RealSubjectProxy proxy = (RealSubjectProxy) realSubjectA.getProxy();
proxy.method1();
proxy.method2();
proxy.method3();
proxy.methodIProxy();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3
I/System.out: 新加了一个接口,实现了其中的方法!

代理类不仅仅是可以有自己的运算方法,通常的情况下代理的职责并不一定单一,它可以组合其他的真实角色,也可以实现自己的职责。

代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。

当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。


8、动态代理

什么是动态代理?

动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那个对象。面向横切面编程(AOP)的核心就是采用了动态代理机制。

接口和实现类很简单,定义需要实现的方法即可。

ISubject:

public interface ISubject {
    public void method1();

    public void method2();

    public void method3();

}

RealSubjectA:

public class RealSubjectA implements ISubject {

    private String name;

    public RealSubjectA(String name) {
        this.name = name;
    }

    @Override
    public void method1() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method1");
    }

    @Override
    public void method2() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method2");
    }

    @Override
    public void method3() {
        System.out.println(this.name + "\t" + this.getClass().getSimpleName() + "\t" + "method3");
    }


}

定义一个动态代理类RealSubjectHI,实现InvocationHandler接口,构造方法传入需要代理的对象。

public class RealSubjectHI implements InvocationHandler {
    //被代理的实例
    Object obj = null;

    //我要代理谁
    public RealSubjectHI(Object obj) {
        this.obj = obj;
    }

    //invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(this.obj, args);
        return invoke;
    }
}

其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我 们来详细讲解一下InvocationHandler接口,动态代理是根据被代理的接口生成所有的方法, 也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过 InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由 InvocationHandler接管实际的处理任务。

调用:

//定义具体实现类
ISubject iSubject = new RealSubjectA("zlm");
//获得类的classload
ClassLoader classLoader = iSubject.getClass().getClassLoader();
//定义一个handler
InvocationHandler invocationHandler = new RealSubjectHI(iSubject);
//动态产生一个代理者
ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader, new Class[]{ISubject.class}, invocationHandler);
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3

注意:

我们既没有创建代理类,也没有实现ISubject接口,这就是动态代理。

如果我们想在一个方法调用后随即触发一些逻辑操作怎么办呢?简单,只需要改变动态代理Handler类,在invoke方法中添加一段的代码即可,请看:

public class RealSubjectHI implements InvocationHandler {
    //被代理的实例
    Object obj = null;

    //我要代理谁
    public RealSubjectHI(Object obj) {
        this.obj = obj;
    }

    //invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(this.obj, args);
        if (method.getName().equalsIgnoreCase("method1")) {
            System.out.println("调用了method1方法,可以在此处添加逻辑!");
        }
        return invoke;
    }
}

调用方法并无变化:

//定义具体实现类
ISubject iSubject = new RealSubjectA("zlm");
//获得类的classload
ClassLoader classLoader = iSubject.getClass().getClassLoader();
//定义一个handler
InvocationHandler invocationHandler = new RealSubjectHI(iSubject);
//动态产生一个代理者
ISubject proxy = (ISubject) Proxy.newProxyInstance(classLoader, new Class[]{ISubject.class}, invocationHandler);
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: zlm	RealSubjectA	method1
I/System.out: 调用了method1方法,可以在此处添加逻辑!
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3
很棒啊,有木有!这就是AOP编程。AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通 过AOP的方式切过去。

很简单,两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的 逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

AOP及动态代理抽取

在这里插入了较多的AOP术语,如在什么地方(连接点)执行什么行为(通知)。我们 在这里实现了一个简单的横切面编程。

我们来看通知Advice,也就是我们要切入的类,接口和实现如代码清单如下:

/**
 * Created by ZLM on 2018/7/2.
 * Describe 前置通知接口
 */

public interface IAdvice {
    public void exec();
}
/**
 * Created by ZLM on 2018/7/2.
 * Describe 前置通知实现
 */

public class BeforeIAdvice implements IAdvice {
    @Override
    public void exec() {
        System.out.println("我是前置通知!我被执行了!");
    }
}

因此我们抽象动态代理类:

/**
 * Created by ZLM on 2018/7/2.
 * Describe 动态代理类
 */

public class DynamicProxy<T> {
    public static <T> T getNewProxyInstance(ClassLoader classLoader, Class<?>[] tClass, InvocationHandler invocationHandler) {
        //寻找JoinPoint连接点,AOP框架使用元数据定义
        if (true) {
            //执行一个前置通知
            new BeforeIAdvice().exec();
        }
        //执行目标,返回结果
        return (T) Proxy.newProxyInstance(classLoader, tClass, invocationHandler);
    }
}
在返回动态代理对象之前,执行我们要切入的类的方法。

执行变成了这样:


//定义具体实现类
ISubject iSubject = new RealSubjectA("zlm");
//获得类的classload
ClassLoader classLoader = iSubject.getClass().getClassLoader();
//定义一个handler
InvocationHandler invocationHandler = new RealSubjectHI(iSubject);
//产生一个动态代理
ISubject proxy = DynamicProxy.getNewProxyInstance(classLoader, iSubject.getClass().getInterfaces(), invocationHandler);
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: 我是前置通知!我被执行了!
I/System.out: zlm	RealSubjectA	method1
I/System.out: 调用了method1方法,可以在此处添加逻辑!
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3

 Proxy.newProxyInstance(classLoader, tClass, invocationHandler);该方法是重新生成了一个对象,为什么要重新生成?你要使用代理呀,注意 c.getInterfaces()这句话, 这是非常有意思的一句话,是说查找到该类的所有接口,然后实现 接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是new MyInvocationHandler(_Obj)这个对象。 于是我们知道一个类的动态代理类是这样的一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现。

我们注意到上面的动态代理类DynamicProxy是一个通用类,不具有业务意义,我们可以在再产生一个实现类简化代码。

/**
 * Created by ZLM on 2018/7/2.
 * Describe 具有业务逻辑的动态代理类
 */

public class SubjectDynamicProxy extends DynamicProxy {
    public static <T> T getNewProxyInstance(T t) {
        ClassLoader classLoader = t.getClass().getClassLoader();
        Class<?>[] interfaces = t.getClass().getInterfaces();
        InvocationHandler invocationHandler = new RealSubjectHI(t);
        //执行目标,返回结果
        return getNewProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

调用 so easy:

ISubject iSubject = new RealSubjectA("zlm");
ISubject proxy = SubjectDynamicProxy.getNewProxyInstance(iSubject);
proxy.method1();
proxy.method2();
proxy.method3();

输出:

I/System.out: 我是前置通知!我被执行了!
I/System.out: zlm	RealSubjectA	method1
I/System.out: 调用了method1方法,可以在此处添加逻辑!
I/System.out: zlm	RealSubjectA	method2
I/System.out: zlm	RealSubjectA	method3



欢迎关注我的公众号OpenShare,专注移动开发和大数据技术学习和分享,也希望通过公众号,促进自己学习和进步。技术博文会同步分享至公众号,欢迎订阅!














评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值