web-dev-qa-db-ja.com

命名の問題:「ISomething」は「Something」に名前を変更する必要がありますか?

Uncle BobのClean Codeの名前に関する章では、主にハンガリー語表記に関して、名前のエンコードを避けることを推奨しています。彼はまた、インターフェースからのI接頭辞の削除について具体的に言及していますが、この例は示していません。

次のことを前提とします。

  • インターフェースの使用は主に、依存性注入を通じてテスト容易性を達成することです
  • 多くの場合、これは単一の実装者との単一のインターフェースを持つことにつながります

では、たとえば、これら2つは何と名付けるべきでしょうか? ParserおよびConcreteParserParserおよびParserImplementation

public interface IParser {
    string Parse(string content);
    string Parse(FileInfo path);
}

public class Parser : IParser {
     // Implementations
}

または、このような単一実装のケースではこの提案を無視する必要がありますか?

68
Vinko Vrsalovic

「ボブおじさん」を含む多くの人は、インターフェースの接頭辞としてIを使用しないことをお勧めしますが、これはC#で確立された伝統です。一般的に、それは避けるべきです。ただし、C#を記述している場合は、その言語の規則に従って実際に使用する必要があります。そうしないと、コードを読み込もうとするC#に詳しい他の人と大きな混乱を招きます。

188
David Arno

インターフェイスは重要な論理的概念であるため、インターフェイスには一般的な名前を付ける必要があります。だから、私はむしろ

interface Something
class DefaultSomething : Something
class MockSomething : Something

より

interface ISomething
class Something : ISomething
class MockSomething : ISomething

後者にはいくつかの問題があります。

  1. 何かは、ISomethingの実装の1つにすぎませんが、それがなんらかの特別なものであるかのように、総称名を持つものです。
  2. MockSomethingはSomethingから派生しているように見えますが、ISomethingを実装しています。多分それはMockISomethingと名付けられるべきですが、私はそれを野生で見たことがありません。
  3. リファクタリングは難しいです。何かが現在クラスであり、後でインターフェースを導入する必要があることがわかった場合、そのインターフェースには同じ名前を付ける必要があります。これは、型が具象クラス、抽象クラス、インターフェースのいずれであっても、クライアントに対して透過的であるためです。 。何かがそれを壊します。
39
wallenborn

これは、命名規則に関するjustではありません。 C#は多重継承をサポートしていないため、このハンガリー語表記のレガシー使用は、基本クラスから継承して1つ以上のインターフェースを実装する場合に有用ではありますが、小さな利点があります。したがって、この...

class Foo : BarB, IBarA
{
    // Better...
}

...これよりも好ましい...

class Foo : BarB, BarA
{
    // Dafuq?
}

IMO

27
Robbie Dee

ダメダメダメ。

命名規則はボブおじさんのファンダムの機能ではありません。これらは、c#などの言語の関数ではありません。

それらはコードベースの機能です。あなたのお店のはるかに少ない程度に。つまり、コードベースの最初のものが標準を設定します。

そうして初めて、あなたは決断することができます。その後は一貫しているだけです。誰かが一貫性を失い始めたら、ナーフガンを取り上げ、悔い改めるまでドキュメントを更新させます。

新しいコードベースを読み取るときに、Iプレフィックスが表示されない場合は、言語に関係なく問題ありません。すべてのインターフェイスで1つ表示されれば問題ありません。私が時々それを見た場合、誰かが支払うでしょう。

コードベースにこの先例を設定するという希薄な状況に自分がいる場合は、これを検討することをお勧めします。

私はクライアントクラスとして、私が話していることを気にしないようにする権利を留保します。私が必要なのは、名前と、その名前に対して呼び出すことができるもののリストです。私がそうしなければ、それらは変わらないかもしれません。私はその部分を所有しています。私が話しているのは、インターフェイスかどうかにかかわらず、私の部門ではありません。

