前一段时间,我写了一篇文章 ,我在其中基本上说过,MVC和其他类似的编程模式更多地是由木偶大师而不是工程师来完成。
我在整个博客中也多次说过,使用get / set和“ Service”类会使您的应用程序无法控制地增长,直到变得一团糟,以至于荒谬的复杂性的唯一借口就是简单地将其称为“企业解决方案”。
为了让您更好地了解我要说的内容,让我们以一个常见的示例进行研究。
以在Books上常见的CRUD为例。 您有3个主要部分:
- 具有四个HTTP端点的(希望的)RESTful API层。
- 服务层基本上将API层的输入映射到DB实体,反之亦然。
- 与数据库对话并为我们处理实体的存储库或持久层。
现在的问题如下:我们都同意API层应该清除任何逻辑,并且只应处理数据和链接的编组/解组工作; 也许有一些用于输入验证的注释,仅此而已。 我们还同意,持久性/存储库层应仅处理实体和CRUD数据库操作。
这意味着,我们的BooksService始于4个简单的方法和一些映射逻辑,这是我们的应用程序真正可以增长的唯一地方。 最初的样子如下:
如果您问我,上面的类已经很丑了,但让我们回到问题所在:您的代码库在哪里以及如何增长? 除了这些CRUD操作之外,您还将在何处添加任何其他Book逻辑?
假设,在创建图书之前,您应该调用一些Web服务,以检查图书的ISBN是否正确,并与作者和书名匹配。 这段代码会去哪儿? 我看到一些选择:
- 就在方法
BooksService.createBook(Book),然后再进行其他操作。 - 在
BookService某些私有方法中,这将是createBook方法首先调用的方法。 - 在名为
ISBNService的类中创建一个公共方法checkISBN(String),将其插入此BooksService并在createBook内createBook。 - 您可以定义一个注释以放置在方法
createBook(Web服务调用将在注释的处理器中)。
前两个显然是幼稚的解决方案。 第三和第四似乎还可以,但它们仍然是程序性的。 您所要做的就是将一个过程(Web服务调用)放在一个称为“服务”或“处理器”的类中,然后使用框架的DI机制将您的过程放在需要的地方:在BooksService ,当createBook方法为叫。
因此,除了基本的CRUD之外,您还有一个很小的要求,并且您已经在这里和那里注入了方法。 到目前为止,只有一个实体。 这真的很干净并且可扩展吗? 当需要对这些可怜的书有更多逻辑时,您将怎么办? 同样的事情:在某处添加一个方法或处理器,直到您不知道正在使用什么以及由谁使用为止。 更不用说,随着时间的流逝,这些程序将如何进行修改( if/else基于可疑标记阻止),以满足每个客户的边缘情况。
但是请注意:还有很多开发人员对前两个选项没有问题。 您的BooksService将通过私有方法以各种不同的标志以及缺少参数的空值相互调用的方式BooksService发展。
更糟糕的是,您也缺少必要的工具来提出一个优雅的解决方案:您没有装饰的接口,没有用于合成的构造函数(因为我们使用DI注释),什么也没有。 new运算符是任何适当对象的父亲,因此无处可寻。
最重要的是,单元测试在哪里? 还是至少进行了一些阴暗的集成测试? 它们不见了,因为在项目开始时,几乎没有要测试的代码:
- 我们不测试API层-它只是框架中与HTTP相关的一些注释;
- 我们也没有测试持久层,因为它只是我们信任的ORM的一些注释;
- 然后,没有人测试
BooksService因为它只是一堆映射逻辑, “不值得测试”或“谁测试getter和setter的人?” 。 现在,当然,现在进行测试为时已晚。 无论如何,您都不知道如何将DI容器模拟到单元测试中。 除此之外,新功能请求同时泛滥。
请说实话:以上内容听起来是否熟悉?
总而言之,我希望上面的例子能更好地说明为什么这种类型的体系结构实际上是过程性的,因此从长远来看会损害代码质量。
如果您对坚持该ISBN验证还有其他想法,请在下面发表评论!
翻译自: https://www.javacodegeeks.com/2020/05/the-almighty-service-layer.html