渡された引数を使用してメソッドを許可する必要がありますか?[閉まっている]


21

foo(String bar)特定の条件を満たす文字列に対してのみ動作するメソッドがあるとします。たとえば、小文字である必要があり、空または空白のみでなく、パターンに一致する必要があります[a-z0-9-_./@]+。メソッドのドキュメントには、これらの基準が記載されています。

メソッドはこの基準からのすべての逸脱を拒否する必要がありますそれともメソッドはいくつかの基準についてより寛容である必要がありますか?たとえば、初期メソッドが

public void foo(String bar) {
    if (bar == null) {
        throw new IllegalArgumentException("bar must not be null");
    }
    if (!bar.matches(BAR_PATTERN_STRING)) {
        throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
    }
    this.bar = bar;
}

そして2番目の寛容な方法は

public void foo(String bar) {
    if (bar == null) {
        throw new IllegalArgumentException("bar must not be null");
    }
    if (!bar.matches(BAR_PATTERN_STRING)) {
        bar = bar.toLowerCase().trim().replaceAll(" ", "_");
        if (!bar.matches(BAR_PATTERN_STRING) {
            throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
        }
    }
    this.bar = bar;
}

可能な場合はドキュメントを変換して変換値に設定するようにドキュメントを変更する必要がありますか、それともメソッドを可能な限りシンプルにして、すべての逸脱を拒否する必要がありますか?この場合、barアプリケーションのユーザーが設定できます。

これの主なユースケースは、特定の文字列識別子によってリポジトリのオブジェクトにアクセスするユーザーです。リポジトリ内の各オブジェクトには、それを識別する一意の文字列が必要です。これらのリポジトリは、オブジェクトをさまざまな方法(SQLサーバー、JSON、XML、バイナリなど)で保存できるため、ほとんどの命名規則に一致する最小公分母を特定しようとしました。


1
これはおそらくユースケースに大きく依存します。どちらかが合理的である可能性があり、両方のメソッドを提供し、ユーザーに決定させるクラスを見てきました。このメソッド/クラス/フィールドが何をすべきかを詳しく説明してください。実際にアドバイスを提供できますか?
Ixrec

1
メソッドを呼び出すすべての人を知っていますか?たとえば、変更した場合、すべてのクライアントを確実に識別できますか?もしそうなら、パフォーマンスの問題が許す限り寛容で寛容になります。ドキュメントを削除することもあります。そうでなく、それがライブラリAPIの一部である場合、宣伝されているAPIに正確に実装されたコードを確認します。
ジョンチェスターフィールド

7
Separation of Concernsは、必要に応じて、foo受け入れる引数に厳密な厳密な関数を用意し、使用する引数を「クリーン」にしようとする2番目のヘルパー関数を用意する必要があると主張できますfoo。このように、各メソッドはそれ自体で行う必要が少なく、よりきれいに管理および統合できます。そのルートを下る場合、例外が多い設計から離れることもおそらく役立つでしょう。Optional代わりに次のようなものを使用しfoo、必要に応じて例外をスローする関数を使用できます。
gntskn

1
これは、「誰かが私を不当に扱いました。彼らを許すべきですか?」明らかに、どちらか一方事情があるか、他のが適切であるが。プログラミングは人間関係ほど複雑ではないかもしれませんが、このような包括的な処方箋が機能しないほど十分に複雑です。
キリアンフォス

2
@Boggin また、ロバストネス原則の再考を指摘します。実装を拡張する必要がある場合に困難が発生し、寛容な実装は拡張された実装で曖昧なケースにつながります。

回答:


47

あなたのメソッドはそれが言うことをするべきです。

これにより、後で使用したり、メンテナーが動作を変更したりすることによるバグを防ぎます。メンテナーは何が起こっているかを理解するのにそれほど時間を費やす必要がないため、時間を節約できます。

ただし、定義されたロジックがユーザーフレンドリーでない場合は、おそらく改善する必要があります。


8
これが鍵です。メソッドが指定どおりに実行する場合、メソッドを使用するコーダーは特定のユースケースを補正します。メソッドが有用だと思うからといって、ドキュメント化されていない何かをこのメソッドで実行しないでください。変更する必要がある場合は、コンテナを作成するか、ドキュメントを変更してください。
ネルソン

@Nelsonのコメントに、メソッドを真空内で設計するべきではないことを付け加えます。コーダーがそれを使用すると言っても、それを補償し、その補償が汎用的な価値を持つ場合、それらをクラスの一部にすることを検討してください。(たとえば、前者に渡す前に後者が修正を行うhave fooおよびfooForUncleanStringメソッド。)
Blrfl

20

いくつかのポイントがあります。

  1. 実装は、文書化された契約に記載されていることを実行する必要があり、それ以上何も実行しないでください。
  2. シンプルさとは、契約と実装の両方にとって重要ですが、前者にとってはより重要です。
  3. 誤った入力を修正しようとすると、複雑さを増します。これは、契約と実装だけでなく、使用するのも直感的ではありません。
  4. エラーは、デバッグ可能性を改善し、効率をあまり損なわない場合にのみ早期に捕捉する必要があります。
    デバッグモードには、ロジックエラーを診断するためのデバッグアサーションがあり、これはほとんどのパフォーマンス上の懸念を軽減することに注意してください。
  5. シンプルさを過度に犠牲にすることなく、利用可能な時間とお金が許す限り、効率が常に目標です。

ユーザーインターフェイスを実装する場合、わかりやすいエラーメッセージ(提案やその他のヘルプを含む)は優れた設計の一部です。
ただし、APIはプログラマ向けであり、エンドユーザー向けではないことに注意してください。


入力があいまいで許容性のある実際の実験はHTMLです。
その結果、誰もが少し違ったやり方でそれを行うようになり、その仕様は今では文書化されており、特別なケースでいっぱいの巨大な本になっています。
参照ポステルの法則(「あなたは他人から受け入れ何でリベラルなこと、あなたが何をすべきかで保守的である。」)その上の評論家の感動またはMichaelTはの私は認識してはるかに優れた1)。


