后端面试38讲_12_10_软件设计的目的糟糕的程序员比优秀的程序员差在哪里
你好,我是李志慧。
有人说呢在软件开发中,优秀的程序员比糟糕的程序员的工作产出高一百倍,这听起来有点夸张。
事实上呢,我可能更悲观一点,就在我看来,有时候后者的工作成果可能是负的,也就是说因为他的工作项目会变得更加困延,代码变得更加晦涩,难以维护工期。
因此,推延各种莫名其妙、改来改去的bug一再出现,而且这种局面还会扩散蔓延,连那些本来还好的代码模块也逐渐腐烂告坏,最后项目难以为继,以失败告终。
如果仅仅是看过程糟糕的程序员和优秀的程序员之间,差别呢并没有那么明显。
但是从结果看,如果最后的结果是失败的,那么产出就是负的和成功的项目比,差别不是一百倍,而是无穷倍。
程序员的好坏啊,一方面体现在编程能力上,比如并不是每个程序员都有编写一个编译器程序的能力。
另一方面呢体现在程序设计方面,即使是在没有太多编程技能要求的领域下,比如开发一个订单管理模块。
只要需求明确,具有一定的编程经验,大家都能开发出这样一个程序。
但是优秀的程序和糟糕的程序员之间依然有巨大的差别。
在软件开发设计这个领域,好的设计和坏的设计,最大的差别就体现在应对需求变更的能力上。
而好的程序员和差的程序员之间的一个重要区别,就是对待需求变更的态度差的程序员害怕需求变更。
因为每次针对需求变更而开发的代码,而有可能会导致无尽的bug.而好的程序员呢,则欢迎需求变更。
因为他们一开始就是针对需求变更,进行了软件设计。
如果没有需求变更,那么他们的优秀设计就没有了用户之地产生了一圈落空的感觉。
而这两种不同态度的背后呢,是软件设计能力的差异。
一个优秀的程序员,一旦习惯设计编写,能够灵活应对需求变更的代码,他就再也不会去编写那些僵化的、脆弱的、晦涩的代码了,甚至仅仅是看这样的代码,也会产生强烈的不舒服的感觉。
十多年前发生的一件事,有一天下午一个技术不错的同学突然跟我请假,说身体不舒服,需要回去休息一下。
我看他脸色苍白,有气无力,就问他怎么了。
他回答说,刚才跟另一个组里的同事review代码代码太恶心了,看到中途去厕所吐了,现在浑身难受需要休息,听起来是不是有点不可思议。
但实际上,糟糕的代码就是能够产生这么大的威力。
这些代码在运行过程中使系统崩溃,测试过程中使bug无法收敛,越改越多开发过程中使开发者陷入迷宫掉到一个又一的坑里,而仅仅是看这样的代码,就会使阅读者头晕眼花。
糟糕的设计和代码有以下的一些特点,这些特点共同铸造了糟糕的软件。
第一个是僵化性出现,代码之间耦合严重难以改动,任何微小的改动都可能会引起更大范围的改动。
一个看似微小的需求变更,最后却发现需要在很多地方修改代码比僵化性更糟糕的脆弱性僵化,导致任何一个微小的改动都可能引起更大范围的改动。
而脆弱呢则是微小的改动,就会引起莫名其妙的崩溃或者bug.出现bug的地方,开似与改动的地方毫无关联,或者是软件进行一个看似简单的改动,重新改动,僵化就越读者应该崩溃的。
如果说僵化性容易导致原本只用三个小时的工作,变成了需要三天让程序员加班加点的工作,于是开始吐槽工作的话。
那么脆弱性导致的是突然崩溃,只让程序员开始抓狂回忆人生。
第三个是牢固性,它指的是软件无法进行快速有效的拆分,想要复用软件的一部分的功能,却无法轻易的将这部分功能从其他部分中分离出来。
目前微服务架构大行其道。
但是呢一些项目在没有解决软件牢固性的前提下,就硬着头皮进行微服务改造。
结果可想而知。
要知道微服务是低耦合模块的服务化,首先需要的就是低耦合的模块。
其次呢才是微服务的架构。
如果单体系统都做不到模块的低耦合,那么由此改造出来的微服务系统只会将问题加倍放大,最后就归微服务了。
第四个呢是粘滞性需求变更,导致软件变更的时候,如果糟糕的代码变更方案比优秀的方案更容易实施,那么软件就会向糟糕的方向发展。
许多软件在设计之初也许有着良好的设计,但是随着一次又一次的需求变更,最后变得千疮百孔。
趋向腐汇,第五个是晦涩性。
我们知道代码首先是给人看的,其次才是给计算机执行的。
如果代码晦涩难懂,必然导致代码的维护者,以设计者不期望的方式对代码进行修改。
如致系统腐秽变质,但果软件设计者期望自己的设计在软件开发和维护的过程中,一直都能够被良好执行。
那么在软件最开始的模块中呢,就应该保证代码清晰易懂,后继者参与开发维护的时候才有章法可循。
如果软件是一次性的,只运行一次就被永远的丢弃,那么无所谓设计能实现功能就可以了。
然而现制中的软件呢大多数在其漫长的生命周期中都会被不断的修改、迭代、演化和发展。
淘宝从最初的小网站发展到今天,由上万名工程师维护的大系统。
Facebook从扎克伯格一人开发的一个小软件,如今成为全球服务数十亿人的巨无霸,无不经历并将继续经历演化发展的过程。
接下来我们就看一个软件在需求变更过程中不断腐坏的例子。
假设你需要开发一个程序,将键盘输入的字符输出到打印机上,任务看起来很简单,几行代码就能够搞定。
你可以在文章中看到这几行代码,你将程序开发出来测试没有问题,就很开心的发布了其他的程序员在他们的项目中依赖你的代码。
过了几个月,老板会然过来说这个程序需要支持从纸代机读取数据,于是你不得不修改代码。
为了支持从子代机输入数据,你不得不增加一个布尔变量。
为了让其他程序员依赖你代码的时候,能够正确的使用这个方法,你还添加了一句注释。
但就算是这样,还是有人忘记了重设这个布尔值,还有人搞错了,这个布尔值代表的意思,用心的时候呢就出bug了。
虽然没有人责怪你,但是这些问题还是让你很沮丧。
这个时候你的老板又来找你了,说程序需要支持,输出到子代机上,你只好硬着头皮继续修改代码。
虽然你很贴心的把注释里的这个flag改成了这些flag,但还是有更多的程序员忘记要重设这些奇怪的flag,或者搞错了。
布尔值的意思,因为依赖你的代码而导致的bug越来越多,你开始犹豫是不是需要跑路了?从这个例子我们可以看到一段看起来比较简单清晰的代码,只需要经历两次需求变更,就有可能会变得僵化、脆弱、颜值和晦涩。
这样的问题场景在各种各样的软件开发中呢,随处可见。
人们为了改善软件开发中的这些问题,使程序更加的灵活、强壮,易于使用。
阅读和维护总结了很多的设计原则和设计模式,遵循这些设计原则,灵活应用各种设计模式,可以避免程序腐坏,开发出更强大灵活的软件。
比如针对上面这个例子更加灵活,对需求更有弹性的设计和编程方法,可以是另外一种样子。
我在文稿中也改错了。
代码你可以看一下,我们通过接口将输入和输出抽象出来,copy程序只需要负责读取输入并进行输出,具体输入和输出实现则由接口提供。
这样copy程序就不会因为要支持更多的输入和输出设备而不停的修改,导致代码复杂,使用困难。
可以这么说,应对需求变更最好的办法就是一开始的时候设计就是针对需求变更的,并在开发过程中根据真实的需求变更不断的重构代码,保持代码对需求变更的灵活性。
我们在开始软件设计的时候呢,就需要考虑程序如何应对需求变更,并因此指导自己进行软件设计。
在开发过程中,需要敏锐的察觉,哪些地方正在变得腐坏。
然后用设计原则去判断问题是什么,再用设计模式去重构代码解决问题。
我在面试过程中考察候选人编程能力和编程技巧的主要方式,就是问有关设计原则和设计模式的问题。
我将在软件设计原理。
这一模块主要讲如何使用设计原则和设计模式,去设计强壮、灵活、易复用、易维护的程序。
希望这部分内容能够帮助你掌握如何进行良好的程序设计。
你在软件开发实践中是否曾经见到过一些糟糕的代码?这些糟糕的代码是否符合僵化、脆弱、牢固、廉滞晦涩。
这些特点,这些代码给工作带来了怎样的问题呢?欢迎你在评论区写下你的体验,我会和你一起交流。
也欢迎你把这篇文章分享给你的朋友,或者同事一起交流进步一下。