命名の問題:「ISomething」の名前を「Something」に変更する必要がありますか?[閉まっている]


68

Clean Codeの名前に関するボブおじさんの章では、主にハンガリーの表記法に関して、名前のエンコーディングを避けることを推奨しています。またI、インターフェイスからプレフィックスを削除することについて具体的に言及していますが、この例は示していません。

以下を想定してみましょう:

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

それで、例えば、これらの2つは何と命名されるべきでしょうか?ParserそしてConcreteParserParserそしてParserImplementation

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

public class Parser : IParser {
     // Implementations
}

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


30
だからといって、宗教に劣ることはありません。あなただけの誰かXがYで言う、理由のためではない見なきゃ
マイクDunlavey

35
@MikeDunlaveyそして、それが質問の理由です!
ビンコヴサロビッチ

14
コードの既存の本体がある場合(書き換えるつもりはない)、それが使用する規則に固執します。新しいコードを作成する場合は、そのコードに取り組んでいる人の大部分が/で最も幸せなものを使用します。上記のどちらも当てはまらない場合にのみ、哲学的な質問になります。
-TripeHound

9
@TripeHoundマジョリティルールが常に最適なオプションとは限りません。ある理由が他の理由よりも優れている場合、1人の人間がチームの残りのメンバーにそれらの理由に基づいてケースを作成するよう説得することができます。物事を評価することなく盲目的に多数派に提出するだけで、停滞につながります。この具体的なケースでは、Isを削除する正当な理由はありませんが、それはそもそも尋ねたことを無効にしないという答えからわかります。
ビンコヴサロビッチ

35
「おじさんのおじさん」の本は、C#ではなくJAVAに焦点を当てています。ほとんどのパラダイムはC#と同じですが、いくつかの命名規則が異なります(JavaのlowerCamelCase関数名も見てください)。C#で記述する場合は、C#の命名規則を使用してください。しかし、とにかく、彼の命名の章の主要なポイントに従ってください:アイテムの意味を伝える読みやすい名前!
ベルンハルトヒラー

回答:


191

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


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

9
「一般的には、避けるべきです」という根拠はありません。Javaでは、私は避けるべきです。C#ではこれを採用する必要があります。他の言語は独自の規則に従います。
基本的な

4
「一般的な用語で」の原則は単純です。クライアントは、インターフェースと実装のどちらと通信しているかを知らない権利を持つべきです。両方の言語でこれが間違っていました。C#の命名規則が間違っていました。Javaがバイトコードを間違えました。Javaで同じ名前のインターフェイスに実装を切り替えると、名前とメソッドが変更されていなくても、すべてのクライアントを再コンパイルする必要があります。それは「毒を摘む」ことではありません。私たちはちょうどそれを間違っていました。新しい言語を作成する場合は、こちらから学んでください。
candied_orange

39

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

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. リファクタリングはより困難です。現在何かがクラスであり、インターフェースを導入する必要があることが後でわかる場合は、タイプが具象クラス、抽象クラス、またはインターフェースであるかどうかをクライアントに対して透過的にする必要があるため、そのインターフェースに同じ名前を付ける必要があります。何かがそれを壊します。

59
C#が提供する確立された標準に従わないのはなぜですか?これは、あなたをフォローしているすべてのC#プログラマーを混乱させ、いらいらさせるコストを超えるメリットをもたらしますか?
ロバートハーベイ

6
@wallenbornそれは問題ありませんが、現実的には、インターフェイスの正当な実装は1つしかありません。なぜなら、単体テストでモック可能性のためにインターフェイスを使用するのが一般的だからです。私は入れたくないDefaultか、RealまたはImpl私のクラス名の多くに
ベン・アーロンソン

4
私は同意しますが、この道を旅するなら、C#のために作られたすべてのリンターと戦わなければなりません。
ラバーダック

