我有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
创建一个类的测试时,我需要模拟UserTypeManager
,然后这个模拟需要返回其他模拟(存储库和管理器)。
这当然是可行的,但它让我想到是否可以避免这种情况。我能想到的唯一另一件事就是允许在测试过程中避免上述复杂性,这样就可以了:
interface UserTypeManagerInterface {
public function addUserType($name, RepositoryInterface $repository, ManagerInterface $manager);
}
/**
* My class managing multiple types of user.
*/
class ManageMultipleTypesOfUsers implements UserTypeManagerInterface {
// ...
}
所以我只想将所有存储库和管理器添加到实现UserTypeManagerInterface
接口的所有类中。所以对象会直接使用给他们的东西。
这样测试会更加清晰,因为我只需要模拟一个管理器和一个存储库来测试类ManageMultipleTypesOfUsers
,但这感觉太过于过度工程了。 ;)
这里的中间地方是否可能?
答案 0 :(得分:1)
我无法给出明确的答案,因为这是一种权衡。
据我所知,User是一个纯值对象?这是一个好的开始。这意味着操纵没有副作用是微不足道的。
现在,考虑一下这些影响你设计的变量:
delete(User $user)
- 只允许管理员,其他实现只是抛出,基本权限检查)或使用截然不同的代码?那么,这些变量如何影响我们的决定?
由于我不知道要求是什么,我无法给你一个理想的解决方案。你提出的(第二)解决方案适用于过度设计的#34;情况下。
更为温和的解决方案是功能性方法:
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为您提供了更具功能性的方法,但不太可测试。有时,这是一个很好的权衡,有时不是。这取决于具体的代码所在的中间位置。
P.s。:在你的第二个提案中,真正对我来说有点气味的唯一一件事就是在addUserType方法签名中有RepositoryInterface $repository, ManagerInterface $manager
。你确定它不是构造函数的一部分吗?