26

我最近偶然发现了Bob 叔叔的Clean Architecture,我很想知道 Interactors 是否可以执行其他 Interactors。

例如,这些是我现在的交互器:getEmptyAlbums、getOtherAlbums。两者都有回调,分别返回专辑列表(专辑模型的 ArrayList)。

我是否允许有一个名为 getAllAlbums 的交互器在它的运行块中执行前两个交互器?

@Override
public void run() {
    getEmptyAlbums.execute();       
}

void onEmptyAlbumsReceived(ArrayList<Album albums){
     getOtherAlbums.execute;
}
void onOtherAlbumsReceived(ArrayList<Album albums){
         mMainThread.post(new Runnable() {
         callback.onAlbumsReceived(albums);
     }
});
4

4 回答 4

25

我一直在思考同样的事情,在发现关于这个主题的内容很少之后,我得出的结论是“是的”,这可能是最好的选择。

我的推理如下:

  1. 单一职责:如果您不能聚合用例,那么每个用例就不能真正成为单一职责。如果没有聚合,这意味着域逻辑最终会出现在表示层中,从而无法达到目的。
  2. DRY:用例可以共享,并且应该在有意义的地方。只要用例的意图是相同的。显然,这应该在完成之前考虑清楚。根据我的经验,除了下一点之外,很少需要这样做。
  3. Orchestrator 类:例如,如果您需要获取多个数据源并持久保存到存储库。需要一个将运行所有这些子用例的用例,以确保正确实现操作顺序和并发性等内容。我认为这是调用其他用例的最令人信服的原因。

为了保持单一职责,我会考虑将聚合用例限制为仅执行此操作,即执行这些用例并进行任何最终转换。

考虑到这个问题的年龄,我很想知道您对此采取了哪种方式以及遇到的问题。

于 2017-10-08T23:57:38.167 回答
16

我的回答是否定的。让我解释一下原因:

  1. 那将打破界限

清洁架构最重要的概念之一是边界。每个用例都定义了一个边界,即系统的垂直层。因此,没有理由让用例知道另一个用例的存在。该垂直层允许获得用例的独立开发能力和可部署性。想象一下,我们作为一个团队工作,您开发 GetEmptyAlbums 用例,而我处理 GetAllAlbums 用例。如果我自己调用您的用例,我们不是独立开发的。我们都没有实现独立的可部署性。垂直边界中断。有关详细信息,请参阅 Clean Architecture 书的第 152 页和第 16 章。

  1. SRP 也会被破坏

假设 GetEmptyAlbums 业务规则因任何原因发生更改。您将需要重构该用例。现在也许你需要接受一些输入。如果 GetAllAlbums 调用 GetEmptyAlbums,这个用例也必须重构。换句话说,通过耦合用例,您正在添加更多职责。因此 SRP 中断。

  1. DRY 仍在抱怨

复制有两种:真实复制和意外复制。通过定义 2 个或更多彼此非常相似的用例,您会意外重复。这是偶然的,因为将来可能会因为不同的原因而变得不同(这很重要)。有关此概念,请参见第 154 页。

  1. 测试变得更加脆弱

与 SRP 非常相关。如果您在用例 A 上更改某些内容,并且 C 调用 A,则不仅 A 测试会中断,C 测试也会中断。

总之,答案是否定的,您不能从另一个用例交互器调用用例交互器。但是,如果您想实现纯粹的清洁架构方法,则此规则适用,这并不总是正确的决定。

需要指出的另一件事是,用例必须声明输入和输出数据结构。我不确定您的 Album 类是否是实体,但如果是,则存在问题。正如鲍勃叔叔所说:“我们不想在边界之间作弊和传递实体对象”(第 207 页)。

于 2020-04-25T23:16:46.663 回答
4

看看令人惊叹的“清洁建筑”一书的第 16 章。鲍勃叔叔在名为“复制”的部分回答了这个问题。有2种类型的复制:

真正的重复——引入更改会影响存在重复代码的多个地方。

意外重复——代码现在很相似,但背后的想法不同,随着时间的推移代码变得不同。

在真正重复的情况下,您可以结合用例,但要小心,因为随着软件的发展,在意外重复的情况下拆分它们会更加困难。

于 2021-02-23T17:02:31.533 回答
2

我对鲍勃叔叔的工作非常陌生,我也正在经历这些完全相同的问题。

我对维护 SRP 和不重复用例 (DRY) 的回答是将用例与交互器分开。这可能是矫枉过正,但对我来说确实效果很好。

我将我的用例放在他们自己的文件中,与交互者分开,以便所有单独的交互者可以使用他们想要和共享的任何用例。一直以来,交互者只是“使用”(导入、依赖等)它想要的任何用例。

这样做使我的交互器非常简单,并且实际上只是所需的依赖注入和一些类级别成员变量的容器。

因此,总而言之,getAllAlbums、getEmptyAlbums 和 getOtherAlbums 用例成为它们自己的文件并遵循 SRP,并且您有一个交互器类,可以随意聚合和/或按顺序将用例缝合在一起。

最近我也让我的用例只做实际的业务逻辑,而不包括来自依赖注入网关的东西,比如数据库或网络调用。然后,我将这些依赖网关操作的代码放在操作用例的方法中......

现在,如果您在用例中只有“黑盒”业务逻辑概念,您可以在不包括紧密耦合的依赖项的情况下进行测试。因此,例如,如果您正在制作“井字游戏”游戏,您的用例(快速浏览)将仅使用“井字游戏”的语言,而不是“保存”、“提交”或“获取[ X ]”。您可以在交互器测试或网关本身中保存测试这些内容。

于 2020-03-29T20:34:33.057 回答