3
インターフェイスを実装するインスタンスの大部分がそのクラスになる場合、インターフェイスと同じ名前のインスタンス化可能なクラスを使用しても問題はありません。たとえば、多くのコードには、単純な汎用実装が必要であり、IList<T>内部にどのように格納されているかはあまり気にしません。をただI使用して、使用可能なクラス名を持たせることができるのは、の通常の実装がList<T>必要なコードを使用する必要があることを(Javaのように)魔法のように知るよりも優れているようArrayList<T>です。
-supercat

2
@ArtBいくつかの理由。1つは、規則がのようなクラスの短いマーカーである言語はありませんCFoo : Foo。そのため、そのオプションは実際には利用できません。2つ目は、同じインターフェイスの他の実装あるかどうかに応じて、マーカーを持つクラスと持たないクラスがあるため、奇妙な矛盾が発生します。CFoo : FooしかしSqlStore : StoreImplこれは、私が抱えている問題の1つです。これは、本質的に単なるいマーカーです。たぶん、常に a C(または何でも)を追加するという慣習のある言語があった場合、それはI
Ben Aaronson

27

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

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

...これが望ましい...

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

IMO


7
Javaがクラスの継承とインターフェースの実装に異なるキーワード、つまりinheritsvs を使用することで、この問題をきちんと回避していることは注目に値しますimplements
コンラッドルドルフ

6
@KonradRudolphという意味extendsです。
ハーミングモニカを停止

2
@Andy付加価値は、ハンガリー記法の問題を回避できるが、この回答で宣伝されているすべての明快さを保持できることです。
コンラッドルドルフ

2
混乱はないはずです。クラスを継承する場合、実装されたインターフェースのリストの前に、リストの最初になければなりません。これはコンパイラーによって強制されると確信しています。
アンディ

1
@Andy クラスを継承する場合は...、それはリストの最初のでなければならない ...素晴らしいことだが、あなたは、インタフェースのカップルや基底クラスとインタフェースを扱った場合の接頭辞なしで、あなたが知っているだろう
ロビーディー

15

ダメダメダメ。

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

それらはコードベースの機能です。あなたの店のはるかに少ない範囲に。言い換えれば、コードベースの最初のものが標準を設定します。

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

I接頭辞が表示されない場合に新しいコードベースを読み取るときに、言語に関係なく問題ありません。すべてのインターフェイスで表示される場合は問題ありません。時々見かけたら、誰かが支払います。

あなたのコードベースにこの前例を設定するという希少なコンテキストに自分自身を見つけたなら、私はあなたにこれを考慮することを勧めます:

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

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

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

たぶんそれはあなたにとって問題ではないでしょう。そうでないかもしれない。もしそうなら、それは気にする正当な理由です。しかし、机のおもちゃの愛のために、私たちは盲目的にこれを行うべきだと主張しないでください。正当な理由があるか気にしないかのどちらかです。

それと永遠に生きることではありません。どこでも「問題」を修正する準備ができていない限り、コードベースが特定のメンタリティに適合していないことを私にばかげたことはありません。正当な理由がない限り、私は均一性への抵抗が最も少ない道を進むべきではありません。私にはもっと良いことがあります。


6
一貫性が重要ですが、静的に型付けされた言語と最新のリファクタリングツールでは、名前を変更するのは非常に簡単で安全です。したがって、最初の開発者がネーミングの選択を間違えたとしても、それを永遠に生きる必要はありません。ただし、独自のコードベースで名前を変更することはできますが、フレームワークまたはサードパーティのライブラリで名前を変更することはできません。I-prefixは(好きかどうかにかかわらず).netの世界で確立された慣習であるため、従わないよりも慣習に従う方が良いです。
ジャックB

C#を使用している場合は、それらが表示されますI。コードがそれらを使用している場合、どこにでも表示されます。コードがそれらを使用していない場合は、フレームワークコードからそれらが表示され、正確に不要なミックスにつながります。
セバスチャンレッド

