Bootstrap

策略模式-实战

策略模式定义

范文:一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

白话:去除IF-ELSE

代码场景:
根据用户等级,计算商品金额。

上代码:
NormalStrategy 普通用户:

package com.foxgl.tools.pattern.strategy;

import java.math.BigDecimal;

import com.foxgl.tools.pattern.strategy.annotation.Strategy;

@Strategy("NORMAL")
public class NormalStrategy implements CalculateStrategy {

  @Override
  public BigDecimal sale(BigDecimal amt) {
    return amt = amt.multiply(new BigDecimal(0.9)).setScale(2, BigDecimal.ROUND_HALF_UP);
  }

  @Override
  public String userType() {
    return "NORMAL";
  }
}

VIP用户:

package com.foxgl.tools.pattern.strategy;

import java.math.BigDecimal;

import com.foxgl.tools.pattern.strategy.annotation.Strategy;

@Strategy("VIP")
public class VIPStrategy implements CalculateStrategy {

  @Override
  public BigDecimal sale(BigDecimal amt) {
    return amt = amt.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
  }

  @Override
  public String userType() {
    return "VIP";
  }
}

接口

package com.foxgl.tools.pattern.strategy;

import java.math.BigDecimal;

public interface CalculateStrategy {
  public String userType();

  public BigDecimal sale(BigDecimal amt);
}

SaleService计算实现

package com.foxgl.tools.pattern.strategy;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 享元 + 策略 spring特性--默认将所有的实现类,放入到这个LIST表
 *
 * @author jy027
 */
@Service
public class SaleService {

  @Autowired NormalStrategy normalStrategy;

  @Autowired VIPStrategy vIPStrategy;

  //	@Override
  //	public BigDecimal sale(String userType,BigDecimal amt){
  //		//由于会员体系一定会修改,此场景使用【策略模式】
  //		//换言之,如果有IF ELSE就违反开闭原则
  //		//spring源码中--bean 由开发者自己生成,由spring直接去容器拿--
  //		if("normal".equalsIgnoreCase(userType)){
  //			return normalStrategy.sale(amt);
  //		}else if("vip".equalsIgnoreCase(userType)){
  //			return vIPStrategy.sale(amt);
  //		}else{
  //			return amt;
  //		}
  //	}
  // 享元  + 策略
  Map<String, CalculateStrategy> calculateStrategyHashMap = new HashMap<>();

  /**
   * spring特性--默认将所有的实现类,放入到这个LIST表
   *
   * @param calculateStrategys
   */
  public SaleService(List<CalculateStrategy> calculateStrategys) { // spring特性--默认将所有的实现类,放入到这个LIST表
    for (CalculateStrategy calculateStrategy : calculateStrategys) {
      calculateStrategyHashMap.put(calculateStrategy.userType(), calculateStrategy);
    }
  }

  public BigDecimal sale(String userType, BigDecimal amt) {
    return calculateStrategyHashMap.get(userType).sale(amt);
  }
}

Main

@Autowired 
SaleService saleService;

saleService.sale("VIP", new BigDecimal(100))

上面这种是基于一个场景下的策略模式。
策略模式strategy + annotation + builder + 单例interfaceStrategyHashMap
我利用Annotation扩展了个多种场景的版本:
Annotation

package com.foxgl.tools.pattern.strategy.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Strategy {
  String value();
}

启动装载,此版本利用spirng框架。后期可以拓展,去除框架依赖

package com.foxgl.tools.pattern.strategy.loader;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import com.foxgl.tools.compnent.application.SpringContextHolder;
import com.foxgl.tools.pattern.strategy.annotation.Strategy;

import lombok.extern.log4j.Log4j2;

@Log4j2
@Component
public class StrategySpringLoader implements ApplicationRunner {

  private static Map<Object, Map<String, Object>> interfaceStrategyHashMap =
      new ConcurrentHashMap<Object, Map<String, Object>>();

  private Map<String, Object> interfaceMap;

  @Override
  public void run(ApplicationArguments args) throws Exception {
    Map<String, Object> interfaces = SpringContextHolder.getBeansWithAnnotation(Strategy.class);
    interfaces.forEach(
        (key, value) -> {
          if (value.getClass().getInterfaces().length > 0) {
            interfaceMap =
                interfaceStrategyHashMap.get(
                    value.getClass().getInterfaces()[0]); // 1.根据interfaceType获取子类Map集合
            //				interfaceMap = StrategyContextUtil.get();
            if (null != interfaceMap) { // 子类Map集合不为空,判断key值是否重复
              if (!interfaceMap.containsKey(key)) {
                interfaceMap.put(key, value);
              } else {
                throw new IllegalArgumentException("策略标签@Strategy-存在重复key值[" + key + "]");
              }
            } else { // 子类Map集合为空,new一下,然后放
              interfaceMap = new HashMap<>();
              interfaceMap.put(key, value);
            }
            interfaceStrategyHashMap.put(value.getClass().getInterfaces()[0], interfaceMap);
          } else {
            throw new IllegalArgumentException("策略标签@Strategy-未找到实现接口[" + value + "]");
          }
        });
    log.info("策略标签@Strategy 装载完毕!");
  }

  public static Map<Object, Map<String, Object>> getInterfaceStrategyHashMap() {
    return interfaceStrategyHashMap;
  }
}

再加一个StrategyBuilder

package com.foxgl.tools.pattern.strategy.build;

import java.util.Map;

import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.foxgl.tools.pattern.strategy.loader.StrategySpringLoader;

/** @author jy027 */
@Component
public class StrategyBuilder<T> {

  private Class<?> clazz;

  private String buinessType;

  public StrategyBuilder<T> setClazz(Class<?> clazz) {
    this.clazz = clazz;
    return this;
  }

  public StrategyBuilder<T> setBuinessType(String buinessType) {
    this.buinessType = buinessType;
    return this;
  }

  /**
   * @Strategy("REDIS") interfaceStrategyHashMap<Object,Map<String,Object>>
   * <Interface.class,Map<anntotaion-Value,InterfaceImpl.class>>
   *
   * @return
   */
  public T build() {
    Map<Object, Map<String, Object>> interfaceStrategyHashMap =
        StrategySpringLoader.getInterfaceStrategyHashMap();
    Assert.notEmpty(interfaceStrategyHashMap, "no found any class use @Strategy!");
    Map<String, Object> interfaceMap = interfaceStrategyHashMap.get(this.clazz);
    Assert.notNull(interfaceMap, "no found class[" + this.clazz + "] use @Strategy!");
    T t = (T) interfaceMap.get(this.buinessType);
    Assert.notNull(t, "no found buinessType[" + this.buinessType + "] use @Strategy!");
    return t;
  }
}

Main

@Autowired 
StrategyBuilder<CalculateStrategy> strategyBuilder;

CalculateStrategy test = (CalculateStrategy)strategyBuilder.setClazz(CalculateStrategy.class).setBuinessType("NORMAL").build();
System.out.println(test.sale(new BigDecimal(100)));
;