私は最近、継承よりも構成の利点についていくつかの記事を読みました。彼らの作者は、あなたはいつでも継承を合成に置き換えることができると言いました(正確には、彼らはそれができないような状況を知らないと言っています)。しかし、私は次の場合にこれを行う方法を考えることができません:
abstract class Reader
{
public Int32 ReadSum()
{
return this.ReadValue() + this.ReadValue();
}
public Int32 ReadSub()
{
return this.ReadValue() - this.ReadValue();
}
public Int32 ReadMul()
{
return this.ReadValue() * this.ReadValue();
}
public Int32 ReadDiv()
{
return this.ReadValue() / this.ReadValue();
}
public abstract Int32 ReadValue();
}
class RandomSource: Reader
{
public Int32 ReadValue()
{
return Math.RandomInt32();
}
}
class UserSource: Reader
{
public Int32 ReadValue()
{
return Console.ReadInt32();
}
}
これはまったく可能ですか?
この例は少し工夫されていますが、ここに1つの方法があります。
あなたはここで起こっていることをしているようです-操作と読書。読み取りはReaderで行う必要があり、操作は別のクラスで実行できます。
class ReadOperations {
private Reader reader;
public Int32 ReadSum()
{
return reader.ReadValue() + reader.ReadValue();
}
public Int32 ReadSub()
{
return reader.ReadValue() - reader.ReadValue();
}
public Int32 ReadMul()
{
return reader.ReadValue() * reader.ReadValue();
}
public Int32 ReadDiv()
{
return reader.ReadValue() / reader.ReadValue();
}
}
interface Reader {
public Int32 ReadValue();
}
class RandomSource: Reader
{
public Int32 ReadValue()
{
return Math.RandomInt32();
}
}
class UserSource: Reader
{
public Int32 ReadValue()
{
return Console.ReadInt32();
}
}
これを行う方法は他にもあります。
おそらくコンストラクタでリーダーを挿入する方法を見つける必要があります。
免責事項-実用的な目的のために、コンポジションを使用する必要はありませんが、そうすることは拡張可能であり、プログラムが特定の方向に進化できると思うときはいつでも使用する必要があります。
この場合、あなたの操作はあなたが「読む」方法とは独立して進化すると思うなら。
もちろん、たとえば、外部からReader
クラスに読み取り動作を渡すことができます。
_class Reader
{
private Func<Int32> read;
public Reader(Func<Int32> read)
{
this.read = read;
}
public Int32 ReadSum()
{
return read() + read();
}
…
}
_
次に、RandomSource
に相当するものを構築するためにnew Reader(() => Math.RandomInt32())
のように言うか、UserSource
の場合はnew Reader(() => Console.ReadInt32())
と言います。
一般に、継承をコンポジションに置き換えると、UserSource
などの名義型が少なくなります。これは、それらの動作がより単純なコンポーネントのコンポジションから出現するためです。もちろん、便宜上、NewUserSource()
のような「コンストラクタ関数」を作成することもできます。
(私はC#を使用していません。このため、マイナーな言語の詳細が間違っている場合はご容赦ください。)