让我们尝试定义SinOperator:UnaryOperator
假设你已经做了GetValue
virtual on Node
,SinOperator
应该只提供一个实现,GetValue
它通过对通过计算它期望的单个子节点获得的操作数进行操作来计算一个数字的 Sin。
“计算单个子节点”的任务对于每个节点都是多余的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
应该期望一元委托(即接受双精度类型的单个参数并返回双精度)。
var sinOperator = new UnaryOperator(childNode, Math.Sin);
//In UnaryOperator Class
public override double GetValue()
{
return _OpDelegate(_childNode.GetValue());
}
在这种情况下,也建议使用工厂来创建不同的操作符。
在这里使用委托的一个潜在缺点是,在 C# 中,无法阻止提供多播委托,而在您的情况下,您只需要单播委托。此外,根据Microsoft 的指南(特别注意 的示例IComparable
),您应该为此特定实现选择接口而不是委托。
单播委托相对于接口的一个优势是它们只是快了一个档次。但是为了从中获得任何可以想象的性能增益,您将需要一个包含大量运算符的巨大函数树。