好好的“代码优化”是怎么一步步变成“过度设计”的(下)

简介: 好好的“代码优化”是怎么一步步变成“过度设计”的(下)

好好的“代码优化”是怎么一步步变成“过度设计”的(上):https://developer.aliyun.com/article/1443490


继续拓展:设计模式

枚举的前提还是基于无状态前提,如果转换的的映射关系不再单纯,变得复杂,枚举的简单映射管理就不work了。



“万事不决,上设计模式”



哎~就是玩儿~


 策略模式-简单实现


首先,依然将传入的字符串作为路由依据,但是传入的内容为了防止有未来扩展,所以构造一个上下文,策略本身基于上下文来处理,借助上文定义的值枚举做策略路由。


/**
  * 定义策略接口
  */
public interface SaleTypeParseStrategy{
  Integer parse(SaleTypeParseContext saleTypeParseContext);
}

/**
  * 策略实现
  */
public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{
  @Override
  public Integer parse(SaleTypeParseContext saleTypeParseContext) {
    return SaleTypeIntEnum.JX.getCode();
  }
}

/**
  * 调用上下文
  */
@Data
public class SaleTypeParseContext{
  private SaleTypeStrEnum saleTypeStr;

  private SaleTypeParseStrategy parseStrategy;

  public Integer pasre(){
    return parseStrategy.parse(this);
  }
}

public static Integer parseSaleType(String saleTypeStr){
  SaleTypeStrEnum saleTypeEnum = SaleTypeStrEnum.getByName(saleTypeStr);
  SaleTypeParseContext context = new SaleTypeParseContext();
  context.setSaleTypeStr(saleTypeEnum);
  switch(saleTypeStr){
    // 策略路由
    case JX:context.setParseStrategy(new JxSaleTypeParseStrategy());break;
    // 继续扩展
    default:return null;
  }
  return context.parse();
}


当然,如果是这种没有上下文强依赖的策略,无论是静态单例还是Spring单例都会是一个不错的选择。SaleTypeParseContext本身可以继续扩展内容和其他属性继续丰富参数,策略实现中也可以继续针对更多参数扩充逻辑。


 策略工厂-手动容器


策略是个好东西,但是简单实现下,这里依然将策略实现的路由过程交给了调用方来做,那么每增加一种实现,调用点还要继续改,要是恰好有若干调用点就完犊子了,并不优雅,所以搞个中间层容器工厂,解耦一下依赖。



@Component
public static class SaleTypeParseStrategyContainer{
  public final static Map<SaleTypeStrEnum, SaleTypeParseStrategy> STRATEGY_MAP = new HashMap<>();

  @PostConstruct
  public void init(){
    STRATEGY_MAP.put(SaleTypeStrEnum.JX, new JxSaleTypeParseStrategy());
    // 继续拓展
  }

  public Integer parse(SaleTypeParseContext saleTypeParseContext){
    return Optional.ofNullable(STRATEGY_MAP.get(saleTypeParseContext.getSaleTypeStr())).map(strategy-> strategy.parse(saleTypeParseContext)).orElse(null);
  }
}


容器内手动创建各个策略的实现的单例后进行托管,那调用方只需要去构建上下文就好了,实际调用的方法更换为 SaleTypeParseStrategyContainer::parse,那后续无论策略如何丰富,调用方都不需要再感知这部分变化。后续出现了新的策略实现,则在工厂内继续追加路由表即可。


 注册与发现&策略工厂-Spring容器


如果考虑到策略会依赖Spring的bean和其他有状态对象,那么这里也可以改成Spring的注入模式,同时继续将“支持哪种情况”由托管方容器移动至策略内部,改成由策略实现自身去注册到容器中。




public interface SaleTypeParseStrategy{
  Integer parse(SaleTypeParseContext saleTypeParseContext);
  // 所支持的情况
  SaleTypeStrEnum support();
}

@Component
public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{
  @Override
  public Integer parse(SaleTypeParseContext saleTypeParseContext) {
    return SaleTypeIntEnum.JX.getCode();
  }
  @Override
  public SaleTypeStrEnum support() {
    return SaleTypeStrEnum.JX;
  }
}

@Component
public static class SaleTypeParseStrategyContainer{
  public final static Map<SaleTypeStrEnum, SaleTypeParseStrategy> STRATEGY_MAP = new HashMap<>();
  @Autowired
  private List<SaleTypeParseStrategy> parseStrategyList;

  @PostConstruct
  public void init(){
    parseStrategyList.stream().forEach(strategy-> STRATEGY_MAP.put(strategy.support(), strategy));
  }
  public Integer parse(SaleTypeParseContext saleTypeParseContext){
    return Optional.ofNullable(STRATEGY_MAP.get(saleTypeParseContext.getSaleTypeStr())).map(strategy-> strategy.parse(saleTypeParseContext)).orElse(null);
  }
}


这样的话,连容器都不用改了,追加策略实现的改动只与当前策略有关,调用方和容器类都不需要感知了,但是缺点就在于如果有俩策略支持的情况相同,取到的是哪个就听天由命了~


 注册与发现&责任链


