样例说明

通过本样例,您可以了解:

  1. 什么是Reduce策略,目前提供了哪些策略
  2. 具体的Reduce调用样例

环境准备

您需要:

  1. 用于运行程序的IDE(集成开发环境),比如IntelliJ IDEA 或其类似工具;
  2. Java™ Development Kit (JDK),需要JDK 8及以上版本

版本依赖

<dependency>
      <groupId>org.hiforce.lattice</groupId>
      <artifactId>lattice-model</artifactId>
      <version>1.0.13</version>
</dependency>
<dependency>
      <groupId>org.hiforce.lattice</groupId>
      <artifactId>lattice-runtime</artifactId>
      <version>1.0.13</version>
</dependency>

为什么扩展点执行结果需要Reduce策略

Lattice – 关键概念 – 能力 一文中,我们知道能力是可以叠加的。所以,在同一个扩展点上,是可能会产生多个扩展实现。这些扩展实现,会结合业务上下文来决定是否可能会被调用。 比如,Lattice - 业务叠加产品 样例中,“团购产品”的生效条件是,商品销售渠道 等于 “groupBuy”。 但“团购产品”生效后,我们就可以很容易发现,在“商品自定义单价” 这个扩展点上,在业务叠加之后就会产生冲突。“商品单价”究竟是来自业务侧的定义,还是来自“团购产品”的定义?这就需要通过Reduce策略来决定了。

Reduce策略种类

在org.hiforce.lattice.runtime.ability.reduce.Reducers中,我们提供了以下Reduce策略:

  • none: 不需要做reduce处理,这个策略常用于返回结果是集合的情况,并且不需要对集合内的空值做处理
  • firstOf: 首个命中即返回策略。当扩展点有多份实现,那该策略会从中返回首个命中特定条件的结果
  • allMatch: 返回值Boolean类型,判断扩展点所有实现都满足特定条件
  • anyMatch: 返回值Boolean类型,判断扩展点所有实现中有任何一个满足特定条件即可
  • noneMatch: 返回值Boolean类型,判断扩展点所有实现中都不满足特定条件
  • flatList: 扩展点的返回值定义是集合类型,flatList则会把所有所有结果扁平化后输出,而不是嵌套的 List 的形式
  • flatMap: 扩展点的返回值定义是Map类型,flatMap则会把所有所有结果扁平化后输出,而不是嵌套的 List 的形式

Reduce策略DEMO

firstOf 策略

我们在 ReduceSampleAbility 中定义一个扩展点,该扩展点要求返回一个确定的字符串,如下:

public interface ReduceSampleExt extends IBusinessExt {

    String EXT_FIRST_NOT_NULL_POLICY = "EXT_FIRST_NOT_NULL_POLICY";

    @Extension(code = EXT_FIRST_NOT_NULL_POLICY, reduceType = ReduceType.FIRST)
    String firstNotNullReducePolicy();
}

ReduceSampleAbility 会以FirstOf这个策略去调用该扩展点,如下:

@Ability(name = "ReduceSampleAbility")
public class ReduceSampleAbility extends BaseLatticeAbility<BlankReduceSampleExt> {
    ......
    public String sampleFirstNotNullReduce() {
        return this.reduceExecute(EXT_FIRST_NOT_NULL_POLICY,
                BlankReduceSampleExt::firstNotNullReducePolicy, Reducers.firstOf(Objects::nonNull));
    }
    ......
}

我们让业务和产品分别实现该扩展点,产品的扩展点实现返回空值,而业务则返回一串字符,如下:

@Realization(codes = SampleBusiness.CODE)
public class SampleBusinessExt extends BlankReduceSampleExt {

    @Override
    public String firstNotNullReducePolicy() {
        return "SampleBusiness Hello World!";
    }
}

@Realization(codes = SampleProduct.CODE)
public class SampleProductExt extends BlankReduceSampleExt {
    @Override
    public String firstNotNullReducePolicy() {
        return null;
    }
}

现在,让我们启动Lattice,模拟一次业务调用过程,启动类是 org.hiforce.lattice.sample.reduce.FirstOfReducer,定义如下:

