あなたはすでにこれを何度も自分で見たことがあります。
public SomeObject findSomeObject(Arguments args) {
SomeObject so = queryFirstSource(args); // the most likely source first, hopefully
if (so != null) return so;
so = querySecondSource(args); // a source less likely than the first, hopefully
if (so != null) return so;
so = queryThirdSource(args); // a source less likely than the previous, hopefully
if (so != null) return so;
// and so on
}
検索するオブジェクトのソースはさまざまです。より鮮明な例として、ユーザーIDが特権ユーザーのリストにあるかどうかを最初に確認することをイメージできます。そうでない場合は、useridが許可されたユーザーのリストにあるかどうかを確認します。それ以外の場合はnullを返します。(これは最良の例ではありませんが、鮮明で十分なものであることを願っています。)
グアバは、上記のコードを美化できるいくつかのヘルパーを提供してくれます:
public SomeObject findSomeObject(Arguments args) {
// if there are only two objects
return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));
// or else
return com.google.common.collect.Iterables.find(
Arrays.asList(
queryFirstSource(args)
, querySecondSource(args)
, queryThirdSource(args)
// , ...
)
, com.google.common.base.Predicates.notNull()
);
}
しかし、私たちの間で経験を積んだ人はすでに見てきたように、ルックアップ(つまりqueryXXXXSource(args)
)に一定の時間がかかる場合、パフォーマンスが低下する可能性があります。これは、最初にすべてのソースにクエリを実行し、結果をメソッドに渡して、それらの結果の中で最初ではないを見つけるためnull
です。
前者が何かを返さない場合にのみ次のソースが評価される最初の例とは対照的に、この2番目のソリューションは、最初は見栄えがよくなるかもしれませんが、パフォーマンスが大幅に低下する可能性があります。
ここで私が実際の質問に行き、誰かがすでにそのベースを実装していることを望んでいるか、または誰かがさらにスマートなソリューションを提案するかもしれないことを提案します。
平易な英語で:誰かがすでにそのようなdefferedFirstNonNull
(以下を参照)または同様の何かを実装していますか?新しいStreamフレームワークでこれを実現する簡単なプレーンJavaソリューションはありますか?同じ結果を達成する別のエレガントなソリューションを提案できますか?
ルール: Java 8は許可されていますが、GoogleのGuavaやApacheのCommons Langなどのアクティブな維持された有名なサードパーティライブラリもApacheライセンスまたは同様のもの(GPLなし)で使用できます。
提案されたソリューション:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
したがって、メソッドdefferedFirstNonNull
は各ラムダ式を次々に評価し、述語(x -> x != null
)が真(つまり、一致が見つかった)とすぐに、メソッドはすぐに結果を返し、それ以上のソースを照会しません。
PS:式args -> queryXXXXSource(args)
はに短縮できることは知っていますqueryXXXXSource
。しかし、何が起こるかは一目で明らかではないため、提案されたソリューションは読みにくくなります。
それはあなたが定義していないいくつかの要因に依存します。query…Source
質問に示されているように、固定された、かなり小さいアクションのセットがありますか、それとも、より柔軟で拡張可能なアクションのリストを作成することを目指していますか?
最初のケースでは、query…Source
メソッドを変更してまたはではOptional<SomeObject>
なくを返すことを検討します。メソッドを次のように変更した場合SomeObject
null
Optional<SomeObject> queryFirstSource(Arguments args) {
…
}
あなたはこのようにそれらを連鎖させることができます:
public SomeObject findSomeObject(Arguments args) {
return queryFirstSource(args).orElseGet(
()->querySecondSource(args).orElseGet(
()->queryThirdSource(args).orElse(null)));
}
それらを変更できない場合、または返すnull
ことを優先する場合でも、Optional
クラスを使用できます。
public SomeObject findSomeObject(Arguments args) {
return Optional.ofNullable(queryFirstSource(args)).orElseGet(
()->Optional.ofNullable(querySecondSource(args)).orElseGet(
()->queryThirdSource(args)));
}
可能性のあるクエリの数を増やすためのより柔軟な方法を探している場合、それらをある種のリストまたはのストリームに変換することは避けられませんFunction
。1つの可能な解決策は次のとおりです。
public SomeObject findSomeObject(Arguments args) {
return Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).map(f->f.apply(args)).filter(Objects::nonNull).findFirst().orElse(null);
}
これにより目的の操作が実行されますが、メソッドを呼び出すたびに必要なアクションが構成されます。このメソッドをより頻繁に呼び出したい場合は、再利用できる操作を作成することを検討してください。
Function<Arguments, SomeObject> find = Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).reduce(a->null,(f,g)->a->Optional.ofNullable(f.apply(a)).orElseGet(()->g.apply(a)));
public SomeObject findSomeObject(Arguments args) {
return find.apply(args);
}
つまり、1つ以上の方法があります。そして、それは実際のタスクがどの方向に進むかによります。場合によっては、単純なif
シーケンスでも適切な場合があります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加