如何在不使用 if 条件的前提下,通过父类单个方法调用子类差异化校验逻辑?

通过将验证器(validator)作为依赖注入到具体子类实例中,并在子类中实现无参的 `runallvalidations()` 方法,可彻底消除运行时类型判断与冗余参数传递,实现真正面向对象的多态校验。

在面向对象设计中,强制“用一个父类引用调用不同子类特有行为”时,核心原则是:行为差异应由类型决定,而非由条件分支或泛化参数暴露。上述问题的本质矛盾在于——既要保持 Vehicle 的统一接口,又要避免 if/instanceof 和“传入所有可能验证器”的反模式。最佳解法是采用依赖注入 + 模板方法(Template Method)风格的抽象设计

✅ 推荐方案:验证器预绑定 + 无参多态调用

我们将验证器(如 TireValidator、BrakeValidator)作为成员变量,在创建子类实例后、调用校验前完成注入。这样,runAllValidations() 可以是完全无参的抽象方法,各子类仅需关注自身职责范围内的校验顺序:

abstract 

class Vehicle { protected Tire tire; protected TireValidator tireValidator; public void setTireValidator(TireValidator validator) { this.tireValidator = validator; } protected void checkTire() { if (tireValidator == null) { throw new IllegalStateException("TireValidator not set"); } tireValidator.check(tire); } public abstract void runAllValidations(); // ← 统一入口,无参、无条件、无冗余依赖 }

子类仅声明自身所需的额外验证器,并在 runAllValidations() 中组合调用:

class Bike extends Vehicle {
    private Brakes brakes;
    private BrakeValidator brakeValidator;

    public void setBrakeValidator(BrakeValidator validator) {
        this.brakeValidator = validator;
    }

    protected void checkBrakes() {
        if (brakeValidator == null) {
            throw new IllegalStateException("BrakeValidator not set");
        }
        brakeValidator.check(brakes);
    }

    @Override
    public void runAllValidations() {
        checkTire();     // 公共逻辑(来自父类)
        checkBrakes();   // 特有逻辑(本类封装)
    }
}

class Car extends Vehicle {
    private Gas gas;
    private GasValidator gasValidator;

    public void setGasValidator(GasValidator validator) {
        this.gasValidator = validator;
    }

    protected void checkGas() {
        if (gasValidator == null) {
            throw new IllegalStateException("GasValidator not set");
        }
        gasValidator.check(gas);
    }

    @Override
    public void runAllValidations() {
        checkTire();   // 公共逻辑
        checkGas();    // 特有逻辑
    }
}

? 使用示例:类型安全、零 if、零冗余参数

public static void main(String[] args) {
    // 实例化具体类型(此时已知其校验需求)
    Bike bike = new Bike(/*...*/);
    Car car = new Car(/*...*/);

    // 注入各自所需的验证器(编译期类型明确,无强制转型)
    bike.setTireValidator(new DefaultTireValidator());
    bike.setBrakeValidator(new DefaultBrakeValidator());

    car.setTireValidator(new DefaultTireValidator());
    car.setGasValidator(new DefaultGasValidator());

    // ✅ 统一调用 —— 多态生效,无需 if,无需传参
    List vehicles = List.of(bike, car);
    vehicles.forEach(Vehicle::runAllValidations); // 各自执行专属校验流程

    // 或单独调用:
    bike.runAllValidations();
    car.runAllValidations();
}

⚠️ 注意事项与进阶建议

  • 空验证器防护:示例中加入了 null 检查并抛出 IllegalStateException,确保校验逻辑不会静默失败;生产环境建议配合构造器注入或 @RequiredArgsConstructor(Lombok)提升初始化安全性。
  • 扩展性友好:新增车型(如 Truck 需校验 Cargo 和 Axle)只需新增对应验证器字段与 checkXxx() 方法,重写 runAllValidations() 即可,不侵入现有类。
  • 替代方案对比
    • ❌ instanceof + 强转:破坏封装,违背开闭原则,难以维护;
    • ❌ 统一接收全部验证器:违反单一职责与最小知识原则,耦合高、易出错;
    • ✅ 当前方案:符合里氏替换、依赖倒置、迪米特法则,是典型的“组合优于继承 + 依赖注入”实践。

该设计不仅解决了原始问题,更体现了面向对象设计的精髓:让类型自己说话,而不是让程序员替它做决定。