拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DesignPatternsPHP 原型模式(Prototype Pattern)实战:用克隆替代 new 提升 PHP 8 对象创建效率

DesignPatternsPHP 原型模式(Prototype Pattern)实战:用克隆替代 new 提升 PHP 8 对象创建效率
  • 示例工程
  • 教程

【免费下载链接】DesignPatternsPHP

Sample code for several design patterns in PHP 8.x

项目地址:https://gitcode.com/gh_mirrors/de/DesignPatternsPHP
点击查看免费下载

导读

原型模式(Prototype)是创建型设计模式之一,核心思路是用"克隆现有对象"取代"通过构造函数新建对象",从而规避new Foo()在对象初始化上的高昂成本。本文以 DesignPatternsPHP 仓库中Creational/Prototype模块的完整实现为骨架,讲解原型模式的抽象原型设计、PHP 8 中__clone()的克隆机制、单元测试验证方式,并给出它在"批量创建大量数据对象"(如通过 ORM 一次性写入百万行记录)场景下的实用方案。读完本文,你将掌握如何基于抽象原型类派生出不同分类的具体原型,并通过clone关键字低成本批量产出带默认状态的实例。

模式定位与目的

在软件工程中,创建型设计模式关注对象创建机制,试图以更适合场景的方式来创建对象。常规的new Foo()方式在对象初始化逻辑复杂、构造开销大时,可能带来设计问题或增加复杂度。原型模式的思路非常直接:创建一个原型对象,然后克隆它,而不是每次都用标准方式重新走一遍构造流程。

仓库中Creational/Prototype/README.rst对这一目的的描述是:

To avoid the cost of creating objects the standard way (new Foo()) and instead create a prototype and clone it.

即:用"创建原型 + 克隆"替代"标准方式创建对象",从而避免对象创建成本。Prototype属于 Creational(创建型)模式家族,与 AbstractFactory、Builder、FactoryMethod、Pool、SimpleFactory、Singleton、StaticFactory 并列。

典型适用场景:批量数据写入

原型模式最典型的应用场景是一次性产生大量结构相同、仅有少量字段差异的对象。原文档给出的示例是:

Large amounts of data (e.g. create 1,000,000 rows in a database at once via a ORM).

即通过 ORM 一次性向数据库写入 1,000,000 行记录时,如果逐行new实体对象,构造逻辑会被反复执行;而先创建一个原型实体,再在循环中克隆并只修改差异字段,可以显著降低初始化开销。批量导入、报表生成、模板消息发送等"同构对象批量产出"的场景均适用。

模式结构:UML 类图

仓库为 Prototype 模式提供了对应的 UML 类图(Creational/Prototype/uml/uml.png),展示了本模块的核心结构:

从 UML 图可以看出:BookPrototype为抽象原型类,持有title、category两个字段并声明__clone()抽象方法,BarBookPrototype与FooBookPrototype通过继承(泛化)关系实现该抽象原型,各自定义默认分类并实现克隆逻辑——这正是 Prototype 模式"定义抽象原型接口/基类,由具体原型通过自克隆产出新对象"的标准结构。

源码实现逐层拆解

1. 抽象原型:BookPrototype

BookPrototype.php 定义了原型的抽象基类,是整套实现的核心骨架:

<?php declare(strict_types=1); namespace DesignPatterns\Creational\Prototype; abstract class BookPrototype { protected string $title; protected string $category; abstract public function __clone(); final public function getTitle(): string { return $this->title; } final public function setTitle(string $title): void { $this->title = $title; } }

关键设计点:

  • 抽象__clone()方法:PHP 语言层面的克隆钩子,子类必须实现。克隆一个对象时 PHP 会自动调用该方法,可在此执行"克隆后收尾"逻辑(如重置引用、重新生成唯一标识等)。
  • final修饰的访问器:getTitle()/setTitle()被声明为final,禁止子类覆写,保证原型对外接口统一,避免子类破坏基类约定的读写行为。
  • declare(strict_types=1):全仓库代码统一开启严格类型模式,setTitle(string $title)会拒绝非字符串参数传入。
  • 命名空间:DesignPatterns\Creational\Prototype,与仓库其他模块的 PSR 风格命名空间一致。

2. 具体原型:FooBookPrototype 与 BarBookPrototype

两个具体原型类分别以不同的默认分类值体现"每个原型自带一组默认状态"的思想:

FooBookPrototype.php:

<?php declare(strict_types=1); namespace DesignPatterns\Creational\Prototype; class FooBookPrototype extends BookPrototype { protected string $category = 'Foo'; public function __clone() { } }

BarBookPrototype.php:

<?php declare(strict_types=1); namespace DesignPatterns\Creational\Prototype; class BarBookPrototype extends BookPrototype { protected string $category = 'Bar'; public function __clone() { } }

设计要点:

  • 每个具体原型在属性声明处直接给$category赋默认值('Foo'/'Bar'),克隆出的新对象天然携带所属分类,无需在每次克隆后重新设置。
  • 当前示例中__clone()为空实现,是因为$title、$category均为标量字符串,PHP 默认的浅拷贝已能满足需求;__clone()的存在更多是为"克隆后需要深拷贝引用属性、重置状态"的复杂场景预留扩展点。

克隆机制的底层原理:浅拷贝与__clone()钩子

