在通用类的层次结构中使用访问者模式的最佳方法是啥?
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);
【讨论】:
以上是关于在通用类的层次结构中使用访问者模式的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
9Bridge 桥梁模式 将类的功能层次结构与实现层结构分离 结构型设计模式