その名前にIをこっそり入れても、クライアントは気にしません。 Iがない場合、クライアントは気にしません。それが話していることは具体的かもしれません。そうではないかもしれません。どちらの方法でもnewを呼び出さないため、違いはありません。クライアントとして、私にはわかりません。知りたくない。

今や、コードモンキーがそのコードを見るとき、たとえJavaであっても、これは重要かもしれません。結局のところ、実装をインターフェイスに変更する必要がある場合、クライアントに通知せずにそれを行うことができます。 Iの伝統がない場合、名前を変更することすらできません。これは問題になる可能性があります。ソースがバイナリを気にしていないのに、クライアントを再コンパイルする必要があるからです。名前を変更したことがない場合、見逃しやすいものです。

多分それはあなたにとって問題ではありません。そうでないかもしれない。もしそうなら、それは気にする良い理由です。しかし、デスクのおもちゃが大好きなため、盲目的にこれを行う必要があると主張してはいけません。これは、言語によって行われる方法だからです。正当な理由があるのか​​、気にしないのか。

永遠に一緒に暮らすことではありません。どこにでも「問題」を修正する準備ができていない限り、コードベースが特定の考え方に準拠していないことについて、私にがらくたを与えないことです。あなたが私が均一性への抵抗を最小にする道をとってはならないという正当な理由がない限り、私に適合を説教するようなことはしないでください。やるべきことがもっとあります。

15
candied_orange

JavaとC#の間には、ここに関連する小さな違いが1つあります。Javaでは、すべてのメンバーがデフォルトで仮想です。C#では、すべてのメンバーがデフォルトでシールされます-インターフェースメンバーを除きます。

これに伴う仮定がガイドラインに影響を与えます。Javaでは、Liskovの代入原理[1]に従って、すべてのパブリックタイプは最終ではないと見なされます。実装が1つしかない場合は、クラスにParserという名前を付けます。複数の実装が必要な場合は、クラスを同じ名前のインターフェイスに変更し、具体的な実装の名前をわかりやすい名前に変更します。

C#では、主な前提は、クラスを取得するとき(名前がIで始まらない)、それが必要なクラスであることです。ちなみに、これは100%正確ではありません-典型的な反例はStream(実際には1つまたは複数のインターフェイスであるはずです)のようなクラスであり、誰もが独自のガイドラインと背景を持っています他の言語から。抽象クラスを示すためにかなり広く使用されているBaseサフィックスのような他の例外もあります-インターフェースと同様に、あなたはknow型は多態性であると想定されています。

インターフェイスを抽象クラスにすることなく、そのインターフェイスに関連する機能にI以外の接頭辞が付いた名前を残すことで、使いやすさを向上させる優れた機能もあります(C#にはクラスの多重継承がないために問題が生じます)。これはIEnumerable<T>をインターフェイスとして使用し、Enumerableをそのインターフェイスに適用されるメソッドのリポジトリとして使用するLINQによって普及しました。これはJavaでは不要であり、インターフェイスにはメソッド実装も含めることができます。

結局のところ、IプレフィックスはC#の世界で広く使用されており、拡張機能として.NETの世界(ほとんどの.NETコードはC#で記述されているため、ほとんどのC#ガイドラインに従うのが理にかなっています)公開インターフェース)。これは、ほぼ確実にこの表記法に従うライブラリとコードで作業することを意味し、不要な混乱を防ぐために伝統を採用することは理にかなっています-接頭辞を省略することはコードをより良くするようなものではありません:)

ボブおじさんの推論はこのようなものだったと思います:

IBananaはバナナの抽象的な概念です。 Banana以上の名前を持つ実装クラスが存在する可能性がある場合、抽象化は完全に無意味なので、インターフェイスを削除してクラスを使用する必要があります。より適切な名前(たとえば、LongBananaまたはAppleBanana)がある場合、Bananaをインターフェースの名前として使用しない理由はありません。したがって、I接頭辞を使用することは、役に立たない抽象化があることを意味します。これにより、コードを理解することが難しくなり、利益がなくなります。そして、厳密なOOPは常にインターフェイスに対してコーディングするため、型のIプレフィックスが表示されない唯一の場所は、コンストラクタ上になります-まったく意味のないノイズ。

これをサンプルのIParserインターフェースに適用すると、抽象化が完全に「無意味な」領域にあることがはっきりとわかります。パーサーの具体的な実装について何か特定のもの(例:JsonParserXmlParser、...)があるか、クラスを使用する必要があります。特定の実装があるか、または「デフォルト」の抽象クラスまたは拡張メソッドが必要な場合、「デフォルト実装」などのものがありません(一部の環境では、これは実際に意味があります-特にCOM)。ただし、C#では、コードベースでI-プレフィックスが既に省略されていない限り、そのままにしておきます。 class Something: ISomethingのようなコードが表示されるたびに、メンタルなメモをとってください。誰かがYAGNIをフォローし、合理的な抽象化を構築するのがあまり得意でないことを意味します。

[1]-技術的には、これはリスコフの論文では特に言及されていませんが、オリジナルのOOP論文の基礎の1つであり、リスコフの私の読書では、彼女はこれに挑戦しませんでした。それほど厳密ではない解釈(ほとんどのOOP言語で採用されている)の解釈)では、これは、置換を目的とするパブリックタイプを使用するすべてのコード(つまり、非最終/封印)がそのタイプの準拠実装。

