趣谈程序员真香定律:源码即设计


来源 | 码砖杂役

责编 | Carol

封图 | CSDN 付费下载自视觉中国

我们经常谈论架构,讨论设计,却甚少关注实现和代码本身,架构和设计固然重要,但要说代码本身不重要,我不同意,Robert C.Martin大叔也不同意,Martin认为“源码即设计”。

在讨论具体的实施细则之前,我们不妨讨论一下什么是好代码?Robert C.Martin认为:衡量代码质量的唯一标准是:WTF/min,也就是review代码的时候每分钟说“握草”的次数。这个定义虽有辱斯文,但粗野中不失奔放,调皮中又蕴含哲理。

好的代码如同文笔优美的散文,行云流水,赏心悦目,阅读的时候,如沐春风,带给人愉悦与启迪。

好的代码犹如构思精巧的小说,它或许不够平铺直述,却足够引人入胜,读到最后,你会豁然开朗:原来是这样的啊,那一刻,你会觉得过程中的曲折和探索都是值得的。

好的代码,透过一个个函数,你仿佛可以窥视到作者有趣的灵魂;透过一行行代码,你仿佛在与一个充满智慧的朋友聊天,她总是条理清晰,逻辑严谨,有条不紊,娓娓道来。

而坏的代码,犹如病毒,它不仅瘫痪你的程序,还有很强的传播效应,等到它扩散开来,神仙难治。

坏的代码,像一个泥团,阅读的时候,你仿佛被困于黑暗的迷宫,又仿佛在跟一个絮絮叨叨的人交谈,他的脑回路经常短路,说话含混不清,主次不分,叨逼半天,你依然get不到他的中心思想,你常常感觉智商受到了莫大的侮辱,你面露艰难神色,心中万马奔腾。

有很多区分好坏代码的规则,我也看过一些,对于文章中提到的一些标准做法,就不重复嚼舌头根子了,我想结合自己的工作经历,谈一谈自己的切身体会。

闲扯半日,言归正传,要编写弥漫好味道的代码,要遵循哪些约束和指引呢?

一致性

持之以恒的遵从一致性规则,在代码风格上,争论个三天三夜估计也定不出个好坏出来,但好的风格一定是强一致性的,这一点应该比较容易达成一致吧?风格的好坏其实更多受习惯的影响,头发少一点的程序员应该都有自己风格变迁的经历,多年前自己笃信不疑的Good Style或许正是当前深恶痛绝的Bad Style,所以我主张在Style上搁置嘴炮,一个项目应该有一个编码规则,好的规则应该是以理服人的,好的规则应该是拒绝任性夹带私货的,规则定了之后,就遵照执行吧,可能某个风格跟你不相符,但没关系,你要知道,这并不意味,你在style之战败下阵来,也并不表示它说服了你,你遵守的是规则和纪律本身。

我经历过一些c的项目,函数命名有大驼峰,有小驼峰,有下划线连词,还有驼峰加下划线(有些是两个),还有函数名下划线或者两个下划线开头,总结一下它的规律就是没有规律,非常随心所欲,令我莫衷一是。

变量(包括文件、类/结构体、函数)命名,比如ohmygod,你可能搞不清哪些字母是一伙的,所以需要界定单词。驼峰通过单词首字母大写来界定单词,另一个惯用做法是用下划线拼接单词。驼峰的弊端是丑,下划线拼接的弊端是增加了标识符长度(相比首字母大写),好处是跟std c/c++、linux kernel的做法一致,喜欢kernel的码农容易找到如家般的归属感。

c++有namespace避免冲突,c 经常用prefix防止命名污染全局空间,但我认为命名简洁扼要很重要,所以我支持简短的前缀而反对冗长的前缀。

代码密度

实现同样的功能,你喜欢100行代码,还是20行代码?如果贵司不以代码行数考核绩效,我建议你把代码写的精简,而如果贵司以代码行数考核绩效,我建议你离职,开滴滴,送外卖或者摆摊都行,因为在这种公司耗费青春基本上也不会有什么发展前途。

