php面向对象是用class、__construct、protected等约束行为与责任边界的实践,核心在于职责明确、可测试、可复用;需避免将类写成函数集合或配置容器,坚持单一职责、显式依赖注入与合理访问控制。

PHP面向对象不是语法通关游戏,而是用class、__construct、protected这些关键词去约束行为、划清责任边界的实践过程。入门不难,但多数人卡在“写了类却不敢改、不敢测、不敢复用”这一步。
从写第一个真正有职责的class开始,别碰extends和interface
新手常把class当成函数集合或配置容器,比如写一个Tools类塞进所有str2date()、array2csv()——这不是面向对象,是面向“方便”。真正该做的,是定义一个有明确边界、能独立存在的实体:
-
User类负责用户身份、状态、密码变更逻辑,不处理数据库连接 -
Logger类专注日志格式化与输出目标(文件/Socket/Stdout),不关心业务发生了什么 - 每个属性必须显式声明访问修饰符:
public $name可以,public $db不行——后者暴露了不该暴露的依赖 - 方法只做一件事:
save()只持久化,校验交给validate(),加密交给hashPassword()
常见错误:$user->email = "x@y.z"直接赋值,后续加邮箱格式校验时得全局搜索->email =;正确做法是删掉public $email,补上setEmail()方法。
__construct()里只放初始化动作,别塞业务逻辑
构造函数不是“启动器”,它的唯一任务是让对象处于可使用状态。一旦在里面调用file_get_contents()、连数据库、发HTTP请求,这个类就失去了可测试性、不可被DI容器管理,也违背单一职责。
- 允许的写法:
$this->id = $id ?? null、$this->status = self::STATUS_DRAFT - 禁止的写法:
$this->config = json_decode(file_get_contents('config.json'))、$this->pdo = new PDO(...) - 如果真需要外部资源,用依赖注入:
public function __construct(Database $db),而不是在内部创建
判断标准很简单:执行new User()能否不报错?如果必须传参数才能活,那就该用构造函数参数,而不是在内部硬编码加载逻辑。
private vs protected:日常主力是protected
很多教程鼓吹“一律用private”,结果导致子类只能复制粘贴父类逻辑,或者绕过封装直接操作数组字段。真实项目中:
-
private只用于真正只属于当前类的临时状态,比如$this->cachedFullName这种计算缓存 -
protected才是继承链的契约:父类提供protected function formatDateTime($ts),子类可覆盖;提供protected function canAccess(),子类可重写权限规则 -
public只暴露调用入口,如login()、exportCsv(),绝不暴露$this->rawData或connectDb()
典型反例:private function connectDb()写死在父类里,子类想换MySQL为PostgreSQL就得整个类重写;改成protected function createConnection(),子类重写即可,这才是可扩展的设计。
写完类立刻问:它能不能独立new出来并跑通?
这是检验是否真懂OOP的硬标准。如果new Order()必须依赖一个已初始化的PaymentGateway实例才不报错,那它就不是完整对象,而是半成品。
- 解决方式只有两个:要么通过构造函数注入(推荐),如
public function __construct(PaymentGateway $gateway) - 要么用工厂或Builder模式延迟创建,但别在
__construct()里自己new - 如果类里大量出现
if (isset($this->xxx))或is_null($this->xxx),说明初始化没到位,职责没理清
复杂点往往不在语法,而在于你有没有勇气删掉那个“看起来很方便但其实谁都改不动”的public $config属性,换成一次注入、一处控制。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