public class FirstOfReducer {

    public static void main(String[] args) {
        Lattice.getInstance().setSimpleMode(true);
        Lattice.getInstance().start();

        SampleScenarioResult result = ReduceSample.startReduceSample(request -> {
            ReduceSampleAbility ability = new ReduceSampleAbility(request.getBizObject());
            request.getResult().setFirstNotNullResult(ability.sampleFirstNotNullReduce());
            return request.getResult();
        });
        System.out.println("FirstNotNull result => " + result.getFirstNotNullResult());
    }
}

执行的结果如下,我们可以看出扩展点执行策略是找到了第一个非空返回值:

FirstNotNull result => SampleBusiness Hello World!

none 策略

我们继续定义一个扩展点,让这个扩展点返回一个List,如下:

public interface ReduceSampleExt extends IBusinessExt {
    ......
    String EXT_CUSTOM_LIST_RESULT = "EXT_CUSTOM_LIST_RESULT";

    @Extension(code = EXT_CUSTOM_LIST_RESULT)
    List<String> getCustomListResult();
}

我们继续让产品针对这个扩展点,返回空值,而业务则返回一个字符串List,如下:

@Realization(codes = SampleProduct.CODE)
public class SampleProductExt extends BlankReduceSampleExt {
    ......
    @Override
    public List<String> getCustomListResult() {
        return null;
    }
}

@Realization(codes = SampleBusiness.CODE)
public class SampleBusinessExt extends BlankReduceSampleExt {
    ......
    @Override
    public List<String> getCustomListResult() {
        return Lists.newArrayList("Jack", "Tom");
    }
}

现在,让我们启动Lattice,模拟一次业务调用过程,启动类是 org.hiforce.lattice.sample.reduce.FirstOfReducer,定义如下:

public class NoneReducer {

    public static void main(String[] args) {
        Lattice.getInstance().setSimpleMode(true);
        Lattice.getInstance().start();

        SampleScenarioResult result = ReduceSample.startReduceSample(request -> {
            ReduceSampleAbility ability = new ReduceSampleAbility(request.getBizObject());
            request.getResult().setNoneReduceResult(ability.sampleNoneReduce());
            return request.getResult();
        });
        System.out.println("NoneReduce result => " + result.getNoneReduceResult());
    }
}

因为该扩展点的返回值定义是List,同时Reduce策略是None。所以,最终的返回结果是一个 ‘List<List>’ 嵌套结构,并且没有过滤空值。运行结果如下:

NoneReduce result => [null, [Jack, Tom]]

None() 策略一般用在需要对扩展点返回的多个值做综合判断,比如假设有一个 “订单超时时间” 这个扩展点,我们需要根据所有的结果,返回超时时间最短的值,这时我们就需要用到 None Reduce策略了。

flatList 策略

我们在 none() 策略样例基础上,我们再写一个关于这个扩展点的调用,并将执行结果 List结构,扁平化成一层List ,并且过滤掉空值。我们在能力的调用上,代码如下:

@Ability(name = "ReduceSampleAbility")
public class ReduceSampleAbility extends BaseLatticeAbility<BlankReduceSampleExt> {
    ......
    public List<String> flatListReduce() {
        return this.reduceExecute(EXT_CUSTOM_LIST_RESULT,
                BlankReduceSampleExt::getCustomListResult,
                Reducers.flatList(CollectionUtils::isNotEmpty));
    }
    ......
}

我们运行org.hiforce.lattice.sample.reduce.FlatListReducer,可以看到打印结果如下:

FlatListReducer result => [Jack, Tom]

这个策略已经把List 结果扁平化成一层List进行输出,并且过滤掉了空值。

关于 anyMatch、allMatch、flatMap基本类似,这个可以作为小作业留给大家自行探索。当然,您也可以参考样例中的:

  • org.hiforce.lattice.sample.reduce.AllMatchReducer
  • org.hiforce.lattice.sample.reduce.AnyMatchReducer
  • org.hiforce.lattice.sample.reduce.NoneMatchReducer

本样例代码URL:https://github.com/hiforce/lattice-sample/tree/main/lattice-reduce-policy