メソッドを引数名(型ではなく)で区別するだけで十分ですか?


36

メソッドを引数名(型ではなく)で区別するだけで十分ですか、それとも明示的に名前を付ける方がよいでしょうか?

たとえば、T Find<T>(int id)VS T FindById<T>(int id)

ById引数名だけを保持するのではなく、より明示的に名前を付ける(つまり、追加する)正当な理由はありますか?

私が考えることができる理由の1つは、メソッドのシグネチャが同じであるが、意味が異なる場合です。

FindByFirstName(string name) そして FindByLastName(string name)


4
Findをオーバーロードした場合、T Find<T>(string name)または(int size)避けられない問題をどのように解決する予定ですか?
UKMonkey

3
@UKMonkeyどんな避けられない問題ですか?
コンラッド

3
最初の場合:複数のエントリの名前が同じ場合、関数のシグネチャを変更する必要があります。つまり、人々はおそらくそれが返すことを意味するものと混同されるでしょう。後者の場合、引数は同じです-したがって、不正なオーバーロードです。「byX」で関数の命名を開始するか、同じ引数でオーバーロードに相当するものを持つことができるように引数のオブジェクトを作成します。どちらもさまざまな状況でうまく機能します。
UKMonkey

2
あなたがしたい場合は、いくつかのコード例で答えを投稿することができます@UKMonkey
コンラート

3
IDは、ID単なるオブジェクトではなく、おそらく不透明なオブジェクトである必要がありますint。そのようにして、コードの一部でintまたはその逆にidを使用していないことをコンパイル時にチェックします。そして、それをあなたが持つことができるfind(int value)find(ID id)
バクリウ

回答:


68

より明確に名前を付ける正当な理由があります。

主に自明のメソッド定義ではなく、メソッドuseです。しながらfindById(string id)かつfind(string id)自明の両方である、との間に大きな差があるfindById("BOB")とはfind("BOB")。前者の場合、ランダムリテラルは実際にはIDであることがわかります。後者の場合、あなたは確信が持てません-それは実際には与えられた名前か何か他のものであるかもしれません。


9
変数またはプロパティ名がある他のケースの99%を除き、参照点があります:findById(id)vs find(id)。これについては、どちらの方法でも実行できます。
グレッグブルクハート

16
@GregBurghardt:特定の値は、メソッドとその呼び出し元で必ずしも同じ名前が付けられているわけではありません。たとえばdouble Divide(int numerator, int denominator)、メソッドでの使用を検討してくださいdouble murdersPerCapita = Divide(murderCount, citizenCount)。同じ変数名を使用する2つの方法に依存することはできません。多くの場合、そうでない場合(または、そうである場合、それは悪い命名です)
Flater

1
@Flater:質問のコード(ある種の永続的なストレージからものを見つける)を考えると、このメソッドを "murderedCitizenId"または "citizenId"で呼び出すことができると思います...引数または変数名は実際にはないと思いますここではあいまいです。そして、正直なところ、私はこれについてどちらにでも行くことができました。それは実際には非常に熱心な質問です。
グレッグブルクハート

4
@GregBurghardt:1つの例からグローバルルールを抽出することはできません。OPの質問は一般に、与えられた例に固有のものではありません。はい、同じ名前を使用する意味がある場合もありますが、そうでない場合もあります。したがって、この回答は、ユースケースのサブセットで不要な場合でも一貫性を目指すのが最善です。
フラット

1
メソッド定義を確認する必要があるため、名前パラメーターは混乱が既に存在した後の混乱を解決しますが、明示的に名前を付けたメソッドはメソッド呼び出しに名前を付けることで混乱を完全に回避します。また、名前と引数タイプが同じで引数名が異なる2つのメソッドを持つことはできません。つまり、とにかく特定の場合に明示的な名前が必要になります。
ダークホッグ

36

FindById()の利点

  1. 今後の防:あなたが開始した場合はFind(int)、後に、他の方法を追加する必要があります(FindByName(string)FindByLegacyId(int)FindByCustomerId(int)FindByOrderId(int)、など)、私のような人は探している年齢を費やす傾向にあります FindById(int)。可能になり、必要になったら変更するのであれば、実際には問題ではありません。将来の校正は、これらの場合についてです。Find(int)FindById(int)

  2. 読みやすいFind以下のようなコールに見える場合は完全に罰金ですrecord = Find(customerId);けれどもがFindById、それはだ場合読み取るため少し簡単ですrecord = FindById(AFunction());

  3. 一貫性。どこでも一貫してFindByX(int)/ FindByY(int)パターンを適用できますが、Find(int X)/ Find(int Y)は競合するため不可能です。

Find()の利点

  • キッス。Findシンプルでわかりoperator[]やすく、このコンテキストで最も期待される2つの関数名の1つです。(いくつかの人気のある代替はget、コンテキストに応じて、、lookupまたはfetch、です)。
  • 経験則として、関数の機能を正確に説明する単一のよく知られた単語である関数名がある場合は、それを使用します。関数が何をするかを説明するのに少し長めの長い複数語の名前がある場合でも。例:Length vs NumberOfElements。トレードオフがあり、どこに線を引くかは継続的な議論の対象です。
  • 一般に、冗長性を回避するのが適切です。を見てみるとFindById(int id)、冗長性をに変更することで簡単に冗長性を削除できますFind(int id)が、トレードオフがあります-明確さが失われます。

