仓储模式
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 81 课,共 91 课。
仓储模式是一种设计模式,用于在应用程序的业务逻辑和数据存储之间进行协调。它提供了类似集合的接口来访问领域对象,并隐藏数据实际存储或检索方式的细节。
可以把仓库看作一个专门的集合,它知道如何持久化和检索对象。应用程序代码可以按 ID 从仓库中查找用户,或保存新产品,而不需要知道数据存储在数据库、文件还是 API 中。这种分离让代码更容易测试,也更灵活。
<?php
interface UserRepositoryInterface {
public function find(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): void;
}
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}
仓储实现负责处理实际的存储机制:
<?php
class InMemoryUserRepository implements UserRepositoryInterface {
private array $users = [];
public function find(int $id): ?User {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
public function save(User $user): void {
$this->users[$user->id] = $user;
}
public function delete(int $id): void {
unset($this->users[$id]);
}
}
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$repo->save(new User(2, "Bob", "bob@example.com"));
echo $repo->find(1)->name . "\n";
echo count($repo->findAll());
输出:
Alice
2这种模式的优点是,你可以替换实现,而无需更改业务逻辑。需要从内存存储切换到数据库吗?创建一个实现相同接口的新仓储类。应用程序代码无需改动,因为它依赖的是接口,而不是具体实现。
挑战
简单让我们使用仓储模式构建一个产品库存系统。你将清晰地分离领域对象与存储方式,让应用程序代码能够处理产品,而无需了解存储细节。
你将把代码组织到 4 个文件中:
Product.php:创建一个表示库存商品的Product类。每个产品都有一个id(int)、name(string)和price(float)。使用构造函数属性提升,并将属性设为 public,方便访问。ProductRepositoryInterface.php:定义一个ProductRepositoryInterface,为任何产品存储实现规定契约。接口应声明以下方法:find(int $id): ?Product:按 ID 获取产品,如果找不到则返回 nullfindAll(): array:以数组形式获取所有产品save(Product $product): void:存储或更新产品delete(int $id): void:按 ID 删除产品
InMemoryProductRepository.php:引入 Product 类和该接口。创建一个实现此接口的InMemoryProductRepository类,使用私有数组存储产品。产品应以其 ID 作为索引,以便高效查找。findAll()方法应只返回产品对象(使用array_values()重置数组键)。main.php:引入仓储文件。你将收到两个输入:第一行是要添加的产品 JSON 字符串,下一行是要搜索的整数 ID。JSON 格式如下:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]创建一个
InMemoryProductRepository,保存 JSON 输入中的所有产品,然后使用第二个输入(ID)查找指定产品。按以下格式打印结果:Total products: [count] Found: [name] - $[price]将价格格式化为两位小数。如果找不到该产品,则打印
Found: Not found。
需要更换存储实现时,仓储模式就能发挥优势。主代码只依赖接口:从内存存储切换到数据库时,只需创建一个新的仓储类,无需更改任何应用逻辑。
亲手试试
<?php
require_once 'InMemoryProductRepository.php';
// 读取输入
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// 将 JSON 输入解析为数组
$productsData = (array)json_decode($jsonInput, true);
// TODO:创建一个 InMemoryProductRepository 实例
// TODO:遍历 $productsData,保存每个产品
// 每项都有 'id'、'name' 和 'price' 键
// TODO:获取所有产品并打印总数
// 格式:"Total products: [count]"
// TODO:查找 $searchId 对应的产品
// 如果找到,打印:"Found: [name] - $[price]"(价格保留 2 位小数)
// 如果没找到,打印:"Found: Not found"
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器