滚动新闻 →
PHP 更新数据时加入 version 字段,可以解决哪些并发问题 57个粉丝的博主发AI视频调侃问界 被索赔百万 笔记本电脑插电才能开机,拔掉充电器马上关机,电池可能出了什么问题 Windows 11 文件分组功能怎么用?让大量文件更加容易浏览 PHP PDO 预处理语句怎么写?从查询参数到安全执行完整演示 辛弃疾为什么能够把豪情写进词里 骗子被耍惨 澳洲初创公司用AI扮受害者收集情报 浙江男没工作杀人泄愤 进写字楼随机砍死女职员 枸杞在中国传统饮食文化中的历史 中国书法为什么强调“气韵” 韩国军事基地曝漏洞 近200辆比亚迪可进出 个人是养老第一责任人? 山东民政厅厅长言论惹议 传成都人从俄罗斯回国后死亡 官方称鼠疫信息保密 如何培养孩子对校园同学的尊重 视频压缩为什么会影响画面细节 川普乔治亚州助选 博顿斯拼首位黑人女州长 胡塞武装持续攻击沙特 油价飙破110美元 美拟升级中国旅行警告 提高“非法拘押”风险等级 中国人存钱不消费 IMF:因沉重医疗成本 伊朗镇压黑幕曝光 革命卫队陷缺钱危机 欧洲三国爆发抗议 比利时警民激烈冲突 双台风夹击太平洋 墨西哥和关岛紧急戒备 武则天为何能够登上皇帝之位 旧房改造是不是一定要大拆大建 官媒转向?《经济日报》下场批判“懂车帝” 川普救油价暂缓对俄制裁 泽连斯基:无法接受 加州多地爆发野火 数栋豪宅遭焚毁 豆瓣酱背后有什么样的地域饮食文化 喜欢自然风景的人怎样安排旅行 汽车启动机故障有哪些常见表现 固态电池为什么被认为是下一代电池技术 被警察盗走?浙江男出狱后发现账户近50万元消失 白俄罗斯恐参战!俄拟部署弹道导弹袭击乌西 沙特阿联酋联手反击胡塞 伊朗幕后援助被曝光 打破中共稀土垄断!六大国际银行重磅结盟 美国买房时如何计算每月真正的住房支出 北美家庭应该如何制定长期储蓄目标 湖南娄底公园暗夜砍人 两男子倒地不起 五中全会禅让李强?习近平恐成最后党魁! 美国和加拿大购买游艇有什么区别

PHP 构造函数怎么写?实际项目中初始化对象的常见方法

发布时间: 2026-09-27 12:30:01    最后更新: 2026-10-16 10:31:35    阅读:72  约7 分钟阅读     

