委派模式
委派模式的定义及应用场景
委派模式(Delegate Pattern)的基本作用就是 负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理 的全权代理,但是代理模式注重过程,而委派模式注重结果。
委派模式在 Spring 中应用 非常多,大家常用的DispatcherServlet
其实就是用到了委派模式。现实生活中也常有委派的场景发生,例如:老板(Boss)给项目经理(Leader)下达任务,项目经理会根据 实际情况给每个员工派发工作任务,待员工把工作任务完成之后,再由项目经理汇报工 作进度和结果给老板。我们用代码来模拟下这个业务场景,先来看一下类图:
1 | /** |
委派模式在源码中的体现
下面我们再来还原一下SpringMVC
的DispatcherServlet
是如何实现委派模式的。
1 | /** |
策略模式
策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互 相替换,此模式让算法的变化不会影响到使用算法的用户。
策略模式的应用场景
- 假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
- 一个系统需要动态地在几种算法中选择一种。
以优惠活动为例
以优惠活动为例,优惠策略会有很多种可能 如:领取优惠券抵扣、返现促销、拼团优惠。下面我们用代码来模拟,首先我们创建一个促销策略的抽象PromotionStrategy
:
1 | public interface PromotionStrategy { |
然后分别创建优惠券抵扣策略 CouponStrategy 类、返现促销策略 CashbackStrategy 类、拼团优惠策略 GroupbuyStrategy 类和无优惠策略EmptyStrategy
类:
1 | public class CouponStrategy implements PromotionStrategy { |
然后创建促销活动方案PromotionActivity
类:
1 | public class PromotionActivity { |
我们做活动时候往往是要根据不同的需求对促销策略进行动态选择的 ,编写客户端代码进行测试:
1 | public class PromotionActivityTest { |
这样改造之后,满足了业务需求,客户可根据自己的需求选择不同的优惠策略了。但是, 经过一段时间的业务积累,我们的促销活动会越来越多。但是每次上活动之前都要改代码,而且要做重复测试,判断逻辑可能也变得 越来越复杂。这时候,我们是不需要思考代码是不是应该重构了?回顾我们之前学过的 设计模式应该如何来优化这段代码呢?其实,我们可以结合单例模式和工厂模式。
1 | public class PromotionStrategyFactory { |
用策略模式实现选择支付方式的业务场景
相信小伙伴们都 用过支付宝、微信支付、银联支付以及京东白条。一个常见的应用场景就是大家在下单 支付时会提示选择支付方式,如果用户未选,系统也会默认好推荐的支付方式进行结算。
创建
Payment
抽象类,定义支付规范和支付逻辑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28/**
* 定义支付规范和支付逻辑
*/
public abstract class Payment {
/**
* 支付类型名称
* @return
*/
public abstract String getName();
/**
* 查询余额
* @param uid
* @return
*/
protected abstract double queryBalance(String uid);
/**
* 扣款支付
* @param uid
* @param amount
* @return
*/
public PayState pay(String uid, double amount) {
if (queryBalance(uid) < amount) {
return new PayState(500, "支付失败", "余额不足");
}
return new PayState(200, "支付成功", "支付金额:" + amount);
}
}分别创建具体的支付方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class AliPay extends Payment {
public String getName() {
return "支付宝";
}
protected double queryBalance(String uid) {
return 900;
}
}
public class WeChatPay extends Payment {
public String getName() {
return "微信支付";
}
protected double queryBalance(String uid) {
return 100;
}
}创建支付方式管理类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class PaymentManager {
public static final String ALI_PAY = "AliPay";
public static final String JD_PAY = "JdPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String, Payment> mapping = new HashMap<>();
static {
mapping.put(ALI_PAY, new AliPay());
mapping.put(JD_PAY, new JDPay());
mapping.put(WECHAT_PAY, new WeChatPay());
}
/**
* 根据payKey获取支付方式
*
* @param payKey
* @return
*/
public static Payment getPayment(String payKey) {
Payment payment = mapping.get(payKey);
return payment == null ? mapping.get(DEFAULT_PAY) : payment;
}
}订单类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Order {
private String uid;
private String orderId;
private double amount;
/**
* 完美地解决了 switch 的过程,不需要在代码逻辑中写 switch 了
* 也不用写 if else
* @return
*/
public PayState pay() {
return pay(PaymentManager.DEFAULT_PAY);
}
public PayState pay(String payKey) {
Payment payment = PaymentManager.getPayment(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次交易金额为:" + amount + ",开始扣款...");
return payment.pay(uid, amount);
}
}
策略模式在 JDK 源码中的体现
首先来看一个比较常用的比较器Comparator
接口,我们看到的一个大家常用的compare()
方法,就是一个策略抽象实现:Comparator
抽象下面有非常多的实现类,我们经常会把Comparator
作为参数传入作 为排序策略,例如Arrays
类的parallelSort
方法等:
策略模式的优缺点
优点:
- 策略模式符合开闭原则。
- 避免使用多重条件转移语句,如
if...else...
语句、switch
语句 - 使用策略模式可以提高算法的保密性和安全性。
缺点:
- 客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
- 代码中会产生非常多策略类,增加维护难度。
委派模式与策略模式综合应用
现在,我们再来回顾一下,DispatcherServlet
的委派逻辑,代码如下:
这样的代码扩展性不太优雅,也不现实,因为我们实际项目中一定不止这几个 Controller, 往往是成千上万个 Controller,显然,我们不能写成千上万个 if…else… 。那么我们如何 来改造呢?小伙伴们一定首先就想到了策略模式,来看一下我是怎么优化的:
1 | public class DispatcherServlet extends HttpServlet { |
上面的代码我结合了策略模式、工厂模式、单例模式。
源码:https://github.com/chentianming11/design-pattern
delegate包!