设计模式四:代理模式(Proxy pattern)

相关视频:

模式的秘密---代理模式

java设计模式23种设计模式视频(第13~15章代理模式 复合模式 桥接模式)

相关文章:

第一推荐:轻松学,Java 中的代理模式及动态代理

第二推荐:10分钟看懂动态代理设计模式

1、代理模式基本概念及分类

1.1、代理模式定义:

为其他对象提供一种代理,以控制对这个对象的访问。代理对象起到中介作用,可以去掉功能服务或增加额外的服务。
    举例:火车购票,可以去直接去火车站也可以去代售点,这个代售点就可以称之为火车站的代理。而代售点除了卖火车票外,可能会有自己额外的服务,比如电话预约。同时,代售点是不能退火车票的,要退的话只能去火车站。这个代售点,少了一些功能服务(退票),增加了一些额外服务(电话预约)。

1.2、几种常见的代理模式:

远程代理:为不同地理的对象,提供局域网代表对象。类似于客户端-服务器的模式。

举例:我有一家连锁店,我想实时了解各个门店的销售信息,我们可以通过远程代理来构造各个门店的监视器,来随时报告各个门店的销售和库存信息。

设计模式四:代理模式(Proxy pattern)_第1张图片

虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。

举例:我们在浏览一个新闻的时候,会有文字和图片,如果网络情况不好,我们可以使用虚拟代理,用一个本地的图片暂时替代我们要加载的图片,当图片获取成功后再展示出来,如图二所示。这就是虚拟代理的一个简单应用。

图一:

设计模式四:代理模式(Proxy pattern)_第2张图片

图二:

设计模式四:代理模式(Proxy pattern)_第3张图片

 

保护代理:权限控制。例如,有些网站,只有注册登陆后才能发帖、评论。

智能引用代理:刚才举的火车票代售点的例子。

2、静态代理和动态代理

2.1、静态代理

2.1.1、定义

静态代理:代理和被代理对象在代理之前是确定的。他们都实现相同的接口或者继承相同的抽象类。

设计模式四:代理模式(Proxy pattern)_第4张图片

 2.1.2、代码实战:

我们用代码来实现车行驶。

2.1.2.1、常规方式

Moveable.java

public interface Moveable {
    void move();
}

Car.java

public class Car implements Moveable {


    /**
     * 如果不使用代理模式的话,我们如果想记录车行驶的时间,会采用如下方式
     */
    @Override
    public void move() {

        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        // 实现开车
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        System.out.println("汽车结束行驶... ");
        System.out.println("汽车行驶时间为:" + time + "毫秒");
    }


}

Client.java

/**
 * 测试类
 */
public class Client {
    public static void main(String[] args) {
        // 不使用代理模式,常规方式
        Car car = new Car();
        car.move();
    }
}

 打印结果为:

设计模式四:代理模式(Proxy pattern)_第5张图片

2.1.2.2、通过继承的方式实现

Car.java

public class Car implements Moveable {
    @Override
    public void move() {
        // 实现开车
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Car2.java

public class Car2 extends Car {
    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");

        // 调用父类 Car 的方法 move()
        super.move();

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        System.out.println("汽车结束行驶... ");
        System.out.println("汽车行驶时间为:" + time + "毫秒");
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        // 使用继承的方式
        Moveable moveable = new Car2();
        moveable.move();
    }
}

 打印结果为:

设计模式四:代理模式(Proxy pattern)_第6张图片

 2.1.2.3、通过聚合的方式实现

聚合:简单理解就是在一个类中调用另一个类的对象。

Car3.java

public class Car3 implements Moveable {

    private Car car;

    // 通过构造方法 把 Car 传进来
    public Car3(Car car) {
        this.car = car;
    }

    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");

        car.move();

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        System.out.println("汽车结束行驶... ");
        System.out.println("汽车行驶时间为:" + time + "毫秒");
    }

}

Clien.java

public class Client {
    public static void main(String[] args) {
        // 不使用代理模式,常规方式
//        Car car = new Car();
//        car.move();

        // 使用继承的方式
//        Moveable moveable = new Car2();
//        moveable.move();

        // 使用聚合的方式
        Car car = new Car();
        Car3 car3 = new Car3(car);
        car3.move();

    }
}

打印结果不变。

以上我们通过继承和聚合两种方式来实现静态代理,那么哪种方式更适合静态代理呢?

我们分析一下,先来说继承这种方式,现在如果需求发生变化,我们就得新建一个子类,如下图:

设计模式四:代理模式(Proxy pattern)_第7张图片

很显然,这种方式会随着功能的增加而变得越来越臃肿,因此这种方式应该被排除!

那么,聚合的方式呢?

CarTimeProxy.java

public class CarTimeProxy implements Moveable {

    private Moveable moveable;

    public CarTimeProxy(Moveable moveable) {
        this.moveable = moveable;
    }

    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");

        moveable.move();

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        System.out.println("汽车结束行驶... ");
        System.out.println("汽车行驶时间为:" + time + "毫秒");
    }
}

CarLogProxy.java

