投稿    登录
欢迎加入Nice Coder,与众多Coder分享经验,群号:530244901

【原创】设计模式系列(二十)——代理模式

设计之道 wjx@admin.cc 158浏览 0评论

概念:

      为其他对象提供一个代理以控制对这个对象的访问。(维基百科)

理解概念:

      代理这个词很容易理解,一件事情,本应是A去做的,但是A不想去做,找到了B帮他做,B就是A的代理。所谓代理模式就是实现了这个东西。

应用场景:

      代理模式的应用十分广泛,之前写的几篇设计模式,比如门面模式,中介者模式等都可以看成是代理模式的具体化。下面举个例子看一下代理模式。java君的公司实行打卡查勤制度,每天员工到来的时候需要打卡,然后确定员工的确到了。整个流程是这样的。

package proxy;
/**
 * Created by 17854 on 2016/11/14.
 */
interface Staff
{
    void signIn();
}
class ConcreteStaff implements Staff
{
    private String name = null;
    public ConcreteStaff(String name)
    {
        this.name = name;
    }
    public void signIn()
    {
        System.out.println(name+"已经签到!");
    }
}
public class Client
{
    public static void main(String[] args)
    {
        Staff java = new ConcreteStaff("java");
        java.signIn();
    }
}

输出结果如下:

java已经签到!

程序很简单,Staff接口中有一个签到方法,然后ConcreteStaff类实现该接口,有一个name属性,签到的时候就输出name+已经签到。但是,java君每天都这样签到,有一天他工作累了,不想去工作,不过还得签到,所以他就找了一个人替他签到,这个人就是java君的代理,代码如下:

package proxy;
/**
 * Created by 17854 on 2016/11/14.
 */
interface Staff
{
    void signIn();
}
class ConcreteStaff implements Staff
{
    private String name = null;
    public ConcreteStaff(String name)
    {
        this.name = name;
    }
    public void signIn()
    {
        System.out.println(name+"已经签到!");
    }
}
class ConcreteStaffProxy implements Staff
{
    private Staff staff = null;
    public ConcreateStaffProxy(Staff staff)
    {
        this.staff = staff;
    }
    public void signIn()
    {
        this.staff.signIn();
    }
}
public class Client
{
    public static void main(String[] args)
    {
        Staff java = new ConcreteStaff("java");
        Staff javaProxy = new ConcreteStaffProxy(java);
        javaProxy.signIn();
    }
}

修改之后的代码也很简单,添加了一个ConcreteStaffProxy类这个类也实现了Staff接口,然后构造方法中传入一个被代理的对象,然后实际调用的时候是该被代理对象进行具体的逻辑处理。这个就是代理模式,java有了代理,就不用自己去签到了。

提炼总结:

      代理模式很常用。这是一个很基本很基本的设计技巧,我们在其他的设计模式中会看到他的影子,委托,代理,是这个模式的核心。我们日常生活中的找代理律师,找代理中介,经纪人,这些都是代理模式。下面做出该模式的类图。

代理模式很简单。只有三个角色

 Subject主题角色:这是一个接口,当然也可以是一个普通的类。比如上例中的Staff接口
 ConcreteSubject 具体主题角色,实现了Subject接口 这个角色是被代理的角色,具体的处理业务逻辑的类角色,比如上例中的ConcreteStaff类。
 Proxy 代理主题角色 他也实现了Subject接口,这个是真正实行代理职责的角色,他把抽象主题角色中的方法逻辑委托给具体主题角色,也就是被代理者去实现,通常会在是先前后做预处理和事后处理。比如上例中的ConcreteStaffProxy类。
以上就是代理模式,很简单,也很常用。关于上面说的预处理和事后处理,我们可以在代理角色的方法调用前后加上自己的处理逻辑,使得在具体方法执行前后有我们自己的处理逻辑,这也是面向切面编程AOP的雏形。代理模式可以扩展为强制代理模式,就是不能直接访问具体主题。必须通过主题指定的代理进行访问。这种很好实现,在此不再赘述。代理类中不仅是将业务逻辑委托给被代理类,他当然也可以有自己的方法,比如可以在执行被代理类的逻辑之前和之后加上前置处理和事后处理。这种处理的逻辑就可以直接放到代理类中。代理模式最重要的部分,也是重头大戏。