8

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

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

C#では、クラスを取得するとき(名前がで始まらないI)、それが目的のクラスであるという主な仮定です。念のため、これは100%近い精度ではありません-典型的な反例はStream(実際にはインターフェイスまたはいくつかのインターフェイスであるはずの)クラスであり、誰もが他の言語からの独自のガイドラインと背景を持っています。かなり広く使われているような他の例外もありますBase抽象クラスを意味する接尾辞は-ちょうどインタフェースと同じように、あなたが知っているタイプが多型であることを想定しています。

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

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

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

IBananaバナナの抽象的な概念です。実装クラスの名前がの場合よりも優れている可能性がある場合Banana、抽象化はまったく意味がないため、インターフェイスを削除してクラスを使用する必要があります。より良い名前(たとえば、LongBananaまたはAppleBanana)がある場合Banana、インターフェイスの名前として使用しない理由はありません。したがって、Iプレフィックスを使用することは、無駄な抽象化があることを意味します。これにより、コードが理解しにくくなり、メリットがありません。また、厳密なOOPを使用すると、常にインターフェイスに対してコードIが作成されるため、型のプレフィックスが表示されない唯一の場所は、コンストラクターになります-非常に無意味なノイズです。

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

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


3
Nitpick:LSPは、すべてのタイプが非ファイナルであるべきだとは言っていません。それは、最終でない型について、消費者はサブクラスを透過的に使用できる必要があると言っています。—もちろん、Javaにもfinal型があります。
コンラッドルドルフ

@KonradRudolphこれに関する脚注を追加しました。フィードバックをありがとう:)
ルアーン

4

それで、例えば、これらの2つは何と命名されるべきでしょうか?パーサーとコンクリートパーサー?パーサーとParserImplementation?

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

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

言い換えれば、規約は物事の命名の難しさにイージーアウトを与え、イージーアウトは微妙に間違っています。


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

ISomething規約では、悪名/悪名を紹介しています。とは言っても、コードを書く際に使用されている慣習や慣行がもはや実際的でない限り、クラフは実際のクラフではありません*)。規約に「ISomethingを使用する」と書かれている場合は、他の開発者に適合させる(そしてより良く動作させる)ために使用するのが最善です。


*)「cruft」はとにかく正確な概念ではないため、このことを言っても大丈夫です:)


2

他の回答が言及しているように、インターフェイス名の接頭辞Iは.NETフレームワークのコーディングガイドラインの一部です。具象クラスはC#やVB.NETのジェネリックインターフェイスよりも頻繁にやり取りされるため、命名スキームのファーストクラスであり、したがって、IParserインターフェイスとParserデフォルト実装の最も単純な名前を使用する必要があります。

しかし、Javaや同様の言語の場合、具体的なクラスの代わりにインターフェイスが優先されるため、ボブおじさんはI削除する必要があり、インターフェイスは最も簡単な名前(Parserたとえば、パーサーインターフェイス)を持つ必要があります。ただしParserDefault、のようなロールベースの名前の代わりに、クラスにはパーサーの実装方法を説明する名前が必要です。

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>命名されています。


5
インターフェイスはC#でも優先されます。誰があなたに、ドットネットで具体的なタイプを使用することが望ましいと言ったでしょうか?
ラバーダック

@RubberDuckそれは、言語があなたに課すいくつかの仮定に隠されています。例えば、メンバーはsealedデフォルトであり、明示的に作成する必要があるという事実virtual。仮想であることは、C#の特殊なケースです。コードを書くに推奨されるアプローチは、長年にわたって変更されてもちろん、過去の多くの遺物は、互換性のために滞在するのに必要な:)キーポイントは、あなたが(で始まるC#でインターフェースを取得する場合ということですI)、あなたが知っている、それは完全です抽象型; 非インターフェイスを取得する場合、典型的な仮定は、それがあなたが望むタイプであるということです、LSPはとんでもないです。
ルアーン

