策略模式

60 阅读4分钟

策略模式

好处:可以减少if else,新增策略只需新增实现类和枚举。
缺点:类增多了。

实现策略模式所需的类

  1. 策略枚举类
  2. 策略接口类
  3. 策略实现类
  4. 策略上下文

策略枚举类

后面会用到枚举作为策略Map的key,策略实现类作为value,形成一对一的关系,便于根据策略类型获取策略实现类。

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;

/**
 * 策略枚举
 */
@Getter
@AllArgsConstructor
public enum TestEnum {
    /**
     * 一
     */
    ONE(1, "策略一"),
    /**
     * 二
     */
    TWO(2, "策略二");
    private Integer code;
    private String description;

    /**
     * 根据编码找枚举
     * @param code 编码
     * @return 匹配不到返回null
     */
    public static TestEnum getByCode(Integer code) {
        return Arrays.stream(TestEnum.values()).filter(t -> code.equals(t.code)).findFirst().orElse(null);
    }
}

策略接口类

提供接口给策略实现类实现。(听君一席话,如听一席话 hhhh)

import com.sc.cloud.common.enums.TestEnum;

/**
 * 策略接口
 */
public interface TestStrategy {
    /**
     * 获取策略类型
     * @date 2022/9/14
     * @return com.sc.cloud.common.enums.TestEnum
     */
    TestEnum getType();

    /**
     * 随便打印些东西
     * @date 2022/9/14
     */
    void print();
}

策略实现类

通过getType()方法将枚举与实现类关联起来。
策略一实现类:

import com.sc.cloud.common.enums.TestEnum;
import com.sc.cloud.strategy.TestStrategy;
import org.springframework.stereotype.Service;

/**
 * 策略一实现类
 */
@Service
public class OneStrategyImpl implements TestStrategy {
    @Override
    public TestEnum getType() {
        return TestEnum.ONE;
    }
    
    @Override
    public void print() {
        System.out.println("策略一");
    }
}

策略二实现类:

import com.sc.cloud.common.enums.TestEnum;
import com.sc.cloud.strategy.TestStrategy;
import org.springframework.stereotype.Service;

/**
 * 策略而实现类
 */
@Service
public class TwoStrategyImpl implements TestStrategy {
    @Override
    public TestEnum getType() {
        return TestEnum.TWO;
    }

    @Override
    public void print() {
        System.out.println("策略二");
    }
}

策略上下文

通过Spring的构造注入将实现了策略接口的类放到strategyList中,使用时再根据策略类型获取对应的实现类,执行对应的策略方法。

import com.sc.cloud.common.enums.TestEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 策略上下文
 */
@Component
public class TestStrategyContext {

    private final ConcurrentHashMap<TestEnum, TestStrategy> strategy = new ConcurrentHashMap<>();

    /**
     * 构造注入所有实现了策略接口的类
     * @param strategyList 所有实现 TestStrategy 接口的类
     */
    @Autowired
    public TestStrategyContext(List<TestStrategy> strategyList) {
        strategyList.forEach(s -> strategy.put(s.getType(), s));
    }

    /**
     * 对外部调用提供方法
     * @param type 策略类型
     */
    public void print(Integer type) {
        TestEnum testEnum = TestEnum.getByCode(type);
        if (Objects.isNull(testEnum)) {
            System.out.println("策略匹配失败");
            return;
        }
        TestStrategy testStrategy = strategy.get(testEnum);
        testStrategy.print();
    }
}

策略模式 + 工厂模式

参考自:Spring boot 运用策略模式实现,避免多次使用if
参考作者:我赢了算我输

实现策略模式所需的类

与本文第一种策略模式比较,在其基础上新增一个策略工厂类。

  1. 策略枚举类
  2. 策略接口类
  3. 策略实现类
  4. 策略工厂类
  5. 策略上下文

支付策略枚举类

此处的className可以使用Spring默认的bean名称,也可以自定义bean名称,如weChatPayStrategyImpl为默认名称,wechatPayStrategy则为自定义的bean名称。

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 支付策略枚举类
 */
