bar*_*-md 5 java oop dynamic parameter-passing dispatch
是否有一种优雅的方法来获取具有(单个)动态调度的OO语言中具有2个参数(或更多参数)的方法的多个调度?
可能的问题示例:
这是一个受Java启发的示例。(问题与语言无关!)
// Visitable elements
abstract class Operand {
}
class Integer extends Operand {
int value;
public int getValue() {
return value;
}
}
class Matrix extends Operand {
int[][] value;
public int[][] getValue() {
return value;
}
}
// Visitors
abstract class Operator {
// Binary operator
public Operand eval(Operand a, Operand b) {
return null; // unknown operation
}
}
class Addition extends Operator {
public Operand eval(Integer a, Integer b) {
return new Integer(a.getValue() + b.getValue());
}
}
class Multiplication extends Operator {
public Operand eval(Integer a, Integer b) {
return new Integer(a.getValue() * b.getValue());
}
// you can multiply an integer with a matrix
public Operand eval(Integer a, Matrix b) {
return new Matrix();
}
}
Run Code Online (Sandbox Code Playgroud)
我有很多运算符和操作数具体类型,但仅通过它们的抽象类型来引用它们:
Operand a = new Integer()
Operand b = new Matrix();
Operand result;
Operator mul = new Multiplication();
result = mul.eval(a, b); // Here I want Multiplication::eval(Integer, Matrix) to be called
Run Code Online (Sandbox Code Playgroud)
我不确定我是否会回答你的问题,但我希望我能为讨论添加一些内容。我稍后会尝试提供一个更笼统的答案,但在本篇中我将只关注上面的示例。
问题中给出的示例的问题在于它基于算术运算,这使得它本质上很复杂,因为给定运算符的实现会根据其操作数的类型而变化。
我认为这个问题有点模糊了问题,例如我们可以花时间尝试使您的示例正常工作,而不是处理多个调度问题。
让代码正常工作的一种方法是从不同的角度思考。不是定义一个称为Operator我们可以做的抽象概念,而是认识操作数的固有性质,即它们必须提供可能影响它们的每个可能操作的实现。用面向对象的术语来说,每个操作数都包含一堆可以影响它们的操作。
因此,假设我有一个Operand这样的接口,它定义了操作数支持的每个可能的操作。请注意,我不仅为每个可能的已知操作数定义了一种方法方差,而且还为另一个未知操作数的一般情况定义了一种方法:
interface Operand {
Operand neg();
Operand add(Int that);
Operand add(Decimal that);
Operand add(Operand that);
Operand mult(Int that);
Operand mult(Decimal that);
Operand mult(Operand that);
Operand sub(Int that);
Operand sub(Decimal that);
Operand sub(Operand that);
}
Run Code Online (Sandbox Code Playgroud)
现在考虑我们有两个实现:Int和Decimal(为了简单起见,我将使用十进制而不是示例的矩阵)。
class Int implements Operand {
final int value;
Int(int value) { this.value = value; }
public Int neg(){ return new Int(-this.value); }
public Int add(Int that) { return new Int(this.value + that.value); }
public Decimal add(Decimal that) { return new Decimal(this.value + that.value); }
public Operand add(Operand that) { return that.add(this); } //!
public Int mult(Int that) { return new Int(this.value * that.value); }
public Decimal mult(Decimal that) { return new Decimal(this.value * that.value); }
public Operand mult(Operand that) { return that.mult(this); } //!
public Int sub(Int that) { return new Int(this.value - that.value); }
public Decimal sub(Decimal that) { return new Decimal(this.value - that.value); }
public Operand sub(Operand that) { return that.neg().add(this); } //!
public String toString() { return String.valueOf(this.value); }
}
class Decimal implements Operand {
final double value;
Decimal(double value) { this.value = value; }
public Decimal neg(){ return new Decimal(-this.value); }
public Decimal add(Int that) { return new Decimal(this.value + that.value); }
public Decimal add(Decimal that) { return new Decimal(this.value + that.value); }
public Operand add(Operand that) { return that.add(this); } //!
public Decimal mult(Int that) { return new Decimal(this.value * that.value); }
public Decimal mult(Decimal that) { return new Decimal(this.value * that.value); }
public Operand mult(Operand that) { return that.mult(this); } //!
public Decimal sub(Int that) { return new Decimal(this.value - that.value); }
public Decimal sub(Decimal that) { return new Decimal(this.value - that.value); }
public Operand sub(Operand that) { return that.neg().add(this); } //!
public String toString() { return String.valueOf(this.value); }
}
Run Code Online (Sandbox Code Playgroud)
然后我可以这样做:
Operand a = new Int(10);
Operand b = new Int(10);
Operand c = new Decimal(10.0);
Operand d = new Int(20);
Operand x = a.mult(b).mult(c).mult(d);
Operand y = b.mult(a);
System.out.println(x); //yields 20000.0
System.out.println(y); //yields 100
Operand m = new Int(1);
Operand n = new Int(9);
Operand q = m.sub(n);
Operand t = n.sub(m);
System.out.println(q); //yields -8
System.out.println(t); //yeilds 8
Run Code Online (Sandbox Code Playgroud)
这里的关键点是:
Operand。这是我们利用访问者调度能力的地方,因为我们知道 的确切类型this,但不知道 的确切类型that,所以我们通过做强制调度that.method(this),问题就解决了!现在你就得到了它。现在我找到了特定示例的解决方案,但是我还没有为您最初遇到的多重调度问题提供一个好的解决方案。这就是为什么我说这个例子不够好。
也许您可以提供一个更好的示例,例如维基百科页面中的Multiple Dispatch。
然而,我认为解决方案可能永远是,将问题减少为一系列单一调度,并使用某种访问者模式来解决,就像我所做的那样。如果我有时间,我会稍后尝试给出更一般的答案,而不仅仅是这个具体的例子。
但希望这篇文章有助于促进进一步的讨论,幸运的是,它朝着实际答案的方向迈出了一步。