web-dev-qa-db-ja.com

クラスのインターフェースが多すぎますか?

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

28
Jonas Elfström

王室:彼らは特に奇抜なことは何もしませんが、10億の称号を持ち、他のほとんどの王室と何らかの関係があります。

36
World Engineer

私はこのアンチパターンがジャックオブオールトレードズ、あるいは多すぎる帽子と名付けられたと私は考えています。

23
Mike Partridge

名前を付ける必要がある場合は、 Hydra と呼びます。

Hydra

ギリシャ神話では、Lernaean Hydra(ギリシャ語:ΛερναίαὝδρα)は古代の無名の蛇のようなChthonic Water Beastであり、爬虫類の特徴を持ち(その名前の証拠として)、多くの頭を持ちました-詩人は花瓶の画家が扱えるよりも多くの頭について言及していますペンキを塗ると、頭を切り落とすごとにさらに2つ成長し、毒の息が彼女の足跡さえも致命的だったので、非常に有毒でした。

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

"Hydra"によるDoomの差し迫った初期兆候の1つは、多くの健全性チェックなしに、あるインターフェースを別のインターフェースに頻繁にキャストすることです。

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

コンテキストから外れると、このコードに何か怪しげなことがあるのは明らかですが、開発者が直感的に知っている常に対処しているため、オブジェクトが大きくなるにつれて、これが発生する可能性が高くなります。同じ生き物で。

頑張ってくださいever 23のインターフェースを実装するオブジェクトのメンテナンスを行います。あなたの可能性notプロセスで付随的な損傷を引き起こす可能性はごくわずかです。

17
Aaronaught

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

SOLIDでは、このオブジェクトの作成者は明らかにインターフェース分離の原則を守ろうとしましたが、以前のルールに違反しています。単一の責任原則。それが「固体」である理由があります。デザインについて考えるときは常にSが最初に来て、他のすべてのルールに従います。

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

16
KeithS

23はただの数です!最も可能性の高いシナリオでは、警報を出すのに十分な高さです。しかし、尋ねると、「アンチパターン」として呼び出されるタグを取得する前に、メソッド/インターフェースの最大数はいくつですか? 5、10、25ですか? 10が良い場合、11も-になり、その後は任意の整数になるため、数値は実際には答えではないことがわかります。

本当の問題は複雑さです。そして、長いコード、メソッドの数、またはクラスの大きなサイズは、実際には[〜#〜]ではない[〜#〜]複雑さの定義。はい、コードがどんどん大きくなる(メソッドの数が増える)と、新しい初心者のために読み、把握することが難しくなります。また、さまざまな可能性のある機能、多数の例外、さまざまなシナリオに対応する非常に進化したアルゴリズムも処理します。これは、それが複雑であることを意味するのではなく、消化するのが難しいだけです。

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

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

  1. 相互知識。(a.k.aカップリング)物事がクラスとして書かれているとき、私たちは皆、それを "Nice"オブジェクト指向コードであると考えています。しかし、他のクラスについての仮定は、本質的に実際に必要なカプセル化を壊します。アルゴリズムの内部状態に関する詳細を「漏らす」メソッドがあり、アプリケーションが、提供クラスの内部状態に関する主要な仮定を使用して構築されている場合。

  2. Too many repeats(breaking in)メソッドが類似した名前を持っているが、矛盾する作業を行う場合-または類似した機能を持つ矛盾した名前。多くの場合、コードは進化して、アプリケーションごとにわずかに異なるインターフェースをサポートします。

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

  4. (In)consistency of API私が行う場合book_a_ticket()-チケットを予約します!これは、それを実現するためにいくつのチェックと処理を行うかに関係なく、非常に簡単です。時間の流れがこれに影響を及ぼし始めると、複雑になります。通常、「検索」と可能な/利用不可のステータスを許可してから、前後に削減するには、チケット内にいくつかのコンテキストポインタを保存し始めますreferチケットを予約すること。機能は検索だけではありません。このような「サイド機能」が多くなると、事態は悪化します。その過程で、book_a_ticket()の意味は、book_that_ticket()を意味します!そしてthatは想像を絶するほど複雑になる可能性があります。

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

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

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

8
Dipan Mehta

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

「多すぎる」と言うことは、それらのインターフェースのすべてがそのクラスで有用な目的を果たしているかどうかを見なければ、不可能です。オブジェクトがインターフェイスを実装することを宣言すると、クラスのコンパイルが予想される場合は、そのメソッドを実装する必要があります。 (私はあなたが見ているクラスがそうであると思います。)インターフェースのすべてが実装され、それらの実装がクラスの内部に関連して何かをする場合、実装はそこにあるべきではないと言うのは難しいでしょう。これらの条件を満たすインターフェイスが属していないと考えることができる唯一のケースは外部です。つまり、誰もそれを使用しない場合です。

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

7
Blrfl

汎用オブジェクトがさまざまな環境で使用されると、インターフェイスの数が増えることがよくあります。 C#では、IComparable、IEqualityComparer、IComparerのバリアントを使用すると、個別のセットアップで並べ替えることができるため、それらすべてを実装することになる場合があります。ジェネリックな厳密に型指定されたバージョンと非ジェネリックバージョンを実装できるため、そのうちのいくつかは複数回実行される場合がありますさらに、複数のジェネリックを実装できます。

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

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

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

属性(注釈)の使用は反映されます。 1つの欠点は、マイナーな(初期)パフォーマンスの損失です。 (感情的なことが多い)欠点は、カプセル化などの原則を緩和する必要があることです。

常に他のソリューションがありますが、すべての気の利いたソリューションには、トレードオフまたは問題があります。たとえば、ORMソリューションを使用すると、すべてのプロパティを仮想として宣言する必要がある場合があります。シリアライゼーションソリューションは、クラスのデフォルトコンストラクターを要求する場合があります。依存性注入を利用している場合、単一のクラスに23のインターフェースを実装することになるかもしれません。

私の目には、23のインターフェイスは当然のことながら悪いわけではありません。その背後には、よく考えられた計画、または反射や極端なカプセル化の信念の使用を回避するなどのいくつかの基本的な決定があるかもしれません。

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

1
Louis Somers

典型的な「bean」では一般的であるように、プロパティごとに「getter」/「setter」のペアがあると思われるように聞こえ、これらすべてのメソッドをインターフェースにプロモートしました。では、「beansを含む」と呼ぶのはどうでしょう。または、あまりにも多くの豆のよく知られている影響の後のより多くのラベライシアンの「フラトゥランス」。

1
James Anderson

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

コードが正しく動作し、保守性の問題が発生しない限り、23が新しい標準になる可能性さえあります。いつの日か、「23のインターフェースで優れた作業を行うことができます。JonasElfströmを参照してください」と言うことができます。

0
Kris

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

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

クラスが実装するインターフェースが多すぎる場合、それはおそらく god-class です。

0
Danny Varod