委派模式
介绍
并不属于23种设计模式,可以理解为一种特殊的静态代理,
但比起代理关注过程(例如JDK动态代理的Invoke),委派更关注结果(对对应返回值的处理,派遣任务后处理结果)
举例
例如在SpirngMVC的dispatchservlet中对于请求的映射派遣,就用到了委派模式
@RestController
public class HelloController {
@GetMapping("/")
public String hello(@RequestParam(value = "message", required = false) String message) {
return message;
}
}
org.springframework.web.servlet.DispatcherServlet#handlerMappings
List handlerMappings
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// Determine handler for the current request.
//确定委派给谁处理
mappedHandler = getHandler(processedRequest);
...
// Actually invoke the handler.
//实际处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
策略模式
介绍
策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。
策略模式的应用场景
1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
2、一个执行方法需要动态地在几种算法中选择一种。
用策略模式实现选择支付方式的业务场景
案例
例如很多项目都存在的支付场景,如果不使用策略模式
public class AliPay implements PayStrategy {
@Override
public void pay() {
System.out.println("支付宝");
}
}
public class JDPay implements PayStrategy {
@Override
public void pay() {
System.out.println("白条");
}
}
public interface PayStrategy {
public void pay();
}
public static void main(String[] args) {
String payment = "jd";
if ("ali".equals(payment)) {
new AliPay().pay();
} else if ("jd".equals(payment)) {
new JDPay().pay();
}
}
可以看到调用方法存在很多if else,且如果添加新的支付方式,又要添加新的if else,该调用方法不仅会变得越来越难以阅读,还违背了开闭原则.
重构
现在用策略模式结合之前单例模式和工厂模式,对代码进行重构.
除了方法测试类以外,其他类都在同一包下
Pay类
public abstract class PaymentStrategy {
abstract PaymentStrategy auth();
public abstract void pay();
}
public class AliPay extends PaymentStrategy {
AliPay() {
if (PayStrategy.AliPay.getPaymentStrategy() != null) {
throw new RuntimeException("multiInstanceException");
}
}
@Override
PaymentStrategy auth() {
System.out.println("AliPayInterfaceAuth");
return this;
}
@Override
public void pay() {
System.out.println("AliPay付款");
}
}
public class JDPay extends PaymentStrategy {
JDPay() {
if (PayStrategy.JDPay.getPaymentStrategy() != null) {
throw new RuntimeException("multiInstanceException");
}
}
@Override
PaymentStrategy auth() {
System.out.println("JDPayInterfaceAuth");
return this;
}
@Override
public void pay() {
System.out.println("JD付款");
}
}
public class WeChatPay extends PaymentStrategy {
WeChatPay() {
if (PayStrategy.WeChatPay.getPaymentStrategy() != null) {
throw new RuntimeException("multiInstanceException");
}
}
@Override
PaymentStrategy auth() {
System.out.println("WeChatPayInterfaceAuth");
return this;
}
@Override
public void pay() {
System.out.println("WeChat付款");
}
}
工厂加单例实现类
public class PayStrategyFactory {
private static Map<PayStrategy, PaymentStrategy> payStrategies = new HashMap<>();
static {
System.out.println("加载配置文件");
init();
}
private static void init(){
securityCheck();
PayStrategy.AliPay.setPaymentStrategy((AliPay) new AliPay().auth());
PayStrategy.JDPay.setPaymentStrategy((JDPay) new JDPay().auth());
PayStrategy.WeChatPay.setPaymentStrategy((WeChatPay) new WeChatPay().auth());
payStrategies.put(PayStrategy.AliPay, PayStrategy.AliPay.getPaymentStrategy());
payStrategies.put(PayStrategy.JDPay, PayStrategy.AliPay.getPaymentStrategy());
payStrategies.put(PayStrategy.WeChatPay, PayStrategy.AliPay.getPaymentStrategy());
}
private static void securityCheck(){
for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace()) {
if (stackTraceElement.getClassName().contains("reflect")) {
throw new RuntimeException("can't invoke from reflect");
}
}
}
public static PaymentStrategy get(PayStrategy payStrategy) {
return payStrategies.get(payStrategy);
}
}
public enum PayStrategy {
AliPay(),
JDPay(),
WeChatPay();
private PaymentStrategy payment;
PaymentStrategy getPaymentStrategy() {
return payment;
}
void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.payment = paymentStrategy;
}
}
测试
public static void main(String[] args) throws Exception{
PayStrategyFactory factory = new PayStrategyFactory();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(factory.get(PayStrategy.AliPay));
}).start();
}
Thread.sleep(100);
factory.get(PayStrategy.AliPay).pay();
//验证安全判断,拒绝反射调用
Method init = factory.getClass().getDeclaredMethod("init", null);
init.setAccessible(true);
init.invoke(factory,null);
}
结果
加载配置文件
AliPayInterfaceAuth
JDPayInterfaceAuth
WeChatPayInterfaceAuth
com.example.patterndelegatestrategy.strategy.payStrategy.AliPay@1f64aa1d
com.example.patterndelegatestrategy.strategy.payStrategy.AliPay@1f64aa1d
com.example.patterndelegatestrategy.strategy.payStrategy.AliPay@1f64aa1d
com.example.patterndelegatestrategy.strategy.payStrategy.AliPay@1f64aa1d
com.example.patterndelegatestrategy.strategy.payStrategy.AliPay@1f64aa1d
AliPay付款
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.example.patterndelegatestrategy.strategy.Test.main(Test.java:20)
Caused by: java.lang.RuntimeException: can't invoke from reflect
at com.example.patterndelegatestrategy.strategy.payStrategy.PayStrategyFactory.securityCheck(PayStrategyFactory.java:26)
at com.example.patterndelegatestrategy.strategy.payStrategy.PayStrategyFactory.init(PayStrategyFactory.java:14)
... 5 more
Process finished with exit code 1
可以看到,这样一来,调用代码变得更加容易维护,同时也更容易理解
以上代码其实还可以优化,删除枚举类,通过扫描指定包下的类,通过反射去初始化它们,并保存一个<String类名,Object调用>Map作为存储.这样工厂类也实现了开闭原则,但是感觉代码会更加复杂,且没那么易读了
个人认为,代码总是要升级的,只要不是影响到阅读和使用的类,那就不必遵循开闭原则.
策略模式在源码中的实现
首先来看一个比较常用的比较器 Comparator 接口,我们看到一个大家常用的 compare()方法,就是一个策略抽象实现:
public interface Comparator<T> {
int compare(T o1, T o2);
...
}
java.util.Arrays#parallelSort(T[], java.util.Comparator<? super T>)
public static <T> void parallelSort(T[] a, Comparator<? super T> cmp) {
if (cmp == null)
cmp = NaturalOrder.INSTANCE;
int n = a.length, p, g;
if (n <= MIN_ARRAY_SORT_GRAN ||
(p = ForkJoinPool.getCommonPoolParallelism()) == 1)
TimSort.sort(a, 0, n, cmp, null, 0, 0);
else
new ArraysParallelSortHelpers.FJObject.Sorter<T>
(null, a,
(T[])Array.newInstance(a.getClass().getComponentType(), n),
0, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
MIN_ARRAY_SORT_GRAN : g, cmp).invoke();
}
根据传入不同的比较器,返回不同排序(策略)的结果
总结
策略模式的优缺点
优点:
1、策略模式符合开闭原则。
2、避免使用多重条件转移语句,如 if...else...语句、switch 语句
3、使用策略模式可以提高算法的保密性和安全性。
缺点:
1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2、代码中会产生非常多策略类,增加维护难度。