代码之丑
郑晔
开源项目 Moco 作者
19833 人已学习
新⼈⾸单¥59
登录后,你可以任选2讲全文学习
课程目录
已完结/共 24 讲
代码之丑
15
15
1.0x
00:00/00:00
登录|注册

11 | 依赖混乱:你可能还没发现问题,代码就已经无法挽救了

你好,我是郑晔。
我们前面已经讲了许多坏味道,无论是你很容易接受的,还是挑战你编程习惯的,它们都有相对直观的表现形式,属于你很容易一下子就看出来问题的。这一讲,我们要讲的坏味道就不属于一下子就能看出来的,需要你稍微仔细一点看代码才会发现问题,那就是依赖关系。
我前面在讲“大类”这个坏味道的时候曾经说过,为了避免同时面对所有细节,我们需要把程序进行拆分,分解成一个又一个的小模块。但随之而来的问题就是,我们需要把这些拆分出来的模块按照一定的规则重新组装在一起,这就是依赖的缘起。
一个模块要依赖另外一个模块完成完整的业务功能,而到底怎么去依赖,这里就很容易产生问题。

缺少防腐层

我们还是先来看一段代码:
@PostMapping("/books")
public NewBookResponse createBook(final NewBookRequest request) {
boolean result = this.service.createBook(request);
...
}
这段代码是创建一部作品的入口,也就是说,它提供了一个 REST 服务,只要我们对 /books 这个地址发出一个 POST 请求,就可以创建一部作品出来。那么,这段代码有问题吗?
按照一般代码的分层逻辑,一个 Resource (有的团队称之为 Controller)调用一个 Service,这符合大多数人的编程习惯,所以看起来,这段代码简直是正常得不能再正常了,这能有什么问题?
从 Resource 调用 Service,这几乎是行业里的标准做法,是没有问题的,但问题出在传递的参数上。请问,这个 NewBookRequest 的参数类应该属于哪一层,是 resource 层,还是 service 层呢?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了软件开发中常见的依赖混乱问题,并提出了解决方案。通过具体的代码示例,作者指出了依赖关系不清晰可能导致的问题,以及如何通过引入新的模型来解决这些问题。文章强调了在设计中构建防腐层的重要性,以隔离不同层次的责任,避免混乱和重复。作者还介绍了一些工具,如ArchUnit,可以帮助程序员在写代码时保证依赖关系的稳定性。总之,本文通过深入浅出的方式,帮助技术人员更好地理解和解决依赖混乱问题,为他们提供了有益的技术指导。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《代码之丑》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • qinsi
    对照DDD,是否可以理解为接口层接收DTO,转换为DO后传入业务层?那么缺少防腐层的问题也可能发生在业务层和持久化层之间,比如业务层直接操作持久化对象(PO)? 相比把DTO当成DO用,把PO当DO用似乎更常见。除了违反单一职责原则之外,实际使用中似乎问题不大,因为很多系统都是从先从数据表开始设计的。郑老师对此怎么看?

    作者回复: 严格地说,持久化对象也应该与业务对象分开,但在实际的项目中,一般来说,持久化对象和业务对象是合在一起的,这么做的主要原因就是,一般持久化都是一个系统在内部完成的,对于一个系统而言,是一种可控的变化。

    2021-01-23
    4
    16
  • pc
    想引申问一个问题,service层的参数也用struct/class来表达,就会构建很多类,感觉很别扭,这是没问题的吗?

    作者回复: 你要习惯于用业务术语去表达,而不仅仅是技术术语。封装是为了概念,而不单纯是为了封装。

    2021-07-10
    5
  • 明星
    老师,如果后期把飞书替换成别的消息发送,需要怎么做呢?假如换成email的话,新建一个继承那个FailureSender接口的EmailFailureSenderImp然后加上@Service,再把飞书的那个服务的@Service注释或删除掉吗?

    作者回复: 这是依赖注入怎么做的事,依赖注入的 bean 其实都是有名字的,可以指定一个名字就好了,也就是 Qulifier。

    2021-01-28
    2
    5
  • 邓志国
    看了bob大叔的干净架构后就改成这样了

    作者回复: 你基本上已经理解了Clean Code。

    2021-01-25
    4
    4
  • 慎独明强
    抽象不依赖于细节,细节依赖于抽象。我们业务中就出现了违反依赖倒置原则。 影响点:当抽象新增一种实现时,业务代码就很难兼容。 到店任务业务由调拨单具体实现来开展,当到店任务新增一种单据,代码写不动了... 增加一种模型,到店任务来进行解藕。具体实现依赖抽象,而抽象不依赖具体实现。这就是血淋淋的教训,也加深了对依赖倒置的理解

    作者回复: 有教训不可怕,能汲取经验最重要。

    2021-06-28
    3
  • bigben
    dubbo接口是resource层还是service层?

    作者回复: 如果你这里的 dubbo 接口指的是 dubbo 框架本身的接口,那它不属于这里任何一层。它只是一种实现,不在业务中,需要适配到业务接口中。

    2021-08-16
    1
  • Jxin
    1.所以说,按ddd的分层。应用层不能接收dto,应当新建一个参数类(这样工厂类的实现不依赖dto倒是很好保证了)?那么这个参数类归属什么?感觉不属于实体也不属于值对象。 2.dto转pojo感觉还是不好。虽然从职责分配的角度看,dto能满足pojo创建的诉求,所以归属到dto做pojo的工厂方法。但这样dto就依赖了pojo,而这个依赖是非强必要的,所以减少依赖可能更好,借助工具单独实现一个dto转pojo的转换器。

    作者回复: 参数类属于业务层,不用纠结它属于什么,它就是一个参数。 具体的转换其实就是一个工厂方法,放到 Request 里面而不是单独放置,就不用多谢一个额外的类了。

    2021-01-26
    2
    1
  • 岁月神偷
    提供稳定的接口,去除不稳定的实现

    作者回复: 接口要稳定,实现可以变。

    2021-01-25
    1
  • Geek_c1529b
    老师,您好,请教个事情,@Task这个注解是做什么用的?是线程池吗?能否给个包全路径

    作者回复: 其实可以忽略它,这不是重点

    2022-06-20
  • 感谢老师的分享,解了自己很久的疑惑。有个问题请教,防腐层的目录该如何组织,既然不属于resource和service层,他该放在哪个目录,如何命名。有没有参考的完整工程目录组织供学习参考?
    2022-07-01
    1
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部