我的编程感悟

1. 代码与读书

1.1 重视代码基础

程序由一行行简单代码构成的,如果我们不注意每一行代码的质量(细节),整体的代码怎么会好呢?

  • 比如如何命名变量,函数,类?
  • 比如两个语句的顺序(谁在前,谁在后)?
  • 比如如何去写if和else?
  • 比如是否应该加个空行?
  • 比如是否应该抽取出一个方法,是否应该抽取出一个类?

上面的问题都看似简单,在评审代码中,很多研发都不重视这些细节,喜欢随意的抽取一些方法或类,只关注长方法的抽取,但并不关注抽取的方法是否符合单一职责,也并不关注方法中每一行的代码质量,这样随意抽取不如不抽取。

对于这些基础知识,我们没必要从头思考,虽然从头思考也可以得到正确的结果,但参考业内成熟的知识沉淀,可以加速这个学习过程,这些代码的基础知识,业内很多经典书籍都是在反反复复的强调这些基础,比如《代码大全》,《重构》,《clean code》等,讲的都是这些基础。

1.2 读书

国内很多公司对代码质量重视程度都不够,造成很多程序员对基础知识的不够重视,相对来说,这方面外国书籍写的更好,并不是我们国内程序员不如外国作者优秀,而是我们大部分时间都在疲于项目进度,思考时间过少。比如我在代码评审过程中,发现身边的同事很多都不怎么读书,都在忙于项目。但评审代码中发现的问题都是是书籍中很清楚的描述的,大家只是按照自己最直观的感觉,写出自己认为最省事的代码,如果可以沉下心来思考代码如何写的更容易维护,并参考行业书籍,完全有能力写出更优秀的代码。

读书很多时候并不是学习新知识,更多的是共鸣;读书的时候,很多场景是很多问题我们也思考过,嗯,和作者想的差不多,但作者在某一些点上想的更加深入,通过这种思想交流,我们的知识也就可以逐渐成为更体系更具备复用性的方法论了,读书其实是和作者对话的过程。能读懂作者的,基本都有过和作者类似的思考,读书过程是个再创作的过程,是读者和作者的一个思想碰撞和讨论。站在巨人的肩上,通过读书和优秀的人对话,我们才能不断具备越来越优秀的知识沉淀。

2. 代码/设计/解决方案基本原理

所有的沉淀都是为了复用,软件项目的细节设计很难服用,只有抽象出其中的道理,沉淀为方法论,才能更好的传承和复用,设计原理就是抽象的方法论。但设计原理很抽象,甚至很简单,简单到很多人认为是废话,要做到能够真正的理解,需要在生活和工作中不断的进行尝试和思考。

软件设计的基本原理和代码的基本原理做不到严格的区分,设计和代码其实是逻辑本体的不同形式表现,我认为不仅没有严格区分,应该是一致的,软件方面的原理都是同时适用于代码与设计。所以此处不进行设计的原理和代码的原理的区分。

2.1 利益驱动原则

自己在设计中,尤其是在意识到过度设计时刻,深刻体会到设计都是有成本的,设计带来的灵活性一定要能Cover住成本,才有实施的必要。我甚至觉得「如果一个系统处处有设计,不如处处没有设计」,好钢用在刀刃上,如果一个刀全是好钢,太不经济了。所以不管评审团队成员的设计,还是对自己的设计进行评估,我不再只关注于设计方案本身是否优美,不存在放在那里都适用的设计,评价的标准是非常势利的(向钱看,向利益看),"利益驱动原则"。你这个设计的收益是什么?带来的成本又是什么?是否赚了?利益驱动不仅可以应用于设计,也可以用到代码的写法评审中,不要说只偏信编码原理(比如重构这本书这样说的),需要思考这样写和那样写我们到底有什么我们可以感知的切实的收益,为啥好维护?有收益的写法才是好的写法。不要死读书,只有真正理解了书中做法在哪些场景有收益,才能成为在自己的知识。

利益驱动是个人在工作中不断实践,认为是最有价值的原则,可以充当不同原则是否使用的裁判,不管你对于设计或代码都有效果。背后的原理就是,没有绝对正确的方案,不同方案的区别不是正确和错误,而是适用的场景不同。列举几个不符合利益驱动的场景:

  • 我这用的XXX设计模式。疑问:当前场景适用这个模式真的解决了痛点吗?还是为了适用模式,而使用模式。
  • 《重构》中说不能有长方法,我就抽取了很多短方法。疑问:这些方法符合单一职责吗?命名合理吗?抽取完后真的易读了吗?