PHP 构造函数怎么写?实际项目中初始化对象的常见方法
图片说明:示意图   图片来源:Public Domain(公有领域)
在 PHP 项目中,只要使用面向对象编程,就很难绕开构造函数。它看起来只是一个简单的方法,但在实际项目里,构造函数承担的任务远不只是给几个属性赋值。一个对象应该在什么状态下被创建、哪些依赖应该由外部传入、哪些参数必须验证、数据库连接该不该放进去,这些问题最终都会落到构造函数的设计上。 PHP 的构造函数写法其实很明确。在现代 PHP 中,标准写法是 __construct(),注意是两个下划线,而不是 construct()。例如: class Product { private string $name; private float $price; public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; } } $product = new Product('笔记本电脑', 1299.99); 当执行 new Product() 时,PHP 会自动调用 __construct()。构造函数最基本的作用,就是确保对象创建完成以后处于一个可以正常使用的状态。 这也是判断构造函数应该放什么逻辑的一个重要标准:这段逻辑是不是对象建立时就必须完成。 例如商品对象必须有商品名称和价格,那么这些内容放在构造函数中非常自然。反过来,如果创建商品对象以后,还需要查询数据库、调用第三方支付接口、计算复杂促销方案,通常就不应该把所有工作都塞进构造函数。 很多初学者容易把“初始化对象”和“执行业务流程”混在一起。 例如: class Product { private PDO $db; public function __construct(PDO $db) { $this->db = $db; } } 这种写法很合理。商品对象需要数据库连接作为依赖,因此由外部把 PDO 对象传进来。 但如果写成: public function __construct() { $this->db = new PDO(...); } 问题就出现了。Product 类自己决定数据库服务器、账号、密码以及连接方式以后,它和具体数据库环境产生了直接耦合。测试时也很麻烦,因为你很难轻易换成测试数据库或者模拟对象。 因此,实际项目中非常常见的一种做法就是依赖注入。 class ProductRepository { public function __construct(private PDO $db) { } } 创建对象时: $repository = new ProductRepository($pdo); 这里的重点并不是“构造函数可以传参数”这么简单,而是依赖由外部管理。ProductRepository 不负责决定数据库连接怎么建立,它只负责使用已经提供的数据库连接。 这会直接影响代码的可测试性、可维护性和模块之间的耦合关系。 当然,并不是所有属性都需要通过构造函数传入。如果一个对象非常简单,例如配置对象、值对象或者普通的数据封装类,构造函数直接初始化属性往往是最清晰的方案。 PHP 8 以后还可以利用构造函数属性提升进一步简化代码: class Product { public function __construct( private string $name, private float $price ) { } public function getName(): string { return $this->name; } public function getPrice(): float { return $this->price; } } 这比先声明属性、再在构造函数中重复赋值更加简洁。 但类型声明并不能解决所有问题。 例如: public function __construct( private string $name, private float $price ) { } 虽然 $price 必须是 float,但 -100 依然可能是一个合法的 float。PHP 知道它是数字,却不知道负数是否符合你的商品业务规则。 所以,如果商品价格必须大于零,可以在构造函数中进行不变量检查: public function __construct( private string $name, private float $price ) { if (trim($name) === '') { throw new InvalidArgumentException('商品名称不能为空'); } if ($price <= 0) { throw new InvalidArgumentException('商品价格必须大于零'); } } 这里需要纠正一个很常见的说法:参数验证并不是“不能放在构造函数里”。 恰恰相反,如果某个条件是对象能够成立的基本条件,那么在构造函数中检查非常合适。比如价格不能为负数、邮箱对象必须包含合法邮箱地址、金额对象不能出现非法数值。如果不满足这些条件,对象本身就不应该被创建出来。 但是,业务流程验证是另一回事。 例如“这个用户今天是否已经购买过这个商品”“当前会员是否有资格享受这个折扣”“库存是否足够完成这次订单”,这些都可能需要访问数据库或者调用其他业务服务。它们属于业务流程,而不是简单的对象自身状态检查。 这种逻辑放进构造函数,就容易让构造函数变得非常沉重。 例如: class Order { public function __construct(PDO $db, int $userId, int $productId) { // 查询用户 // 查询库存 // 查询会员等级 // 查询优惠券 // 计算价格 // 写入数据库 } } 这样的设计会让 new Order() 不再只是创建对象,而变成一次复杂的业务操作。测试、异常处理、事务管理都会变得更加困难。 更合理的方式通常是让服务层负责业务流程: class OrderService { public function __construct( private ProductRepository $products, private UserRepository $users ) { } public function createOrder(int $userId, int $productId): Order { // 查询用户 // 查询商品 // 检查库存 // 执行业务规则 return new Order($userId, $productId); } } 这样职责就比较清楚:Order 负责表达一个订单对象,OrderService 负责“如何创建一个符合业务规则的订单”。 那么工厂方法又是什么? 工厂方法适合处理“创建对象本身需要选择不同构造方式”的情况。例如同一个领域对象可能根据不同来源、不同配置或者不同业务场景采用不同的创建过程。 class Product { private function __construct( private string $name, private float $price ) { } public static function create(string $name, float $price): self { if (trim($name) === '') { throw new InvalidArgumentException('商品名称不能为空'); } if ($price <= 0) { throw new InvalidArgumentException('商品价格必须大于零'); } return new self($name, $price); } } 此时外部不能直接调用 new Product(),而是: $product = Product::create('笔记本电脑', 1299.99); 工厂方法并不是构造函数的替代品。new 最终仍然会调用构造函数,只不过对象创建过程被工厂方法封装起来了。 如果一个类有多种合理的创建方式,工厂方法尤其有价值: $product = Product::fromDatabase($row); $product = Product::fromApi($data); $product = Product::create('键盘', 99.99); 这样调用代码可以直接表达对象的来源和创建语义。 不过,工厂方法也不能被滥用。如果一个类只需要两个参数就可以正常创建,没有复杂创建过程,直接使用构造函数往往更加简单。为了“设计模式而设计模式”,反而会增加代码量。 大型 PHP 项目中还有一个经常出现的问题,就是构造函数参数越来越多。 例如: public function __construct( PDO $db, CacheInterface $cache, LoggerInterface $logger, MailerInterface $mailer, PaymentInterface $payment, Config $config ) { // ... } 如果一个类需要这么多依赖,不能简单认为“依赖注入太多,所以不好”。依赖注入本身没有问题,真正应该检查的是这个类是不是承担了太多职责。 一个类同时操作数据库、缓存、邮件、支付、日志,很可能已经违反了单一职责原则。 依赖注入的价值就在这里体现出来:它不仅让依赖可以替换,也会把类的结构问题暴露出来。 现代 PHP 项目通常会使用依赖注入容器自动创建对象。开发者不需要在每个地方手动 new 出所有依赖,而是让容器根据类型和配置完成对象组装。 例如: class ProductService { public function __construct( private ProductRepository $repository, private CacheInterface $cache ) { } } ProductService 不需要自己创建 ProductRepository,也不需要自己决定使用哪一种缓存实现。具体对象由应用程序的依赖注入容器负责组装。 这也是为什么现代 PHP 框架和大型应用普遍强调依赖注入。 还有一个经常被误解的概念,就是“延迟加载”。 如果商品对象只有在调用 getStock() 时才查询数据库,那么这并不是简单地“把属性访问器放进构造函数”。更合理的设计通常是把需要延迟加载的资源交给专门的 Repository、Service 或 Lazy 对象处理。 例如: class ProductService { public function __construct( private StockRepository $stocks ) { } public function getStock(int $productId): int { return $this->stocks->getStock($productId); } } 这样商品实体本身不需要知道数据库库存表怎么查询。 这也涉及一个重要的架构原则:对象应该尽量只管理自己真正需要管理的状态,而不是把整个应用程序的业务流程都塞进一个类。 对于构造函数,可以把实际项目中的判断方法概括成几个问题。 第一,这个数据是不是对象成立所必需的?如果是,可以考虑放入构造函数。 第二,这个依赖是不是由类自己创建的?如果是数据库、缓存、日志等外部服务,通常应该通过依赖注入传入,而不是在构造函数里直接 new。 第三,这个验证是在保证对象本身合法,还是在执行一项业务流程?前者可以放在构造函数,后者通常应该交给 Service 等业务层。 第四,对象是不是存在多种合理的创建方式?如果存在,可以考虑工厂方法或命名构造方法。 第五,构造函数是不是已经塞入了大量依赖和复杂逻辑?如果是,与其继续优化构造函数,不如重新检查类的职责划分。 构造函数最重要的作用,不是“把属性赋值进去”,而是建立对象的有效状态和明确依赖关系。简单对象直接使用 __construct() 完成初始化即可;需要外部资源时使用依赖注入;存在复杂创建流程时考虑工厂方法;涉及跨对象、数据库和业务规则的流程,则应该交给服务层。 真正好的 PHP 面向对象设计,并不是让每个类都使用某一种设计模式,而是让对象的创建过程、依赖关系和业务职责保持清楚。构造函数越容易理解,通常也意味着整个系统的职责边界越清晰。

喜欢这篇报道?

使用下面的功能,方便以后继续阅读和分享 MNewsTV

设为 Google 新闻首选来源 让 Google 新闻优先显示 MNewsTV 的最新报道 ›
★ 我的收藏 查看已经收藏的文章
关于文章收藏 收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。 删除收藏请进入「我的收藏」进行管理。
分享这篇报道

捐助(Paypal): https://www.paypal.me/observeccp
订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP