Patrón de estrategia: una sutil alternativa a su if-else

¿Qué es el patrón de estrategia?

El patrón de estrategia , como patrón de diseño de uso común, encapsula funciones de diferentes escenarios a través de clases de estrategia y funciones de método. De esta forma, la llamada a la capa superior puede blindar las diferencias provocadas por las diferencias en los parámetros de entrada y los escenarios. Para la lógica interna, se pueden ejecutar diferentes métodos y clases de acuerdo con diferentes escenarios, y se pueden agregar y expandir nuevas estrategias fácilmente. El diagrama de marco de composición principal del patrón de estrategia es el siguiente:
inserte la descripción de la imagen aquí

¿Cuándo aplicar el patrón de estrategia?

Aquí hay una cita de Ye Zhiye, el hermano mayor de Ali , sobre cuándo aplicar el patrón de estrategia:

Cuando el bloque de código de if-else viola el principio de responsabilidad única y el principio de apertura y cierre, especialmente cuando la cantidad de código en el bloque es grande, la expansión y el mantenimiento del código subsiguiente serán muy difíciles y propensos a errores. y también se evita el uso de declaraciones de guardia No los dos problemas anteriores. Entonces, en base a mi experiencia, se me ocurrió una práctica que personalmente creo que es mejor:

  • If-else no supera las 2 capas, el código en el bloque es de 1 a 5 líneas, se escribe directamente en el bloque; de ​​lo contrario, se encapsula como un método
  • El if-else tiene más de 2 capas, pero el código en el bloque no excede las 3 líneas, intente usar la declaración de guardia
  • If-else excede 2 capas, y el código en el bloque excede 3 líneas, intente usar el modo de estrategia

PD: La declaración de guardia es la siguiente

public Boolean guard(int tags){
    
    
  if (tags == 1){
    
    
    //内部逻辑代码
    return true;
  }
  if (tags == 2){
    
    
    //内部逻辑代码
    return false;
  }
  if(tags == 3){
    
    
    //内部逻辑代码
    return true;
  }
}

De esto también podemos derivar otro aspecto, el patrón de estrategia no es muy alto, en realidad es solo un producto que puede ser reemplazado por un simple if-else.

Cómo usar el patrón de estrategia

Aquí se supone un trasfondo comercial simple.Si actualmente somos la persona principal a cargo de un servicio de cálculo de precios, principalmente brindamos servicios de cálculo de precios para diferentes escenarios en un centro comercial.

Pero el problema es que hay muchos escenarios para el cálculo de precios en el centro comercial, tales como: cálculo de venta flash, cálculo de actividad, cálculo de descuento total entre tiendas, etc.

Cómo mantener múltiples situaciones de precios y cómo acceder mejor a nuevos escenarios de precios en el futuro se ha convertido en un problema difícil para usted.

En respuesta a esta situación, primero piensa en usar una determinada estrategia para determinar a qué escena pertenece actualmente y usa if-else para llamar al método de cálculo correspondiente.

public Result calcPrice(CalcPriceParam calcPriceParam){
    
    
  //判断对应的计算价格的场景
  Integer type = judgeType(calcPriceParam);
  //根据场景调用不同的方法 ,建议更好的编码习惯是把type改成枚举类型哈~
  if(type == 1){
    
    
    return calcPriceForTypeOne();
  }
  if(type == 2){
    
    
    return calcPriceForTypeTwo();
  }
  if(type == 3){
    
    
    return calcPriceForTypeThree();
  }
  .....
  if(typr == 10){
    
    
    return calcPriceForTypeTen();
  }
}

Pero el código escrito de esta manera es muy poco elegante. Entonces, usa el patrón de estrategia para modificar el código para que se vea así:

//定义接口,供子类继承
public Interface CalcPriceInterface{
    
    
  public Result calcPrice(CalcPriceParam calcPriceParam);
}
@Service
public class StrategyContext{
    
    
  Map<Integer,CalcPriceInterface> strategyContextMap = new HashMap<>();
  //注入对应的策略类
  @Autowired
  FirstStrategy firstStrategy;
  @Autowired
  SecondStrategy secondStrategy;
  ......
  @Autowired
  TenStrategy tenStrategy;
    
