定義してみましょうSinOperator:UnaryOperator
GetValue
でvirtualを作成したと仮定すると、Node
期待する単一の子ノードを計算することによって取得されたオペランドを操作することにより、数値の Sin を計算するSinOperator
の実装を提供する必要があります。GetValue
「単一の子ノードを計算する」というこのタスクはそれぞれに冗長であるため、本質的にクラスにUnaryOperator
属し、単一の引数を受け入れて返すメソッドをオーバーライドする必要があります。UnaryOperator
SinOperator
Eval
double
double
abstract class UnaryOperator:Operator //specializes GetValue
{
//....Rest of the implementatiom
//....
public sealed override double GetValue()
{
return Eval(_childNode.GetValue());
}
protected abstract double Operate(double arg);
}
class SinOperator:UnaryOperator
{
public override double Eval(double arg)
{
return Math.Sin(arg);
}
}
ここで、SinOperator が「特殊な方法で値を取得する」というノードの目標からどのように逸脱しているかに注意してください。代わりに、その責任を「価値の評価」に変更しました。これはまったく新しい責任であり、元の継承チェーンに属していないようです。
では、どうすればいいのでしょうか?答えは合成です (UnaryOperator には単項関数を計算するアルゴリズムが「ある」と見なすことができます)。
interface UnaryFunction
{
double Eval(double arg);
}
class UnaryOperator:Operator //specializes GetValue
{
private UnaryFunction _evaluator; //For Sin this is SinFunction
//....Rest of the implementatiom
//....
public sealed override double GetValue()
{
return _evaluator.Eval(_childNode.GetValue());
}
}
class SinFuntion:UnaryFunction
{
public override double Eval(double arg)
{
return Math.Sin(arg);
}
}
そして、SinOperator を取得する方法は? Sinfunction に結び付けられた UnaryOperator の作成を一元化し、常に一貫性を保つことができるように、ファクトリを使用することをお勧めします。後で、クラスを簡単に作成できるSine
よりも高速な新しい計算方法に出くわした場合、ファクトリ プラントでこのクラスを.Math.Sin
SinFunctionFast
SinFunction
Java のような言語では、おそらくこのようになるでしょうが、C# ではデリゲートが許可されているため、デリゲートを使用して多くの関数クラスを定義することを避けることができます。(デリゲートは、SinFunction などのクラスを定義する簡単な方法です)。
これを言って、私はあなたの現在の考えに対してあなたに警告したいと思います:
var sinOperator = new UnaryOperator(
childNode,
delegate()
{
return Math.Sin(childNode.GetValue());
});
任意のデリゲートから保護するには、Math.Sin 自体をデリゲートに渡しUnaryOperator
、単項デリゲートを想定する必要があります (つまり、double 型の引数を 1 つ受け取り、double を返す)。
var sinOperator = new UnaryOperator(childNode, Math.Sin);
//In UnaryOperator Class
public override double GetValue()
{
return _OpDelegate(_childNode.GetValue());
}
この場合も、さまざまなオペレーターを作成するためにファクトリーを使用することをお勧めします。
ここでデリゲートを使用することの潜在的な欠点の 1 つは、C# ではマルチキャスト デリゲートの供給を防ぐ方法がないことですが、あなたの場合はユニキャスト デリゲートのみが必要です。また、Microsoft のガイドライン(特に の例に注意してくださいIComparable
) によると、この特定の実装ではデリゲートではなくインターフェイスを選択する必要があります。
インターフェイスに対するユニキャスト デリゲートの利点の 1 つは、わずかに高速であることです。しかし、これから考えられるパフォーマンスの向上を得るには、多くの演算子を持つ巨大な関数ツリーが必要になります。