这是一个错误的设计模式。与其使用一个泛型方法和一个 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 的默认实现。球和棒球现在应该看起来像这样:
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);
}
}
为了使事情变得更整洁,最好将包设为私有,并在 中提供通用的 getter。然后,工厂可以管理其他检查,例如 null 和任何引发的异常。例如。getBallUserInterface
BallUserInterfaceFactory
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();
}
}
访客模式
正如评论中指出的那样,上述问题的一个问题是它要求类具有UI的知识,这是非常不可取的。但是,您可以使用访问者模式,这使您能够使用双重调度,还可以分离各种类和 UI。Ball
Ball
首先,必要的访客类和工厂功能:
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