【问题标题】:What is the best way to use visitor pattern in a hierarchy of generic classes?在通用类的层次结构中使用访问者模式的最佳方法是什么?
【发布时间】:2016-07-28 18:00:24
【问题描述】:

我有矩阵类的层次结构。我不想更改我的类,所以我决定使用访问者模式来包含诸如加法、乘法等矩阵运算。我的层次结构如下所示:

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 是否会重载运算符“+”。我决定使用委托函数,例如具体访问者的参数。现在我的具体访问者类看起来像这样:

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]);
        }
}

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

【问题讨论】:

    标签: c# visitor-pattern


    【解决方案1】:

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

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 2014-11-03
      • 2014-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多