8
Luaan

では、たとえば、これら2つは何と名付けるべきでしょうか? ParserとConcreteParser? ParserとParserImplementation?

理想的な世界では、名前の前に省略形( "I ...")を付けないでください。名前に概念を表現させるだけです。

私はどこかを読んで(そしてそれを入手することはできません)、「Dollar」クラスが必要なときにこのISomething規則が「IDollar」の概念を定義するように導くという意見を読みましたが、正しい解決策は単に「通貨」と呼ばれる概念でしょう。

言い換えれば、この慣例は、物事を命名する難しさを簡単に解消し、簡単な修正は微妙に間違っているです。


現実の世界では、コードのクライアント(「未来からのあなた」かもしれません)は、後でそれを読んで、できるだけ早く(または少しの努力で)慣れる必要があります(クライアントにあなたのコードの彼らが得ることを期待しています)。

ISomething規約では、くだらない/悪い名前が導入されています。とは言っても、コードの記述に使用されている規則と実践がもはや実際のものでない限り、クリフトは実際のクリフトではありません*)。 規約に「ISomethingを使用する」と記載されている場合は、それを使用して、他の開発者に準拠する(そしてよりよく機能する)のが最善です。


*)とにかく、この "くだらない"は正確な概念ではないので、私は言い逃れができます:)

3
utnapistim

他の回答が述べているように、インターフェース名の前にIを付けることは、.NETフレームワークのコーディングガイドラインの一部です。具象クラスは、C#およびVB.NETの一般的なインターフェイスよりも頻繁に操作されるため、命名スキームのファーストクラスであり、したがって最も単純な名前を持つ必要があります。したがって、インターフェイスのIParserおよびParserデフォルトの実装。

しかし、Javaおよび類似の言語の場合、具体的なクラスの代わりにインターフェースが優先されます。ボブおじさんは、Iを削除し、インターフェースを最もシンプルにする必要があるという点で正しいです。 names-Parser(パーサーインターフェイスなど)ですが、ParserDefaultのような役割ベースの名前ではなく、クラスにはhowパーサーを説明する名前を付ける必要がありますis 実装

public interface Parser{
}

public class RegexParser implements Parser {
    // parses using regular expressions
}

public class RecursiveParser implements Parser {
    // parses using a recursive call structure
}

public class MockParser implements Parser {
    // BSes the parsing methods so you can get your tests working
}

これは、たとえば、Set<T>HashSet<T>TreeSet<T>に名前が付けられます。

2
TheHansinator