@Getter
@AllArgsConstructor
public enum PayStrategyEnum {
//    /**
//     * 微信支付
//     */
//    WECHAT_PAY("wechat","wechatPayStrategy","微信支付"),
//    /**
//     * 支付宝支付
//     */
//    ALIPAY("alipay","alipayStrategy","支付宝支付")
//    ;
    /**
     * 微信支付
     */
    WECHAT_PAY("wechat", "weChatPayStrategyImpl", "微信支付"),
    /**
     * 支付宝支付
     */
    ALIPAY("alipay", "aliPayStrategyImpl", "支付宝支付");
    private String code;
    private String className;
    private String info;
}

支付策略接口

与本文第一种策略模式比较,不需要提供获取策略类型接口,因为策略用的Map的key是策略实现类的bean名称。

/**
 * 支付策略接口
 */
public interface PayStrategy {
    boolean pay();
}

支付宝支付策略类

注意:可以给bean重命名,也可以不重命名,此处没有自定义bean名称,使用默认的bean名称,默认的bean名称开头字母是小写,即默认bean名称为aliPayStrategyImpl

import com.sc.cloud.strategy.PayStrategy;
import org.springframework.stereotype.Service;

/**
 * 支付宝支付策略类
 */
//@Service("alipayStrategy") // 重命名bean
@Service()
public class AliPayStrategyImpl implements PayStrategy {
    @Override
    public boolean pay() {
        System.out.println("使用了阿里支付");
        return true;
    }
}

微信支付策略类

import com.sc.cloud.strategy.PayStrategy;
import org.springframework.stereotype.Service;

/**
 * 微信支付策略类
 */
//@Service("wechatPayStrategy") // 重命名bean
@Service()
public class WeChatPayStrategyImpl implements PayStrategy {
    @Override
    public boolean pay() {
        System.out.println("使用微信支付");
        return true;
    }
}

支付策略工厂类

通过Spring根据bean名称自动注入到payStrategyMap,若前面指定了策略实现类bean名称,则此处用的就是指定的名称,否则是默认bean名称

import com.sc.cloud.common.enums.PayStrategyEnum;
import com.sc.cloud.strategy.PayStrategy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 支付策略工厂类
 */
@Component
public class PayStrategyFactory {
    /**
     * 通过Spring容器的方式注入
     */
    @Resource
    private Map<String, PayStrategy> payStrategyMap;

    public PayStrategy getPayStrategy(PayStrategyEnum payStrategyEnum) {
        if (!payStrategyMap.containsKey(payStrategyEnum.getClassName())) {
            System.out.println("没有对应的支付策略,无法进行支付");
            return null;
        }
        return payStrategyMap.get(payStrategyEnum.getClassName());
    }
}

支付策略上下文

就这样

mport com.sc.cloud.common.enums.PayStrategyEnum;
import com.sc.cloud.strategy.factory.PayStrategyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;

/**
 * 支付策略上下文
 */
@Component
public class PayStrategyContext {
    @Autowired
    private PayStrategyFactory payStrategyFactory;

    /**
     * 支付执行
     * @param type 支付类型
     */
    public boolean payHandle(String type) {
        Optional<PayStrategyEnum> payStrategyEnumOptional = Arrays.stream(PayStrategyEnum.class.getEnumConstants())
                .filter((e) -> e.getCode().equals(type)).findAny();
        if (!payStrategyEnumOptional.isPresent()) {
            System.out.println("匹配不到具体支付策略");
            return false;
        }
        PayStrategyEnum payStrategyEnum = payStrategyEnumOptional.get();
        PayStrategy payStrategy = payStrategyFactory.getPayStrategy(payStrategyEnum);
        if (Objects.isNull(payStrategy)) {
            System.out.println("具体支付策略未实现");
            return false;
        }
        boolean pay = payStrategy.pay();
        return true;
    }
}

结束