场景

策略模式(Strategy Pattern)

是指定义了算法家族并分别封装起来,让它们之间可以互相替换,

此模式使得算法的变化不会影响使用算法的用户。

策略模式的应用场景

1、系统中有很多类,而它们的区别仅仅在于行为不同。

2、一个系统需要动态地在几种算法中选择一种。

注:


关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。

业务举例-物流策略选择

在某物流管理系统中,车辆有人车、料车、皮卡车辆类型,三种车型分别有不同的物流规则。

调度员需要根据不同的货物制定不同的物流方案。

新建物流规则接口LogisticRules

package com.ruoyi.demo.designPattern.strategyPattern;

/**
 * 物流规则类
 */
public interface LogisticsRules {
    //执行物流
    void doLogistics();
}

然后分别新建人车、料车、皮卡物流规则类

人车物流规则类

package com.ruoyi.demo.designPattern.strategyPattern;

public class PersonCarLogisticsRules implements LogisticsRules{
    @Override
    public void doLogistics() {
        System.out.println("人车执行物流规则");
    }
}

料车物流规则类

package com.ruoyi.demo.designPattern.strategyPattern;

public class SkipCatLogisticsRules implements LogisticsRules{
    @Override
    public void doLogistics() {
        System.out.println("料车执行物流规则");
    }
}

皮卡车物流规则类

package com.ruoyi.demo.designPattern.strategyPattern;

public class PickupCarLogisticsRules implements LogisticsRules{
    @Override
    public void doLogistics() {
        System.out.println("皮卡车执行物流规则");
    }
}

创建物流方案类

package com.ruoyi.demo.designPattern.strategyPattern;

/**
 * 物流方案类
 */
public class LogisticsPlan {
    private LogisticsRules logisticsRules;

    public LogisticsPlan(LogisticsRules logisticsRules){
        this.logisticsRules = logisticsRules;
    }

    public void execute(){
        logisticsRules.doLogistics();
    }
}

编写客户端测试类

package com.ruoyi.demo.designPattern.strategyPattern;

import org.apache.commons.lang.StringUtils;

public class logicticsTest {
    public static void main(String[] args) {

        //游客物流计划
        LogisticsPlan  visitorPlan = new LogisticsPlan(new PersonCarLogisticsRules());
        //快递物流计划
        LogisticsPlan  expressPlan = new LogisticsPlan(new SkipCatLogisticsRules());
        visitorPlan.execute();
        expressPlan.execute();
    }
}

在实际业务中,往往会根据不同的需求对物流规则进行动态选择,代码通常如下

//实际业务中会根据不同的需求对物流规则进行动态选择
        LogisticsPlan logisticsPlan = null;
        String logisticsKey = "EXPRESS";
        if(StringUtils.equals(logisticsKey,"EXPRESS")){
            logisticsPlan = new LogisticsPlan(new SkipCatLogisticsRules());
        }else if(StringUtils.equals(logisticsKey,"VISITOR")){
            logisticsPlan = new LogisticsPlan(new PersonCarLogisticsRules());
        }
        logisticsPlan.execute();

这样改造之后,如果经过业务积累,物流规则越来越多,每次上新规则都要改代码、重复测试。

所以可以结合单例模式和工厂模式进行优化。


设计模式-单例模式-饿汉式单例模式、懒汉式单例模式、静态内部类在Java中的使用示例:



创建LogisticsRulesFactory

package com.ruoyi.demo.designPattern.strategyPattern;

import java.util.HashMap;
import java.util.Map;

public class LogisticsRulesFactory {
    private static Map<String,LogisticsRules> LOGISTICS_RULES_MAP = new HashMap<>();

    static {
        LOGISTICS_RULES_MAP.put(LogisticsKey.EXPRESS,new SkipCatLogisticsRules());
        LOGISTICS_RULES_MAP.put(LogisticsKey.VISTOR,new PersonCarLogisticsRules());
    }

    //默认规则是皮卡车规则-既能拉人、又能拉货
    private static final LogisticsRules DEFAULT_RULES = new PickupCarLogisticsRules();

    private LogisticsRulesFactory(){}

    public static LogisticsRules getLogsiticsRules(String logisticsKey){
        LogisticsRules logisticsRules = LOGISTICS_RULES_MAP.get(logisticsKey);
        return logisticsRules == null?DEFAULT_RULES:logisticsRules;

    }
    private interface LogisticsKey{
        String VISTOR = "VISTOR";
        String EXPRESS = "EXPRESS";
    }
}

