我有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
一次管理多种类型的地方,我将注入它,而在我想管理特定类型的用户的地方,我只能为它的类型注入特定的存储库和管理器对象。
似乎是一种非常干净的方法,但是同时,当我想使用UserTypeManager
I创建类的测试时,需要模拟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,其他实现只是抛出,基本权限检查)还是使用截然不同的代码?那么,这些变量如何影响我们的决定?
由于我不知道要求是什么,因此我无法为您提供理想的解决方案。您建议的(第二个)解决方案适用于“过度设计”的情况。
一个更适度的解决方案是功能方法:
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 $manager
addUserType方法签名。您确定它不属于构造函数吗?
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句