滚动新闻 →
Windows 11 任务管理器怎么用?从结束程序到查看硬件状态全面掌握 中共盘算落空 美国五大电视网拒播川习会 美制裁伊朗航空逼全球切割 中共成变数 PHP 构造函数怎么写?实际项目中初始化对象的常见方法 川习会成果是真是假?专家点出五大验收指标 《论语》究竟应该怎样读才不会变成背诵 中共央行再施宽松货币政策 专家析背后企 川普拒伊朗最新提议 批其聪明反被聪明误 “东北风暴”横扫美东沿海 淹水停电 1人遇难 《神农本草经》与中国药物学传统 瑞士公投初步结果:逾2/3反对收紧中立政策 鹿角缠吊床险象环生 纽约长岛月内救出5只鹿 英国美军基地爆重大事件 多人被捕 居民撤离 围棋为什么适合训练耐心 潘石屹披露的任志强发信11人名单 传有王岐山 孩子觉得家务都是父母做的怎么办 川普预测美国和古巴将达协议 无需军事行动 快门速度对视频有什么影响 广州为什么很早就成为国际贸易城市 哪国护照免签国最多?最强护照不是新加坡 小户型应该选择什么样的窗帘 饺子为什么成为中国人过年的重要食物 青年旅舍适合什么样的旅行者 发动机机油颜色变黑后需要马上更换吗 中国牛蛙验出致癌兽药 多地超市餐馆中招 习时代的官员有多荒诞 一个正科级小官的生活实录 新能源汽车电池包里面究竟有哪些东西 潘石屹自曝:任志强出事后 他在美国躲过一劫 电动工具电池平台为什么值得考虑 AI 模型量化为什么能够降低显存需求,INT8 和 INT4 背后到底发生了什么 Google Cloud Load Balancing 是什么,多台服务器如何共同处理访问请求 49岁歌手洪楗华癌逝 9月中港多名公众人物离世 SaaS 用户和企业之间是什么关系,数据库建模时容易忽略哪些问题 英空军紧急疏散民宅 逮捕数名违反爆裂物法男子 电脑插上显卡以后无法开机,辅助供电线连接错误会造成什么问题 韩国“习近平”餐厅遭破坏 4名中国嫌犯潜逃回国 美国专科医生年薪150万 中国医生惊叹:颠覆想像 Windows 11 磁盘使用率突然达到 100% 怎么办?常见原因和排查方法 PHP 类和对象怎么理解?从简单实例进入面向对象编程 川普政府推出人工智能服务 开创科技“黄金时代”

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

发布时间: 2026-09-27 12:30:01    最后更新: 2026-09-27 13:18:31    阅读:2  约7 分钟阅读     

在 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 的最新报道 ›
★ 我的收藏 查看已经收藏的文章
关于文章收藏 收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。 删除收藏请进入「我的收藏」进行管理。
分享这篇报道

分享 Facebook | X | WhatsApp | LinkedIn

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