クラス上のインターフェイスが多すぎますか?[閉まっている]


28

23個のインターフェイスを実装するクラスを持つことは、おそらくコードの匂いやアンチパターンであると考えるでしょう。それが本当にアンチパターンである場合、あなたはそれを何と呼びますか?それとも、単に単一責任の原則に従っていないだけですか?


3
まあ、どちらにしても疑わしいです。クラスに23のメソッドがあり、すべてが単一の責任を果たしている場合(すべてのクラスがそれほど多くのメソッドを必要とするわけではありませんが、ほとんどが他のメソッドの3行の便利なラッパーである場合は特にそうではありません)、インターフェイスがきめ細かすぎます; )

問題のクラスは一種のエンティティであり、インターフェイスはほとんどプロパティになります。これらのうち113があり、4つの方法しかありません。
ジョナスエルフストローム

6
静的にコンパイルされた言語のアヒルのタイピングには、基本的にインターフェイスが使用されると思いました。どうしたの?:)
ashes999

2
質問です。このエンティティの各インスタンスに113のプロパティがすべて設定されていますか?それとも、いくつかの種類の「共通エンティティ」がいくつかのプロパティのみが満たされた異なるコンテキストで使用されていますか?
デビッド

1
「クラス」を構成するものの概念的な理解に戻りましょう。クラスは、明確に定義された責任と役割を持つ単一のものです。この方法で23インターフェイスオブジェクトを定義できますか?クラスを適切に要約した単一の(Joycean以外の)文を投稿できますか?その場合、23個のインターフェイスで問題ありません。そうでない場合は、大きすぎて複雑すぎます。
スティーブングロス

回答:



23

私は、このアンチパターンを「すべての取引のジャック」または「Too Many Hats」と命名することを提案します。


2
スイスアーミーナイフはどうですか?
FrustratedWithFormsDesigner

この用語はすでにメソッドが多すぎるインターフェースに使用されているようです:-)それはここでも理にかなっていますが。
ジャレイン

1
@FrustratedWithFormsDesignerスイスアーミーナイフには、IValueという名前のインターフェイスに30のプロパティが含まれ、そのうち20の前に新しい修飾子キーワードが付いているという悪い味はありません。
ジョナスエルフストローム

3
多すぎる帽子の+1 ... 多頭の神
ニュートピア

1
スイスアーミーナイフは再利用のモデルです。すなわち、単一の「ブレード」が多くの方法を実行できるため、おそらく悪い例えです!
ジェームズアンダーソン

17

名前を付けなければならないなら、私はそれをハイドラと呼ぶだろうと思う:

ヒドラ

ギリシャ神話では、Lernaean Hydra(ギリシャ語:ΛερναίαὝδρα)は古代の無名の蛇のような地質の水獣でした。ペイントし、頭を切り落とすごとにさらに2つずつ成長しました。そして、毒の息は彼女の足跡さえ致命的だったので非常に強烈でした。

特に重要なのは、頭がたくさんあるだけでなく、頭がどんどん増え続け、そのために殺すことができないという事実です。それが、こうしたデザインの私の経験でした。開発者は、画面に収まらないほど多くのインターフェイスをジャムし続け、それまでにプログラムの設計と仮定に深く根付いてしまい、それを分割することは絶望的な見通しです(試してみると、実際には多くの場合、ギャップを埋めるためにより多くのインターフェースが必要になります)。

「Hydra」による差し迫った破滅の初期兆候の1つは、次のように、多くの健全性チェックなしで、多くの場合、意味のないターゲットに1つのインターフェイスを別のインターフェイスにキャストすることです。

public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
    var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
    return ((IAssembler)locator).BuildWidget(partsNeeded);
}

コンテキストから外れると、このコードについて何か怪しいものがあることは明らかですが、この発生の可能性は、開発者が常に同じクリーチャーを扱っていることを直感的に知っているため、オブジェクトが成長する「頭」が増えるにつれて上がります。

幸運は、これまでその実装23のインターフェースオブジェクト上の任意のメンテナンスを行っています。プロセスで付随的な損傷を引き起こさない可能性は、ほとんどありません。


16

神オブジェクトが頭に浮かぶ。すべてを行う方法を知っている単一のオブジェクト。これは、2つの主要な設計方法論の「凝集」要件への順守が低いことに起因しています。23個のインターフェイスを持つオブジェクトがある場合、ユーザーに対して23個の異なるものになる方法を知っているオブジェクトがあり、それらの23個の異なるものは、システムの単一のタスクまたは領域のラインに沿っていない可能性があります。

