阅读 1260

你竟然写出这样的代码

上周开会时,跟同事在讨论,什么是好的代码。

什么是好的代码,怎么定义好的代码。

往简单了说,自己刚写的代码都是好的代码,别人写的都是垃圾。 往复杂了说,高内聚,低耦合,OCP、SRP、ISP,各种概念能写一本厚厚的书。其实这事就跟评价程序员的工作一样,一天写1000行代码的程序员一定比一天写100行代码的程序员好吗?

大佬们闲着无聊也在讨论这个问题,而已已经争论了几十年,其中以Martin大叔的这句话最为经典。

衡量代码质量的唯一有效标准:WTF/min —— Robert C. Martin

再配上这幅经典的图,几乎可以完美的定义,什么是好的代码。

没有最好的代码,只有挨骂的代码,挨骂的多少,决定了代码的好坏。

那么怎么才能写出少挨骂的代码呢。

从宏观上来讲,就是「整洁」二字,这也是《代码整洁之道》这本书的核心。

整洁的代码有几个核心:

  1. 风格整洁
  2. 逻辑整洁

首先,写代码最好有一些洁癖,变量命名前缀、函数名、换行空行,这些最基本的格式,最好能做到团队统一,至少是个人统一,每次写完一部分代码,通过格式化来统一风格。

整洁的代码如同优美的散文。—— Grady Booch

写代码其实跟文学创作一样,古人作诗,首先看的平仄对仗,音律押韵,只有风格统一,才能在看的时候有一种赏心悦目的感觉。

其次,写代码要先思考,程序员写的代码其实并不是给机器看的,而是给人看的。好的代码、不好的代码,在机器看来,其实并没有太大的区别(除了效率以外),无非都是机器码。

任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。—— Martin Fowler

写代码不是为了炫技,一段难读的代码,总是会招致更多的WTF。这也是为什么Kotlin的语法糖在有些场合经常被WTF的原因,团队内的每个人的思考角度、思路可能都不相同,所以有时候最简单的代码反而是最好的代码,Java的代码虽然语法冗长,废话多,但是读上去就像是白话文,更容易理解、维护。而反观Kotlin,如果对语法糖理解不够深入、对函数式编程的理念模棱两可,就很容易写出四不像的代码,俗称Java风格的Kotlin代码,很多人写Kotlin,仅仅是为了使用那些语法糖,少写几行代码而已。

以上,我们可以总结下,从宏观上看,怎么写好代码。

  1. 规范命名风格,比如说公开变量与私有变量的区分,资源命名统一拼音还是翻译,至少不要写出generateWoDeDaiMa这样的命名
  2. 通用流程统一代码模板,比如说请求接口,展示列表数据,刷新与加载更多,这样的操作尽量统一一种写法,避免千人前面
  3. 函数单一职责,尽量函数式编程,做到函数无副作用,减少成员变量、全局标记变量的使用

那么再具体一点,从移动端的角度来看看什么是好的代码。

移动端,或者说是大前端,与后端有很大的不同,后端代码偏逻辑代码,界面很少,大部分的维护,也是逻辑上的改动,而大前端则相反,UI和逻辑几乎绑的很死,大部分时间的修改,都是在处理UI。

在理解了这一点之后,你就能站在一个更高的维度来嘲笑那些为了选择使用MVC、MVP、MVVM还是MVVVVVIP而争论不休的人了,其实这些争论的出发点就有问题,站在前端的角度,这些所谓的分层,很难在所有代码中都合适,借助大佬的一句话「软件工程没有银弹」。

在后端架构中,通过MVC这些分层架构的解耦,确实可以让整体利于维护、拓展,但是在前端,不管是MVC还是MVP,都必须针对特定的使用场景。

移动端的业务场景,大致可以分为两类,业务逻辑代码和功能逻辑代码。

对于功能逻辑代码来说,与后端类似,良好的架构可以让这部分代码变的更加健壮,而对于业务代码来说,这些架构反而容易显得多余,相信很多人都有被Presenter弄疯的经历,原本非常简单的业务逻辑,由于使用了MVP,在修改的时候,需要冗余的修改一堆代码。所以,偏业务逻辑的代码,我们的思路是——写容易删除的代码。

