Java设计模式(13)----------代理模式

原文链接: https://www.imooc.com/article/24850

原文:https://www.imooc.com/article/24850

默课道可工程师学习地址:https://www.imooc.com/article/24850

介绍

代理模式中,在客户端与对象之间增加了一个代理层。客户端在进行访问时候,不是直接访问对象,而是访问代理。代理模式是一种结构型的设计模式。通过代理模式,可以解决直接访问对象带来的一些问题,并且可以进行访问控制。

代理模式的实现中,代理类中依赖实体类,但两者共同实现相同的接口。代理类中对应接口中,会调用实体类的对应接口。听上去与装饰器的实现一样,是的,两者的实现基本上是相同的,只是使用意义上的侧重点不同。两者的区别,会在文末的总结中进行分析。

代理模式的应用场景比较广泛,跟“代理”两个字沾边的,一般都会涉及代理模式。比如火车票代售站,对于乘客来说,他不需要去真正的火车站,而通过代售点,就可以实现买票取票的功能。比如VPN,由于众所周知的原因,我们不能访问youtube,那么我们可以设置一个VPN,通过VPN进行访问。再比如说windows的快捷方式,也是一种代理。还有就是Spring的AOP。

代理模式从类型上来说分为两种,静态代理和动态代理。所谓静态代理,就是在编译期间就确定的代理关系,一般是一对一的。而动态代理,是一对多的代理关系,是在运行时态才能确定的代理关系。动态代理的原理比较复杂,本篇文章只简单介绍动态代理的使用,在下一篇文章中,会从原理和代码的层面,对动态代理做专门的解读。

案例

静态代理

背景

以购买火车票为例。乘客(客户端)在购买火车票的时候,可以在代售点(代理类)进行购买(功能),而不需要花费很长的时间跑去火车站(真实类)进行购买(功能)。

实现

定义抽象接口

public interface shop {
    void buy();
    void take();
}

定义真实火车站

public class Station implements shop
{
    @Override
    public void buy() {
        System.out.println("购买火车票");
    }

    @Override
    public void take() {
        System.out.println("领取火车票");
    }
}

定义代售点

public class ProxyStation implements shop
{
    private Station station = new Station();

    @Override
    public void buy() {
        beforeBuy();
        station.buy();
        afterBuy();
    }

    private void beforeBuy() {
        System.out.println("购买之前操作");
    }

    private void afterBuy() {
        System.out.println("购买之后的操作");
    }

    @Override
    public void take() {
        beforeTake();
        station.take();
        afterTake();
    }

    private void afterTake() {
        System.out.println("取票之前的操作");
    }

    private void beforeTake() {
        System.out.println("取票之后的操作");
    }
}

验证程序

public class Test {
    public static void main(String args[])
    {
        ProxyStation proxyStation = new ProxyStation();
        proxyStation.buy();
        proxyStation.take();
    }
}

运行结果

购买之前操作
购买火车票
购买之后的操作
取票之后的操作
领取火车票
取票之前的操作

Process finished with exit code 0

动态代理

动态代理的场景是一对多的关系,即是一个代理类,多个被代理的类。

在动态代理中,不再关注向客户端屏蔽原始对象(客户端可以看到原始对象),而重点是对于一系列的原始实现类,能够对抽象中的方法进行统一的横向扩展(方法执行前/后的操作),而不需要为每一个原始实现类都创建一个代理。

动态代理的原理,会在下篇文章中进行分析。

案例

以汽车(抽象)行驶为例子,我们要统计多种车型(原始实现类)通过某段距离的用时。在汽车行驶前,需要进行开始计时的操作(功能扩展)。汽车行驶后,要进行结束计时的操作(功能扩展)。

实现

抽象接口

public interface Moveable {
    void move()  throws Exception;
    void move_back()  throws Exception;
}

定义实现类

import java.util.Random;

public class Car implements Moveable {
    public void move() throws Exception {
        Thread.sleep(new Random().nextInt(1000));
        System.out.println("轿车行驶中…");
    }
    public void move_back() throws Exception {
        Thread.sleep(new Random().nextInt(1000));
        System.out.println("轿车向后行驶中…");
    }
}
import java.util.Random;

public class Truck implements Moveable {
    public void move() throws Exception {
        Thread.sleep(new Random().nextInt(1000));
        System.out.println("卡车行驶中…");
    }
    public void move_back() throws Exception {
        Thread.sleep(new Random().nextInt(1000));
        System.out.println("卡车向后行驶中…");
    }
}

定义动态代理工具类
动态代理工具类需要实现InvocationHandler接口,实现invoke方法,通过反射实现对抽象接口方法的调用method.invoke(target, args);

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

public class TimeHandler implements InvocationHandler {
    private Object target;

    public TimeHandler(Object target) {
        super();
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶…");
        method.invoke(target, args);
        long stopTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
        return null;
    }

}

验证程序

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

public class Test {
    public static void main(String[] args) throws Exception{
        Car car = new Car();
        Class<?> cls = car.getClass();

        InvocationHandler h = new TimeHandler(car);
        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
        m.move();
        System.out.println("");
        m.move_back();
        System.out.println("");System.out.println("");

        Truck truck = new Truck();
        cls = truck.getClass();
        h = new TimeHandler(truck);
        m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
        m.move();
        System.out.println("");
        m.move_back();
        System.out.println("");System.out.println("");
    }
}

运行结果

汽车开始行驶…
轿车行驶中…
汽车结束行驶…汽车行驶时间:200毫秒!

汽车开始行驶…
轿车向后行驶中…
汽车结束行驶…汽车行驶时间:681毫秒!

汽车开始行驶…
卡车行驶中…
汽车结束行驶…汽车行驶时间:492毫秒!

汽车开始行驶…
卡车向后行驶中…
汽车结束行驶…汽车行驶时间:338毫秒!

Process finished with exit code 0

总结

代理模式优缺点:

  • 静态代理模式:优点是具有较好的扩展性,屏蔽原始实现类;缺点是增加了中间层可能会导致性能下降。
  • 动态代理模式:优点是能对多个原始实现类进行统一的功能扩展;缺点是增加了中间层可能会导致性能下降。

代理模式与装饰器模式的区别:

  • 代理模式强调对于访问的控制,在调用实体类的方法之前或者之后,会添加一些操作,是扩展的广度。装饰器模式强调对于方法的加强,扩展的深度。打个比方,张无忌学习乾坤大挪移。装饰器模式,则增加其深度,从第3层境地学习到第4层境地,注重功能的加强。代理模式则是让其在学习乾坤大挪移之前,先学习一下九阳神功,两种武功能够有所联系。从外界看来,都是武功的增强,但是一个是深度,一个是广度。
  • 代理模式是代理,装饰器模式是装饰,两者在对原始对象的可见性上是不同的。代理模式中,在客户看来,无法感知到原始对象,只能接触代理对象。装饰器模式中,客户看来,是能够感知到原始对象的。

猜你喜欢

转载自blog.csdn.net/ywl470812087/article/details/102672781