  @PostConstruct
  public void setStrategyContextMap(){
    
    
    //设置对应的方法
    strategyContextMap.set(1,firstStrategy);
    strategyContextMap.set(2,secondStrategy);
    .....
  }

  //根据场景调用不同的方法 
  public Result calcPrice(CalcPriceParam calcPriceParam){
    
    
  	Integer type = judgeType(calcPriceParam);
    CalcPriceInterface calcPriceInstance = strategyContextMap.get(type);
    return calcPriceInstance.calcPrice(calcPriceParam);
  }
}
@Autowired
StrategyContext strategyContext;

public Result calcPrice(CalcPriceParam calcPriceParam){
    
    
		strategyContext.calcPrice(calcPriceParam);
}

Después de esta refactorización, surge un nuevo problema de que cada vez que se agrega una estrategia, se debe agregar una clase de estrategia, lo que obviamente aumentará demasiado la cantidad de código.

Entonces, considera el artefacto de JDK1.8, la interfaz funcional.

Function<CalcPriceParam, CalcPriceDTO> func;

Así que el código cambió y se convirtió en el siguiente:

@Service
public class StrategyContext {
    
    
    //当前对应的策略采用lambda的方法存储
    Map<Integer, Function<CalcPriceParam, CalcPriceDTO>> strategyContextMap = new HashMap<>();

    @Resource
    CalcPriceService calcPriceService;

    @PostConstruct
    @SneakyThrows
    public void setStrategyMap() {
    
    
      //设置对应的算价逻辑
        strategyContextMap.put(1,(calcPriceParam)-> calcPriceService.calcFirstPrice(calcPriceParam));
        strategyContextMap.put(2,(calcPriceParam)-> calcPriceService.calcSecondPrice(calcPriceParam));
        strategyContextMap.put(3,(calcPriceParam)-> calcPriceService.calcThirdPrice(calcPriceParam));
    }

    public ResultDTO<Object> calcPrice(CalcPriceParam calcPriceParam){
    
    
      //判断场景
      Integer type = judgeType(calcPriceParam);
      //获取对应的方法计算并返回
      Function<CalcPriceParam, CalcPriceDTO> calcPriceFunc = strategyContextMap.get(type);
      CalcPriceDTO calcPriceDTO = calcPriceFunc.apply(calcPriceParam);
      return new ResultDTO<>().success(calcPriceDTO);
    }
}
@Service
//对应的计算价格的方法类
public class CalcPriceService {
    
    
    public CalcPriceDTO calcFirstPrice(CalcPriceParam calcPriceParam){
    
    
        CalcPriceDTO calcPriceDTO = new CalcPriceDTO();
        calcPriceDTO.setResultStr("场景一计算成功");
        return calcPriceDTO;
    }

    public CalcPriceDTO calcSecondPrice(CalcPriceParam calcPriceParam){
    
    
        CalcPriceDTO calcPriceDTO = new CalcPriceDTO();
        calcPriceDTO.setResultStr("场景二计算成功");
        return calcPriceDTO;
    }

    public CalcPriceDTO calcThirdPrice(CalcPriceParam calcPriceParam){
    
    
        CalcPriceDTO calcPriceDTO = new CalcPriceDTO();
        calcPriceDTO.setResultStr("场景三计算成功");
        return calcPriceDTO;
    }
}

De esta forma, no solo se mejora la capacidad de mantenimiento del código, sino que tampoco aumenta drásticamente el número correspondiente de líneas de código.

Resumen y revisión

1. El modo de estrategia no es algo de alto nivel. Su esencia es usar la estructura hash del mapa para optimizar la estructura de código poco elegante como if-else.

2. Además del caso en el que varias funciones deben encapsularse en una clase abstracta, el uso de la interfaz funcional de JDK1.8 puede simplificar el código de manera más efectiva.

Supongo que te gusta

Origin blog.csdn.net/qq_43842093/article/details/132031907
Recomendado
Clasificación