2.2 问题与答案

"很多时候,别人来和我讨论问题的解决方案,很多我都会追问,然后呢?然后呢?然后呢?不断逼问提出问题面临真正的痛点是什么?识别到了问题真正的痛点,很多问题就解决了。"当你真正的识别到了问题的本身,大部分问题已经有了答案"。很多问题不是没有想出解决方案,而是根本没有认清问题,药不对症。需要的是识别出真正的问题痛点,抛开其它思想束缚,直接通过问题的痛点思考解决方案,大概率会有收获。然后再基于思考的解决方案,参考成熟的方法论进行校准和优化。

2.3 业务逻辑与代码

在团队的代码评审中,发现很多人的代码之所以有问题,并不是编程技艺问题,而是对于业务逻辑的理解就有问题,代码只是业务逻辑的一种表现形式,业务逻辑理解都不对,实现代码怎么会正确。不应该在代码中去讨论业务逻辑,而是应该在代码之前先梳理清楚业务逻辑。具体参考《先梳理业务逻辑再写代码》。

2.4 童子军军规

来自《CleanCode》的这一条,可以作为写代码态度的基本态度。我们不应该总是抱怨老代码太烂了,所以我也跟着烂,不是我的责任,项目中很多人代码都这么写,老的代码不合理,我就继续保持不合理的风格,这不就恶性循环了?!何不这样?从你这次开始,新增功能按照正确的方式书写,并在老的不好的写法上增加注释,下次有需求改动,请改到新的写法上来,代码不就越来越好了吗?

  • 童子军军规可以作为写代码的基本原则,让你的营地比你来的时候更干净。(clean code 1.6)。

2.5 简单

简单是管理复杂性手段之一。面对复杂的方案,到底是问题复杂,还是我们把问题搞复杂了?不要用复杂的方法解决简单的问题。解决方案要简单,容易看懂。不要有和解决方案无关的代码和设计。具体参考《何为简单和简单的相对性》。

  • 最小复杂度,不要用复杂的方法解决简单问题(代码大全 5.2)
  • 精简性,系统没有多余的部分,不可删。(代码大全 5.2)

2.6 先平铺直述清楚再修辞

在代码评审中,发现代码最难读懂的代码并不是几千行的长方法,而是把长方法抽取为各种不符合单一职责的小方法,方法命名和做的事情也不一致,抽取的层次也非常混乱,要想读懂,你需要人脑把这些小方法全读懂,并且人脑拼接成大长方法。"错误的抽取不如不抽取" 。所以开始思考,代码书写的过程,能平铺直述把逻辑写清楚,用代码块区分下不同的逻辑块,就及格了。如果能针对复用性做出一些合适的抽取就非常不错了。我承认,这样的代码远算不上优秀,但一个团队如果还走不好的时候,不要着急跑,不如老老实实的先走稳来。

代码大全的顺序代码编写部分可以作为怎么走的好的不错的教程(代码大全 3.1 顺序代码的书写)

2.7 夸夸其谈的未来

夸夸其谈的未来,利益驱动最麻烦的是大家就会说未来,我这么设计或代码这么写,是思考未来会如何如何?未来没有发生,怎么说都行,现在也无法考证。我自己以前也做总用未来作为设计的依据,但实际未来发生的时候,很多猜测都是错的,并且认为不可能的,很多都发生了。设计要针对确定性,而不是不确定性。针对未来不如针对过去,我们不可预知未来,或者说预知未来都是通过过去发生的事情,还不如老老实实的根据现在和过去来设计。程序不要盲目提前设计,而是在发生变化的时候再针对设计进行新的扩展性设计。"不要过早的设计,设计要针对确定性"。

  • AHA原理:避免过早抽象。
  • YAGNI原理:在需要功能之前,不要提前实现。

2.8 最小化原则

一个类,一个方法,一个变量应该有一个职责(单一职责)。评审中太多的类,方法,变量都命名非常混乱,不好起名字。问题不是出在了文学功底上,不是因为语文或英文不好,起不了好名字。而是类或方法里面做了太多的事情,没有办法命名。

