java
package com.example.demo;
public class UserDemo {
public static void main(String[] args) {
Driver a = new FemaleDriver();
Driver b = new MaleDriver();
Vehicle x = new Car();
Vehicle y = new Bus();
a.drives(x);
b.drives(y);
}
}
abstract class Vehicle {
private String type;
public Vehicle() {};
public Vehicle(String s) {
type = s;
};
public abstract void driveByFemaleDriver();
public abstract void driveByMaleDriver();
}
class Bus extends Vehicle {
public Bus() {}
public void driveByFemaleDriver(){
System.out.println("bus drive by female driver");
}
public void driveByMaleDriver() {
System.out.println("bus drive by male driver");
}
}
class Car extends Vehicle {
public Car() {}
public void driveByFemaleDriver(){
System.out.println("bus drive by female driver");
}
public void driveByMaleDriver() {
System.out.println("bus drive by male driver");
}
}
abstract class Driver{
public Driver() {}
public abstract void drives(Vehicle v);
}
// 二次分发
class FemaleDriver extends Driver {
public FemaleDriver() {}
public void drives(Vehicle v) {
v.driveByFemaleDriver();
}
}
class MaleDriver extends Driver {
public MaleDriver() {}
public void drives(Vehicle v) {
v.driveByMaleDriver();
}
}
这段代码演示了一个经典的设计模式------双分派(Double Dispatch),我来逐层解释:
📐 整体类结构
#mermaid-svg-ik2oUyw5tiUM60vP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ik2oUyw5tiUM60vP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ik2oUyw5tiUM60vP .error-icon{fill:#552222;}#mermaid-svg-ik2oUyw5tiUM60vP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ik2oUyw5tiUM60vP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ik2oUyw5tiUM60vP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ik2oUyw5tiUM60vP .marker.cross{stroke:#333333;}#mermaid-svg-ik2oUyw5tiUM60vP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ik2oUyw5tiUM60vP p{margin:0;}#mermaid-svg-ik2oUyw5tiUM60vP g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-ik2oUyw5tiUM60vP g.classGroup text .title{font-weight:bolder;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster-label text{fill:#333;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster-label span{color:#333;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster-label span p{background-color:transparent;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster text{fill:#333;}#mermaid-svg-ik2oUyw5tiUM60vP .cluster span{color:#333;}#mermaid-svg-ik2oUyw5tiUM60vP .nodeLabel,#mermaid-svg-ik2oUyw5tiUM60vP .edgeLabel{color:#131300;}#mermaid-svg-ik2oUyw5tiUM60vP .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-ik2oUyw5tiUM60vP .label text{fill:#131300;}#mermaid-svg-ik2oUyw5tiUM60vP .labelBkg{background:#ECECFF;}#mermaid-svg-ik2oUyw5tiUM60vP .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-ik2oUyw5tiUM60vP .classTitle{font-weight:bolder;}#mermaid-svg-ik2oUyw5tiUM60vP .node rect,#mermaid-svg-ik2oUyw5tiUM60vP .node circle,#mermaid-svg-ik2oUyw5tiUM60vP .node ellipse,#mermaid-svg-ik2oUyw5tiUM60vP .node polygon,#mermaid-svg-ik2oUyw5tiUM60vP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ik2oUyw5tiUM60vP .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP g.clickable{cursor:pointer;}#mermaid-svg-ik2oUyw5tiUM60vP g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-ik2oUyw5tiUM60vP g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-ik2oUyw5tiUM60vP .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-ik2oUyw5tiUM60vP .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-ik2oUyw5tiUM60vP .dashed-line{stroke-dasharray:3;}#mermaid-svg-ik2oUyw5tiUM60vP .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-ik2oUyw5tiUM60vP #compositionStart,#mermaid-svg-ik2oUyw5tiUM60vP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #compositionEnd,#mermaid-svg-ik2oUyw5tiUM60vP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #dependencyStart,#mermaid-svg-ik2oUyw5tiUM60vP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #dependencyStart,#mermaid-svg-ik2oUyw5tiUM60vP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #extensionStart,#mermaid-svg-ik2oUyw5tiUM60vP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #extensionEnd,#mermaid-svg-ik2oUyw5tiUM60vP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #aggregationStart,#mermaid-svg-ik2oUyw5tiUM60vP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #aggregationEnd,#mermaid-svg-ik2oUyw5tiUM60vP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #lollipopStart,#mermaid-svg-ik2oUyw5tiUM60vP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP #lollipopEnd,#mermaid-svg-ik2oUyw5tiUM60vP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-ik2oUyw5tiUM60vP .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-ik2oUyw5tiUM60vP .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ik2oUyw5tiUM60vP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ik2oUyw5tiUM60vP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ik2oUyw5tiUM60vP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<abstract>>
Vehicle
-String type
+driveByFemaleDriver()
+driveByMaleDriver()
<<abstract>>
Driver
+drives(Vehicle v)
Bus
Car
FemaleDriver
MaleDriver
🔍 逐段解析
第一层:抽象类 Vehicle(载具)
java
abstract class Vehicle {
private String type;
public Vehicle() {};
public Vehicle(String s) { type = s; }
// 定义两个抽象方法:谁来开,由子类实现
public abstract void driveByFemaleDriver();
public abstract void driveByMaleDriver();
}
abstract表示这是抽象类,不能直接 new- 声明了两个抽象方法,强制子类(Car、Bus)必须实现
第二层:Car 和 Bus 实现具体行为
java
class Bus extends Vehicle {
public void driveByFemaleDriver() {
System.out.println("bus drive by female driver");
}
public void driveByMaleDriver() {
System.out.println("bus drive by male driver");
}
}
class Car extends Vehicle {
// 同理,实现自己的驾驶行为
}
第三层:抽象类 Driver(司机)
java
abstract class Driver {
public abstract void drives(Vehicle v); // 接收一个载具
}
第四层:FemaleDriver 和 MaleDriver 实现分派
java
class FemaleDriver extends Driver {
public void drives(Vehicle v) {
v.driveByFemaleDriver(); // 调用载具上"女性驾驶"方法
}
}
class MaleDriver extends Driver {
public void drives(Vehicle v) {
v.driveByMaleDriver(); // 调用载具上"男性驾驶"方法
}
}
第五层:主方法执行
java
Driver a = new FemaleDriver();
Driver b = new MaleDriver();
Vehicle x = new Car();
Vehicle y = new Bus();
a.drives(x); // FemaleDriver.drives(Car) → Car.driveByFemaleDriver()
b.drives(y); // MaleDriver.drives(Bus) → Bus.driveByMaleDriver()
🎯 核心:什么是双分派?
Java 本身只支持单分派 (只根据调用者的类型决定执行哪个方法),但这段代码通过两次多态调用模拟了双分派:
第一次分派:根据 Driver 的实际类型(Female / Male)
↓
a.drives(x) → FemaleDriver.drives()
第二次分派:根据 Vehicle 的实际类型(Car / Bus)
↓
v.driveByFemaleDriver() → Car.driveByFemaleDriver()
用表格表示最终会调用哪个方法:
Car |
Bus |
|
|---|---|---|
| FemaleDriver | Car.driveByFemaleDriver() |
Bus.driveByFemaleDriver() |
| MaleDriver | Car.driveByMaleDriver() |
Bus.driveByMaleDriver() |
💡 这个模式解决了什么问题?
不用双分派的写法(反例):
java
// ❌ 用 if-else 判断类型,难以扩展
public void drives(Vehicle v) {
if (v instanceof Car) {
System.out.println("female drives car");
} else if (v instanceof Bus) {
System.out.println("female drives bus");
}
// 每次新增车辆类型都要修改这里 ❌
}
用双分派的写法:
java
// ✅ 新增一种车辆,只需新建子类并实现两个方法,无需改动 Driver
class Truck extends Vehicle {
public void driveByFemaleDriver() { ... }
public void driveByMaleDriver() { ... }
}
📝 总结
| 概念 | 说明 |
|---|---|
| 抽象类 | Vehicle 和 Driver 定义骨架,不能直接实例化 |
| 多态 | 父类引用指向子类对象(Driver a = new FemaleDriver()) |
| 双分派 | 通过两次虚方法调用,根据司机类型 + 载具类型共同决定最终行为 |
| 设计目的 | 避免 instanceof 判断,新增类型时只需扩展,不修改已有代码(开闭原则) |