面试风向:大厂面向对象考察已从“背诵概念”升级为“设计决策 + 代码演进能力”。本章聚焦 OOP 三大核心:抽象建模、SOLID 原则、模式选型与重构。
场景还原 模式匹配 结构阐述 优缺点权衡 项目落地示例 替代方案 + 演化方向
本章关键词:抽象 vs 接口选择、策略模式重构 if-else、观察者模式解耦、工厂模式与 DI、SOLID 违反场景

1. 如何在 PHP 中正确选择抽象类(abstract class)和接口(interface)?

考察点 面向对象设计能力、对 IS-A vs CAN-DO 的理解、实际项目中的权衡
回答框架 1. 指出核心区别:抽象类定义共性 + 部分实现;接口定义契约(纯抽象)
2. 结合业务举例:父类「支付处理」抽象类提供日志/事务骨架;接口「Payable」定义 charge()、refund()
3. 强调 PHP 单继承限制下优先用接口组合
4. 提及现代框架(Laravel Contract)的应用
示例回答 “我倾向把‘不变流程’放抽象类,如订单支付模板;把‘行为契约’放接口,如可退款。项目中有多个支付渠道,用接口统一 charge(),再用抽象类复用验签逻辑,这样方便做 mock 测试。”
(65字)
可能追问 如果抽象类里全是抽象方法,那和接口有什么区别?——功能等价,但接口可被多实现,抽象类占用单继承位。
避坑提醒 ❌ 不要背诵语法表,要结合“演进需求”——当未来需要加入新渠道时,哪种设计改动成本更低。

2. 一个订单结算方法里存在大量 if-else 判断不同优惠类型,如何用设计模式重构?

考察点 策略模式实践、开闭原则、代码可测试性
回答框架 1. 先分析 if-else 的变体是什么(优惠计算算法)
2. 抽象出策略接口(CouponStrategy)含 calculate(Order $order)
3. 每个优惠类实现该接口,用容器或工厂注册
4. 结算上下文类接受策略实例,循环执行
5. 同样可扩展:组合策略(Composite)、责任链(多级优惠叠加)
示例回答 “曾重构满减/折扣/红包混合结算:定义 Strategy 接口,每个优惠类实现;用数组收集所有可用策略,foreach 调用计算。新增‘拼团价’只需加一个类,无需改核心逻辑,同时单测覆盖每个策略类。”
(73字)
可能追问 策略之间需要依赖执行顺序或互斥时怎么处理?——引入责任链模式,或抽象出排序逻辑(PriorityAware)。
避坑提醒 ⚠️ 避免过度设计——如果优惠类型固定且很少变化,保留 switch 但用 enum + match 更简洁。

3. 你如何理解依赖倒置原则(DIP)?在 PHP 框架中如何体现?

考察点 SOLID 理解深度、框架源码阅读能力、解耦意识
回答框架 1. 一句话定义:高层模块不应依赖低层模块,两者都应依赖抽象
2. 举反例:Controller 直接 new UserModel() → 紧耦合,难测试
3. 正例:依赖注入 + 接口(UserRepositoryInterface)
4. 关联 Laravel 服务容器:bind()、interface 绑定具体类
5. 进阶说明:配置在外部(config/app.php)而非代码硬编码
示例回答 “在 Laravel 中,UserController 构造注入 UserRepositoryInterface,容器自动决议具体类。这样切换数据源(从 MySQL 到 Redis)只需新增实现并改容器绑定,业务层零改动。这是 DIP 的典型应用。”
(76字)
可能追问 如果接口只有一个实现类,还有必要抽象吗?——有必要,测试时可 mock,且为未来扩展留接口位。
避坑提醒 ❌ 不要只讲理论,必须结合 Laravel ServiceProvider 或 Yii 的依赖注入配置。

项目案例:高并发优惠券计算引擎

项目名称 秒杀优惠券叠加计算引擎
业务背景 大促期间用户可同时使用满减券、折扣券、红包,需按优先级顺序计算最终实付金额,并保证 QPS 5000+ 下的计算性能。
规模指标 日订单 100万;峰值 QPS 8000;优惠券策略 12 种;单次计算耗时要求 < 10ms(含 Redis 读取)。
技术栈 PHP 8.1 + Laravel 9 + Redis + MySQL + RabbitMQ
核心难点 1. 优惠券叠加规则复杂(互斥、排序、最大折扣封顶)
2. 每次计算需从 Redis 读取多个策略配置,网络开销大
3. 新活动频繁上线,需要可插拔策略而不改核心流程
面试高频追问 • 为什么选择策略模式而不是责任链?
• 如何保证策略执行顺序?(配置排序 + 依赖注入优先级)
• 缓存策略结果是否可行?(用 Rule Engine 预计算)
• 单元测试如何覆盖所有组合?(参数化测试 + 工厂类生成场景)
可迁移回答话术 “我们采用策略模式 + 工厂注册表管理所有优惠策略。每个策略类实现 `calculate(Order $order, ApplyContext $ctx)` 接口,通过管道依次执行。新增策略只需在配置数组添加类名,并实现接口即可,完全符合开闭原则。同时用 Redis 缓存每个策略的配置项,减少数据库查询。单测使用 Mockery 模拟复杂组合,覆盖边界情况。”
内化点:将设计模式与“可测试性”“可扩展性”挂钩,大厂面试官更看重你对“未来变化”的预见力。