Kotlin で Result をいつ、どのように使用するか? 質問する

Kotlin で Result をいつ、どのように使用するか? 質問する

Result というアイデアが気に入りました。カプセル化された try/catch が気に入りました。

しかし、Result をどのように、いつ使用すればよいのかが少しわかりません。

私は現在、次のように使用しています:

私のアダプタとサービスは結果を返します。失敗とスタックトレースは記録されますが、それ以外は何もしません。

runCatching{
    .... // do something cool 
}.onFailure {
    logger.error("Something bad happened ", it)
}

私のリソースクラスは結果を折り畳んで処理します

return service.method().fold(
    onFailure = {
        Response.serverError().entity("Oops").build()
    },
    onSuccess = {
        Response.ok().entity(doSomethingWith(it)).build()
    }
)

これは本当に Result を使用する正しい方法でしょうか? それとも、Kotlin でより慣用的なコーディング方法があるのでしょうか?

ベストアンサー1

要約:ビジネス コードでは決して使用しないでください (カスタム シール クラスを推奨)。ただし、Result例外メカニズムとは異なる方法 (kotlinx.coroutines の実装など) であらゆる種類のエラーを中継する必要があるフレームワークを構築する場合は検討できます。


まず、実際には使用例のリストの最初の導入の動機についてはResult、興味があれば、同じ文書で以下もご覧ください。

Result クラスは、Kotlin 関数の一般的な失敗をキャプチャして後で処理するように設計されており、Kotlin コード ブロックの呼び出しを処理し、実行の成功と失敗の両方の結果を表すことができる必要がある、futures などの汎用 API で使用する必要があります。Result クラスは、ドメイン固有のエラー状態を表すようには設計されていません。

また、とても良い記事ですRoman Elizarov による、Kotlin の例外について、特に例外ではなく型システムを使用する場合について説明します。

以下の大部分は私の個人的な意見です。事実に基づいていますが、あくまでも意見に過ぎませんので、鵜呑みにしないでください。

ビジネスコードでは使用したりrunCatchingキャッチしたりしないでくださいThrowable

は、、、、などのJVM を含むrunCatchingあらゆる種類の をキャッチすることに注意してください。通常、このような JVM エラーに対しては、ほとんど何もできません (OOME の場合など、エラーを報告することさえ不可能な場合があります)。ThrowableErrorNoClassDefFoundErrorThreadDeathOutOfMemoryErrorStackOverflowError

Javaのドキュメントによると、エラークラス「合理的なアプリケーションでは検出できない重大な問題を示します」非常に特殊なケースでは、試すエラーから回復することは可能ですが、これは非常に例外的なことです (しゃれです)。

このような包括的なメカニズム ( もcatch(Exception)) は、何らかの方法でエラーを報告しようとするフレームワークを実装している場合を除き、通常は推奨されません。

だから、もし私たちがキャッチエラーを回避 (代わりに自然に発生するようにする) は、Resultここでは考慮されていません。

ビジネスコードでプログラミング例外をキャッチしない

JVM エラーは別として、プログラミング エラーによる例外も、ビジネス コードを肥大化させるような方法で処理すべきではないと私は考えています。適切な場所でerror()、 、 を使用するとcheck()require()例外を利用して、コンパイラ ( IllegalStateExceptionIllegalArgumentException) でキャッチできないバグを検出できます。これらの例外は、プログラマがミスを犯してコードのロジックが壊れているときに発生するため、ビジネス コードでこれらの例外をキャッチしても意味がないことがよくあります。代わりに、コードのロジックを修正する必要があります。

場合によっては、コード領域周辺のすべての例外 (これらの例外を含む) をキャッチし、失敗した操作全体を高レベルで置き換えて回復することが依然として有用なことがあります (通常はフレームワーク コード内ですが、ビジネス コードの場合もあります)。これはおそらく some で実行されますがtry-catch(Exception)Resultこのようなコードのポイントはtry-catch置き換え可能な高レベルの操作で区切ることであるため、ここでは取り上げません。低レベルのコードは、プログラミング エラーが発生した場合に何かに置き換えることができる高レベルの操作があるかどうか、または単にバブルアップする必要があるかどうかがわからないため、戻りませResultん。

ビジネスエラーのモデリング

結果のような型にはビジネス エラーが残ります。ビジネス エラーとは、エンティティの欠落、外部システムからの不明な値、ユーザー入力の誤りなどを指します。ただし、通常は を使用するよりも優れたモデル化方法を見つけますkotlin.Result(設計ドキュメントで規定されているように、これはこのためのものではありません)。値の欠如をモデル化することは、通常、null 許容型 を使用すると簡単ですfun find(username: String): User?。結果セットのモデル化は、結果型のようにさまざまなケースをカバーするカスタム シール クラスを使用して実行できますが、特定のエラー サブタイプ (および だけよりもエラーに関する興味深いビジネス データThrowable) を使用します。

つまり、結局のところ、私はkotlin.Resultビジネス コードで自分自身を使用することはありません (すべてのエラーを報告する必要がある汎用フレームワーク コードでは検討できます)。


私のアダプタとサービスは結果を返します。失敗とスタックトレースは記録されますが、それ以外は何もしません。

これに関する補足です。ご覧のとおり、サービス自体でエラーをログに記録していますが、サービス コンシューマーの観点からは不明瞭です。コンシューマーは を受け取るのでResult、ここでエラーを処理する責任は誰にあるのでしょうか。回復可能なエラーの場合、エラーとしてログに記録することが適切かどうかはわかりません。警告として記録する方がよい場合もあれば、まったく記録しないほうがよい場合もあります。おそらく、コンシューマーはサービスよりも問題の重大性をよく知っているでしょう。また、サービスは、JVM エラー、プログラミング エラー (IAE、ISE など)、およびビジネス エラーをログに記録する方法に区別しません。

おすすめ記事