封装是多态运行的前提,没有封装则多态失去意义;多态依赖“统一接口调用→内部按实际类型执行逻辑”,而该逻辑需靠封装可靠维护对象状态。

封装是多态能跑起来的前提
没有封装,多态就失去意义。比如你把 $name 声明为 public,外部代码直接改 $animal->name = 'hacker',那后续所有基于 makeSound() 的行为就可能错乱——因为对象状态已不可控。多态依赖的是“调用统一接口 → 内部按实际类型执行逻辑”,而这个“内部逻辑”必须能可靠地维护自身状态,靠的就是封装。
常见错误现象:
- 子类重写方法时直接读写父类
public属性,导致不同子类间意外共享数据 - 抽象类里把关键控制字段设为
public,结果实现类绕过校验逻辑直接赋值 - 接口只约束方法签名,但没配合封装,导致传入的对象虽符合类型却处于非法状态(比如
BankAccount余额为负却没被拦截)
多态让封装不变成黑盒,而是可替换的契约
封装不是把东西锁死,而是定义好“谁可以怎么用”。多态正是这个“怎么用”的扩展机制:只要遵守接口或抽象类的约定(比如必须实现 process()),子类就可以自由封装自己的内部细节——用数组还是 Redis 存缓存、用 cURL 还是 Guzzle 发请求,调用方完全不用知道。
使用场景中要注意:
立即学习“PHP免费学习笔记(深入)”;
- 接口里不要暴露
public属性,否则封装边界被打破;所有状态应通过getXXX()/setXXX()或行为方法间接访问 - 抽象类中若含具体方法(比如
logOperation()),它内部调用的属性必须是protected,否则子类无法安全继承和扩展 - PHP 不支持方法重载,所以别指望靠参数类型自动分发;多态绑定只发生在运行时对象实例上,不是函数签名匹配
抽象类 vs 接口:封装粒度决定多态弹性
选抽象类还是接口,本质是在权衡“共享实现”和“纯粹契约”。抽象类允许你封装共用逻辑(比如初始化连接、统一日志格式),但强制单继承;接口只承诺行为,支持 implements A, B, C,但没法帮你藏重复代码。
参数差异直接影响多态落地:
- 用抽象类时,
abstract protected function doWork();要求子类自己封装具体实现,但你可以放心在execute()里调用它并封装前置/后置逻辑 - 用接口时,
interface PaymentGateway只能定义pay()和refund(),所有封装细节(密钥加载、签名生成、重试策略)都得由AlipayGateway或WechatGateway自己搞定 - PHP 8+ 支持接口中带
default方法,但它不能访问实现类的私有属性——这意味着它只能做纯逻辑封装,不能碰状态
最容易被忽略的坑:类型声明 + 封装失效组合拳
你以为写了 function handle(Animal $a) 就安全了?不一定。如果 Animal 是接口,而某个实现类把 makeSound() 里关键状态(比如音量 $this->volume)设成 public,外部代码就能在调用前偷偷改掉它,导致多态行为失真。
性能与兼容性影响很小,但设计债会滚雪球:
- 后期想加参数校验?发现几十个子类都在直接读写同一个
public字段,改一处得全改 - 想换存储方式(比如从文件切到数据库)?如果封装不彻底,所有子类里散落着
fopen()调用,根本没法统一替换 - PHP 的类型声明只管“是不是这个类型”,不管“是不是合法状态”——这点比 Java 的
final字段或 Kotlin 的val更容易掉坑
真正稳的写法,是让每个类自己管好自己的状态,再通过多态接口暴露可控的行为入口。否则,封装只是纸糊的墙,多态就是裸奔的马。



















