web-dev-qa-db-ja.com

メソッドは、渡される引数を許容する必要がありますか?

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

メソッドがこの基準からの逸脱をすべて拒否する場合、orメソッドはいくつかの基準についてより寛容でなければなりませんか?たとえば、最初のメソッドが

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番目のforgivingメソッドは

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、バイナリーなど)でオブジェクトを保管できるため、ほとんどの命名規則に一致する最も一般的な分母を特定しようとしました。

21
Zymus

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

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

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

47
Telastyn

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

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

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


入力が曖昧で許容される実際の実験はHTMLです。
その結果、誰もがわずかに異なる方法で実行するようになりました。仕様は、今では文書化されており、特別なケースでいっぱいの巨大な書物です。
を参照してください ポステルの法則( "あなたがしていることを保守的にし、他人から受け入れることを寛容にしなさい。")そのことについて触れている批評家またははるかに優れたMichaelTが私に気づかせた )。

20
Deduplicator

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

とはいえ、メソッドが「寛容」であることが理にかなっている場合はまれですが、常に呼び出し元の能力の範囲内である必要がありますいつかを決定するこれらの処理手順が状況に適用されるかどうか。したがって、さまざまな状況で引数に適用する一般的な前処理手順を特定したら、次のインターフェイスを公開する必要があります。

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

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

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

15
jpmc26

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

それで、寛大な文字列マッチングはそのすべての追加コストに値するのでしょうか?他の人が指摘したように、それはユースケースに依存します。文字列があなたが制御できない何らかの外部入力であり、寛大なマッチングに明確な利点がある場合、それは価値がありますmight。おそらく、入力はスペース文字や大文字の使い方にあまり慣れていないエンドユーザーからのものであり、製品を使いやすくする強い動機があります。

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

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

4
mat_noshi

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

この場合、メソッドに1つの処理を実行させるだけで、文字列の要件を表明し、それに基づいて実行できるようにします-ここでは変換を試みません。 シンプルに保ち、明確にしてください。ドキュメント化してください。ドキュメントとコードの同期を保つように努めてください。

ユーザーデータベースからのデータをより寛容な方法で変換したい場合は、その機能を別の変換方法に入れ、機能を関連付けて文書化しますにします。

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

ここで重要なのは明確さとコードの機能を文書化することです。

3
Niall