public class CarLogProxy implements Moveable {

    private Moveable moveable;

    public CarLogProxy(Moveable moveable) {
        this.moveable = moveable;
    }

    @Override
    public void move() {
        System.out.println("日志开始...");
        moveable.move();
        System.out.println("日志结束...");
    }
}

Client.java

public class Client {
    public static void main(String[] args) {
        // 不使用代理模式,常规方式
//        Car car = new Car();
//        car.move();

        // 使用继承的方式
//        Moveable moveable = new Car2();
//        moveable.move();

        // 使用聚合的方式
//        Car car = new Car();
//        Car3 car3 = new Car3(car);
//        car3.move();

        //先记录日志,再记录时间
        Car car = new Car();
        CarTimeProxy carTimeProxy = new CarTimeProxy(car);
        CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
        carLogProxy.move();

    }
}

打印结果为:

设计模式四:代理模式(Proxy pattern)_第8张图片

如果我们想先记录时间再记录日志呢?很简单:

Client.java

public class Client {
    public static void main(String[] args) {
        // 不使用代理模式,常规方式
//        Car car = new Car();
//        car.move();

        // 使用继承的方式
//        Moveable moveable = new Car2();
//        moveable.move();

        // 使用聚合的方式
//        Car car = new Car();
//        Car3 car3 = new Car3(car);
//        car3.move();

        //先记录日志,再记录时间
//        Car car = new Car();
//        CarTimeProxy carTimeProxy = new CarTimeProxy(car);
//        CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
//        carLogProxy.move();

        //先记录时间,再记录日志
        Car car = new Car();
        CarLogProxy carLogProxy = new CarLogProxy(car);
        CarTimeProxy carTimeProxy = new CarTimeProxy(carLogProxy);
        carTimeProxy.move();
    }
}

打印结果为:

设计模式四:代理模式(Proxy pattern)_第9张图片

可以看到,使用聚合的方式会比使用继承的方式灵活很多。

那么,接下来,如果我们需要给火车加上行驶时间和日志的记录呢?我们是否需要再新建两个代理类?有没有更简单的方法呢?

设计模式四:代理模式(Proxy pattern)_第10张图片

那么,有没有一种方法能够动态产生代理,实现对不同类、不同方法的代理呢?下面我们来了解一下动态代理。

2.2、动态代理

2.2.1、用JDK来实现动态代理

我们先来了解一下JDK的动态代理:

设计模式四:代理模式(Proxy pattern)_第11张图片

图一:

设计模式四:代理模式(Proxy pattern)_第12张图片

图二:

设计模式四:代理模式(Proxy pattern)_第13张图片

下面通过代码来了解一下:

TimeHandler.java

/**
 * InvocationHandler 事务处理器
 */
public class TimeHandler implements InvocationHandler {

    private Object target;

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

    /**
     * @param proxy  被代理的对象
     * @param method 被代理的对象的方法
     * @param args   被代理的对象的方法的参数
     * @return       所调用方法的返回值
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");

        method.invoke(target);

        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        System.out.println("汽车结束行驶... ");
        System.out.println("汽车行驶时间为:" + time + "毫秒");

        return null;
    }

}

Test.java

public class Test {
    public static void main(String[] args) {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Class cls = car.getClass();
        /**
         * newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
         * loader: 被代理类的类加载器
         * interfaces: 我们要实现的接口
         * h:事务处理器
         */
        Moveable m =  (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
        m.move();
    }
}

打印结果:

设计模式四:代理模式(Proxy pattern)_第14张图片

总结:

设计模式四:代理模式(Proxy pattern)_第15张图片

动态代理实现步骤:

设计模式四:代理模式(Proxy pattern)_第16张图片

作业,实现时间记录和日志记录:

public class Test {
    public static void main(String[] args) {

//        timeTest();
//        logTest();

        timeAndLogTest();

    }

    private static void timeAndLogTest() {
        Car car = new Car();

        InvocationHandler timeHandler = new TimeHandler(car);
        Class cls = car.getClass();
        Moveable moveable = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(), timeHandler);

        InvocationHandler logHandler = new LogHandler(moveable);
        Class class2 = moveable.getClass();
        Moveable moveable2 = (Moveable) Proxy.newProxyInstance(class2.getClassLoader(),
                class2.getInterfaces(), logHandler);

        moveable2.move();
    }

    private static void logTest() {
        Car car = new Car();
        InvocationHandler logHandler = new LogHandler(car);
        Class cls = car.getClass();

        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(), logHandler);
        m.move();
    }

    private static void timeTest() {
        Car car = new Car();
        InvocationHandler timeHandler = new TimeHandler(car);
        Class cls = car.getClass();
        /**
         * newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
         * loader: 被代理类的类加载器
         * interfaces: 我们要实现的接口
         * h:事务处理器
         */
        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
                cls.getInterfaces(), timeHandler);
        m.move();
    }
}

2.2.2、JDK动态代理和CGLIB动态代理的区别

设计模式四:代理模式(Proxy pattern)_第17张图片

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(设计模式)