Replace Type Code 是什么
用枚举、值类或子类取代魔法数字与字符串编码:取值集合受类型系统约束,比较与分支更安全,非法值在编译期就被拦下。演进路径通常是常量 → 枚举或值类 → 子类,只有当不同编码对应明显不同的行为时,才值得用子类把行为分解开。
基本信息
| 演进 | 常量 → 枚举/值类 → 子类(行为差异大时) |
|---|---|
| 收益 | 非法取值在编译期被拦下,分支可被穷尽检查 |
| 持久化 | 显式指定存储值,不依赖枚举顺序 |
| 适用 | 状态码、类型标识、单位、等级、血型这类有限集合 |
判断与做法
- 集合有限且稳定:优先枚举,switch 可获得穷尽性检查
- 需要携带额外数据(显示名、费率):用带字段的枚举或值类
- 编码与行为一一对应且行为复杂:考虑子类或策略加枚举
- 对外协议里的编码值:在边界处转换,内部用类型表达
- 已有 int 常量的地方先加转换方法,再逐步替换调用点
- 数据库列保持原编码,映射层负责与枚举互转
Java 示例
Java 示例
// 之前:用 int 编码血型,任何整数都能传进来
static final int O = 0;
static final int A = 1;
static final int B = 2;
static final int AB = 3;
// 之后:值域受类型约束,并可携带额外信息
enum BloodGroup {
O("O", 0), A("A", 1), B("B", 2), AB("AB", 3);
private final String label;
private final int storedValue; // 显式存储值,不依赖 ordinal
BloodGroup(String label, int storedValue) {
this.label = label;
this.storedValue = storedValue;
}
static BloodGroup fromStored(int v) {
for (BloodGroup g : values()) if (g.storedValue == v) return g;
throw new IllegalArgumentException("unknown blood group: " + v);
}
}演进阶段选择
| 阶段 | 形态 | 适用信号 |
|---|---|---|
| 一 | 常量集合 | 仅用于比较,数量少 |
| 二 | 枚举 / 值类 | 需要穷尽分支、携带附加数据 |
| 三 | 子类 / 策略 | 每个取值对应全不同的行为 |
| 边界 | 字符串编码 | 对外协议约定,转成内部类型后不再外泄 |
枚举的持久化值不要依赖 ordinal:插入一个新枚举项就会让后续所有值的含义错位,历史数据读出来就变了。显式指定存储值,并在映射方法里做严格校验。
类型码集合频繁扩展时,子类方案会让类数量随组合增长,此时用「枚举加策略」更合适:枚举承担类型标识,策略承担行为,扩展时新增策略实现而不动调用方。