データベース関連のメソッドの設計。どちらを返す方が良いですか。true/ falseまたは行に影響がありますか?


10

データベースでデータの変更(挿入、更新、削除)を実行するいくつかのメソッドがあります。ORMは、私は方法のこれらのタイプの戻り行の影響を受けたint型の値を使用しています。操作の成功/失敗の状態を示すために、「私のメソッド」に対して何を返す必要がありますか?

を返すコードを考えてみましょうint

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

次に、使用法:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

ブール値を使用する場合と比較してください。

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

次に、使用法:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

どちらが良いですか(AまたはB)?またはそれぞれの長所/短所?


あなたの「A.2」の中。ゼロ行が影響を受ける場合、ゼロ行が影響を受ける必要がある場合、なぜ失敗するのですか?つまり、データベースエラーがない場合、なぜ失敗するのでしょうか。
ジェイディー2014

5
「失敗」と「影響を受ける行がゼロ」の間に意味上の違いはありますか?たとえば、顧客のすべての注文を削除する場合、「顧客が存在しない」と「顧客に注文がない」には違いがあります。
頭足類2014

ゼロ行の削除が予期しないものと考えますか?その場合は投げます。
usr 14

@Arian私はそれが本当の質問だと思います。私はBを選択したと思います。Aを使用すると、コードの一部の場所で0のチェックと他の場所で-1のチェックが含まれるようになったためです
Hoang Tran

回答:


18

別のオプションは、基本タイプの代わりに結果オブジェクトを返すことです。例えば:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

これにより、何らかの理由で影響を受ける行の数を返す必要がある場合は、単純にOperationResultにメソッドを追加できます。

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

この設計により、既存のコードを変更せずに、システムを拡張して新しい機能(影響を受ける行について知っている)を含めることができます。


2
+1「この設計により、既存のコードを変更せずに、システムを拡張して新しい機能(影響を受ける行について知っている)を含めることができます」これは、このカテゴリの質問についての正しい考え方です。
InformedA

私は私の古いプロジェクトでも同様のことをしました。ラッパーのRequestオブジェクトとResultオブジェクトがあります。どちらもコンポジションを使用して詳細を含めます。どちらにも基本的なデータが含まれています。この場合、Resultオブジェクトにはステータスコードとmsgフィールドがあります。
InformedA 2014

特にORMを使用するシステムでは、これをベストプラクティスと呼びます。問題のORMには、そのような結果オブジェクトタイプもすでに含まれている可能性があります。
ブライアンS

OPの例を見ると、deleteByIdがある時点で2を返すということしか考えられません:)確かにカスタムタイプです。
デッドスベン2014

私はこのアプローチが好きです。それはノンブロッキングであり、私のアプローチの両方をカバーし、変更可能/拡張可能です(将来、必要に応じて)。私が考えることができる唯一の欠点は、特にそれが私のプロジェクトの真ん中に来るとき、それが少し多くのコードであることです。これを答えとしてマークします。ありがとうございました。
Hoang Tran 2014

12

操作の進行状況に関する追加情報を提供するため、影響を受ける行の数を返す方が優れています。

操作中に変更があったかどうかを確認するためにこれを書く必要があるので、プログラマはあなたを責めません。

if(affectedRows > 0) {
    // success
} else {
    // fail
}

しかし、影響を受ける行の数を知る必要があり、その数を取得する方法がないことに気づくと、彼らはあなたを非難します。

ところで、「失敗」によって構文クエリエラーが発生した場合(影響を受ける行の数は明らかに0です)、例外をスローする方が適切です。


4
あなたが与えた理由により-1。追加情報を提供することは常に良い考えではありません。多くの場合、より良い設計は、必要なものだけを発信者に伝達することにつながり、それ以上は何もしません。
Arseni Mourzenko 2014

1
@MainMaは、オーバーロードされたメソッドの作成を妨げるものはありません。さらに、ネイティブ言語(Cファミリーなど)では、行数をロジックで直接使用できます(0はfalse、それ以外はtrue)。
PTwr 2014

@PTwr:メソッドが返す情報が多すぎるという事実に対処するために、オーバーロードを作成することをお勧めしますか?これは正しくないようです。「ゼロは偽、非ゼロは真」については、これは私のコメントのポイントではなく、一部の言語では不適切な行為です。
Arseni Mourzenko 14

1
トリックを使うことは良い習慣とは思えないので、おそらく私は甘やかされて育ったプログラマーの一人です。実際、誰か他の人のコードを処理する必要があるときはいつでも、それを書いて誰もトリックを使用しなかった人に感謝します。
proskor 2014

4
影響を受ける行の数を常にここに返す必要があります!!! 行数= 0が失敗であるかどうか、または行数= 3が成功であるかどうかを知ることができないので、誰かが3行を挿入したいが2行しか挿入されない場合、trueを返しますが、それは正しくありません!そして、誰かがupdate t set category = 'default' where category IS NULL影響を受ける行を0にしたい場合、成功します。これは、影響を受ける行がない場合でも、カテゴリのないアイテムがないためです。
Falco 14

10

どれもお勧めしません。代わりに、成功時には何も返さず(void)、失敗すると例外をスローします。

これは、クラスの特定のメンバーをプライベートとして宣言することを選択したのとまったく同じ理由です。また、機能が使いやすくなります。より多くの運用コンテキストは、常により良いことを意味するわけではありませんが、それは確かにより複雑であることを意味します。約束が少ないほど、抽象化が進むほど、クライアントは理解しやすくなり、実装方法を選択する自由が広がります。

