设计模式轻松学之05 代理模式

代理模式

前言

GOF( Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)联合出版的书中提到的设计模式一共有23种,可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。

代理模式的特点

这一章我们主要介绍结构型模式之代理模式(Proxy Pattern)。它的特点是,在调用方与被调用方之间增加一个代理类,用来增强被调用方的功能。

也就是,在不修改原来类代码的情况下,增强其功能。

一个例子

在我们系统的订单模块,有一个为客户新增订单的功能:

OrderService接口

public class Order {
    public void saveOrder(int userId, double money) {
        // 往数据库存储数据
        System.out.println("id:" + userId + "的用户新增订单,订单金额为:" + money);
    }
}

OrderServiceImpl实现类

public class OrderServiceImpl implements OrderService {
    @Override
    public void saveOrder(int userId, double money) {
        // 往数据库存储数据
        System.out.println("id:" + userId + "的用户新增订单,订单金额为:" + money);
    }
}

使用起来也非常简单:

OrderService order = new OrderServiceImpl();
order.saveOrder(100001, 100);

现在的需求是:在不修改原来代码的情况下,引入代理模式为下单用户发送成功短信和邮件提示。

1. 静态代理

静态代理的主要实现逻辑是:实现与目标对象相同的接口,在调用其方法前后添加功能。

1. 使用静态代理创建【被观察者】类

public class EnhancedOrderServiceImpl extends Observable implements OrderService {
    // 内部维护一个目标对象
    private OrderService orderService;
    // 隐藏空参构造器
    private EnhancedOrderServiceImpl(){}
    // 传入一个目标对象
    public EnhancedOrderServiceImpl(OrderService orderService) {
        this.orderService = orderService;
    }

    @Override
    public void saveOrder(int userId, double money) {
        System.out.println("代理类开始执行...");
        // 实际干活的还是原来的目标对象
        orderService.saveOrder(userId, money);
        // 1.修改changed变量状态
        setChanged();
        // 2.通知所有观察者,并传递UserId
        notifyObservers(userId);
    }
}

2. 引入【观察者】通知类

短信通知:

public class SmsOrderNotifier implements Observer {
 @Override
 public void update(Observable o, Object arg) {
     // 判断传给我们信息的人是否正确
     if (o instanceof OrderService) {
         int userId = (int) arg;
         System.out.println("根据用户id" + userId + "查询用户电话号码。");
         System.out.println("为用户发送短信");
     }
 }
}

Email通知:

public class EmailOrderNotifier implements Observer {
 @Override
 public void update(Observable o, Object arg) {
     // 判断传给我们信息的人是否正确
     if (o instanceof OrderService) {
         int userId = (int) arg;
         System.out.println("根据用户id" + userId + "查询用户email地址。");
         System.out.println("为用户发送email信息");
     }
 }
}

3. 测试

// 原来的目标对象
OrderService orderService = new OrderServiceImpl();
// 使用目标对象构建代理类
EnhancedOrderServiceImpl enhancedOrderServiceImpl = new EnhancedOrderServiceImpl(orderService);
// 添加短信通知类进观察者中
enhancedOrderServiceImpl.addObserver(new SmsOrderNotifier());
// 添加Email通知类进观察者中
enhancedOrderServiceImpl.addObserver(new EmailOrderNotifier());
enhancedOrderServiceImpl.saveOrder(100002, 300);

4. 静态代理总结

静态代理模式需要实现代理目标的接口,所以接口中的方法都需要写一遍,略显繁琐。

下面我们使用Jdk动态代理实现功能。

2. Jdk动态代理

创建Jdk动态代理类需要实现InvocationHandler接口,通常我们在内部维护一个代理对象,然后利用反射使用getInstance()方法将目标对象注入。代理对象每次执行方法,都将会调用内部的Object invoke(Object proxy, Method method, Object[] args)方法,我们把需要增强的代码写在里面即可。

1. 使用Jdk动态代理创建【被观察者】类

public class JdkProxyOrderServiceImpl extends Observable implements InvocationHandler {
    // 目标对象类型根据实际情况调整
    private OrderService target;

    // 利用反射获取代理对象,返回类型根据target实际类型调整
    public OrderService getInstance(OrderService target){
        this.target = target;
        Class<?> clazz =  target.getClass();
        return (OrderService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    // 代理对象每次执行方法,invoke就会被调用一次
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 用来保存返回值
        Object result;
        // 判断是否为新增订单方法
        if ("saveOrder".equals(method.getName())) {
            System.out.println("JdkProxy代理类开始执行...");
            // 实际干活的还是原来的目标对象
            result = method.invoke(this.target, args);
            // 添加短信通知类进观察者中
            this.addObserver(new SmsOrderNotifier());
            // 添加Email通知类进观察者中
            this.addObserver(new EmailOrderNotifier());
            // 1.修改changed变量状态
            setChanged();
            // 2.通知所有观察者,并传递UserId
            notifyObservers(args[0]);
            return result;
        }
        // 其他方法则原封不动地执行
        result = method.invoke(this.target, args);
        return result;
    }
}

2. 短信通知类和Email通知类修改判断逻辑

短信通知:

public class SmsOrderNotifier implements Observer {
 @Override
 public void update(Observable o, Object arg) {
     // 判断传给我们信息的人是否正确
     if (o instanceof JdkProxyOrderServiceImpl) {
         int userId = (int) arg;
         System.out.println("根据用户id" + userId + "查询用户电话号码。");
         System.out.println("为用户发送短信");
     }
 }
}

Email通知:

public class EmailOrderNotifier implements Observer {
 @Override
 public void update(Observable o, Object arg) {
     // 判断传给我们信息的人是否正确
     if (o instanceof JdkProxyOrderServiceImpl) {
         int userId = (int) arg;
         System.out.println("根据用户id" + userId + "查询用户email地址。");
         System.out.println("为用户发送email信息");
     }
 }
}

4. 测试

JdkProxyOrderServiceImpl jdkProxyOrderServiceImpl = new JdkProxyOrderServiceImpl();
OrderService orderService = jdkProxyOrderServiceImpl.getInstance(new OrderServiceImpl());
orderService.saveOrder(100003, 202);

总结

代理模式的功能非常强大,而且用处非常广泛,Spring框架中的AOP技术就是使用动态代理来实现的。

这篇文章中不但使用了代理模式,还结合了观察者模式,因此逻辑上会稍微繁琐一些,但是更加贴近实际使用情况。

另外就是文中例子里的代码只是为了说明如何解决问题,还有很多的优化空间,实际需要使用相关功能时切莫生搬硬套。

上一篇:.NetCore选项数据热更新


下一篇:spring源码初体验01 从对象到bean的生命历程