Skip to content

Reduce Strategy

Ryu Xin edited this page Oct 17, 2022 · 6 revisions

What you'll build

With this sample, you can learn:

  1. What is a Reduce strategy and what strategies are currently available
  2. Specific Reduce call example

What you’ll need

  1. An Integrated Developer Environment (IDE). Popular choices include IntelliJ IDEA, Spring Tools, Visual Studio Code, or Eclipse, and many more.
  2. A Java™ Development Kit (JDK). We recommend BellSoft Liberica JDK version 8 or version 11.

Maven dependency

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

Why Reduce strategy required

In the article Key Concepts - Ability, we know that Ability can stack. Therefore, at the same extension point, it is possible to generate multiple extension implementations. These extension implementations will determine whether they may be called in combination with the business context. For example, in the Business Overlay Product, the effective condition for "GroupBuyProduct" is that the commodity sales channel is equal to "groupBuy". However, after the "GroupBuyProduct" takes effect, we can easily find that in the extension point of "Custom Item Unit Price", conflicts will arise after the business is superimposed. Is the "Custom Item Unit Price" from the definition of the business side, or from the definition of "GroupBuyProduct"? This needs to be decided by the Reduce strategy.

Types of Reduce strategies

In org.hiforce.lattice.runtime.ability.reduce.Reducers we provide the following Reduce strategies:

  • None: No need to do reduce processing, this strategy is often used when the returned result is a collection, and does not need to deal with null values in the collection
  • FirstOf: The first hit is the return policy. When there are multiple implementations of an extension point, the strategy will return the first result that hits a specific condition
  • AllMatch: The return value is of type Boolean, which determines that all implementations of the extension point meet certain conditions
  • AnyMatch: The return value is of type Boolean, and it is enough to determine whether any one of all implementations of the extension point satisfies a specific condition
  • NoneMatch: The return value is of type Boolean, which determines that all implementations of the extension point do not meet specific conditions
  • FlatList: The return value of the extension point is defined as a collection type, and flatList will flatten all the results and output them instead of a nested List
  • FlatMap: The return value of the extension point is defined as a Map type, and flatMap will flatten all the results and output them instead of the form of a nested List

Reduce strategy DEMO

FirstOf strategy

We define an extension point in ReduceSampleAbility that requires a certain string to be returned, as follows:

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 will call the extension point with the strategy of FirstOf, as follows:

@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));
    }
    ......
}

We let the business and product implement this extension point respectively. The extension point implementation of the product returns a null value, while the business returns a string of characters, as follows:

@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;
    }
}

Now, let's start Lattice and simulate a business invocation process. The startup class is org.hiforce.lattice.sample.reduce.FirstOfReducer, defined as follows:

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());
    }
}

The result of the execution is as follows, we can see that the extension point execution strategy is to find the first non-null return value:

FirstNotNull result => SampleBusiness Hello World!

None strategy

We continue to define an extension point and let this extension point return a List, as follows:

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

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

We continue to let the product return a null value for this extension point, while the business returns a string List, as follows:

@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");
    }
}

Now, let's start Lattice and simulate a business invocation process. The startup class is org.hiforce.lattice.sample.reduce.FirstOfReducer, which is defined as follows:

ublic 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());
    }
}

Because the return value definition of this extension point is List, and the Reduce strategy is None. So, the final return result is a 'List' nested structure, and no empty values are filtered. The results are as follows:

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

The None strategy is generally used to make comprehensive judgments on multiple values returned by the extension point. For example, if there is an extension point of "Custom Order Timeout", we need to return the value with the shortest timeout according to all the results. At this time, we You need to use the None Reduce strategy.

FlatList strategy

Based on the None strategy example, we write another call about this extension point, flatten the execution result List structure into a layer of List , and filter out null values. We are in the ability to call, the code is as follows:

@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));
    }
    ......
}

We run org.hiforce.lattice.sample.reduce.FlatListReducer, and we can see that the print results are as follows:

FlatListReducer result => [Jack, Tom]

This strategy has flattened the List result into a layer of List for output, and filtered out null values.

About AnyMatch, AllMatch, FlatMap is basically similar, this can be left as a small homework for everyone to explore. Of course, you can also refer to the example in:

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

The sample code can be obtained by visiting: https://github.com/hiforce/lattice-sample/tree/main/lattice-reduce-policy


中文版:https://www.ryu.xin/2022/09/27/lattice-reduce-policy/