代理模式与动态代理

代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,它只要一个能够提供该功能的对象即可,此时就可以返回该对象的代理。
在这个设计模式中,需要一个对象所需要实现的接口,然后需要一个真实对象类以及一个代理对象类,这两个类都需要实现此接口。
举一个例子,在多线程的处理上使用的就是代理设计模式。Runnable作为对象接口,MyThread类作为我们自己定义的真实业务类,而Thread类作为代理类来确认客户端是否需要调用run方法。
以下用代理模式来演示一个是否绘制图片的具体例子。
首先,定义提供一个Image接口。

interface Image {
    void show();
}

再提供一个真实实现类。

class RealImage implements Image {

    public RealImage() {
        try {
            //程序暂停2s模拟系统开销
            Thread.sleep(2000);
            System.out.println("图片正在装载中...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void show() {
        System.out.println("成功绘制实际的图片");
    }
}

最后提供一个代理实现类。

class ProxyImage implements Image {
    private Image image;

    public ProxyImage(Image image) {
        this.image = image;
    }

    @Override
    public void show() {
        //只有当真正需要调用image的show方法时才创建被代理对象
        if (image == null) {
            image = new RealImage();
            image.show();
        } else {
            System.out.println("此业务无序绘制图片");
        }
    }
}

提供一个测试用例。

public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //程序返回一个image对象,该对象只是RealImage的代理对象
        Image image = new ProxyImage(null);
        long mid = System.currentTimeMillis();
        System.out.println("系统得到image对象的时间开销:" + (mid - start));
        image.show();
        long end = System.currentTimeMillis();
        System.out.println("系统完成业务的时间开销:" + (end - mid));
    }

结果如下:
在这里插入图片描述
使用此代理模式有两个好处:
1.把创建RealImage推迟到真正需要它时才创建,这样能保证前面程序运行的流畅性,而且能减少RealImage在内存中的存活时间,从宏观上节省了系统的内存开销。
2.在有些情况下,也许程序永远都不会真正调用ProxyImage对象的show方法,这意味着系统根本无需创建RealImage对象。在这种情况下,使用代理模式可以显著地提高系统运行性能。

代理模式还有另一种常用场景:当目标对象的功能不足以满足客户端需求时,系统可以为该对象创建一个代理对象,而代理对象可以增强原目标对象的功能,这就是动态代理。
下面举一个用动态代理来实现吃饭的例子。
提供一个指定接口。

interface Lunch {
    void eat(String food, Integer num);
}

提供一个真实实现类。

class RealLunch implements Lunch {

    @Override
    public void eat(String food, Integer num) {
        System.out.println("我要吃" + num + "分量的" + food);
    }
}

提供一个拦截器。
拦截器就是就是用于增强目标对象的功能。

class TxUtil {
    public void preTx() {
        System.out.println("======午饭开始前======");
    }

    public void afterTx() {
        System.out.println("======午饭吃完后======");
    }
}

提供一个InvocationHandler实现类,该类的invoke方法将会作为代理对象的方法实现。

class MyInvokationHandle implements InvocationHandler {
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TxUtil tx = new TxUtil();
        tx.preTx();
        Object result = method.invoke(target, args);//通过反射调用真实类的实现方法
        tx.afterTx();
        return result;
    }
}

上面的invoke方法将会作为动态代理对象的所有方法的实现体。
通过这种方式,使得代理对象的方法既回调了被代理对象的方法,并为被代理对象的方法增加了事物功能。
提供一个动态代理工厂。

class MyProxyFactory {
    public static Object getProxy(Object target) {
        MyInvokationHandle handle = new MyInvokationHandle();
        handle.setTarget(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handle);
    }
}

上面的动态代理工厂提供了一个getProxy方法,该方法为target对象生产一个动态代理对象。当程序调用动态代理对象的指定方法时,实际上将变为执行MyInvokationHandle对象的invoke方法。
提供一个测试用例。

public static void main(String[] args) {
        Lunch target = new RealLunch();
        Lunch lunch = (Lunch) MyProxyFactory.getProxy(target);
        lunch.eat("宫保鸡丁", 10);
    }

结果如下:
在这里插入图片描述
从运行的结果可知,动态代理模式可以非常灵活地实现解耦。
当系统需要扩展RealLunch实例的功能时,程序只需要提供额外的拦截器类,并在MyInvokationHandle的invoke方法回调这些拦截器方法即可。

发布了49 篇原创文章 · 获赞 18 · 访问量 4363

猜你喜欢

转载自blog.csdn.net/asd0356/article/details/92840900
今日推荐