我如何设计一个类以两种不同的方式进行扩展



我刚开始OOP,我有一个问题是如何在这种情况下正确设计我的类:

想象一下,我想创建一个代数库。我将任何表达式表示为树,每个节点要么是叶变量或常量,要么是父运算,如+或^。

我还希望这个表达式能够以多种不同的方式进行评估。我想允许使用不同的方法将表达式求值为float、int或rational等,因为每种求值方法都有不同的优点和缺点。

这意味着我有两种不同的方式,我的库的潜在用户可能想要扩展我的表达式类——要么添加一个新的节点类型(即一个新操作(,要么添加一种新的求值方法。

为了允许添加新的节点类型,我将使用继承-我可以使用一个抽象基类来表示一个表达式,然后库的用户可以简单地覆盖这个类来创建一个新的操作:

abstract class Expression
{
public abstract float Evaluate();
}
class Addition : Expression
{
Expression a;
Expression b;
public override float Evaluate()
{
return a.Evaluate() + b.Evaluate();
}
}
etc...

但是为了允许添加新的评估方法,我将使用一个单独的评估类将评估方法从Expression类中移出:

abstract class Expression
{
public abstract float Evaluate();
}
class Addition : Expression
{
Expression a;
Expression b;
}
abstract class ExpressionEvaluator<T>
{
public abstract T Evaluate(Expression e);
}
class FloatExpressionEvaluator : ExpressionEvaluator<float>
{
public override float Evaluate(Expression e)
{ 
switch (e)
{
case Addition addition:
return Evaluate(addition.a) + Evaluate(addition.b);
etc...
}
}
}

但是这种方法不允许添加额外的操作。它还感觉很容易出错,因为在编译时没有检查所有类在evaluate方法中是否都有case。

有没有任何方法可以让Expression类在两个方向上都可以扩展,并在编译时检查所有表达式是否可以在所有求值方法中使用?

有很多方法,但由于您想要一种通用方法,我建议您使用MiscUtil,它是一个具有通用运算符(Add、Subtract..等(的通用库。您可以在代码中使用它,它会为您简化操作。

对于运算符,不需要为每个运算符创建一个类,因为它们的功能是一对一的。例如,Addition类将只包含加法运算符(a+b(。它没有复杂的数学运算。所以,我建议对这些运算符使用method。并专注于主要课程。主类可以根据需要包含许多运算符。有些很简单(如加法、减法等(,有些可能很复杂。对于简单的运算符,请保持简单,而对于复杂的运算符,您可能需要为它们实现一些类来简化它们。

对于实现设计本身,我认为Fluent API在您的情况下会更有益。所以,想象一下库的用法是这样的:

// would result 30 (as double).
var result = 
new ExpressionOperator<double>(15.5)
.Add(15)
.Subtract(0.5)
.Evaluate();

这将像这样实现(使用MiscUtil-lib(:

public sealed class ExpressionOperator<T> where T : struct
{
private T _result;
public ExpressionOperator(T a)
{
_result = a;
}
public ExpressionOperator<T> Add(T b)
{
_result = Operator.Add(_result, b);
return this;
}
public ExpressionOperator<T> Subtract(T b)
{
_result = Operator.Subtract(_result , b);
return this;
}
public T Evaluate() => _result;
public static explicit operator T(ExpressionOperator<T> exp) => exp._result; 
}

然后,这取决于你将如何使用它并扩展它以满足你的需求。

最新更新