谈谈组合策略模式及在生产的应用

2022/03/27

谈谈组合策略模式及在生产的应用

组合模式

学设计模式第一次听到这个模式时被迷惑了,以为所谓的组合模式是跟面向对象编程里的组合概念的一样

// 想象中的组合模式
class Car {
  private Door door;
  private Wheel Whee;
}

读了 GOF 的设计模式之后,书里是将组合模式描述为

将对象组合成树形结构的部分-整体层次结构,使得客户使用单个对象或组合对象都有一致性

也就是说组合部分与被组合部分在对外表现上,应该都是一致的,也就是其实是这样的

class Node {
  private Node part1;
  private Node part2;
}

真正让我理解组合模式和它的妙用是在《领域驱动设计》这本书里面,作者提出了一个航线的概念,也就是这样,通过组合基本组件以达到功能更强大的一个组件

interface Route {
    Route segement1, segement2;
} // 路线
class NationalHighway implements Route{} // 国道
class CountryRoad implements Route {} // 乡道

策略模式

所谓策略,则可以理解为针对某种特定问题的算法,由于不同的算法各有优劣,所以需要将策略抽象为一个接口,让客户代码根据需求选择不同策略来解决问题,代码如下

interface Strategy{
    void execute(..);
}

class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    
    void execute(){
        strategy.execute(..);
    }
}

class StrategyA implements Strategy{
    // 具体实现
}
// 使用
Context context = new Context(new StrategyA());
context.execute();

二者的结合

组合与策略两者的结合,不仅可以充分利用组合模式的灵活性,同时也可以利用策略模式的不同算法可选择性

interface Strategy{
    void execute(..);
}
class StrategyA implements Strategy{
    // 具体实现
}
class StrategyB implements Strategy{
    // 具体实现
}

// 组合策略
class CompositeStrategy implements Strategy {
  private Strategy strategyA, strategyB;
}

通过组合不仅可以显式编程,交由不同的具体客户满足不同的需求,同时细粒度的策略也暴露了出去,也能让策略更有针对性及可选择性,更进一步,可以使用工厂模式将策略的组装与生成封装在工厂内部,这样客户代码也不必关心他使用的具体策略是什么,只要策略满足他的需求即可

生产上的应用

这个业务场景是这样

  • 我们有许多三方接口来获取某种信息
  • 每种接口不仅需要单独对外提供服务,同时也需要聚合所有接口数据再对外提供服务

通过封装,这些返回的信息基本上都是一样的,但由于我们的项目在不同的地方运行,不同的地方可能有些接口可用,有些接口不可用,所以此时就通过组合策略模式来满足以上的所有需求

interface FetchInfoStrategy {
    Info fetch(...);
}
// 具体三方接口
class ThirdPartAFetchInfoStrategy implements FetchInfoStrategy{...}
class ThirdPartBFetchInfoStrategy implements FetchInfoStrategy{...}
class ThirdPartCFetchInfoStrategy implements FetchInfoStrategy{...}

// 组合三方接口
class CompositeFetchInfoStrtegy implements FetchInfoStrategy {
  // 通过配置化动态注入
  private static final List<Class<? extends FetchInfoStrategy>> STRATEGY_LIST;

  Info fetch(...) {
    // 循环调用策略,记录各个接口返回结果
    // 合并结果并返回
  }
}

通过这样子的组合策略,不仅满足了需求,也为后续的优化留下了空间,若想通过异步调用各个接口,或者调整具体合并逻辑,都可以通过新增一个组合策略,避免修改老代码带来预期之外的问题

Post Directory