sendmailの著者による別の重要な部分:堅牢性原則の再検討

15

メソッドの動作は、明確で、直感的で、予測可能で、シンプルでなければなりません。一般に、発信者の入力に対して追加の処理を行うことを非常にためらう必要があります。呼び出し元が意図したものについてのそのような推測には、常に望ましくない動作を引き起こす多くのエッジケースがあります。ファイルパスの結合と同じくらい簡単な操作を検討してください。結合されるパスの1つがルート化されているように見える場合、多くの(またはおそらくほとんどの)ファイルパス結合関数は、先行するパスを静かに破棄します!たとえば、と/abc/xyz結合する/evilと、がちょうどになり/evilます。これは、ファイルパスを結合するときに意図することとはほとんど言えませんが、このように動作しないインターフェイスがないため、これらのケースをカバーするバグを追加するか、余分なコードを記述する必要があります。

つまり、「寛容」されるが、それはすべきメソッドのために理にかなっている時にまれである、と述べ常に決定するために、発信者の力の範囲内でする場合するかどうかをこれらの処理手順は、自分の状況に適用されます。したがって、さまざまな状況で引数に適用する共通の前処理手順を特定したら、次のインターフェイスを公開する必要があります。

  • 前処理なしの生の機能。
  • 前処理ステップ自体
  • 生の機能と前処理の組み合わせ。

最後はオプションです。多数の呼び出しで使用する場合にのみ提供する必要があります。

生の機能を公開することにより、呼び出し元は必要なときに前処理ステップなしでそれを使用できます。プリプロセッサステップを単独で公開することで、呼び出し元は、関数を呼び出さない場合や、関数を呼び出す前に入力を処理したい場合(最初に別の関数に渡したい場合など)に使用できます。組み合わせを提供することで、発信者は手間をかけずに両方を呼び出すことができます。これは、ほとんどの発信者がこの方法で使用する場合に主に役立ちます。


2
+1は予測可能です。そして、もう1つの+1(希望)がシンプルです。間違いを隠そうとするよりも、間違いを見つけて修正するのを手伝ってほしいです。
ジョンMガント

4

他の人が言ったように、文字列マッチングを「許す」ことは、さらに複雑さを導入することを意味します。つまり、マッチングの実装により多くの作業が必要になります。たとえば、さらに多くのテストケースがあります。名前空間に意味的に等しい名前がないことを確認するには、追加の作業を行う必要があります。また、複雑さが増すということは、将来さらに多くの問題が発生することを意味します。自転車などの単純な機構は、自動車などのより複雑な機構よりもメンテナンスが少なくて済みます。

それで、寛大な文字列マッチングは、そのすべての追加コストに値しますか?他の人が述べたように、それはユースケースに依存します。文字列が何らかの種類の外部入力であり、制御できない場合、厳密なマッチングに明確な利点がある場合、それ価値があるかもしれません。おそらく、入力はスペース文字と大文字小文字についてあまり良心的ではないエンドユーザーから来ているので、製品を使いやすくする強いインセンティブがあります。

一方、入力が、たとえば技術関係者によって組み立てられたプロパティファイルからのものである場合、そのことを理解する必要がある"Fred Mertz" != "FredMertz"ため、一致をより厳密にし、開発コストを節約したいと思います。

とにかく、先頭と末尾のスペースをトリミングして無視することには価値があると思いますが、こうした種類の問題のデバッグに多くの時間が浪費されているのを見てきました。


3

あなたはこの質問の出所となる文脈のいくつかに言及します。

それを考えると、メソッドにたった1つのことをさせて、文字列の要件をアサートし、それに基づいて実行させます-ここで変換しようとはしません。シンプルにして、明確にしてください。それを文書化し、文書とコードが互いに同期するように努めてください。

ユーザーデータベースから取得したデータをより寛容な方法で変換する場合は、その機能を別の変換メソッドに入れ、それを結び付けた機能を文書化します

ある時点で、機能の要件を測定し、明確に文書化し、実行を継続する必要があります。彼の時点での「許し」は少し無言であり、それは設計上の決定であり、私はこの関数がその引数を変えないことを主張します。関数に入力を変更させると、クライアントに必要な検証の一部が隠されます。ミューテーションを行う機能があると、クライアントはそれを正しく行うことができます。

ここでの大きな強調点は明快さとコードがすることを記録することです。


-1
  1. doSomething()、takeBackUp()などのアクションに従ってメソッドに名前を付けることができます。
  2. 保守を容易にするために、さまざまな手順で共通の契約と検証を維持できます。ユースケースごとにそれらを呼び出します。
  3. 防御的なプログラミング:プロシージャは、以下を含む幅広い入力を処理します(これらのユースケースである最小限のものはとにかくカバーする必要があります)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.