1
メンバーはデフォルトで封印されているため、継承者が@Luaanを乗り越えることができるかどうかが明確になっています。確かに、これはPITAである場合がありますが、具体的な型の好みとは関係ありません。Virtualは確かにC#の特殊なケースではありません。アマチュアが書いたC#コードベースで作業するという不幸があったようです。ごめんなさい、それはあなたの言語での経験でした。開発者は言語ではなく、その逆も同様であることを忘れないでください。
ラバーダック

@RubberDuckああ、私はそれが好きではないと言ったことはありません。ほとんどの人は間接参照を理解していないので、私はそれを非常に好みます。プログラマーの間でも。私の意見では、デフォルトで封印されている優れています。そして、私がC#で始めたとき、誰もが.NETチームを含むアマチュアでした:)「実際のOOP」では、すべてが仮想であると想定されています-すべてのタイプは派生タイプで置き換え可能である必要があります。 。しかし、「本当のOOP」は専門家向けに設計されたものであり、電球の交換からプログラミングに切り替えた人ではなく、より多くのお金で仕事が減るからです:)
Luaan

いいえ...いいえ。それは「本当のOOP」の仕組みではありません。Javaでは、オーバーライドするべきではないメソッドを封印するために時間をかける必要があります。LSPは、必要なものをオーバーライドできるという意味ではありません。LSPは、あるクラスを別のクラスに安全に置き換えることができることを意味します。SMH
RubberDuck

-8

このI =インターフェイスの規則が(新しい)規則を破るという点で正しい

昔は、すべてをその型intCounter boolFlagなどでプレフィックスしていました。

プレフィックスなしで名前を付けたいが、「ConcreteParser」も避けたい場合は、名前空間を使用できます

namespace myapp.Interfaces
{
    public interface Parser {...
}


namespace myapp.Parsers
{
    public class Parser : myapp.Interfaces.Parser {...
}

11
新着?古い?待って、プログラミングは誇大広告よりも合理性のほうが重要だと思った。それとも昔はこれでしたか?

3
いいえそのすべて彼らは「可読性を向上させる」方法についての規則を構成するとブログについて
ユアン

8
ハンガリー語の表記法を考えていると思います。この表記法では、実際に名前の前にタイプを付けました。でも、intCounterやのようなものを書く時間はありませんboolFlagでした。それらは間違った「タイプ」です。代わりにpszNamecchName(名前を含むNUL終了文字列へのポインタ)、(文字列「名前」の文字数)、xControl(コントロールのx座標)、fVisible(何かが見えることを示すフラグ)、pFile(ファイル)、hWindow(ウィンドウへのハンドル)など。
コーディグレー

2
これにはまだ多くの価値があります。に追加xControlしても、コンパイラはエラーをキャッチしませんcchName。これらは両方ともtype intですが、セマンティクスが非常に異なります。接頭辞は、これらのセマンティクスを人間に明らかにします。NUL終了文字列へのポインタは、コンパイラへのPascalスタイル文字列へのポインタとまったく同じように見えます。同様に、Iインターフェイスのプレフィックスは、インターフェイスが実際には単なるクラスであるC ++言語で出現しました(実際、言語レベルでクラスやインターフェイスのようなものが存在しないCの場合、それはすべて単なる規約です)。
コーディグレー

4
@CodyGray Joel Spolskyは、ハンガリー記法とそれが(誤って)理解されている方法について、間違ったコードを間違ったように見せるという素晴らしい記事を書きました。彼は同様の意見を持っています:プレフィックスは、生データストレージタイプではなく、より高いレベルのタイプでなければなりません。彼は「親切」という言葉を使って、「シモンィは誤って自分の論文で言葉の種類を使っていたので、意図的に種類の言葉を使っています。何世代ものプログラマーが彼の意味を誤解した」
ジョシュアテイラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.