代理模式
前言
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技术就是使用动态代理来实现的。
这篇文章中不但使用了代理模式,还结合了观察者模式,因此逻辑上会稍微繁琐一些,但是更加贴近实际使用情况。
另外就是文中例子里的代码只是为了说明如何解决问题,还有很多的优化空间,实际需要使用相关功能时切莫生搬硬套。