方法的入参经常传入一些不需要的,不符合我们所的"最小化原则 ",这个问题有时候需要得辩证来看,有的时候为了扩展的通用性,可能会引入"大对象 ",大对象虽然设计上认为不优雅,但如果再场景中收益真的大于成本,依然是可以采用的,"利益驱动原则"才是最终的裁判。

比如生活中,你需要带一个螺丝刀去修一个东西,大概率你会带工具箱去,并不会只带螺丝刀。对应如上所描述程序中,我们经常会用到上下文的大对象,虽然不符合设计原则,但确实带来了方便,我认为收益够高的时候也是合适的。

2.9 重复

DRY(Don't Repeat Yourself)原则告诉我们不要重复,《重构》告诉我们要小方法,小方法要符合符合SRP( Single Responsibility Principle),我原来认为按照理论大方法是不对的。但在个人的工作实践中,大方法并不一定会造成不易读懂的问题,并且也没有必要为了复用而过早的抽取小方法,应该根据需要在需要的时候再抽取也不迟。具体代码还是要通过"利益驱动"来分析。

  • WET原理:相对于DRY (取义干湿相对),可以适当重复。 总结:原理就成语,有两面性。是一定场景的经验汇集,方便传播和总结,不用从头思考。

3 代码编写

3.1 顺序代码的书写

第一个问题,怎么写好一个函数内的代码?也就是最简单的一行,一行的代码。

直线代码

直线代码是我们最常用的代码,简单到很多人都不注意的程度,我们应该认真思考一行行执行的代码如何写。这一块《代码大全》有不错的论述:如果有依赖关系就想办法突出依赖关系;没有依赖关系,最常用的是就近原则,谁和谁关系近,哪些代码就挨在一起。如下给出《代码大全》的相关章节参考。

  • 有依赖关系,突出依赖关系。(代码大全 14.1)
  • 无依赖关系,就近原则。(代码大全 14.2)
  • 变量:尽量靠近第一次使用声明。(代码大全 10.3)
  • 变量:尽可能缩短变量的存活时间。(代码大全 10.4)

条件表达式

复杂的条件表达式让人很难读懂,最常见的是抽取一些中间变量,以及使用卫语句,需要注意卫语句处理非正常情况,不要滥用。关于多态和条件表达式的选用,可以参考《重构》第一章的示例,多态一定比条件表达式化代码更好维护吗?答案是否定的。条件表达式才是成本最低最简单的写法,如果条件表达式可以满足程序易维护的诉求,不用抽取。《重构》中也暗示了下,如果同一个表达式在许多地方出现,使用多态的收益才是最大的。当然坚守纯粹面向对象的人会觉得条件表达式是代码的坏味道,这个有点绝对了,没有绝对的面向对象,内部还是会有面向过程的,对象的出现带来的灵活性(收益)必须大于代码的复杂性(成本)。如下给出一些有用的关于条件表达式的论述。

  • 卫语句,如果两个分支都是正常情况,就应该使用if-esle。(重构9.5;实现模式7.11)
  • 使用多态取代条件表达式。(重构9.7)

条件表达式-配置化

配置化在《代码大全》中称为表驱动,大家平时接触更多的应该是配置化,在一定程度上说的是一回事。数据和代码互相转换,配置==代码。

表驱动更合适的说法应该是程序和数据,有些更适合作为数据,而不应该用程序实现。 一般我们更常说的配置化是一样的道理,只是通常配置的开关。 代码大全的意义在于说白了:逻辑和表是可以相互转换的

  • (代码大全 第18章)

3.3 函数

  • 与其纠结方法的长度,不如把重点放在方法的内聚性,嵌套层次,决策点数量上。(代码大全)
  • 对等性,同一个抽象层级。(clean code 17.4,3.3-G34)
  • 行为和数据隔离,行为应该在操作数据较多的类。(重构 3.7 Feature Envy-依恋情结)
  • 函数对象(重构 6.8;实现模式 8.4)
相关推荐
uzong2 小时前
技术故障复盘模版
后端
GetcharZp2 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程3 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研3 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi3 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国4 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack5 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9655 小时前
pip install 已经不再安全
后端
寻月隐君5 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github