web-dev-qa-db-ja.com

このシナリオでは、合成または継承を優先する必要がありますか?

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

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

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

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

問題は、NoteGeneratorIWaveGeneratorを含める必要があるか、それともIWaveGenerator実装から継承する必要があるかです。

私は2つの理由から作曲に傾いています:

1- IWaveGeneratorNoteGeneratorを動的に注入することができます。また、NoteGeneratorSineNoteGeneratorなどではなく、SquareNoteGeneratorクラスが1つだけ必要です。

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

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

ところで:NoteGeneratorisIWaveGeneratorsを生成するので、概念的にはSoundWaveと言います。

11
Aviv Cohn

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

これは明確な兆候であり、ここではコンポジションを使用する方が適切であり、SineGeneratorまたはSquareGeneratorまたは(さらに悪い)両方から継承しないでください。それでも、後者を少し変更する場合は、IWaveGeneratorからNoteGeneratorを直接継承するのが理にかなっています。

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

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

メソッドではなく

SoundWave GenerateWave(double frequency, double lengthInSeconds);

このインターフェースは具体的すぎるためです。 IWaveGeneratorsをSoundWavesを生成するオブジェクトにする必要がありますが、現在のインターフェイスはIWaveGeneratorsを表現してSoundWavesを生成するオブジェクトです周波数と長さのみから。このようにこのようなインターフェースをより良く設計する

interface IWaveGenerator
{
    SoundWave GenerateWave();
}

frequencylengthInSecondsなどのパラメーター、またはSineWaveGeneratorSquareGenerator、またはその他のジェネレーターのコンストラクターを介して完全に異なるパラメーターのセットを渡す思っている。これにより、完全に異なる構築パラメーターを使用して、他の種類のIWaveGeneratorsを作成できます。周波数と2つの長さパラメータを必要とする矩形波ジェネレータなどを追加したい場合もあれば、次に3つ以上のパラメータを持つ三角波ジェネレータを追加したい場合もあります。または、コンストラクタパラメータNoteGeneratornoteName、およびnoOfBeatsを使用したwaveGenerator

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

14
Doc Brown

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

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

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

9
Ixrec

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

NoteGeneratorのようなサウンドはWaveGeneratorではないため、インターフェースを実装しないでください。

構成は正しい選択です。

5
Eric King

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

3
Karl Bielefeldt

NoteGeneratorがインターフェイスを実装し、NoteGeneratorが別のIWaveGeneratorを(構成によって)参照する内部実装を持つことは問題ありません。

一般的に、構成することでコードの保守性が向上します(つまり、コードが読みやすくなります)。理由を説明するためのオーバーライドの複雑さはありません。継承を使用するときに持つクラスのマトリックスについてのあなたの観察も適切であり、おそらく合成を指すコード臭いと考えることができます。

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

2
Erik Eidt