SOLIDでは、このオブジェクトの作成者は明らかにインターフェイス分離の原則に従うことを試みましたが、以前のルールに違反しています。単一責任の原則。「ソリッド」である理由があります。設計について考えるときは、Sが常に最初になり、他のすべてのルールに従います。

GRASPでは、そのようなクラスの作成者は「高凝集」ルールを無視しました。GRASPは、SOLIDとは異なり、オブジェクトには単一の責任があるわけではありませんが、せいぜい2つまたは3つの非常に密接に関連する責任を持つ必要があると教えています。


神はいかなるインターフェースにも拘束されないため、これが神オブジェクトのアンチパターンであるかどうかはわかりません。非常に多くのインターフェイスに制約されると、神の全能性が低下する可能性があります。;)たぶん、これはキメラオブジェクトですか?
FrustratedWithFormsDesigner

神には多くの顔があり、多くの人々にとって多くのものになり得ます。結合されたインターフェースの機能がオブジェクトを「すべてを知っている」ようにした場合、それは本当に神を制約しているのではないでしょうか?
キース

1
インターフェースを作成したものがそれらを変更しない限り。次に、インターフェースの変更に適合するために、神を再実装しなければならない場合があります。
FrustratedWithFormsDesigner

3
クラスを作成したのは私だと思うのが痛いです。
ジョナスエルフストローム

「あなた」の使用は、開発チームを参照して、複数形になります。そのチームの誰かが、過去または現在にかかわらず、このクラスを作成しました。ただし、編集します。
キース

8

23は単なる数字です!最もありそうなシナリオでは、それは警告するのに十分高いです。ただし、「アンチパターン」として呼び出されるタグを取得する前に、メソッド/インターフェイスの最大数は何ですか?5、10、25ですか?10が適切な場合、11も有効である可能性があるため、数値は実際には答えではないことがわかります。

本当の問題は複雑さです。また、長いコード、メソッドの数、または何らかのメジャーによるクラスの大きなサイズは、実際には複雑さの定義ではないことを指摘する必要があります。はい、コードが大きくなると(メソッドの数が増えると)、新しい初心者にとって読みやすく、把握しにくくなります。また、潜在的に多様な機能、多数の例外、およびさまざまなシナリオ向けの非常に進化したアルゴリズムも処理します。これは複雑であることを意味するものではありません-消化するのが難しいだけです。

一方、数時間で読み書きできる比較的小さなサイズのコードは依然として複雑です。ここで、コードが(不必要に)複雑であると思います。

オブジェクト指向設計のあらゆる知恵をここに入れて「複雑」を定義することができますが、「非常に多くのメソッド」が複雑さを示していることを示すためにここで制限します。

  1. 相互知識。(別名カップリング) 多くの場合、物事がクラスとして記述されるとき、私たちは皆、それが「素敵な」オブジェクト指向コードであると考えています。しかし、他のクラスに関する前提は、実際に必要なカプセル化を本質的に破ります。アルゴリズムの内部状態に関する詳細を「リーク」するメソッドがある場合-アプリケーションは、サービングクラスの内部状態に関する重要な仮定で構築されます。

  2. 繰り返しが多すぎる(侵入) 似た名前を持つが、矛盾する作業を行うメソッド、または同様の機能を持つ矛盾する名前のメソッド。多くの場合、コードはさまざまなアプリケーション用にわずかに異なるインターフェイスをサポートするように進化します。

  3. ロールが多すぎる クラスがサイド関数を追加し続け、好きなように拡張し続けると、クラスが実際に2つのクラスになったことがわかります。驚くべきことに、これらはすべて本物から始まりますこれを行うための要件と他のクラスは存在しません。これを考慮して、トランザクションの詳細を伝えるクラスTransactionがあります。これまでのところよさそうだ。今では誰かが「トランザクションの時間」(UTCなど)でフォーマット変換を必要とし、後で、人々は特定の事柄が特定の日付にあったかどうかをチェックするルールを追加してトランザクションを無効にします。-ストーリー全体を書くことはしませんが、最終的にはトランザクションクラスでカレンダー全体を構築し、その後、人々はその「カレンダーのみ」の部分を使用し始めます!これは非常に複雑です(想像してみてください)なぜ「トランザクションクラス」をインスタンス化して、「カレンダー」が提供する機能を持たせるのでしょうか。

  4. book_a_ticket()を実行すると、APIの一貫性が失われます -チケットを予約します!それは、それを実現するためにいくつのチェックとプロセスが行われるかに関係なく、非常に簡単です。現在、時間の流れがこれに影響を及ぼし始めると複雑になります。通常、「検索」と可能な/使用不可のステータスを許可してから、バックnを減らすために、チケット内にいくつかのコンテキストポインターの保存を開始し、それを参照してチケットを予約します。検索だけが機能ではありません。このような「サイド機能」が多くなると事態は悪化します。プロセスでは、book_a_ticket()の意味はbook_that_ticket()を意味します!そしてそれは想像を絶するほど複雑になることがあります。