业务逻辑代码通常不包含太多复杂的逻辑(如果有,那就应该拆入功能逻辑代码),而且大部分情况下,这些要处理的逻辑和UI绑定在一起,所以,这个时候,写容易删除的代码,可以让业务变得更加容易维护,毕竟在现在的迭代条件下,没有一段无辜的代码可以活过下个大版本。

那么有钢筋会说了,难道这些大师这么多年的经验都毫无用处了吗?

当然不是,架构必须结合具体的业务来设计,前面讲了,逻辑功能业务,该怎么架构怎么架构,设计模式什么的,能写多少写多少。而纯业务功能,设计模式,则是能写多少写多少。

对于业务功能来说,通常只在合适场景的局部使用这些设计模式或者说分层架构思想。

举个例子,APP中有些组件的设计是有异曲同工之妙的,这些东西,可以被称之为组件,比如统一弹窗、统一按钮、对话框、再比如大一点的,起点读书,书架列表中的一栏,这些东西可能在很多地方都有使用,这时候,MMVM中的VM,就可以派上用场了,通过抽取ViewModel,可以让UI的复用性加强,通过builder、策略模式、工厂模式等设计模式,能让代码的复用度更高、使用性更好。

那么再再具体一点,具体的代码,要怎么写,才是好的代码呢

资深程序员杨过曾经也在思考什么是好的代码这个问题,想不通跳崖了,不过好在最后因祸得福,朝闻道,夕可死矣。

紫薇软剑,三十岁前所用,误伤义士不祥,乃弃之深谷

玄铁重剑,重剑无锋,大巧不工,四十岁前恃之横行江湖

四十岁之后,不滞于物,草木竹石均可为剑,自此精修,渐进于无剑胜有剑之境

这实际上就是程序员的真实写照,刚毕业的程序员,意气风发,拿着键盘准备开天辟地,代码怎么写牛逼就怎么写,套上各种框架、各种分层、各种设计模式,写完还不忘加上注释,这是一段独孤求败的代码。再之后,被业务的毒瘤磨平了棱角,开始返璞归真,每一行代码都写的稳如狗。最后,写代码已经进入忘我的摸鱼境界,经常几天不写一行代码,一写就是好几行。

所以,杨过在四十岁的时候才理解的真谛,我们现在要早点理解了,不然35岁可能就失业了。

首先,没有一种思想是永恒的。十年前,面向对象才是编程的王道,十年后,函数式编程异军突起。十年河东十年河西的事情,在软件开发界太多了,谁能想到C#中的协程概念,居然在多年后,被Kotlin带火了,年轻的程序员们不要为了站队那种思想而争论不休,能解决问题的思想才是好的思想,多去了解不同的思想,让它们在你体内碰撞碰撞,说不定就一团浆糊了(完全理解了)。

其次,想好再码,先做到胸中有码,才能眼中无码,写代码前先花点时间整理下逻辑,理清楚分支情况和异常情况,然后再去动手,不急着那一点时间,又不是比谁敲得快。

再次,慎用继承,这个要单独拎出来说了。

继承,面向对象开发的三大核心原则之一。继承,规范了子父类具有类似行为的类,但是也带来了著名的香蕉猴子丛林(Banana Monkey Jungle)问题,相信很多开发者都遇到过这种问题,当你准备从另一个项目中copy一个类到现有的工程,或者修改一个新引入的类时,你会发现,还需要copy它的父类,甚至还有父类的父类,以及一连串的类,更不要提继承的菱形问题了。

面向对象语言的问题在于,每个类都随身携带了一个隐形环境。您想要香蕉,但是得到的是一只拿着香蕉的猴子和整个丛林。

所以,继承要慎用,但是怎么解决呢?其实可以从两方面来解决,一个是通过修改架构,尽可能避免无效、过度的继承设计,另一个是通过接口来进行行为的约束而不是通过继承,尽可能通过组合的方式来进行架构。

在Flutter/Dart中,这种方式被表现的淋漓尽致,Flutter的整体设计思想就是组合,万物基于组合,另外还提供了mixin来提供功能的混入,在毫无压力的情况下使用其它类的功能,而不是通过继承。

古今之成大事业、大学问者,必经过三种之境界: “昨夜西风凋碧树,独上高楼,望尽天涯路。” 此第一境也。 “ 衣带渐宽终不悔,为伊消得人憔悴。” 此第二境也。 “ 众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。”此第三境也。

对Flutter感兴趣的朋友可以加入我的Flutter修仙群。