这是错误的设计模式。与其使用一种通用方法和一个 if 阶梯,不如使用重载。重载消除了对 if 阶梯的需要,编译器可以确保调用正确的方法,而不必等到运行时。
例如。
public class BallUserInterfaceFactory {
public static BallUserInterface<Baseball> getUserInterface(
Baseball ball) {
return new BallUserInterface<Baseball>(ball);
}
public static BallUserInterface<Football> getUserInterface(
Football ball) {
return new BallUserInterface<Football>(ball);
}
}
这样,如果您的代码无法BallUserInterface
为适当的球创建一个,您还可以获得编译时错误的额外好处。
为了避免 if 阶梯,您可以使用一种称为双重调度的技术。本质上,我们使用实例知道它属于哪个类并为我们调用适当的工厂方法这一事实。为此,Ball
需要有一个返回适当BallInterface
.
您可以使方法抽象或提供引发异常或返回 null 的默认实现。Ball 和 Baseball 现在应该看起来像:
public abstract class Ball<T extends Ball<T>> {
abstract BallUserInterface<T> getBallUserInterface();
}
.
public class Baseball extends Ball<Baseball> {
@Override
BallUserInterface<Baseball> getBallUserInterface() {
return BallUserInterfaceFactory.getUserInterface(this);
}
}
为了使事情更整洁,最好将getBallUserInterface
包设为私有并在BallUserInterfaceFactory
. 然后,工厂可以管理额外的检查,例如 null 和任何抛出的异常。例如。
public class BallUserInterfaceFactory {
public static BallUserInterface<Baseball> getUserInterface(
Baseball ball) {
return new BallUserInterface<Baseball>(ball);
}
public static <T extends Ball<T>> BallUserInterface<T> getUserInterface(
T ball) {
return ball.getBallUserInterface();
}
}
访客模式
正如评论中所指出的,上述的一个问题是它要求Ball
类了解 UI,这是非常不可取的。但是,您可以使用访问者模式,这使您能够使用双重调度,但也可以将各种Ball
类和 UI 解耦。
首先,必要的访问者类和工厂函数:
public interface Visitor<T> {
public T visit(Baseball ball);
public T visit(Football ball);
}
public class BallUserInterfaceVisitor implements Visitor<BallUserInterface<? extends Ball>> {
@Override
public BallUserInterface<Baseball> visit(Baseball ball) {
// Since we now know the ball type, we can call the appropriate factory function
return BallUserInterfaceFactory.getUserInterface(ball);
}
@Override
public BallUserInterface<Football> visit(Football ball) {
return BallUserInterfaceFactory.getUserInterface(ball);
}
}
public class BallUserInterfaceFactory {
public static BallUserInterface<? extends Ball> getUserInterface(Ball ball) {
return ball.accept(new BallUserInterfaceVisitor());
}
// other factory functions for when concrete ball type is known
}
您会注意到访问者和工厂函数必须使用通配符。这是类型安全所必需的。由于您不知道传递的是什么类型的球,因此该方法无法确定返回的是什么 UI(除了它是一个球 UI)。
其次,您需要accept
在Ball
接受Visitor
. 访问者模式的每个具体实现也Ball
必须实现此方法才能正常工作。实现看起来完全一样,但是类型系统确保了适当方法的分派。
public interface Ball {
public <T> T accept(Visitor<T> visitor);
}
public class Baseball implements Ball {
@Override
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
}
最后,一些可以将所有这些放在一起的代码:
Ball baseball = new Baseball();
Ball football = new Football();
List<BallUserInterface<? extends Ball>> uiList = new ArrayList<>();
uiList.add(BallUserInterfaceFactory.getUserInterface(baseball));
uiList.add(BallUserInterfaceFactory.getUserInterface(football));
for (BallUserInterface<? extends Ball> ui : uiList) {
System.out.println(ui);
}
// Outputs:
// ui.BaseballUserInterface@37e247e2
// ui.FootballUserInterface@1f2f0ce9