PHP:管理实体类型的设计模式

ŁukaszZaroda

我有User实体。我希望该实体具有多个“类型”,并具有不同的管理器和存储库。所有User类型的所有实体都只能共享UserInterface现在,我正在寻找一种组织一切的好方法。我想到的第一件事是创建如下所示的内容:

interface UserTypeManagerInterface
{
  public function addUserType($name, RepositoryInterface $repository, ManagerInterface $manager);
  public function hasType($name);
  public function getRepository($type);
  public function getManager($type);
}

然后,在我想User一次管理多种类型的地方,我将注入它,而在我想管理特定类型的用户的地方,我只能为它的类型注入特定的存储库和管理器对象。

似乎是一种非常干净的方法,但是同时,当我想使用UserTypeManagerI创建类的测试时,需要模拟UserTypeManager,然后此模拟将需要返回其他模拟(存储库和管理器)。

这当然是可行的,但是这让我思考是否可以避免。我能想到的唯一一件事是,它可以避免在测试过程中出现上述复杂性:

interface UserTypeManagerInterface {
    public function addUserType($name, RepositoryInterface $repository, ManagerInterface $manager);
}

/**
 * My class managing multiple types of user.
 */
class ManageMultipleTypesOfUsers implements UserTypeManagerInterface {
    // ...
}

因此,我只是将所有存储库和管理器添加到所有实现UserTypeManagerInterface接口的类中因此,对象将直接使用提供给他们的东西。

这样一来,测试将变得更加简洁,因为我只需要模拟一个管理器和一个存储库来测试类ManageMultipleTypesOfUsers,但这感觉太像过度设计了。;)

这里甚至有中间立场吗?

布韦比

我不能对此给出确切的答案,因为这是一个折衷方案。

据我所知,用户是纯值对象吗?那是一个好的开始。这意味着在没有副作用的情况下进行操作很简单。

现在,请考虑以下将影响您的设计的变量:

  • 接口只是一种方法吗?[或者它的所有方法对于一个方法来说都是琐碎的包装?]
  • 该接口是否旨在让您的库用户定义自定义实现?
  • 这些实现只是添加断言(例如,接口需要一个方法delete(User $user)-仅允许Admins,其他实现只是抛出,基本权限检查)还是使用截然不同的代码?
  • 我们有多少过度设计和无法测试的混乱?

那么,这些变量如何影响我们的决定?

  • 如果您只有一个[较小的]中心方法,则接口和类可能会过分杀人,并且往往会在您的代码库中堆满许多小文件。通常,传递闭包也可以达到相同的效果。
  • 除了简单的Closures / callables足够之外,公共API始终应该是接口。
  • 当实现仅添加断言时,通常最好使用一个类向访问控制管理器分配一个请求[从数据库中读取/缓存权限]。
  • 过度设计与无法测试:最好将每行三行不同代码的50个文件合并为一个三百行的单个文件。

由于我不知道要求是什么,因此我无法为您提供理想的解决方案。您建议的(第二个)解决方案适用于“过度设计”的情况。

一个更适度的解决方案是功能方法:

function addAdminUser($name, RepositoryInterface $repository, ManagerInterface $manager) { /* ... */ }
function addNormalUser($name, RepositoryInterface $repository, ManagerInterface $manager) { /* ... */ }
// you even can pass this around as a callable ($cb = "addAdminUser"), or (pre-binding):
$cb = function($name) use ($repo, $mgr) { addAdminUser($name, $repo, $mgr); };

或从根本上讲(如果普通用户是管理员用户添加的子集)[只要函数本身没有副作用,并且其调用方不会太难测试]:

function addUser($name, $type, RepositoryInterface $repository, ManagerInterface $manager) {
    /* ... common logic ... */
    if ($type == IS_ADMIN_USER) { /* ... */ }
}

如果太激进,还可以向addUser插入回调:

function addUser($name, $cb, RepositoryInterface $repository, ManagerInterface $manager) {
    $user = new User;
    /* ... common logic ... */
    $cb($user, $repository, $manager);
}

是的,功能性方法可能不那么可测试(即,如果直接调用该函数(不传递可调用性),则将无法模拟该函数),但它可能会为您带来巨大的可读性。保存琐碎的间接访问级别可能会使您的代码更易于分析。我强调的是may,因为消除过多的间接访问可能会达到相反的效果。找到适合您的应用程序的中间立场。

TL; DR:PHP为您提供了一种功能更强大的方法,但是却缺乏可测试性。有时这是一个很好的权衡,有时则不然。这取决于中间立场所在的具体代码。

附:在您的第二个建议中,真正让我有点难受的唯一事情是具有RepositoryInterface $repository, ManagerInterface $manageraddUserType方法签名。您确定它不属于构造函数吗?

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

宁静的设计实体类型。

来自分类Dev

域驱动设计。实体类型设计

来自分类Dev

游戏实体类型和子类型的良好模式或架构?

来自分类Dev

关系数据库设计-创建付款类型的实体类?

来自分类Dev

201的响应实体类型已创建?

来自分类Dev

如何确定PartyId的实体类型?

来自分类Dev

Symfony 表单验证实体类型

来自分类Dev

SDN 参数化关系实体类型

来自分类Dev

每个原子实体中的实体类型属性

来自分类Dev

F#实体类型提供程序类型范围

来自分类Dev

DocumentDB:获取相同实体类型的所有文档

来自分类Dev

IntelliJ:要添加到项目中的实体类型

来自分类Dev

云端点:不允许实体类型的数组或集合

来自分类Dev

找出预操作所属的实体类型注释

来自分类Dev

不允许在查询中构造实体类型

来自分类Dev

实体类型没有定义键EF6

来自分类Dev

使用实体类型表达式的Spring数据JPA

来自分类Dev

Hibernate可以自动识别实体类型吗?

来自分类Dev

数据存储区:Put上的无效实体类型

来自分类Dev

为什么我的实体类型关联被切断?

来自分类Dev

breezejs:向实体类型添加引用约束

来自分类Dev

CoreData&RestKit-从多个来源获取相同的实体类型

来自分类Dev

相同实体类型代码的多对多关系首先

来自分类Dev

如何使用Where子句获取实体类型的列表

来自分类Dev

实体类型配置映射问题(针对不同的列名)

来自分类Dev

找出预操作所属的实体类型注释

来自分类Dev

Symfony 2.8表单实体类型自定义属性

来自分类Dev

训练或自定义单词实体类型?

来自分类Dev

对象:不是已知的实体类型。Eclipse链接