おそらく、非常に進化したコードで見られるような多くの状況があり、多くの人がシナリオを追加できると確信しています。「非常に多くのメソッド」が意味をなさないか、明らかに考えていることをしない。これはアンチパターンです。

私の個人的な経験では、合理的にボトムアップで開始するプロジェクトでは、本来のクラスが本来あるべきものの多くが埋められたままであるか、さらに悪いことに、異なるクラスに分割されたままで、結合が増加します。ほとんどの場合、10のクラスに値すると思われますが、4つしかありませんが、それらの多くは多目的で混乱し、多数のメソッドを持っている可能性があります。それを神とドラゴンと呼んでください。これが悪いことです。

しかし、きちんと一貫性のある非常に大きなクラスに出くわします。それらには30のメソッドがありますが、それでも非常にクリーンです。彼らは良いことができます。


問題のクラスには23のインターフェイスがあり、これらには全部で113のパブリックプロパティと4つのメソッドが含まれています。それらの一部のみが読み取り専用です。C#は、私はメソッドとプロパティ間で異なる理由だと、自動実装プロパティを持つmsdn.microsoft.com/en-us/library/bb384054.aspx
ジョナスElfström

7

クラスには、正確な数のインターフェイスが必要です。これ以上でもそれ以下でもありません。

「多すぎる」と言うことは、これらのインターフェイスのすべてがそのクラスで有用な目的を果たしているかどうかを見ないと不可能です。オブジェクトがインターフェイスを実装することを宣言することは、クラスがコンパイルされることを期待する場合、そのメソッドを実装する必要があることを意味します。(私が見ているクラスはそうだと思います。)インターフェイスのすべてが実装され、それらの実装がクラスの内部に対して何かを行う場合、実装がそこにあるべきではないと言うのは難しいでしょう。私が考えることができるのは、これらの基準を満たすインターフェースがどこに属さないかということです。それは外部です:誰も使用しないときです。

一部の言語では、Javaのextendsキーワードのようなメカニズムを使用してインターフェイスを「サブクラス化」できますが、それを書いた人は誰も知らなかったかもしれません。また、23個すべてが十分に離れているため、それらを集約しても意味がありません。


この質問は言語にとらわれないものだと思いました。実際のコードはC#です。
ジョナスエルフストローム

C#はまだ何もしていませんが、同様の形式のインターフェイス継承をサポートしているようです。
Blrfl

はい、インターフェイスは他のインターフェイスを「継承」できます。
ジョナスエルフストローム

インスタンスが何をすべきかではなく、クラスが何すべきかについて多くの議論が焦点を当てていることは興味深いと思います。共通のシャーシに取り付けられたさまざまなイメージングセンサーとオーディオセンサーを備えたロボットは、位置と方向を持ち、視覚と音声を視覚、聴覚、関連付けできるエンティティとしてモデル化する必要があります。これらの機能の背後にある実際のロジックはすべて他のクラスにある可能性がありますが、ロボット自体は「前方にオブジェクトがあるかどうかを確認する」、「エリア内のノイズを聞く」、「前方にあるオブジェクトがそのように見えるかどうかを確認する」などのメソッドをサポートする必要があります検出された音を生成します。」
supercat

特定のロボットはその機能をサブシステムに特定の方法で細分化する必要があるかもしれませんが、ロボットを使用する実用的なコードは、特定のロボット内の機能がどのように細分されるかを知ったり気にしたりする必要はありません。「目」と「耳」が別々のオブジェクトとして外の世界にさらされているが、対応するセンサーが共有拡張アーム上にある場合、外部コードは「耳」に聞いて地上レベルで同時に音を聞くことができます「目」に障害物の上を見るように頼みました。
supercat 14年

