委派模式和策略模式

委派模式

介绍

并不属于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、代码中会产生非常多策略类,增加维护难度。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值