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

08 | 缺乏封装:如何应对火车代码和基本类型偏执问题?

你好,我是郑晔。
上一讲,我们讲的是控制语句体现出的坏味道,它们不是一种坏味道,而是一类坏味道。这一讲,我们再来讲一类代码的坏味道:缺乏封装。
在程序设计中,一个重要的观念就是封装,将零散的代码封装成一个又一个可复用的模块。任何一个程序员都会认同封装的价值,但是,具体到写代码时,每个人对于封装的理解程度却天差地别,造成的结果就是:写代码的人认为自己提供了封装,但实际上,我们还是看到许多的代码散落在那里。
这一讲,我们就来看看,那些被封装遗忘的角落。

火车残骸

我们先从一段你可能很熟悉的代码开始:
String name = book.getAuthor().getName();
这段代码表达的是“获得一部作品作者的名字”。作品里有作者信息,想要获得作者的名字,通过“作者”找到“作者姓名”,这就是很多人凭借直觉写出的代码,不过它是有问题的。
如果你没看出这段代码的问题,说明你可能对封装缺乏理解。
你可以想一想,如果你想写出上面这段代码,是不是必须得先了解 Book 和 Author 这两个类的实现细节?也就是说,我们必须得知道,作者的姓名是存储在作品的作者字段里的。这时你就要注意了:当你必须得先了解一个类的细节,才能写出代码时,这只能说明一件事,这个封装是失败的。
这段代码只是用来说明这种类型坏味道是什么样的,在实际工作中,这种在一行代码中有连续多个函数调用的情况屡见不鲜,数量上总会不断突破你的认知。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了程序设计中的缺乏封装问题,特别是火车代码和基本类型偏执的解决方法。作者指出,缺乏对封装的理解导致代码散落、连续多个函数调用的情况,被形象地比喻为火车残骸。解决这种问题的重构手法是隐藏委托关系,即将调用封装起来,减少对类实现细节的依赖。文章还强调了封装的重要性,指出应该从少暴露细节开始,思考类应该提供的行为。此外,作者提到了编程的指导原则,强调每个单元只与自己最直接的朋友交谈,不与陌生人交谈,并呼吁读者思考类应该提供哪些行为,而非简单地暴露数据。文章内容深入浅出,为读者提供了解决火车代码和基本类型偏执问题的实用建议。文章通过具体的代码示例和重构手法,帮助读者理解并解决了缺乏封装所带来的问题,对于提升软件设计能力具有一定的指导意义。

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

全部留言(24)

  • 最新
  • 精选
  • qinsi
    个人理解: * 链式调用不一定都是火车残骸。比如builder模式,每次调用返回的都是自身,不牵涉到其他对象,不违反迪米特法则。又比如java stream操作,应该就是文中提到的声明性的操作 * 构建模型还有一个好处是增加了一层抽象,屏蔽了外部的变化,类似防腐层的作用。比如写可移植的c代码用typedef定义内部使用的类型而非直接使用基本类型,或是DDD中领域内只处理本领域的对象,使用其他领域的对象要先经过转换而不会直接使用

    作者回复: 这个理解很到位。

    2021-01-16
    47
  • adang
    课程里讲的坏味道自己写的代码上都有,虽然知道不好,但是一直不知道应该怎样优化。没有好好系统地学习过软件设计,凭直觉感觉好或者不好,因为软件设计功底薄弱,明知道不好,却不知道怎样改进。几节课里,老师也一直在强调软件设计的重要性。发现自己确实需要系统的学习一下软件设计,好好扎扎马步,打实基本功了。

    作者回复: 欢迎加入《软件设计之美》

    2021-01-16
    2
    9
  • dev.bin
    今天说的我都干过,一直都没意识到,但总是觉得有许多重复的代码,看着别扭,能早点看到就好了

    作者回复: 早一天知道,早一天应用。

    2021-01-16
    2
    7
  • 克一
    请教老师个问题:Java 里的 JavaBean,用MyBatis Genarater或者Lombok生成都会有Setter方法,这样数据库查询或者接受参数时,数据自动映射到这个对象。如果不用setter的话,应该怎么赋值?

    作者回复: 其实,现在的数据库映射用的都是反射的方式实现,与setter关系不大。下一讲,我们就来说说setter。

    2021-01-16
    7
  • 大京
    我设计了一个客hu模型,包含客hu基本信息(证jian类型,证jian号码,名称),个人信息(有些客hu是自然人,客hu不是用户),企业信息,联xi电话List,地址List(注册地址,经营地址,身份证地址),等等;个人信息、企业信息、联xi电话等都是懒加载,需要用到的时候get才执行查询。如果按照本节的说法,可能这种设计就有问题,但是不知道怎么解决

    作者回复: 先要分析这些模型之间的关系,如果它们是聚合和聚合根之间的关系,那就要一次性的拿出来,没有什么懒加载的问题。如果是组合关系,也许用不同的访问入口更合适。

    2021-01-20
    5
  • 邵俊达
    对这句话不太明白「使用一流的集合」,请问什么是一流的集合?

    作者回复: 简单的理解,就是语言直接提供的数据结构,比如:List、Map、Set 之类的

    2021-08-10
    2
    4
  • 大京
    郑老师,我们实际开发中,每个模型都有对应的ModelService, service里专门负责该模型相关的业务逻辑,譬如某个Model有状态status字段, 如果单考虑模型封装,会在Model里写 激活 active(),取消 cancel(),冻结frozen(),实际状态set成什么service里完全不需要知道。但如果考虑service层就是写业务逻辑的,那Model只需要一个setStatus就行,他们还会觉得封装起来了service层还看不清了。

    作者回复: 这就取决于你模型希望暴露的是什么了,我倾向于暴露行为,而直接暴露状态倾向于暴露细节。行为容易保持一致性,状态如果后面扩充了,是不是所有人都要知道呢,这是个值得考虑的问题。

    2021-03-22
    4
  • 杯莫停
    迪米特法则说的其实就是解耦,“火车残骸”链式调用String name = book.getAuthor().getName();是间接的Author和当前对象发生了直接关系,产生了耦合,也破坏了封装。所以应该通过直接联系的book对象第三方建立间接联系,所谓“隐藏委托关系”。 构建模型封装了数据和处理数据的逻辑,使得外部不需要关心数据的内部逻辑,只关心数据本身。这就是封装性。不知道理解的对不对。

    作者回复: 这个理解很到位

    2022-07-02归属地:陕西
    2
  • AE86
    “根本的问题是缺乏对封装的理解,而一个好的封装是需要基于行为的,所以,如果把视角再提升一个角度,我们应该考虑的问题是类应该提供哪些行为,而非简简单单地把数据换一种形式呈现出来。” 文中提到的这句话,请问能有个较直观具体的例子么?个人感觉这句话很重要,但是理解稍有问题

    作者回复: 《软件设计之美》中,专门有一讲是关于封装的,你可以看一下,这里用的是结论。

    2021-01-20
    2
  • 岁月神偷
    这一章的讲解令人醍醐灌顶,尤其是利用模型解决基本类型偏执的问题讲解上,那个天天环绕在身边的坏味道竟然拨云见雾,感谢

    作者回复: 有收获,可转发😄

    2021-01-20
    2
收起评论
显示
设置
留言
24
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部