在通用类的层次结构中使用访问者模式的最佳方法是啥?

Posted

技术标签:

【中文标题】在通用类的层次结构中使用访问者模式的最佳方法是啥?【英文标题】: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]);
        

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

【问题讨论】:

【参考方案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);

【讨论】:

以上是关于在通用类的层次结构中使用访问者模式的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

(设计模式)组合模式

设计模式---行为变化模式之访问器模式(Visitor)

9Bridge 桥梁模式 将类的功能层次结构与实现层结构分离 结构型设计模式

设计模式-桥接模式

在通用 DAO 中使用 Hibernate 调用存储过程的最佳方法是啥?

这种分层类结构的合适设计模式是啥?