これは、参照が機能する方法として OO 言語に最適です。抽象的なレベルでは、これを次のように処理します。
Expression
プログラム内のすべての値の基本型となる抽象クラス を作成します。何かのようなもの:
public abstract class Expression
{
List<Expression> linkedExpressions;
protected Expression lhs; // left hand side, right hand side
protected Expression rhs;
protected Expression(Expression x, Expression y)
{
List<Expression> linkedExpressions = new List<Expression>();
lhs = x;
rhs = y;
// let the expressions know that they have a expression dependant on them
lhs.NotifyExpressionLinked(this);
rhs.NotifyExpressionLinked(this);
}
private void NotifyExpressionLinked(Expression e)
{
if (e != null)
{
linkedExpressions.Add(e);
}
}
private void NotifyExpressionUnlinked(Expression e)
{
if (linkedExpressions.Contains(e)
{
linkedExpressions.Remove(e);
}
}
// this method will notify all subscribed expressions that
// one of the values they are dependant on has changed
private void NotifyExpressionChanged()
{
if (linkedExpressions.Count != 0) // if we're not a leaf node
{
foreach (Expression e in linkedExpressions)
{
e.NotifyExpressionChanged();
}
}
else Evaluate()
// once we're at a point where there are no dependant expressions
// to notify we can start evaluating
}
// if we only want to update the lhs, y will be null, and vice versa
public sealed void UpdateValues(Expression x, Expression y)
{
if (x != null)
{
lhs.NotifyExpressionUnlinked(this);
x.NotifyExpressionLinked(this);
lhs = x;
}
if (y != null)
{
rhs.NotifyExpressionUnlinked(this);
y.NotifyExpressionLinked(this);
rhs = y;
}
NotifyExpressionChanged();
}
public virtual float Evaluate()
{
throw new NotImplementedException(); // we expect child classes to implement this
}
}
必要な式の種類ごとにクラスを作成します。一番下に が表示されますLiteralExpression
。これは単なる数字です。
public class LiteralExpression : Expression
{
private float value;
public LiteralExpression(float x)
: base(null, null) { } // should not have any linked expressions
public override float Evaluate()
{
return value;
}
}
このクラスについて注意すべき点が 1 つあります。その動作方法のため、UpdateValue() を使用しないでください。代わりに、新しい LiteralExpression を作成して置き換えるだけです。
次に、必要なすべての式の子クラスを作成する必要があります (たとえば、ここでは足し算です)。
public class AdditionExpression : Expression
{
public AdditionExpression(Expression x, Expression y)
: base(x, y) { };
public override float Evaluate()
{
return lhs.Evaluate() + rhs.Evaluate();
}
}
式ごとに記述しなければならないコードはこれだけではありません。重労働はすべて抽象クラスによって処理されます。このプログラムにはいくつかの設計上の欠陥があります - 循環参照を検出せず、null
値を式に渡すことを止めることはありません (LiteralExpression の場合はこれが必要です)。修正します。
あとは、expression から継承するすべての子クラスを実装するだけです。
これを行うためのより良い方法があるかもしれませんが、オブジェクト指向の観点からは、これは、一般的な動作を実装し、そのクラスの「フレーバー」ごとに多くの小さな特定の実装を持つ汎用抽象クラスを作成する良い例です。