把简单的东西搞复杂化很容易,你只需要找一个能力平庸的人就能实现化简为繁的愿望,而化繁为简则堪称化腐朽为神奇。也许你要说,我欠缺简化的能力,这并不奇怪,坦白讲,这不是一件容易的事,你做不到没关系,但你拥有正确的理念更重要,它将帮助你认清前进的方向,而不是在错误的道路上越走越远。

有些项目,充斥各种无效代码,其实你只需要稍加思考,你就能识别出来。

比如大块注释掉的代码像发臭的尸体一样遍布其中。比如大量功能重复的代码像垃圾一样堆砌在那里。

比如本不需要返回值的函数,执拗的固定返回true,然后在调用的地方还要装模作样的check一下返回值,如果返回false,再记一条日志,再assert一下,再抛个异常,这样显得很有职业操守,美其名曰面向failed编程,处理了各种异常情况。

又或者函数一进来,不管三七二十一,对入参一顿无脑检查,一顿操作猛如虎,一看代码二百五,宣称这是符合ISO XX标准的安全做法,全然忘记你在编写的是一个私有实现函数,你在调用它之前已经检查过一遍,私有函数是一个受控的安全上下文,这不仅不优雅而且不绿色(低效耗电)并且不安全(在该崩的时候没崩把雷埋到了更隐蔽的地方),话说你看标准库函数strcpy/strcat,vector operator[]检查传参了吗?

提高代码密度或者说浓度有利于理清思路,有利于突出重点,有利于提高维护性,而充斥各种无效语句的代码只会把关键语句淹没在汪洋大海,使得review代码的人get不到重点,看不清主次。像听一个絮絮叨叨的人做报告,满篇废话,像看一个剧情拖沓的连续剧,昏昏欲睡,像喝一瓶二锅头兑十斤白开水,口能淡出个鸟来。

重构是程序员的口头禅,重构是在保持程序功能不变的情况下调整架构和实现,我认为提高代码密度应作为重构的一项追求。

linux kernel、lua、nginx、skynet 这些优秀的开源库代码浓度都很高,建议读者朋友品尝一下。

封装

我们最常干的一件事就是把重复编写的代码封装到一个函数里去,用多处调用替代重复编写,这个很好理解,但其实即使不被多处调用,把相关的一段代码封装到一个实现函数也是有必要的,因为把代码平铺开来,把细节暴露出来,容易掩盖重要的东西,即框架和脉络会变得不够清晰。

一个见名知义的函数调用比堆砌在那里的一段代码给我的感受好,我如果关心它是怎么做的,我可以跳转到定义看看实现。

封装的一个核心原则是单一职责,符合单一职责的函数更易于被复用。

解耦

构建松散耦合的系统一直是软件工程的一个目标,模块化的一个方向便是解耦,但我们口口声称心心念想的解耦,在实施层面又有几分体现呢?

比如,我们经常干的一件蠢事就是把类似配置文件,或者宏定义的东西集中的一个头文件里去,看起来很统一也很正规,起码我之前也是这样认为的,但忽然有一天,发现自己这样做显得很不聪明的样子,为什么呢?因为你想把所有模块配置相关的东西都塞进配置公共文件真的合适吗?是不是把公共接口抽离出来更好,把配置相关的数据下沉到各模块更合适?

另外,把宏都定义到一起,这意味随便改点东西,都会需要修改宏头文件,而这个头文件就会成为程序世界的中心,修改公共宏文件几乎会引起整个系统的所有源文件rebuild,这简直就是AOE团灭啊。所以更好的方式是分而治之,去集中式。

我们知道c/c++的编译单元是source file(.c/.cpp),编译的第一步是预处理,所有include都会展开替换,所以我们要避免引入任何不必要的头文件,也应该把本编译单元用到的头文件都include进来,这就是所谓的头文件自给自足。这点很重要,但很多人会不以为然,甚至有些人会搞一个allincluded.h,把常用的一些头文件全部include进来,然后自认为一劳永逸的完美的解决了问题,包含不必要的头文件会增加编译时间,会增加依赖,我们不仅应该避免错误的包含,还应该精心设计和划分文件,使得每个文件的功能足够内聚单一。