1

典型的な「bean」の通常のように、プロパティごとに「getter」/「setter」のペアがあると考えられ、これらすべてのメソッドをインターフェースに昇格させました。それでは、「ビーンズを持っている」と呼ぶのはどうでしょう。または、あまりにも多くの豆のよく知られた影響の後の、より多くのラベラス「Flatulence」。


C#であり、自動実装プロパティがあります。これら23のインターフェイスには、全部で113のプロパティが含まれています。
ジョナスエルフストローム

1

さまざまな環境で汎用オブジェクトを使用すると、インターフェイスの数が増えることがよくあります。C#では、IComparable、IEqualityComparer、およびIComparerのバリアントにより、個別のセットアップで並べ替えが可能になります。そのため、すべてを実装することになります。 、さらに、複数のジェネリックを実装できます。

シナリオの例を見てみましょう。クレジットを購入できるWebショップで、他の何かを購入することができます(ストックフォトサイトはこのスキームをよく使用します)。クラス「Valuta」とクラス「Credits」が同じベースを継承する場合があります。Valutaには、「通貨」プロパティを気にせずに計算を実行できる(たとえば、ポンドにドルを追加する)演算子のオーバーロードと比較ルーチンがあります。クレジットはより単純ですが、他の明確な動作がいくつかあります。これらを相互に比較できるようにするには、IComparableとIComparableおよび比較インターフェイスの他のバリアントを両方に実装します(基本クラスまたは他の場所にあるかどうかにかかわらず、共通の実装を使用します)。

シリアル化を実装すると、ISerializable、IDeserializationCallbackが実装されます。次に、やり直し元に戻すスタックを実装します。IClonableが追加されます。IsDirty機能:IObservable、INotifyPropertyChanged。ユーザーが文字列を使用して値を編集できるようにします:IConvertable ...リストは延々と続くことができます...

現代の言語では、これらの側面を分離し、コアクラス以外の独自のクラスに配置するのに役立つ別の傾向が見られます。次に、外部クラスまたはアスペクトは、注釈(属性)を使用してターゲットクラスに関連付けられます。多くの場合、外部アスペクトクラスを多かれ少なかれ汎用的にすることが可能です。

属性(注釈)の使用はリフレクションの対象となります。1つの欠点は、軽微な(初期)パフォーマンスの低下です。(しばしば感情的な)欠点は、カプセル化などの原則を緩和する必要があることです。

常に他の解決策がありますが、すべての気の利いた解決策にはトレードオフまたはキャッチがあります。たとえば、ORMソリューションを使用するには、すべてのプロパティを仮想として宣言する必要があります。シリアル化ソリューションでは、クラスのデフォルトコンストラクターが必要になる場合があります。依存性注入を利用している場合、単一のクラスに23個のインターフェースを実装することになります。

私の目には、23のインターフェイスが定義上悪いものである必要はありません。その背後によく考えられたスキーム、またはリフレクションの使用や極端なカプセル化の信念の回避など、いくつかの原則的な決定があるかもしれません。

ジョブを切り替えるとき、または既存のアーキテクチャに基づいて構築する必要があるとき。私のアドバイスは、最初に完全に知り合うことであり、すべてをリファクタリングしようとしないでください。元の開発者(彼がまだいる場合)を聞いて、あなたが見ているものの背後にある考えやアイデアを理解してください。質問するときは、それを分解するためではなく、学ぶために行います...はい、誰もが自分の金色のハンマーを持っていますが、より多くのハンマーを集めることができれば仲間の仲間と仲良くなります。


0

「多すぎる」は主観的です:プログラミングスタイル?パフォーマンス?規格への適合?前例?単純な快適感/自信?

コードが正しく動作し、保守性の問題が発生しない限り、23が新しい標準になります。いつか「私は23のインターフェースで素晴らしい仕事をすることができます。ジョナス・エルフストロームを見てください」と言うことができました。


0

制限は23よりも小さい必要があります-5または7のように、
ただし、これらのインターフェイスが継承するインターフェイスの数や、基本クラスによって実装されるインターフェイスの数は含まれません。

(つまり、N +任意の数の継承されたインターフェース、N <= 7)

クラスがあまりにも多くのインターフェースを実装している場合、それはおそらくgod-classです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.