在泛型类的层次结构中使用访问者模式的最佳方法是什么?

时间:2016-07-28 18:00:25

标签: c# visitor

我有矩阵类的层次结构。我不想改变我的类,所以我决定使用访问者模式来包括诸如加法,乘法等矩阵运算。我的层次结构看起来像这样:

public abstract class Matrix<T> {
    public abstract T GetValue(int i, int j);
    public abstract void SetValue(int i, int j, T value);
    public abstract Matrix<T> Accept(MatrixVisitor<T> visitor, Matrix<T> matrix);
}

public class SquareMatrix<T> : Matrix<T> {}
public class DiagonalMatrix<T> : Matrix<T> {}
public class SymmetricMatrix<T> : Matrix<T> {}

所有这些类都实现了Accept方法:

public override Matrix<T> Accept(MatrixVisitor<T> visitor, Matrix<T> matrix)
        {
            return visitor.Operation(this, matrix);
        }

但是我被添加了两个类型为T的元素。我不知道类型T是否会重载运算符&#39; +&#39;。我决定使用具体访问者的参数这样的委托Func。现在我的具体访客类看起来像这样:

public class SumOfSquareMatricesVisitor<T> : MatrixVisitor<T>
    {
        public SumOfSquareMatricesVisitor(Func<T, T, T> sumOfTwoTypesOperation)
        {
            this.sumOfTwoTypesOperation = sumOfTwoTypesOperation;
        }

        public override Matrix<T> Operation(Matrix<T> A, Matrix<T> B)
        {
           // example
           // into a loop
           // result = sumOfTwoTypesOperation(A[i,j], B[i,j]);
        }
}

我真的很讨厌我的方式,因为如果我想添加或乘以方矩阵和对角矩阵,我必须创建一个访问者实例并再次定义操作。有更优雅的方式吗?谢谢。

1 个答案:

答案 0 :(得分:1)

通常,正在执行的操作由访问者的类型决定,操作数类型的操作区别由重载的访问者方法定义。这称为双重调度。在您的情况下,实现某些通用二进制矩阵操作的访问者必须如下所示:

public class SomeOperationVisitor<T> : BinaryOperationMatrixVisitor<T>
{
  public SomeOperationVisitor(Func<T, T, T> someItemOp)
  {
    this.someItemOp = someItemOp;
  }

  public override Matrix<T> Operation(SquareMatrix<T> a, SquareMatrix<T> b)
  { ... }

  public override Matrix<T> Operation(SquareMatrix<T> a, DiagonalMatrix<T> b)
  { ... }

  // other methods for all combination of element types
}

使用访问者实现二进制操作非常不寻常,因为必须为所有操作数类型组合实现重载方法。

但在您的特定情况下,如果您仅限于添加和乘法,则您不必这样做。这些操作不依赖于继承类添加到基类Matrix的约束:只要两个矩阵具有兼容的元素类型和匹配的维度,您就不必关心它们是对角线还是诸如此类的东西。因此,您只需一种方法就可以为所有类型的矩阵实现通用和访问:

public class MatrixSumVisitor<T> : BinaryOperationMatrixVisitor<T>
{
  public MatrixSumVisitor(Func<T, T, T> addOp)
  {
    this.addOp = addOp;
  }

  public override Matrix<T> Operation(Matrix<T> a, Matrix<T> b)
  {
    // 1. Check that dimensions of a and b match.
    // 2. Add a and b.
  }
}

但如果是这种情况,那么您现在根本不需要访问者来执行单一调度 - 仅限访问者类型。在这种情况下,您可以完全抛弃 Accept 方法 - 除了简单地转发呼叫之外,它不会做任何有用的事情。而是这样做:

Matrix<int> a, b;
// ...
MatrixBinaryOp op = new MatrixSum(...);
var sum = op.Operation(a, b);