このシナリオで構成または継承を優先すべきですか?


11

インターフェイスを考えてみましょう:

interface IWaveGenerator
{
    SoundWave GenerateWave(double frequency, double lengthInSeconds);
}

このインターフェイスは、さまざまな形状の波を生成するいくつかのクラス(SineWaveGeneratorおよびなどSquareWaveGenerator)によって実装されます。

SoundWave生のサウンドデータではなく、音楽データに基づいてを生成するクラスを実装したいと思います。それは音の名前と拍数(秒ではない)の長さを受け取り、内部的にIWaveGenerator機能を使用してSoundWaveそれを作成します。

質問は、「をNoteGenerator含むIWaveGeneratorべきIWaveGeneratorですか、それとも実装から継承すべきですか?」

次の2つの理由から、作曲に傾倒しています。

1- 動的に任意のIWaveGeneratorものを注入できNoteGeneratorます。またNoteGenerator、、などSineNoteGeneratorSquareNoteGeneratorはなく、1つのクラスのみが必要です。

2-でNoteGenerator定義された下位レベルのインターフェースを公開する必要はありませんIWaveGenerator

しかし、私はこれに関する他の意見を聞くためにこの質問を投稿しています。おそらく考えていない点です。

ところで:私はそれがsを生成するため、概念的に言うNoteGenerator 思います。IWaveGeneratorSoundWave

回答:


14

IWaveGeneratorをNoteGeneratorに動的に挿入できます。また、SineNoteGeneratorSquareNoteGeneratorなどの代わりに、1つのNoteGeneratorクラスのみが必要です。

つまり、ここでの使用組成物には良いだろう明確なサインである、と継承ではないSineGeneratorか、SquareGeneratorまたは(より悪い)の両方。それにもかかわらIWaveGeneratorず、後者を少し変更する場合、NoteGeneratorを直接継承することは理にかなっています。

ここでの本当の問題は、おそらく次のNoteGeneratorようなメソッドを持つことは意味があることです。

SoundWave GenerateWave(string noteName, double noOfBeats, IWaveGenerator waveGenerator);

メソッドではありません

SoundWave GenerateWave(double frequency, double lengthInSeconds);

このインターフェースはあまりにも限定的だからです。IWaveGeneratorsをsを生成するオブジェクトにしたいSoundWaveが、現在あなたのインターフェースはIWaveGeneratorsを、周波数と長さのみからSoundWave s を生成するオブジェクトであると表現している。このように、このようなインターフェースをより良く設計する

interface IWaveGenerator
{
    SoundWave GenerateWave();
}

frequencyまたはlengthInSeconds、またはのようなパラメーター、またはa SineWaveGenerator、a SquareGenerator、または他の考えているジェネレーターのコンストラクターを介してまったく異なるパラメーターのセットを渡します。これによりIWaveGenerator、まったく異なる構築パラメーターで他の種類のを作成できます。周波数と2つの長さのパラメーターを必要とする矩形波ジェネレーター、またはそのようなものを追加したい場合は、少なくとも3つのパラメーターを含む三角波ジェネレーターを次に追加したい場合があります。または、NoteGeneratorコンストラクタのパラメータを持つnoteNamenoOfBeatswaveGenerator

したがって、ここでの一般的な解決策は、入力パラメーターを出力関数から分離し、出力関数のみをインターフェイスの一部にすることです。


興味深いことに、これについては考えていません。しかし、私は疑問に思います:これ(コンストラクターで「パラメーターを多態性関数に設定する」)が実際によく機能するのでしょうか?そのため、コードは実際にどのタイプを扱っているかを知る必要があるため、ポリモーフィズムが台無しになります。これが機能する例はありますか?
テルアビブコーン

2
@AvivCohn:「コードは実際にどのタイプを扱っているかを知る必要があります」-いいえ、それは誤解です。特定のタイプのジェネレーター(mybeファクトリー)を構築するコードの一部のみで、どのタイプを扱っているかを常に知る必要あります。
Doc Brown

