0

0

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

心靈之曲

心靈之曲

发布时间:2026-01-02 15:54:29

|

562人浏览过

|

来源于php中文网

原创

如何在不使用 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() 中组合调用:

Amazon Nova
Amazon Nova

亚马逊云科技(AWS)推出的一系列生成式AI基础模型

下载
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 + 强转:破坏封装,违背开闭原则,难以维护;
    • ❌ 统一接收全部验证器:违反单一职责与最小知识原则,耦合高、易出错;
    • ✅ 当前方案:符合里氏替换、依赖倒置、迪米特法则,是典型的“组合优于继承 + 依赖注入”实践。

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

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

229

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

434

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

713

2023.08.22

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

54

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

47

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

994

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

53

2025.10.17

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.2万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号