缩写

慎用缩写,相比缩写带来的含混不清,我宁愿多敲几下键盘,如果要缩写请符合惯例遵从常规,比如AI,比如App,比如cfg,但是你把threshold缩写成threshod,把Item缩写成Iem,我特木真的搞不懂你是拼错了还是缩错了?

遵从标准

我遇到过一些项目,每个模块单独定义自己的int32,float,char,void,比如定义MoKuaiA_int32,MoKuaiB_int32,MoKuaiC_CHAR,不仅如此,它还把inline,true,false,VOID,const,case,static等一众关键字全部redefine了,令人匪夷所思的是它竟然把标准C API全部重定义一遍,令人发指的是它竟然不让你用语言的标准写法。

我一直搞不懂为什么要这样做?如果你只是需要解决不同体系结构下long等整型的长度差异,我想偷偷告诉你,c库头文件stdint.h已经从标准层面统一解决了这个问题,里面int8_t/16_t/32_t/64_t,还有uint8_t等等应有尽有。

这样的做法是很不好的,会让符合标准的写法寸草不生,建议不要这么做。


宏是c的一个有效武器,在有些情况下确实行之有效,我是宏的中间派,我既反对禁用宏,也反对滥用宏,inline可以部分替代宏,但不能完全替代宏。

但我见过一些项目,到处都是宏,全大写,至少1/3的代码都是各种花里胡哨的宏,你review代码的时候,不停的跳来跳去,看了一眼,哦,就这样啊,然后切回来,频繁的上下文切换是低效的,它打断了你的思路,让你看代码有种撒尿撒到一半憋回去的感觉。其实很多时候完全没有必要,不需要整这些虚头巴脑的。

命名

命名有一些指引,比如类/结构体应该用名词,函数应该用类似动词或者doSomething这样的动宾结构,这些规矩都是耳熟能详的。

我主张命名应该简明扼要,不要罗里吧嗦,要准确的表达出它要做的事情,如果你碰到命名困难,你可能需要考虑你的类定义或者接口划分是否合适。

命名是接口的一部分,很重要,好的命名是自注释的。

如果你没有思路,那我建议你参考一下STD C/C++ API,毕竟这些接口历经几十年没有大的变化,算是经受住了历史的考验,比如malloc/free/atoi,stl 容器的成员函数也有点意思:size(), capacity(),resize(),reserve(),push(),pop(),top(),back(),很干脆,不废话,我觉得很好。

所以,如果你编写的是某某管理器,比如ItemManager,我建议你直接取名add(),remove(),而不用AddItem(),RemoveItem(),因为你本身就是Item的Manager,操作的必然是Item,而且从参数上也能体现出来,少即是多,多不如少。

扩展性

开闭原则是应对扩展性的rule,人无远虑必有近忧,说的是我们不能局限于眼前,但也请不要盲目迷信扩展性,戏太多也是病。

知乎有一篇神贴讲的是如何把helloworld搞成一个big project,当你想给别人项目挑刺的时候,你可以用扩展性说事,但我建议你少说。

避免特例

linus大神分享过他心中的好代码,说的是针对链表的操作,他更喜欢统一性的处理方式,而不是做特例化的处理,我想这个例子很有代表性,它其实代表一种理念,那就是自始至终,我们的头脑里必须优先考虑normal化的处理方式,当然这其实是一个比较高层次的要求,菜鸟互啄可以先跳过这一层要求。

高效而鲁棒

有很多避免运行低效的做法,比如减少拷贝,提高局部性,buffer/cache,空间时间置换,内联,分支预测,判断前置,计算延迟,无锁技术。

提高鲁棒性的关键是面向failed编程,不信任/零信任设计,假设依赖的上下文,上下游都是不可靠的,方法很多,不一一列举了。

最后