PHP 的对象克隆遵循两条核心规则,理解它们才能用好原型模式:

  1. 浅拷贝(shallow copy):clone $obj会创建一个新对象,其属性值与原对象相同。标量属性(如本示例的string)直接复制值,互不影响;但若属性是对象引用,克隆出的新对象与原对象会共享同一个内部对象。
  2. __clone()自动回调:克隆完成后,PHP 会在新对象上自动调用__clone()方法。如果原型持有引用类型属性,通常需要在该方法里手动深拷贝(例如$this->someObj = clone $this->someObj;),避免新对象与原对象共享可变状态。

从源码结构看,BookPrototype.php 将__clone()声明为抽象方法、并要求子类实现,正是为了让每个具体原型显式声明"克隆后如何处理自身状态",这是本模块对 PHP 原生克隆机制的模式化封装。

单元测试:验证克隆产物的类型与行为

仓库为原型模式提供了专门的测试 Tests/PrototypeTest.php,用于验证"克隆出的对象保持原型类型、且可独立设置标题":

<?php declare(strict_types=1); namespace DesignPatterns\Creational\Prototype\Tests; use DesignPatterns\Creational\Prototype\BarBookPrototype; use DesignPatterns\Creational\Prototype\FooBookPrototype; use PHPUnit\Framework\TestCase; class PrototypeTest extends TestCase { public function testCanGetFooBook() { $fooPrototype = new FooBookPrototype(); $barPrototype = new BarBookPrototype(); for ($i = 0; $i < 10; $i++) { $book = clone $fooPrototype; $book->setTitle('Foo Book No ' . $i); $this->assertInstanceOf(FooBookPrototype::class, $book); } for ($i = 0; $i < 5; $i++) { $book = clone $barPrototype; $book->setTitle('Bar Book No ' . $i); $this->assertInstanceOf(BarBookPrototype::class, $book); } } }

测试揭示的实用价值:

  • 一次原型、多次克隆:FooBookPrototype与BarBookPrototype各只new一次,随后在循环中反复clone。这正是原型模式"只创建一次原型"的精髓——若批量产出 1,000,000 个实体,构造函数只执行 1 次而非 1,000,000 次。
  • 克隆对象类型不变:通过assertInstanceOf断言每次克隆产物仍是FooBookPrototype/BarBookPrototype类型,保证多态行为。
  • 克隆对象彼此独立:每个克隆体都通过setTitle()设置各自唯一的标题(如'Foo Book No 0'、'Foo Book No 1'...),说明克隆产物是独立实例,互不干扰。

本地运行与测试验证

本仓库基于 Composer 管理依赖(见 composer.json),要求 PHP >= 8.0("php": ">=8.0"),开发依赖包含 PHPUnit^9,并通过classmap自动加载Behavioral、Creational、Structural、More四个源码目录。安装与运行方式如下:

# 1. 安装依赖 composer install # 2. 运行全部模式的测试(含 Prototype 的 PrototypeTest) ./vendor/bin/phpunit

也可只运行 Prototype 模块的测试:

./vendor/bin/phpunit Creational/Prototype/Tests/PrototypeTest.php

phpunit.xml.dist 中配置了测试套件,会扫描Creational/*/Tests等目录下所有以Test.php结尾的测试文件;执行后应看到testCanGetFooBook通过,即证明原型克隆链路(创建原型 → 克隆 → 设置差异字段 → 类型保持)工作正常。此外,仓库支持用 Docker 构建并浏览文档(见 README.md):

docker-compose up --build

使用原型模式的注意事项

  1. 浅拷贝陷阱:原型若包含对象/数组等引用类型属性,克隆后新对象会与原对象共享内部引用,必须在__clone()中手动深拷贝,否则批量修改可能意外污染所有克隆体。
  2. 克隆成本判断:只有当"构造开销 > 克隆开销"时才值得使用原型模式;对于构造函数极简单的对象,直接new反而更清晰,无需引入克隆。
  3. 默认状态设计:原型对象应承载"稳定的默认状态"(如本示例的$category),每次克隆后只改差异字段(如$title),这才能体现批量生产的效率优势。
  4. 与工厂的配合:实践中常将原型注册表(原型工厂)与模式结合,通过类别键直接取出对应原型再克隆,避免调用方接触具体原型类。

小结

通过 DesignPatternsPHP 仓库的 Prototype 模块,可以看到一个完整可运行的原型模式样板:抽象原型 BookPrototype.php 定义规范与克隆钩子,具体原型 FooBookPrototype.php 与 BarBookPrototype.php 固化默认分类,测试 Tests/PrototypeTest.php 实证了"一次原型、多次克隆、类型保持、实例独立"的核心行为。当你的应用面临批量创建同构对象(如 ORM 一次性写入海量记录)的诉求时,用原型克隆替代逐次new,是把对象创建成本从"每次全量构造"降为"一次构造 + 多次浅拷贝"的务实选择。

  • 示例工程
  • 教程

【免费下载链接】DesignPatternsPHP

Sample code for several design patterns in PHP 8.x

项目地址:https://gitcode.com/gh_mirrors/de/DesignPatternsPHP
点击查看免费下载
上一篇:Dots-TTS-MLX-INT8:重新定义边缘设备上的高效语音合成
下一篇:CogVideoX深度解析:10秒高质量视频生成的Transformer架构实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表