文章目录
【Java设计模式】双重分发模式:增强多态行为
一、概述
双重分发模式用于根据方法调用中涉及的两个对象的类型实现动态多态性。它允许方法行为根据调用方法的对象和作为参数传递的对象的运行时类型的组合而有所不同。
二、详细解释及实际示例
- 实际示例:
- 在一家物流公司中,不同类型的配送车辆,如卡车、无人机和自行车,与各种类型的包裹(易碎、超大、标准)相互作用。双重分发设计模式用于确定最佳的配送方法:卡车可能处理超大物品,无人机用于快速配送轻包裹,自行车用于城市地区。每个车辆 - 包裹组合都会导致不同的处理和配送策略,这些策略在运行时根据车辆和包裹的类型动态确定。
- 通俗解释:
- Java中的双重分发设计模式允许程序根据调用中涉及的两个对象的类型选择不同的函数来执行,增强了处理它们之间交互的灵活性。
- 维基百科解释:
- 在软件工程中,双重分发是多重分发的一种特殊形式,是一种根据调用中涉及的两个对象的运行时类型将函数调用分派到不同具体函数的机制。在大多数面向对象系统中,从代码中的函数调用调用的具体函数取决于单个对象的动态类型,因此它们被称为单分发调用,或简单地称为虚函数调用。
三、Java中双重分发模式的编程示例
Java中的双重分发模式用于处理不同类型的游戏对象之间的碰撞。每个游戏对象都是扩展自GameObject
抽象类的类的实例。GameObject
类有一个collision(GameObject)
方法,在每个子类中被重写,以定义与另一个游戏对象发生碰撞时的行为。以下是GameObject
类及其子类的简化版本:
public abstract class GameObject {
// 其他属性和方法...
public abstract void collision(GameObject gameObject);
}
public class FlamingAsteroid extends GameObject {
// 其他属性和方法...
@Override
public void collision(GameObject gameObject) {
gameObject.collisionWithFlamingAsteroid(this);
}
}
public class SpaceStationMir extends GameObject {
// 其他属性和方法...
@Override
public void collision(GameObject gameObject) {
gameObject.collisionWithSpaceStationMir(this);
}
}
在App
类中,双重分发模式用于检查所有游戏对象对之间的碰撞:
public static void main(String[] args) {
// 初始化游戏对象并打印它们的状态
LOGGER.info("初始化对象并打印它们的状态");
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()));
// 碰撞检查
LOGGER.info("碰撞检查");
objects.forEach(o1 -> objects.forEach(o2 -> {
if (o1!= o2 && o1.intersectsWith(o2)) {
o1.collision(o2);
}
}));
// 输出最终的对象状态
LOGGER.info("打印碰撞检查后的对象状态");
objects.forEach(o -> LOGGER.info(o.toString()));
}
当检测到两个对象之间的碰撞时,collision(GameObject)
方法在第一个对象(o1)上被调用,第二个对象(o2)作为参数。这个方法调用在运行时被分派到o1类中适当的collision(GameObject)
方法。在这个方法内部,另一个方法调用gameObject.collisionWithX(this)
在o2上被执行(其中X是o1的类型),这个调用在运行时被分派到o2类中适当的collisionWithX(GameObject)
方法。这就是Java中的“双重分发” - 两个方法调用在运行时根据两个对象的类型被分派。
以下是程序输出:
15:47:23.763 [main] INFO com.iluwatar.doubledispatch.App -- 初始化对象并打印它们的状态
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- FlamingAsteroid 在 [0,0,5,5] 损坏=false 着火=true
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationMir 在 [1,1,2,2] 损坏=false 着火=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- Meteoroid 在 [10,10,15,15] 损坏=false 着火=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationIss 在 [12,12,14,14] 损坏=false 着火=false
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.App -- 碰撞检查
15:47:23.772 [main] INFO com.iluwatar.doubledispatch.SpaceStationMir -- FlamingAsteroid 撞击 SpaceStationMir. SpaceStationMir 损坏!SpaceStationMir 着火了!
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.Meteoroid -- SpaceStationMir 撞击 FlamingAsteroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.SpaceStationMir -- {} 损坏!撞击 Meteoroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.Meteoroid -- SpaceStationIss 撞击 Meteoroid.
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- 打印碰撞检查后的对象状态
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- FlamingAsteroid 在 [0,0,5,5] 损坏=false 着火=true
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationMir 在 [1,1,2,2] 损坏=true 着火=true
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- Meteoroid 在 [10,10,15,15] 损坏=false 着火=false
15:47:23.773 [main] INFO com.iluwatar.doubledispatch.App -- SpaceStationIss 在 [12,12,14,14] 损坏=true 着火=false
四、何时在Java中使用双重分发模式
- 当方法的行为不仅需要根据调用它的对象,还需要根据参数的类型而变化时。
- 在使用if - else或switch - case类型检查对象类型繁琐且不可扩展的场景中。
- 当在域类中实现操作而不使用关于其他域类的复杂决策逻辑污染其代码时。
五、双重分发模式在Java中的实际应用
- 图形用户界面,其中根据不同类型的鼠标事件与不同类型的元素交互采取不同的操作。
- 模拟系统,其中不同类型的对象之间的交互需要触发不同的行为。
六、双重分发模式的优点和权衡
优点:
- 通过以易于理解和维护的方式处理对象之间的交互,增加了代码的灵活性。
- 通过允许引入新类而无需修改现有类,有助于遵循[开闭原则](https://java - design - patterns.com/principles/#open - closed - principle)。
权衡:
- 可能导致更复杂的代码结构,特别是在像Java这样本身不支持此模式的语言中。
- 随着新类的添加,可能需要额外的努力来维护和扩展。