喷了这么多,也给大家一次喷作者的机会,贴出来自己的代码:https://github.com/ZhuanJia/mynet,这是13年刚进福厂时候周末自己捣鼓的玩具,它或许有这样那样的问题,如果你觉得不错,请给我点赞,如果你觉得很水,那我可以甩锅给时间,毕竟7年前写的嘛。有句话说过,如果你不觉得一年前的自己是傻逼,那说明你这一年没什么进步,何况7年前呢。

推荐阅读
  • 干货 | 大白话彻底搞懂 HBase RowKey 详细设计

  • 那天我去逛街,发现连大编程语言都摆起地摊了……

  • 研发的未来在哪里?Serverless 云开发来了!

  • Facebook 公司:如何清除 960 万句“脏话”?

  • Linux 之父怒删工程师提交的补丁,称“太蠢了”网友:怼得好!

  • 干货!3 个重要因素,带你看透 AI 技术架构方案的可行性!

  • 热评 | 警惕新基建热潮中的区块链项目烂尾

真香,朕在看了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/517574.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nacos 1.1.4 发布,业界率先支持 Istio MCP 协议

Nacos是阿里巴巴开源的服务发现与配置管理项目,本次发布的1.1.4版本,主要带来的是与Istio的对接功能,使用的是Istio最新的MCP协议。本文将介绍包括这个功能在内的新版本发布的功能。 升级指南 服务端 0.8.0及以上版本: 解压安…

Seata 与 Nacos Config配置中心整合_03

前言:之前我们只将nacos注册中心和seata进行了整合,如果需要实现完整的功能还需要与nacos的配置中心进行整合。 文章目录一、配置管理1. 创建文件config.txt2. 创建nacos-config.sh3. 配置导入nacos配置中心4. 配置验证一、配置管理 1. 创建文件config.…

牛!阿里云位居中国金融云市场第一

近日,全球权威市场研究机构IDC发布《中国金融云解决方案市场跟踪研究,2019H1》报告:阿里云位居中国金融云解决方案市场第一,并成为市场份额突破两位数的唯一厂商,同比增长超过90%。 IDC统计数据显示,阿里云…

抖音、快手和直播行业的火爆究竟给了谁机会?

经常收到一些CSDN小伙伴的留言,反馈如下这样的困惑“短视频这么火爆,我该学些什么技术才能入行?”“我想从事音视频开发,该如何入门和进阶?真的像坊间传闻的那么难吗?”音视频的开发前景做一个不恰当的比喻…

闲鱼前端基于serverless的一种多端开发解决方案

前端的发展太快了,前端框架和技术的发展也层出不穷,还包括不同智能设备的出现,对前端开发同学来说是个很大的跳转,简单列举下: 前端框架:vue、react、angular小程序:微信小程序、支付宝小程序、…

Seata 的AT模式需求实战_04

接上一篇:Seata 与 Nacos Config配置中心整合_03 模拟下单场景:首先去在自己的本地创建一条下单记录,同时,还要去调用库存服务,执行减库存操作。 文章目录一、数据库部分1. 订单库创建2. 表结构初始化3. 库存数据库创建…

国产数据库技术全面破冰,金融核心系统打破国外巨头垄断指日可待

作者 | 马超责编 | 伍杏玲头图 | CSDN下载自东方IC出品 | CSDN(ID:CSDNnews) 作为一名金融IT人,打造完全自主掌控的金融核心,是我们心中的梦想。年轻的读者可能不太了解过去金融系统的建设往事,我们一直沿用至2013年的…

Go语言出现后,Java还是最佳选择吗?

阿里妹导读:随着大量新生的异步框架和支持协程的语言(如Go)的出现,在很多场景下操作系统的线程调度成为了性能的瓶颈,Java也因此被质疑是否不再适应最新的云场景了。4年前,阿里JVM团队开始自研Wisp2,将Go语言的协程能力…

为何小程序上线了,他们的内心却留下遗憾?

前言 出于多端投放和开放生态的考虑,闲鱼开始接入整个阿里小程序体系。闲鱼在9月份迅速上线了第一个小程序鱼塘小程序,由于刚接触不熟悉小程序体系,整体性能上有比较大的优化空间,主要表现在以下问题: 小程序加载慢&…

