讲师中心 微信公众号
AI工具推荐 视频效率加速

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

酷伟吖_1520

酷伟吖_1520

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

|

582人浏览过

|

来源于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() 中组合调用:

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<Vehicle> vehicles = List.of(bike, car);
    vehicles.forEach(Vehicle::runAllValidations); // 各自执行专属校验流程

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

⚠️ 注意事项与进阶建议

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

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

热门AI工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

LibLibAI
LibLibAI Hot

一款AI视频创作工具,主要用于国内领先的AI创意平台,以海量模型、低门槛操作与“创作-分享-商业化”生态,让小白与专业创作者都能高效实现图文乃至视频创意表达,适合需要提升相关任务效率的用户。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

相关专题

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

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

509

2023.09.22

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

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

1638

2024.03.01

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

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

5095

2025.09.05

java面向对象
java面向对象

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

282

2025.11.27

C++ 面向对象编程与设计模式实践
C++ 面向对象编程与设计模式实践

本专题整合了 C++ 面向对象编程的核心机制,涵盖类的定义与访问控制、构造函数与析构函数、拷贝语义与移动语义、继承与虚函数多态、抽象类与接口、运算符重载、友元函数等关键知识点,同时结合单例模式、工厂模式、观察者模式、策略模式等常用设计模式的 C++ 实现,帮助开发者写出结构清晰、易于维护的面向对象代码。

284

2026.04.09

Java 面向对象与核心编程机制
Java 面向对象与核心编程机制

系统讲解 Java 面向对象编程的核心知识体系,涵盖类的定义与封装、构造方法与 this/super 关键字、继承与方法重写、多态与向上向下转型、抽象类与接口的区别与设计原则、异常处理机制(try-catch-finally / 自定义异常)、泛型的类型参数与通配符、集合框架(List/Set/Map)的使用与遍历方式,帮助开发者全面掌握 Java 程序设计的核心机制与面向对象思维。

183

2026.04.17

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

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

295

2025.11.27

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

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

3028

2023.10.19

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

0

2026.09.23

热门下载

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

精品课程

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn