Javaの反応器内のネストされたflatMapsのための良好なイディオムとは何ですか?

Phyzz:

私は春と原子炉などの関連ライブラリを使用して、Javaで書かれたRESTサービスの責任を継承しました。RESTのような高価な操作のためのコードが広く炉モノラルで結果をラップされて呼び出したり、データベース操作。

必要性はコードで対処すべきが、現れて続けて1が入れ子になっていることをあらゆる種類のものがありますflatMapオーバーのMono深い読めない混乱にまでインデントいくつかのレベルが終わる高価な操作のシーケンスのためにsが。私が使用してのこの方法は、スカラ座から来たので、私はそれ余分にうんざり見つけるflatMapために理解シンタックスシュガーではなく、より深い行くの範囲のほぼ同じレベルですべてのものを維持するための悪いようではありません。

私はこれまで大規模なリファクタリングから離れて、それをより読みやすく(そしてその後も、私はどこなリファクタリングを開始するにはわからない)ために、これをアプローチする方法を見つける全く成功していませんでした。

コードは、(すべての構文エラーは匿名からのものである)に基づいて、匿名化の例:

public Mono<OutputData> userActivation(InputData input) {
    Mono<DataType1> d1 = service.expensiveOp1(input);

    Mono<OutputData> result =
        d1
          .flatMap(
            d1 -> {
              return service
                  .expensiveOp2(d1.foo())
                  .flatMap(
                      d2 -> {
                        if (Status.ACTIVE.equals(d2.getStatus())) {
                          throw new ConflictException("Already active");
                        }

                        return service
                            .expensiveOp3(d1.bar(), d2.baz())
                            .flatMap(
                                d3 -> {
                                  d2.setStatus(Status.ACTIVE);

                                  return service
                                      .expensiveOp5(d1, d2, d3)
                                      .flatMap(
                                          d4 -> {
                                            return service.expensiveOp6(d1, d4.foobar())
                                          });
                                });
                      });
            })

    return result;
}

マイケル・ベリー:

不潔。いくつかのことは、私はそのスニペットについて好きではないが、私は大きなものから始めましょう - ネスティング。

営巣のための唯一の理由は、(例えば)であることをexpensiveOp5()あなたがへの参照を必要とするd1d2d3だけではなく、d4あなたがそれらの以前の参照を失うため、あなただけの、「通常」を通じてマッピングすることはできませんので- 。時にはそれは、私が最初にそのルートを検討したいので、離れた特定のコンテキストでこれらの依存関係をリファクタリングすることが可能です。

それが可能か望ましくない場合は、私は深くネスト見つける傾向にあってきましたflatMap()。このような呼び出しが最高の構図を介して中間オブジェクトに置き換えられます。

例えば次のようにクラスの束を持っている場合:

@Data
class IntermediateResult1 {
    private DataType1 d1;
    private DataType2 d2;
}

@Data
class IntermediateResult2 {
    public IntermediateResult2(IntermediateResult1 i1, DataType3 d3) {
        this.d1 = i1.getD1();
        this.d2 = i1.getD2();
        this.d3 = d3;
    }
    private DataType1 d1;
    private DataType2 d2;
    private DataType3 d3;
}

...というように、そしてあなただけのような何かを行うことができます。

return d1.flatMap(d1 -> new IntermediateResult1(d1, service.expensiveOp2(d1.foo())))
         .flatMap(i1 -> new IntermediateResult2(i1, service.expensiveOp3(i1.getD1().bar(), i1.getD2().baz())))
         //etc.

もちろん、あなたも、その後(私はおそらく、この場合にお勧めするもの)、それをより明確にするために、独自のメソッドへの呼び出しを抜け出すことができます。

return d1.flatMap(this::doOp1)
         .flatMap(this::doOp2)
         .flatMap(this::doOp3)
         .flatMap(this::doOp4)
         .flatMap(this::doOp5);

もちろん、私は上記の使用していた名前は何もなく、プレースホルダとみなされるべきではない - あなたははるかに自然についての推論や反応性流れを説明するようになりますここで命名良いように、これらの名前について慎重に検討すべきです。

別にネスティングから、他の二つのポイントの価値は、そのコードに注意すること:

  • 使用しreturn Mono.error(new ConflictException("Already active"));、それはあなたが明示的に扱っているということがより明確になりますよう、明示的に投げるのではなく、Mono.errorストリームインチ
  • 決してのような変更可能な方法で使用しないでsetStatus()、後で問題を求めています-反応性連鎖を通じて半分の方法を。代わりに、のようなものを使用しwithたパターンの新しいインスタンスを生成するために、d2更新のフィールドで。その後、呼び出すことができるexpensiveOp5(d1, d2.withStatus(Status.ACTIVE), d3)というセッターコールを没収しながら。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=330814&siteId=1