问题描述
我了解“清洁体系结构”中控制器和演示者的目的,而并不是说用例应该调用控制器来呈现数据(或者至少知道它必须调用控制器),但是假设我们已通过以下方式设置了我们应用程序的界面部分:(伪代码)
interface OutputPort {
+ present(response: Response)
}
interface InputPort {
+ run(data: inptuData)
}
class UseCase implements InputPort {
- presenter: OutputPort
+ run(data: InputData) {
...
response: Response = ...
this.presenter.present(response)
}
}
在这种情况下,我们的UseCase完全不了解外部圈子。它实现了InputPort接口以公开API以让外部圈子调用他,并且它需要一个OutputPort(在Presenter中为Cleaner),将用于传递和“显示”结果。
所以现在通常我们有:
class Presenter implements OutputPort {
+ present(response: Response) {
...
}
}
class Controller {
- presenter: Presenter;
+ executeUseCase() {
data: InputData = ...
useCase: UseCase = UseCase(presenter)
useCase.run(data)
}
}
为什么我不能这样做?
class Controller implements OutputPort {
+ present(response: Response) {
...
}
+ executeUseCase() {
data: InputData = ...
useCase: UseCase = UseCase(this)
useCase.run(data)
}
}
它仅用于SRP还是有其他原因?假设控制器和Presenter足够短并且使用相同的对象,在代码需要拆分之前将两个实现维护在一个类中是否值得?
我的意思是,每次我们谈论Clean Architecture时,我们通常会想到Web应用程序或移动应用程序,在这些应用程序中,与外部世界的交互始终是用户和UI,因此在这种情况下,很明显,应维护两者分开上课是有好处的。 但是,假设外部参与者是一个外部API,可以发送信号通知我们执行某项操作,并且应该根据执行的操作来接收信号。 在这种情况下,如果我创建一个单独的Controller和Presenter,则应同时引用外部API:给控制器以让他监听信号并触发用例,给Presenter以便向他发送信号。用例末尾的API。 考虑到这种交互的简单性,不是最好让控制器和演示者是同一类(尽管仍然实现一个单独的接口以允许将来拆分),以避免在应用程序中传播外部API的知识吗?
解决方法
首先,在这两种情况下,您的依赖项均符合原始架构。
假设控制器和Presenter足够短并且使用相同的对象,在代码需要拆分之前将两个实现维护在一个类中是否值得?
您可以这样做,但是如果将两者分开,则可能更易于测试。因为当您要测试演示者逻辑时,不需要设置控制器的依赖关系。
我的意思是,每次我们谈论Clean Architecture时,我们通常会想到Web应用程序或移动应用程序,在这些应用程序中,与外部世界的交互始终是用户和UI,因此在这种情况下,很明显,应维护两者分开上课是有好处的。但是,假设外部参与者是一个外部API,可以发送信号通知我们执行某项操作,并且应该根据执行的操作来接收信号。
如果使用演示者,通常会处理用户界面,但是可以看到演示者的概念更为笼统。
如果控制器由另一个外部系统调用。该系统还需要某种格式的数据。也许是二进制格式,但也许是XML或JSon。在所有情况下,您都必须处理用例的输出向外部呈现的转换。因此,演示者不一定是UI组件。
因此,术语“展示者”,“序列化器”或“格式化器”都基于相同的概念。而且,如果您将它放在一个单独的类中,则可以轻松对其进行测试。
这是我绘制的图表,用于显示如何在邮件环境中应用干净的体系结构。例如。 JMS。如果外部绝对不是用户界面,那将是一个体系结构。当然,MessageProducer应该使用演示者或序列化器或任何您称为它的东西来创建传出消息。这是一幅较旧的图纸。也许我会改变一段时间。
只要遵守干净架构的依赖关系规则,就可以轻松替换用例向外部展示的方式。