1

My starting point is the following:
- I have a method, transform, which I overloaded to behave differently depending on the type of arguments that are passed in (see transform(A a1, A a2) and transform(A a1, B b) in my example below)
- All these arguments implement the same interface, X

I would like to apply that transform method on various objects all implementing the X interface.

What I came up with was to implement transform(X x1, X x2), which checks for the instance of each object before applying the relevant variant of my transform.

Though it works, the code seems ugly and I am also concerned of the performance overhead for evaluating these various instanceof and casting. Is that transform the best I can do in Java or is there a more elegant and/or efficient way of achieving the same behavior?

Below is a trivial, working example printing out BA. I am looking for examples on how to improve that code. In my real code, I have naturally more implementations of 'transform' and none are trivial like below.

public class A implements X {
}

public class B implements X {
}

interface X {
}

public A transform(A a1, A a2) {
  System.out.print("A");
  return a2;
}

public A transform(A a1, B b) {
  System.out.print("B");
  return a1;
}

// Isn't there something better than the code below???
public X transform(X x1, X x2) {
  if ((x1 instanceof A) && (x2 instanceof A)) {
    return transform((A) x1, (A) x2);
  } else if ((x1 instanceof A) && (x2 instanceof B)) {
    return transform((A) x1, (B) x2);
  } else {
    throw new RuntimeException("Transform not implemented for "
            + x1.getClass() + "," + x2.getClass());
  }
}

@Test
public void trivial() {
  X x1 = new A();
  X x2 = new B();
  X result = transform(x1, x2);
  transform(x1, result);
}
4

3 回答 3

5

Take a look at the Visitor pattern as a starting point.

If your hierarchy changes a lot, the visitor pattern spreads the changes throughout. In that case, also look at the acyclic visitor.

The code could look like this:

public interface X {
  void accept(XVisitor v);
}

public interface XVisitor { 
  void visit(A a);
  void visit(B b);
}

public class A implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

public class B implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

And then your algorithm goes into this class:

public class XTransformerVisitor implements XVisitor {
  private X result;
  private A first;
  public void visit(A a) {
    if (first == null) first = a;
    else result = a;
  }
  public void visit(B b) {
    if (first == null) throw new RuntimeException();
    result = first;
  }
  public X transform(X x1, X x2) {
    x1.accept(this);
    x2.accept(this);
    return result;
  }
}
于 2011-01-13T02:44:53.420 回答
3

您正在寻找的术语是多重分派,它是在多个参数类型中具有多态性的虚函数的概括。大多数编程语言,包括 Java 和 C++,都不支持多分派,因此需要某种黑客技术来模拟它。一种选择是使用上面的代码,另一种选择是使用类似 this的代码。一种常见的解决方案是使用一种称为访问者模式的习语,它可以帮助抽象出复杂性。

于 2011-01-13T02:47:12.623 回答
0

经过更多研究,我遇到了反射的概念。我认为这比访问者模式要简单得多,至少可以解决这里的具体问题。

我上面的原始代码可以保持完全相同,而烦人的方法 transform(X x1, X x2) 就变成了:

public X transform(X x1, X x2) {
  Method m;
  try {
    m = getClass().getMethod("transform",
            new Class[]{x1.getClass(), x2.getClass()});
    return (X) m.invoke(this, new Object[]{x1, x2});
  } catch (Exception e) {
    throw new RuntimeException("Transform not implemented for "
        + x1.getClass() + "," + x2.getClass());
  }
}

优点:
- 摆脱了我在原始帖子中的嵌套 instanceof 测试和强制转换
- 无需实现所有操作数必须实现的接受方法,该方法由双调度/访问者模式方法带来

于 2011-01-20T04:00:56.057 回答