这时候客户端代码可以这样写

package com.ruoyi.demo.designPattern.strategyPattern;

public class logisticsBetterTest {
    public static void main(String[] args) {
        String logisticsKey = "EXPRESS";
        LogisticsPlan logisticsPlan = new LogisticsPlan(LogisticsRulesFactory.getLogsiticsRules(logisticsKey));
        logisticsPlan.execute();
    }
}

业务示例-支付方式选择

支付时会选择支付方式,如果未选择,也会用默认的支付方式进行结算。

创建Payment抽象类,定义支付规范和支付逻辑

package com.ruoyi.demo.designPattern.strategyPattern;

public abstract class Payment {

    //支付类型(名称)
    public abstract String getName();

    //查询余额
    protected  abstract double queryBalance(String uid);

    //扣款支付
    public PayState pay(String uid,double amount){
        if(queryBalance(uid)<amount){
            return new PayState(500,"支付失败","余额不足");
        }
        return new PayState(200,"支付成功","支付金额:"+amount);
    }
}

分别创建具体的支付方式。

支付宝支付方式

package com.ruoyi.demo.designPattern.strategyPattern;

public class AliPay extends Payment{
    @Override
    public String getName() {
        return "支付宝";
    }

    @Override
    protected double queryBalance(String uid) {
        return 900;
    }
}

微信支付类

package com.ruoyi.demo.designPattern.strategyPattern;

public class WechatPay extends Payment{
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 600;
    }
}

银联支付类

package com.ruoyi.demo.designPattern.strategyPattern;

public class UnionPay extends Payment{
    @Override
    public String getName() {
        return "银联支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 1000;
    }
}

创建支付状态的包装类

package com.ruoyi.demo.designPattern.strategyPattern;

public class PayState {
    private int code;
    private Object data;
    private String msg;

    public PayState(int code, Object data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    public String toString(){
        return ("支付状态:["+code+"],"+msg+",交易详情:"+data);
    }
}

创建支付策略管理类

package com.ruoyi.demo.designPattern.strategyPattern;

import java.util.HashMap;
import java.util.Map;

/**
 * 支付策略管理类
 */
public class PayStrategy {
    public static final String ALI_PAY = "ALI_PAY";
    public static final String WECHAT_PAY = "WECHAT_PAY";
    public static final String UNION_PAY = "UNION_PAY";
    public static final String DEFAULT_PAY = "UNION_PAY";

    private static Map<String,Payment> payStrategy = new HashMap<>();

    static {
        payStrategy.put(ALI_PAY,new AliPay());
        payStrategy.put(WECHAT_PAY,new WechatPay());
        payStrategy.put(UNION_PAY,new UnionPay());
    }

    public static Payment get(String payKey){
        if(!payStrategy.containsKey(payKey)){
            return payStrategy.get(DEFAULT_PAY);
        }
        return payStrategy.get(payKey);
    }

}

创建订单类

package com.ruoyi.demo.designPattern.strategyPattern;

public class Order {
    private String uid;
    private String orderId;
    private double amount;

    public Order(String uid, String orderId, double amount) {
        this.uid = uid;
        this.orderId = orderId;
        this.amount = amount;
    }

    public PayState pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("欢迎使用"+payment.getName());
        System.out.println("本次交易金额为:"+amount+",开始扣款。。。");
        return payment.pay(uid,amount);
    }
}

测试代码如下

package com.ruoyi.demo.designPattern.strategyPattern;

public class PayStrategyTest {
    public static void main(String[] args) {
        Order order = new Order("001","2022103119105523",288.33);
        //开始支付,选择支付方式,每个渠道的具体算法是不一样的,基本算法是固定的
        //在支付的时候才决定这个值用哪个
        System.out.println(order.pay(PayStrategy.ALI_PAY));
    }
}

策略模式的优缺点

优点:

1、策略模式符合开闭原则。

2、策略模式可避免使用多重条件语句,如if...else语句、switch语句。

3、使用策略模式可以提高算法的保密性和安全性。

缺点:

1、客户端必须知道所有的策略,并且决定使用哪一个策略类。

2、代码中会产生非常多的策略类,增加了代码的维护难度。