核心要点
什么是仓库模式?
简单来说,它是应用程序和数据源之间中介层的一种实现。双方都不需要了解对方即可执行各自的任务,这使得我们可以拥有一个解耦的架构,从而有助于在大型应用中进行扩展,而无需硬编码依赖关系。
为什么你应该关注它?
让我们用一个例子来理解这一点。假设我们正在构建一个在线商店,销售橙味糖果。这是一个小型商店,它保留本地库存,所以我们不需要任何花哨的东西。店面应用程序可以只连接到数据库,并根据现有的库存量在线接单。由于商店只有一个供应仓库并且运营区域有限,这将运行良好。但是,如果这家商店想要扩大其运营区域会发生什么?商店可能想要扩展到另一个城市或全国各地,而拥有一个中央库存系统将非常麻烦。
如果我们仍然使用数据模型,那么我们的应用程序将是某种程度上紧密耦合的。店面应用程序需要知道它必须与之交互的每个数据源,这是一个糟糕的应用程序设计。店面应用程序的工作是允许客户订购糖果,应用程序不应该关心数据源,它不应该跟踪所有不同的数据源。这就是数据仓库发挥作用的地方。根据仓库模式,一个公共API通过接口公开,每个使用者(在本例中是我们的店面应用程序)都使用该API与数据源进行通信。使用哪个数据源或如何连接到它,这些都不关应用程序的事。应用程序只关心它获得的数据和它发送以保存的数据。
一旦实现了仓库模式,就可以为每个数据源创建仓库。店面应用程序不再需要跟踪任何数据源,它只需使用仓库API来获取所需的数据。
它是万能药吗?
不,它不是。像每个设计模式一样,它有其优缺点。
优点:
缺点:
如何操作?
让我们来看一个简单的代码示例。我将在示例中使用 Laravel 来利用其出色的依赖注入功能。如果您使用任何现代 PHP 框架,那么它应该已经具有依赖注入/IoC 容器。实现仓库模式需要依赖注入,因为如果没有它,您将无法将数据仓库绑定到仓库接口,而整个想法是面向接口编程以避免硬编码耦合。如果您没有使用任何框架或您选择的框架没有 IoC 容器,那么您可以使用现成的 IoC 容器(请参阅脚注)。
让我们开始吧。首先,我们在 Composer 中设置我们的命名空间和自动加载。打开 composer.json 并为我们的命名空间添加 psr-4 自动加载(在 autoload 节点中,紧跟在 classmap 之后)。
"autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds", "app/tests/TestCase.php" ], "psr-4": { "RocketCandy\": "app/RocketCandy" } },
保存后,在终端中执行 composer dump-autoload -o
以注册新命名空间的自动加载。在 app/RocketCandy/Repositories/OrangeCandyRepository/
中创建 OrangeCandyRepository.php
。这将是我们的仓库接口。
<?php namespace RocketCandy\Repositories\OrangeCandyRepository; interface OrangeCandyRepository { public function get_list( $limit = 0, $skip = 0 ); public function get_detail( $candy_id = 0 ); }
现在我们有了接口,我们可以创建一个仓库。在 app/RocketCandy/Repositories/OrangeCandyRepository/
中创建 CityAOrangeCandyRepository.php
。
<?php namespace RocketCandy\Repositories\OrangeCandyRepository; class CityAOrangeCandyRepository implements OrangeCandyRepository { public function get_list( $limit = 0, $skip = 0 ) { // 查询数据源并获取糖果列表 } public function get_detail( $candy_id = 0 ) { // 查询数据源并获取糖果详情 } }
为了将 CityAOrangeCandyRepository
仓库绑定到 OrangeCandyRepository
接口,我们将利用 Laravel 的 IoC 容器。打开 app/start/global.php
并将以下内容添加到文件的末尾。
//OrangeCandyRepository App::bind( 'RocketCandy\Repositories\OrangeCandyRepository\OrangeCandyRepository', 'RocketCandy\Repositories\OrangeCandyRepository\CityAOrangeCandyRepository' );
注意:我只在 global.php
中放置了 IoC 绑定以进行演示。理想情况下,这些应该放在它们自己的单独文件中,您可以在其中放置所有 IoC 绑定,然后在此处的 global.php
中加载该文件,或者您可以创建服务提供程序来注册每个 IoC 绑定。您可以在这里阅读更多信息。
现在我们可以通过接口使用仓库了。在位于 app/controllers/
中的 CandyListingController.php
中。
"autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds", "app/tests/TestCase.php" ], "psr-4": { "RocketCandy\": "app/RocketCandy" } },
在这里,我们将 OrangeCandyRepository
接口注入到我们的控制器中,并将它的对象引用存储在一个类变量中,该变量现在可以被控制器中的任何函数用来查询数据。由于我们将 OrangeCandyRepository
接口绑定到 CityAOrangeCandyRepository
仓库,它将就像我们直接使用 CityAOrangeCandyRepository
仓库一样。
因此,现在,数据源的类型和种类是 CityAOrangeCandyRepository
的唯一关注点。我们的应用程序只知道 OrangeCandyRepository
接口及其公开的 API,每个实现它的仓库都必须遵守该 API。仓库在运行时从 IoC 容器中解析,这意味着可以根据需要设置接口仓库绑定,接口可以绑定到任何数据仓库,而我们的应用程序无需关心数据源的变化,数据源现在可以是数据库、Web 服务或跨维度超数据管道。
并非所有情况都适用
正如我在仓库模式的缺点中提到的那样,它会增加应用程序的一定复杂性。因此,如果您正在制作一个小型应用程序,并且您没有看到它会发展到大型应用的程度(可能需要调用多个数据源),那么最好不要实现它,而坚持使用旧式数据模型。了解某事物与了解何时使用该事物是不同的。这是一个非常方便的设计模式,它在创建应用程序以及必须维护或扩展(或缩减)应用程序时可以节省很多麻烦,但它并非适用于所有应用程序的万能药。
我使用了 Laravel 特定的代码来演示上面的实现,但是它对于任何不错的 IoC 容器来说都相当简单且相似。有问题?请在下面的评论中提出。
脚注:
以下是一些您可以使用的 IoC 容器库,如果您的框架没有或您没有使用框架:
建议阅读:
关于仓库模式的常见问题
(此部分内容与原文内容高度重合,为了避免重复,此处省略。原文中的常见问题解答部分已包含了对仓库模式的全面解释。)
以上是存储库设计模式神秘的详细内容。更多信息请关注PHP中文网其他相关文章!