問題は、成功/エラーをどのように示すかです。この場合、例外をスローして失敗を通知し、成功しても何も返さないことで十分です。ユーザーのニーズ以上のものを提供する必要があるのはなぜですか?

失敗/例外的な状況が発生する可能性があるので、それらに対処する必要があります。それを行うためにtry / catchを使用するか、戻りコードを調べるかは、スタイル/個人的な好みの問題です。try / catchの背後にあるアイデアは、通常のフローを例外的なフローから分離し、例外を最も適切に処理できる層まで泡立たせることです。したがって、多くの人がすでに指摘したように、失敗が本当に例外的であるかどうかによって異なります。


4
-1何かを返すことができる場所に何も返さず、負の副作用がなく、余分な運用コンテキストが提供されるのはなぜですか?
FreeAsInBeer 14

7
まったく同じ理由で、クラスの特定のメンバーをプライベートに宣言することを選択します。また、機能が使いやすくなります。より多くの運用コンテキストは、常により良いことを意味するわけではありませんが、確かにより複雑なことを意味します。約束が少ないほど、抽象化が進むほど、クライアントは理解しやすくなり、実装方法を選択する自由が広がります。
proskor 2014

1
さて、ユーザーは実際にどのようなデータを取得する必要がありますか?問題は、成功/エラーをどのように示すかです。この場合、例外をスローして失敗を通知し、成功しても何も返さないことで十分です。ユーザーのニーズ以上のものを提供する必要があるのはなぜですか?
proskor 2014

4
@proskor:例外は例外的なケースです。このシナリオの「失敗」は、予期される結果である可能性があります。必ず、これを潜在的な代替手段として提示してください。ただし、推奨を行うための十分な情報がここにはありません。
Nick Barnes 14

1
-1例外は通常のプログラムフローの一部であってはなりません。エラーのコンテキストで「障害」が何を意味するかは不明ですが、データベース呼び出しのコンテキストでの例外は、データベースで発生する例外が原因である必要があります。ゼロ行に影響することも例外ではありません。解析できないマングルされたクエリ、存在しないテーブルを参照するクエリなどは、データベースエンジンがそれをチョークしてスローするため、例外になります。

2

「これよりいいの?」2つの選択肢が同じことを行わない場合は、有用な質問ではありません。

影響を受ける行数を知る必要がある場合は、バージョンAを使用する必要あります。必要がない場合は、バージョンBを使用できます。両方のバージョンをオンラインフォーラムに投稿するのに苦労しました!

私のポイントは次のとおりです。どちらのソリューションの方が優れているかは、このアプリケーションに対する具体的な要件に完全に依存し、私たちよりもはるかによくそれらの状況を知っていることです。業界全体で、安全に使用でき、ベストプラクティスであり、一般的にはより優れ選択肢ではありません。あなたはそれについて自分で考えなければなりません。そして、この決定のように簡単に修正できる決定のために、それほど多くの時間を費やす必要もありません。


これはアプリの要件に大きく依存することに同意します。しかし、この種の状況はそれほど独特ではないようであり、私は特効薬を探していませんが、同じ/類似したものを扱った他の人の経験だけを探しています/ B)
Hoang Tran 2014

1

保守可能なソフトウェア設計で最も重要な2つの原則は、KISSYAGNIです。

  • KISS:シンプルに、バカに
  • YAGNI:あなたはそれを必要としないだろう

すぐに必要のないロジックを組み込むことは、ほとんど決して良い考えではありません。他の多くの人々の間で、Jeff Atwood(StackExchangeの共同創設者)がこれについて書いており、私の経験では、彼とこれらの概念の他の支持者は完全に正しいです。

プログラムに追加する複雑さは、コストがかかり、長期間にわたって支払われます。プログラムは読みにくくなり、変更が複雑になり、バグが侵入しやすくなります。「念のため」に何かを追加するという罠に陥らないでください。それは誤った安心感です。

コードを最初から正しく取得することはめったにありません。変更は避けられません。未知の将来の不測の事態に備えて防御的に準備するために投機的ロジックを追加しても、将来が予想とは異なることが判明したときに、コードをリファクタリングする必要がなくなるわけではありません。不必要な/偶発的なロジックの維持は、欠けている機能を追加するために後でリファクタリングするよりも、保守性の問題です。

したがって、今のところプログラムに必要なのは操作が成功したか失敗したかを知ることだけなので、提案されたソリューションB(単一のブール値を返す)が正しいアプローチです。要件が変更された場合は、後でいつでもリファクタリングできます。このソリューションは最も単純で、複雑度が最も低く(KISS)、必要なことだけを実行し、それ以上は実行しません(YAGNI)。


-1

行全体またはエラーステータス

少なくともランタイムオプションとして、行全体を返すことを検討してください。DB挿入では、挿入されたデータを検査する必要がある場合があります。これは、DBに送信したデータと異なることが多いためです。一般的な例には、自動生成された行ID(アプリですぐに必要になる可能性が高い)、DBによって決定されるデフォルト値、および使用した場合のトリガーの結果が含まれます。

あなたが返されたデータを必要としない場合には間違いがある場合、それは0の結果のために有用ではないのですから、あなたはまた、影響を受けた行数を必要としない一方で、あなたは返す必要がどのような種類のプロジェクトのエラー処理の原則(例外、数値エラーコードなど)と一貫した方法で、間違いが発生した。しかし、0行に正しく影響する有効なクエリがあります(つまり、実際に存在しない場合は「期限切れの注文をすべて削除する」)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.