...そしてあなたがオブジェクトの構築プロセスが多型にする必要がある場合は、「抽象工場」パターン(使用することができますen.wikipedia.org/wiki/Abstract_factory_pattern
ドク・ブラウン

これが私が選ぶソリューションです。小さくて不変のクラスがここに行く正しい方法です。
スティーブン

9

NoteGeneratorが「概念的に」IWaveGeneratorであるかどうかは関係ありません。

Liskov Substitution Principleに従って、つまり、正しいセマンティクスと正しい構文で、正確なインターフェイスを実装する予定がある場合にのみ、インターフェイスから継承する必要があります。

NoteGeneratorは構文的には同じインターフェースを持っているように聞こえますが、そのセマンティクス(この場合は、取るパラメーターの意味)は非常に異なるため、この場合の継承の使用は非常に誤解を招き、潜在的にエラーが発生しやすくなります。ここで作曲を好むのはあなたです。


実際、私はNoteGenerator実装するつもりはありませんでしたGenerateWaveが、パラメーターを異なるように解釈します。はい、それはひどい考えだと思います。NoteGeneratorは一種のウェーブジェネレーターの特殊化であり、生のサウンドデータ(たとえば、周波数ではなくノート名)の代わりに「高レベル」の入力データを取り込むことができます。すなわちsineWaveGenerator.generate(440) == noteGenerator.generate("a4")。質問、構成、または継承があります。
テルアビブコーン

高レベルと低レベルの両方のウェーブ生成クラスに適合する単一のインターフェースを考え出すことができる場合、継承は受け入れられる可能性があります。しかし、それは非常に難しいようであり、本当の利点はありません。構図は間違いなく、より自然な選択のようです。
Ixrec

@Ixrec:実際に、発電機のすべてのタイプのための単一のインターフェースを持っていることは非常に難しいことではありません、OPはおそらく低レベルの発電注入するために、使用組成物を両方を行う必要がありますし、単純インタフェースから継承する(ただし、AからNoteGenerator継承低レベルジェネレーターの実装)私の答えをご覧ください。
ドックブラウン

5

2- IWaveGeneratorで定義された低レベルインターフェイスをNoteGeneratorで公開する必要はありません。

のような音NoteGeneratorWaveGeneratorなので、インターフェースを実装すべきではありません。

構成は正しい選択です。


私が言うNoteGenerator ある概念的IWaveGeneratorそれが生成するのでSoundWave秒。
テルアビブコーン

1
それは公開する必要がない場合まあ、GenerateWave、それはありませんIWaveGenerator。しかし、それ IWaveGenerator(おそらくそれ以上?)を使用しているように聞こえます。
エリックキング

@EricKing:これはGenerateWave、質問に書かれているとおりに関数に固執しなければならない限り、正しい答えです。しかし、上記のコメントから、それはOPが本当に念頭に置いていたものではないと思います。
Doc Brown

3

構図がしっかりしている。継承を追加する場合もあります。伝える方法は、呼び出し元のコードを見ることです。NoteGeneratorを予期する既存の呼び出しコードでを使用できるようにするにはIWaveGenerator、インターフェイスを実装する必要があります。代替性の必要性を探しています。それが概念的に「is-a」波発生器であるかどうかは重要です。


その場合、すなわち構成を選択しますが、代用可能性を実現するためにその継承が依然として必要な場合、「継承」に名前が付けられます。たとえばIHasWaveGenerator、そのインターフェイスの関連メソッドGetWaveGeneratorはのインスタンスを返しますIWaveGenerator。もちろんネーミングは変更できます。(詳細を具体化しようとしています-詳細が間違っている場合はお知らせください。)
rwong

2

それがために微細であるNoteGeneratorため、また、インターフェイスを実装し、NoteGeneratorその(組成物による)参照別の内部実装を持っていますIWaveGenerator

一般的に、構成の方が、保守が容易な(読み取り可能な)コードになります。理由は、複雑なオーバーライドの複雑さがないためです。継承を使用するときに持っているクラスのマトリックスについての観察も重要です。おそらく、構成を指すコードの匂いと考えることができます。

継承は、特化またはカスタマイズしたい実装がある場合によく使用されますが、ここではそうではないようです。インターフェースを使用するだけです。


1
ノートにはビートが必要なため、NoteGenerator実装するのは問題ありませんIWaveGenerator。秒ではありません。
Tulainsコルドバ

はい、確かにインターフェイスの賢明な実装がない場合、クラスはそれを実装すべきではありません。ただし、OPは「NoteGenerator概念的IWaveGeneratorにはSoundWavesを生成するからだ」と述べており、彼は継承を検討していたので、別のインターフェイスがある場合でも、インターフェイスの実装がある可能性について精神的な寛容さを取りましたクラスのより良いインターフェースまたは署名。
エリックエイド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.