动态代理:

      所谓的动态代理,就是在实现的时候,不用考虑去代理谁,而是在运行阶段动态的指定代理哪一个对象。所谓的AOP面向切面编程就是使用了动态代理机制,还是以上面的签到为例。看一下动态代理是如何实现的、代码如下:

package proxy;

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

/**
 * Created by 17854 on 2016/11/14.
 */
interface Staff
{
    void signIn();
}
class ConcreteStaff implements Staff
{
    private String name = null;
    public ConcreteStaff(String name)
    {
        this.name = name;
    }
    public void signIn()
    {
        System.out.println(name+"已经签到!");
    }
}
class ConcreteStaffProxy implements InvocationHandler
{
    private Object staff = null;
    public ConcreteStaffProxy(Object staff)
    {
        this.staff = staff;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        Object result = method.invoke(staff,args);
        return result;
    }
}
public class Client
{
    public static void main(String[] args)
    {
        Staff java = new ConcreteStaff("java");
        InvocationHandler proxy = new ConcreteStaffProxy(java);
        Staff jacaProxy = (Staff) Proxy.newProxyInstance(Staff.class.getClassLoader(), new Class[]{Staff.class},proxy);
        jacaProxy.signIn();
    }
}

上面的代码中,我们让代理类实现了InvocationHandler接口然后实现了invoke方法,在实例化该代理类的时候,传入一个Object类型的对象,方法内部使用反射调用传入的Object类的method方法。在主方法中,我们实例化出来ConcreteStaffProxy这个对象,然后利用Proxy的newProxyInstance方法实例化了一个Object类型的对象,然后强转为Staff类型,调用sign方法,在实例化的时候,传入了Staff接口的ClassLoader和一个包含了Staff的class对象的数组,和上面我们实例化的ConcreteStaffProxy最后的输出结果是我们所预期的。这里为什么叫他是动态代理呢。因为我们的代理对象没有去实现Staff这个主题接口,而是实现的InvocationHandler接口,然后在invoke方法中直接使用我们传进去的对象调用method方法,这个类是一个通用的类,我们在诸方法中传入的是Staff的类加载器和Staff的class对象,如果我们传入的是其他的我们想被代理的接口,这个方法照样管用,我们需要代理谁,这是在运行的时候才确定的,这就是动态代理。InvocationHandler接口中有一个invoke方法,我们可以看到这个方法中有三个参数,第一个proxy是动态代理类的引用,一般情况下用不到。第二个是method是动态代理类所调用的方法。第三个是该方法的参数。我们之前说过,其他的很多设计模式,都是代理模式的具体化应用,之前我们写的全是使用的普通的代理实现的各种模式,我们完全可以使用动态代理去实现。java的动态代理有一个不足之处,就是只能够代理接口,也就是说我们只能在调用newProxyInstance的时候,只能够将其类型强转为接口,而不能转为一个类,这是因为这个方法产生的对象,是java利用动态生成字节码生成的,他继承自Proxy类,所以不能让他再去继承其他的类,只能够实现某个接口,所以我们只能够在newProxyInstance方法中传入接口的类加载器,和接口的class对象,这是由java无法进行多继承的特性所决定的,这是动态代理的一个不足之处。不过java的动态代理已经做得很棒了。这次对java的动态代理进行了一个简单的介绍,关于动态代理机制的详细介绍,不久的将来会在本博客发布,如果想继续深入动态代理,请关注本博客。

下一篇预告:组合模式和迭代器模式

转载请注明:王镜鑫的个人博客 » 【原创】设计模式系列(二十)——代理模式

喜欢 (5)or分享 (0)

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请狠狠点击下面的