多路分发
Number.plus(Number)
Number是各种数字对象的超类。 当声明a.plus(b)时,并不知道a,b的确切类型。
如何正确地交互?
Java只支持单路分发,如果要执行的操作包含了不止一个类型未知的对象时,
那么Java的动态绑定机制只能处理其中一个的类型。所以必须自己来判定其他的
类型,从而实现自己的动态绑定行为。
解决的办法就是多路分发,如果想使用2路分发,那么必须有2个方法调用,
第一个方法调用决定第一个未知类型,第二个方法调用决定第二个未知类型。
所以必须为每一个类型提供给一个实际的方法调用
一般而言,需要有设定好的某种配置,以便一个方法调用能够引出更多的方法
调用,从而能够在这个过程中处理多种类型。
为了达到这种效果,我们需要与多个方法一同工作: 因为每个分发都需要一个
方法调用。
下面剪刀 石头 布 游戏对应的方法是compete()和eval(),二者都是同一个类型
的成员, 可以产生三种Outcome实例中的一个作为结果。
public enum Outcome {
WIN, LOSE, DRAW
}
import java.util.Random;
import static enumerated.Outcome.*;
interface Item{
Outcome compete(Item it);
Outcome eval(Paper p);
Outcome eval(Scissors s);
Outcome eval(Rock r);
}
class Paper implements Item{
public Outcome compete(Item it) {
return it.eval(this);
}
public Outcome eval(Paper p) {
return DRAW;
}
public Outcome eval(Scissors s) {
return WIN;
}
public Outcome eval(Rock r) {
return LOSE;
}
public String toString(){
return "Paper";
}
}
class Scissors implements Item{
public Outcome compete(Item it) {
return it.eval(this);
}
public Outcome eval(Paper p) {
return LOSE;
}
public Outcome eval(Scissors s) {
return DRAW;
}
public Outcome eval(Rock r) {
return WIN;
}
public String toString(){
return "Scissors";
}
}
class Rock implements Item{
public Outcome compete(Item it) {
return it.eval(this);
}
public Outcome eval(Paper p) {
return WIN;
}
public Outcome eval(Scissors s) {
return LOSE;
}
public Outcome eval(Rock r) {
return DRAW;
}
public String toString(){
return "Rock";
}
}
public class RoShamBol {
static final int SIZE = 20;
private static Random rand = new Random(47);
public static Item newItem(){
switch(rand.nextInt(3)){
default:
case 0 : return new Scissors();
case 1 : return new Paper();
case 2 : return new Rock();
}
}
public static void match(Item a, Item b){
System.out.println(a +" vs. "+ b+" : "+ a.compete(b));
}
public static void main(String[] args) {
for(int i=0; i<SIZE; i++)
match(newItem(), newItem());
}
}
Item是这几种类型的接口, 用作多路分发。 RoshamBol.match()有2个item参数,
通关过Item.compete()方法开始2路分发,要判定a的类型,分发机制会在a的实际
类型的compete()内部起到分发作用。compete()方法通关过eval()来为另一个类型
实现第二次分发, 将自身(this)作为参数调用eval(),能够调用重载过的eval()
方法,这能够保留第一次分发的类型信息,第二次分发完成时,就能知道两个Item
对象的具体类型了。