Java 中的双重分派模式:增强多态行为
也称为
- 多方法
双重分派设计模式的意图
双重分派模式用于根据方法调用中涉及的两个对象的类型来实现动态多态性。它允许方法行为根据方法被调用的对象和作为参数传递的对象的运行时类型的组合而有所不同。
双重分派模式的详细解释及现实世界示例
现实世界示例
在一家物流公司,不同类型的配送车辆,如卡车、无人机和自行车,与各种类型的包裹(易碎品、超大型、标准型)进行交互。双重分派设计模式用于确定最佳配送方式:卡车可能处理超大型物品,无人机用于快速配送轻型包裹,自行车用于城市地区。每种车辆-包裹组合都会产生不同的处理和配送策略,这些策略是在运行时根据车辆和包裹类型的动态确定的。
通俗地说
Java 中的双重分派设计模式允许程序根据调用中涉及的两个对象的类型选择不同的函数执行,从而提高处理它们之间交互的灵活性。
维基百科说
在软件工程中,双重分派是多重分派的一种特殊形式,是一种机制,根据调用中涉及的两个对象的运行时类型将函数调用分派到不同的具体函数。在大多数面向对象系统中,从代码中的函数调用调用的具体函数取决于单个对象的动态类型,因此它们被称为单分派调用,或简称为虚函数调用。
Java 中双重分派模式的编程示例
Java 中的双重分派模式用于处理不同类型的游戏对象之间的碰撞。每个游戏对象都是一个类实例,该类继承自 GameObject
抽象类。GameObject
类具有一个 collision(GameObject)
方法,该方法在每个子类中都被重写以定义与另一个游戏对象发生碰撞时的行为。以下是 GameObject
类及其子类的简化版本
public abstract class GameObject {
// Other properties and methods...
public abstract void collision(GameObject gameObject);
}
public class FlamingAsteroid extends GameObject {
// Other properties and methods...
@Override
public void collision(GameObject gameObject) {
gameObject.collisionWithFlamingAsteroid(this);
}
}
public class SpaceStationMir extends GameObject {
// Other properties and methods...
@Override
public void collision(GameObject gameObject) {
gameObject.collisionWithSpaceStationMir(this);
}
}
在 App 类中,双重分派模式用于检查所有成对游戏对象之间的碰撞
public static void main(String[] args) {
// initialize game objects and print their status
LOGGER.info("Init objects and print their status");
var objects = List.of(
new FlamingAsteroid(0, 0, 5, 5),
new SpaceStationMir(1, 1, 2, 2),
new Meteoroid(10, 10, 15, 15),
new SpaceStationIss(12, 12, 14, 14)
);
objects.forEach(o -> LOGGER.info(o.toString()));
// collision check
LOGGER.info("Collision check");
objects.forEach(o1 -> objects.forEach(o2 -> {
if (o1 != o2 && o1.intersectsWith(o2)) {
o1.collision(o2);
}
}));
// output eventual object statuses
LOGGER.info("Print object status after collision checks");
objects.forEach(o -> LOGGER.info(o.toString()));
}
当检测到两个对象之间的碰撞时,将使用第二个对象 (o2) 作为参数在第一个对象 (o1) 上调用 collision(GameObject)
方法。此方法调用在运行时被分派到 o1 类中的适当 collision(GameObject)
方法。在此方法内部,另一个方法调用 gameObject.collisionWithX(this)
在 o2 上执行(其中 X 是 o1 的类型),该调用在运行时被分派到 o2 类中的适当 collisionWithX(GameObject)
方法。这就是 Java 中的“双重分派” - 两个方法调用根据两个对象的类型在运行时被分派。
以下是程序输出
15:47:23.763 [main] INFO com.iluwatar.doubledispatch.App -- Init objects and print their status
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- FlamingAsteroid at [0,0,5,5] damaged=false onFire=true
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationMir at [1,1,2,2] damaged=false onFire=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- Meteoroid at [10,10,15,15] damaged=false onFire=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationIss at [12,12,14,14] damaged=false onFire=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- Collision check
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.SpaceStationMir -- FlamingAsteroid hits SpaceStationMir. SpaceStationMir is damaged! SpaceStationMir is set on fire!
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.Meteoroid -- SpaceStationMir hits FlamingAsteroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.SpaceStationMir -- {} is damaged! hits Meteoroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.Meteoroid -- SpaceStationIss hits Meteoroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- Print object status after collision checks
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- FlamingAsteroid at [0,0,5,5] damaged=false onFire=true
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationMir at [1,1,2,2] damaged=true onFire=true
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- Meteoroid at [10,10,15,15] damaged=false onFire=false
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationIss at [12,12,14,14] damaged=true onFire=false
双重分派模式的详细解释及现实世界示例

何时在 Java 中使用双重分派模式
- 当方法的行为不仅需要根据其被调用的对象而变化,还需要根据参数的类型而变化时。
- 在对对象的类型进行 if-else 或 switch-case 类型检查很麻烦且不可扩展的场景中。
- 在实现域类中的操作时,无需将域类代码污染为关于其他域类的复杂决策逻辑。
Java 中双重分派模式的现实世界应用
- 图形用户界面,其中根据不同类型的鼠标事件与不同类型的元素交互来执行不同的操作。
- 模拟系统,其中不同类型的对象之间的交互需要触发不同的行为。
双重分派模式的优缺点
优点
- 通过以易于理解和维护的方式处理对象之间的交互,提高代码的灵活性。
- 有助于遵守 开闭原则,允许引入新的类而无需修改现有类。
权衡
- 会导致更复杂的代码结构,尤其是在像 Java 这样的语言中,这些语言不支持这种模式。
- 在添加新类时可能需要额外的维护和扩展工作。