当然如果不能事先知道“支持哪种情况”,只能在运行时判断“是否支持”,将事前判定改为运行时判定,广义责任链会是一个不错的选择,把所有策略排成一排,谁举手说自己能处理就谁处理。





public interface SaleTypeParseStrategy{
  Integer parse(SaleTypeParseContext saleTypeParseContext);
  // 用于判断是否支持
  boolean support(SaleTypeParseContext saleTypeParseContext);
}

@Component
public class JxSaleTypeParseStrategy implements SaleTypeParseStrategy{
  @Override
  public Integer parse(SaleTypeParseContext saleTypeParseContext) {
    return SaleTypeIntEnum.JX.getCode();
  }
  @Override
  public boolean support(SaleTypeParseContext saleTypeParseContext) {
    return SaleTypeStrEnum.JX.equals(saleTypeParseContext.getSaleTypeStr());
  }
}

@Component
public static class SaleTypeParseStrategyContainer{
  @Autowired
  private List<SaleTypeParseStrategy> parseStrategyList;

  public Integer parse(SaleTypeParseContext saleTypeParseContext){
    return parseStrategyList.stream()
        .filter(strategy->strategy.support(saleTypeParseContext))
        .findAny()
        .map(strategy->strategy.parse(saleTypeParseContext))
        .orElse(null);
  }
}


这样的实现,依然可以将改动收束在策略本体上,修改相对集中,可以无耦地进行扩展。
其他拓展

以上还只是在JAVA语言内去玩一些花样,在当前这种场景下肯定是有过度设计的嫌疑,7行代码可以缩到1行,也可以扩充到70行,所以说嘛:

“用代码行数来考量一个程序员是不太合适滴!~”当然了,也还可以继续借助其他的中间件搞花样,包括但不限于:

  1. 植入Diamond走走动态配置开关的思路;
  2. 植入QLExpress搞搞逻辑表达式的思路;
  3. 把策略实现改成HsfProvider走分布式调用思路;
  4. 借助一些成熟的网关走服务路由的的调用思路;


就不再此再过多展开了。

目录
相关文章
|
7月前
|
设计模式 算法 程序员
程序员为何需要反复修改Bug?探寻代码编写中的挑战与现实
作为开发者,我们在日常开发过程中,往往会遇到反复修改bug的情况,而且不能一次性把代码写的完美无瑕,其实开发项目是一项复杂而富有挑战性的任务,即使经验丰富的程序员也难以在一次性编写完美无瑕地完成代码,我个人觉得一次性写好代码是不可能完成的事情。虽然在设计之初已经尽力思考全面,并在实际操作中力求精确,但程序员仍然需要花费大量时间和精力来调试和修复Bug。那么本文就来分享程序员需要反复修改Bug的原因,以及在开发中所面临的复杂性与挑战。
186 1
程序员为何需要反复修改Bug?探寻代码编写中的挑战与现实
|
7月前
|
设计模式 Java
好好的“代码优化”是怎么一步步变成“过度设计”的(上)
好好的“代码优化”是怎么一步步变成“过度设计”的(上)
311 4
|
7月前
|
设计模式 Java 中间件
好好的“代码优化”是怎么一步步变成“过度设计”的
本文记录了作者从“代码优化”到“过度设计”的典型思考过程,这过程中涉及了很多Java的语法糖及设计模式的东西,很典型,能启发思考,遂记录下来。
|
SQL 前端开发 JavaScript
前端性能优化到底该怎么做(上)— 开门见山(一)
前端性能优化到底该怎么做(上)— 开门见山
143 0
|
前端开发 API
前端性能优化到底该怎么做(上)— 开门见山(二)
前端性能优化到底该怎么做(上)— 开门见山
166 0
|
存储 缓存 JavaScript
前端性能优化到底该怎么做(下)— 直捣黄龙(二)
前端性能优化到底该怎么做(下)— 直捣黄龙
179 0
|
缓存 网络协议 前端开发
前端性能优化到底该怎么做(下)— 直捣黄龙(一)
前端性能优化到底该怎么做(下)— 直捣黄龙
228 0
|
IDE NoSQL Java
我来告诉你代码重构有什么好处
根据两本关于重构的书籍的作者 Martin Fowler的说法 “重构是改变软件系统的过程,它不会改变代码的外部行为,但会改善其内部结构。这是一种清理代码的严格方法,可以最大限度地减少引入错误的机会。本质上,当你重构时,你是在改进编写代码后的设计。”
254 0
|
前端开发 JavaScript
当下做前端开发,不算简单,这篇文章可以让少走很多弯路以及需要掌握的知识
当下做前端开发,不算简单,这篇文章可以让少走很多弯路以及需要掌握的知识
|
缓存 运维 前端开发
面试官:平时工作中有没有做过一些性能优化相关的工作呢?
面试官:平时工作中有没有做过一些性能优化相关的工作呢?
面试官:平时工作中有没有做过一些性能优化相关的工作呢?