高德亿级流量接入层服务的演化之路

2019杭州云栖大会上,高德地图技术团队向与会者分享了包括视觉与机器智能、路线规划、场景化/精细化定位时空数据应用、亿级流量架构演进等多个出行技术领域的热门话题。现场火爆,听众反响强烈。我们把其中的优秀演讲内容整理成文并陆续发布出来&#xff…

一篇与众不同的 String、StringBuilder 和 StringBuffer 详解

来源 | 程序员 cxuan责编 | Carol封图 | CSDN 付费下载自视觉中国这是一道老生常谈的问题了,字符串是不仅是 Java 中非常重要的一个对象,它在其他语言中也存在。比如 C、Visual Basic、C# 等。字符串使用 String 来表示,字符串一旦被创建出来…

Ververica Platform-阿里巴巴全新Flink企业版揭秘

摘要:2019云栖大会大数据 & AI专场,阿里巴巴资深技术专家王峰带来“Ververica Platform-阿里巴巴全新Flink企业版揭秘”的演讲。本文主要从Ververica由来开始谈起,着重讲了Ververica Platform的四个核心插件App Manager、Libra Service、…

滴滴经验分享:SQLFlow如何让运营专家用上AI?

蚂蚁金服过去十五年,重塑支付改变生活,为全球超过十二亿人提供服务,这些背后离不开技术的支撑。在2019杭州云栖大会上,蚂蚁金服将十五年来的技术沉淀,以及面向未来的金融技术创新和参会者分享。我们将其中的优秀演讲整…

秒杀场景_Sentinel在秒杀场景的应用_05

上一篇解决了在高并发秒杀场景下的超卖问题,这一篇主要解决如何利用Sentinel防止服务器雪崩。 文章目录一、 商品微服务集成Sentinel1. 引入依赖2. 添加配置二、 秒杀微服务集成Sentinel2.1. 引入依赖2.2. 添加配置三、 Sentinel流控规则3.1. 登录Sentinel3.2. 请求…

SQLServer 密码验证登录18456错误解决方案

一、SQLServer18456错误 二、解决方案 第一步:确定这两个服务都是开启状态 (1)点击管理 (2)点击服务和应用程序 (3)确定这两个都是开启状态 (4)开启方法,右…

80% 的 Java 焦虑,都可以被这张图解决

昨晚,我在路口等车的时候,听到几个人在那讨论问题:“之前我用 jprofiler 监控 jvm 里的对象,当老年代满了,我手动触发一次 fgc,发现只能回收一半,再触发一次,就完全回收,…

直击案发现场!TCP 10倍延迟的真相是?

阿里妹导读:什么是经验?就是遇到问题,解决问题,总结方法。遇到的问题多了,解决的办法多了,经验自然就积累出来了。今天的文章是阿里技术专家蛰剑在工作中遇到的一个问题引发的对TCP性能和发送接收Buffer关系…

今日头条在消息服务平台和容灾体系建设方面的实践与思考

本篇文章整理自今日头条的沈辉在 RocketMQ 开发者沙龙中的演讲,主要和大家分享一下,RocketMQ 在微服务架构下的实践和容灾体系建设。沈辉是今日头条的架构师,主要负责 RocketMQ 在头条的落地以及架构设计,参与消息系统的时间大概一…

秒杀场景_同步秒杀分析和实战_01

文章目录一、应用部署准备1. mysql安装部署2. redis安装部署3. nacos安装部署二、数据库准备2.1. 创建数据库2.2. 初始化表结构2.3. 搭建微服务父工程三、商品模块微服务3.1. 搭建product-serv模块3.2. 配置yml3.3. 实体3.4. 接口3.5. service3.6. controller3.7. 启动类四、秒…

Kali 2020版 Linux操作系统解决系统语言问题(英文--中文)

文章目录一、更新镜像源(1)进入配置文件(2)更新软件包二、修改配置(1)打开终端输入以下数据(2)修改配置文件三、安装中文字体四、restart(重启Kali Linux系统)一、更新镜像源 镜像源有很多,我这里提供了阿里云的镜像源…