または強く型付けされたIDを使用して、両方の利点を得ることができます

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

実装Id<>C#でのID値の厳密な入力

上記のリンクと同様に、ここでのコメントはId<Customer>、私が対処したいことに関して複数の懸念を提起しました:

  • 懸念1:ジェネリックの乱用です。 CustomerIdOrderIDは異なるタイプ(customerId1 = customerId2;=>良い、customerId1 = orderId1;=>悪い)ですが、それらの実装はほぼ同じであるため、コピーペーストまたはメタプログラミングで実装できます。ジェネリックの公開または非表示に関する議論には価値がありますが、メタプログラミングはジェネリックの目的です。
  • 懸念2:それは簡単なmistakes./It'sに問題の検索でソリューションを停止しない、強く型付けされたIDを使用することによって除去の主な問題は、への呼び出しで間違った引数の順序ですDoSomething(int customerId, int orderId, int productId)。厳密に型指定されたIDは、OPが尋ねたものを含む他の問題も防ぎます。
  • 懸念3:実際にはコードがわかりにくくなっています。idが保持されているかどうかを判断するのは困難int aVariableです。Idがに保持されていることは簡単にわかりId<Customer> aVariableます。また、それが顧客IDであることもわかります。
  • 懸念事項4:これらのIDは強力なタイプではなく、単なるラッパーです。 Stringはの単なるラッパーbyte[]です。ラッピングまたはカプセル化は、厳密な型指定と競合しません。
  • 懸念事項5:過剰に設計されています。以下に最小バージョンを示しますが、追加することoperator==をお勧めしoperator!=ますが、もしあなただけに依存したくない場合はEquals

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}

10

これについて考えるもう1つの方法は、言語の型安全性を使用することです。

次のようなメソッドを実装できます。

Find(FirstName name);

FirstNameは、ファーストネームを含む文字列をラップする単純なオブジェクトであり、メソッドが何をしているのか、それが呼び出される引数に混乱がないことを意味します。


4
OPの質問に対する答えがわからない。引数のタイプに依存して「検索」などの名前を使用することをお勧めしますか?または、引数に明示的な型がある場合にのみそのような名前を使用し、他の場所で「FindById」などのより明示的な名前を使用することをお勧めしますか?または、「検索」などの名前をより実行可能にするために、明示的な型を導入することをお勧めしますか?
ドックブラウン

2
@DocBrown後者だと思うし、気に入っている。実際には、ピーターの答えiiucに似ています。私が理解している理論的根拠は2つあります。(2)string name = "Smith"; findById(name);.descriptのような一般的な型を使用する場合に起こりうる。
ピーター-モニカの復活

5
一般的に私はコンパイル時の型の安全性のファンですが、ラッパー型には注意してください。型の安全性のためにラッパークラスを導入すると、APIを過度に行うとAPIが非常に複雑になる場合があります。たとえば、winapiが持つ「以前はintとして知られていたアーティスト」問題全体。長い目で見れば、ほとんどの人はエンドレスDWORD LPCSTRなどのクローンを見て「int / string / etc」だと思うと、実際にコードを設計するよりも多くの時間をツールのプロップに費やすことになります。
jrh

1
@jrhはい。「名義」型(名前のみが異なる)を導入するための私のリトマステストは、一般的な関数/メソッドがユースケースで意味をなさない場合です。たとえば、intsはしばしば加算、乗算など、IDには無意味です。だから私はとID区別しintます。これは、(私たちが持っている場合例えば、我々は値を与えないことができるものを絞り込んで、APIを簡素化することができID、それはでのみ動作しますfind例えば、ではありませんagehighscore)。私たち自身が多くを変換する、あるいは同じ関数/メソッド(例えば書き込み見つけた場合は逆に、find複数のタイプのために)、それは私たちの区別が細かすぎるというサインだ
Warbo

1

FindByIDのような明示的な宣言に投票します。変更のためにソフトウェアを構築する必要があります。開いて閉じている必要があります(SOLID)。したがって、このクラスは、FindByNameなどのような同様の検索メソッドを追加するために開かれています。

ただし、FindByIDは閉じられており、その実装は単体テスト済みです。

述語を使用したメソッドは提案しませんが、それらは汎用レベルで優れています。フィールド(ByID)に基づいて、完全に異なる方法論がある場合はどうでしょう。


0

次のような述語を使用することを誰も提案していないことに驚いています。

User Find(Predicate<User> predicate)

このアプローチでは、APIの表面を縮小するだけでなく、それを使用するユーザーにより多くの制御を提供します。

それだけでは不十分な場合は、いつでもニーズに合わせて拡張できます。


1
問題は、インデックスのようなものを利用できないという事実のために効率が悪いことです。
ソロモンウッコ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.