普通人:
我今天要买一斤苹果。
程序员:
我今天要买一斤苹果。
因为我只喜欢红富士苹果,所以我只买红富士苹果。
我能接受的最高价格是10元/斤。
正常情况下一斤苹果用一个袋子能装下,但是为防万一,我会带两个袋子。
我知道附近的3家水果店,所以我会依次访问这3家水果店。
根据上述条件,我设计出以下的买苹果的流程:
买苹果流程开始 对水果店0、水果店1、水果店2依次执行: 拜访一家水果店流程开始 走到此水果店 如果此水果店没有开门,则结束当前的“拜访一家水果店流程” 如果此水果店没有苹果,则结束当前的“拜访一家水果店流程” 如果此水果店的苹果当中没有红富士苹果,则结束当前的“拜访一家水果店流程” 如果此水果店的红富士苹果剩余不到一斤,则结束当前的“拜访一家水果店流程” 如果此水果店的红富士苹果的价格高于10元/斤,则执行3次: 讲价流程开始 询问店主是否愿意将价格降到10元/斤或更低 如果店主愿意,则跳过剩余的“讲价流程” 讲价流程结束 如果此水果店的红富士苹果的价格仍然高于10元/斤,则结束当前的“拜访一家水果店流程” 打开一个袋子,将其作为当前的袋子 重复执行以下流程,直到总重量大于一斤: 装袋一个苹果流程开始 从所有的不在袋子中的红富士苹果中选出最好的一个 如果此苹果能装入当前的袋子,则将此苹果装入当前的袋子,否则执行: 换袋子流程开始 如果我有剩余的袋子,则从中任意选出一个并作为当前的袋子,否则执行: 向店主要袋子流程开始 向店主索要一个袋子 如果店主拒绝给我袋子,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程” 将店主给我的袋子作为当前的袋子 向店主要袋子流程结束 换袋子流程结束 测量我的所有袋子里的所有苹果的总重量 装袋一个苹果流程结束 根据我的所有袋子里的所有苹果的总重量和店主给出的价格,计算我应付的价格 向店主询问我应付的价格 如果我不接受店主索要的价格,则执行3次: 校对流程开始 向店主解释我计算出的价格,并询问其是否同意 如果店主同意,则跳过剩余的“校对流程” 校对流程结束 如果我仍然不接受店主索要的价格,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程” 如果我没带钱,则将我的所有袋子里的所有苹果取出,然后结束当前的“拜访一家水果店流程” 付钱拿走苹果 跳过剩余的“拜访一家水果店流程” 拜访一家水果店流程结束 买苹果流程结束
这个流程怎么样?我来设计一些测试样例,测试一下这个流程。
测试发现一个问题:如果水果店0和水果店1都有红富士苹果并且价格都低于10元/斤,而且水果店1的价格比水果店0更低,那么我希望买水果店1的苹果,但我设计的流程会让我买水果店0的苹果。
为了解决这个问题,我应该先询问所有水果店的价格,然后去价格最低的那一家买苹果。
经过修改,我重新设计出以下的买苹果的流程:
买苹果流程开始 对水果店0、水果店1、水果店2依次执行: 询问一家水果店的红富士价格流程开始 走到此水果店 如果此水果店没有开门,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程” 如果此水果店没有苹果,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程” 如果此水果店的苹果当中没有红富士苹果,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程” 如果此水果店的红富士苹果剩余不到一斤,则视此水果店的红富士价格为无穷大元/斤,并结束当前的“询问一家水果店的红富士价格流程” 向店主询问此水果店的红富士苹果价格并记录 询问一家水果店的红富士价格流程结束 从3家水果店中选出红富士价格最低的一家(如果有并列则随机选择),将其作为目标水果店 如果目标水果店的红富士苹果价格为无穷大元/斤,则结束当前的“买苹果流程” 走到目标水果店 如果此水果店的红富士苹果的价格高于10元/斤,则执行3次: 讲价流程开始 询问店主是否愿意将价格降到10元/斤或更低 如果店主愿意,则跳过剩余的“讲价流程” 讲价流程结束 如果此水果店的红富士苹果的价格仍然高于10元/斤,则结束当前的“买苹果流程” 打开一个袋子,将其作为当前的袋子 重复执行以下流程,直到总重量大于一斤: 装袋一个苹果流程开始 从所有的不在袋子中的红富士苹果中选出最好的一个 如果此苹果能装入当前的袋子,则将此苹果装入当前的袋子,否则执行: 换袋子流程开始 如果我有剩余的袋子,则从中任意选出一个并作为当前的袋子,否则执行: 向店主要袋子流程开始 向店主索要一个袋子 如果店主拒绝给我袋子,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程” 将店主给我的袋子作为当前的袋子 向店主要袋子流程结束 换袋子流程结束 测量我的所有袋子里的所有苹果的总重量 装袋一个苹果流程结束 根据我的所有袋子里的所有苹果的总重量和店主给出的价格,计算我应付的价格 向店主询问我应付的价格 如果我不接受店主索要的价格,则执行3次: 校对流程开始 向店主解释我计算出的价格,并询问其是否同意 如果店主同意,则跳过剩余的“校对流程” 校对流程结束 如果我仍然不接受店主索要的价格,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程” 如果我没带钱,则将我的所有袋子里的所有苹果取出,然后结束当前的“买苹果流程” 付钱拿走苹果 买苹果流程结束
现在这个流程是不是完美了呢?不是,我还能发现很多问题。
如果3家水果店都有红富士苹果但都不到一斤,但是三家店加起来能达到一斤,那么我不应该结束流程回家,而是应该把三家店的红富士苹果都买下来。
如果我向水果店询问价格的时候这家店还有红富士苹果,但我询问完所有水果店的价格后这家店的红富士苹果卖完了,那么我的流程会让我试图处理不存在的红富士苹果。
我走路的过程中可能会遇到突发事件,比如发现了新的水果店,比如袋子破掉了苹果掉一地,对于这些情况我的流程都无法进行处理。
啊问题太多了我懒得再改流程了。我还是去X宝买吧。
那么接下来我要设计一个在X宝买红富士苹果的流程……
==========================================================
这篇沉寂了5天只得到1个赞的回答竟然一夜之间收获2000+赞,一举成为个人最多赞的回答。感谢大家的支持,以及精彩的评论。
这篇回答并不是讲述在生活中程序员如何买苹果,而是以买苹果为例说明程序员如何解决问题。程序员需要对问题进行透彻的分析,理清其涉及的所有细节,预测可能发生的所有意外与非意外的情况,列出解决方案的所有步骤,以及对解决方案进行尽量全面的测试。
而这些正是我认为编程难的地方。任何一点遗漏都会成为bug,轻则导致挨骂,重则导致经济损失甚至危害安全。
注意这些难点全部和语言无关。和编程所需要的绝对严密的逻辑相比,语言实在是太简单了。(某些自带代码混淆效果的语言除外)
==========================================================
我勒个去4000+赞了!妈妈!我火了!
好多评论说这代码像python。然而我根本不会讲python。
==========================================================
本回答可以随意转载,只需附带原文链接即可。
==========================================================
好不容易写了篇高赞回答,不打个广告是不是有点浪费?
求偶。本人男,爱好女,27(划掉)28岁,Google员工,现居湾区。要求对方同样现居湾区并有稳定身份(不符合者抱歉勿扰)。有意者请私信。(注:已失效)
更新(5/30/2021):
这篇4年前的老回答竟然又火了一波,我已经不理解互联网是如何运作的了。
上述广告仍然有效。我是一名会及时更新文档的程序员,如果哪天脱团了会回来更新这条广告的。湾区是指美国加州的湾区;稳定身份是指可以在美国长期生活(例如持有绿卡或者在申请绿卡)。
广告是2020年加上的,所以并不是说我4年一来一直单身,不过现在确实是。
==========================================================
更新(2/15/2022):
上述广告已失效!我脱单了!
加一套免费推广:Coffee Meets Bagel,为寻找serious relationship的人而设计的约会平台。
这回答隔了数年被 @微调 小姐姐点赞之后又引来了大量看热闹的观众和评论; (相比之下费劲精力写的专栏技术文章基本没人看真是惨)
想在这里统一回复一个关于认为做“最新东西”写的乱点没问题,反正最后会重构的问题;
这个理论对么?当然对,但是有一个前提,就是做的东西简单, 更确切的讲, 好测, 能很容易验证基本逻辑或者系统的normal path;
对于不好测,且出一个bug就影响重大, 可能会导致黄掉整个项目,那么程序复杂度就不能过于给系统添麻烦,程序复杂度和业务复杂度不成基本线性关系,说明这个程序员写程序没有scalability,做点简单东西还行,做复杂东西就原地爆炸;
程序员产出的code能不能在复杂度这个维度上scale,是我想指出的东西;
————————原文分割线————————
记得之前组里来了一个美国实习生小伙子,很geek的那种,干活快,一天能给你写2000行代码(我code review的速度跟不上他写的速度),让做什么东西,上午告诉做个这个功能,下午就能在测试环境跑起来演示了。跟他单独开会的时候,他说觉的普通的编程没什么意思,太简单了,写程序这方面已经没什么追求了,他比较想跟我研究大数据的框架,数据库,或者机器学习之类的工作,做设计,早日脱离代码这种无脑工作。
我足足花了1周时间,每天review他的代码到凌晨。给他写的comments反馈快赶上我在知乎写的答案文章之和了。。。期间几小时几小时的开会论战,孩子狂,语速快,脑力灵,辩论角度刁钻。他天天要与我论战,看我的评语,速度还算慢下来了。
没来得及讨论完,隔周我要休假了,2周。交代了些他要做的工作。
2周回来,让他改的那个java包爆炸了,本来我们一个支持了7个功能的框架包,总代码量也就5k把,等我回来这包代码量1w5+。也就是说他为了一个小功能加了1w行代码。
这没法review,只能跟他坐一块,先让他给我讲讲这代码都干什么的,然后他说:
(沉思+100)这块我现在也看不太懂当时为什么这么写了。。。
(沉思)这边写的比较复杂是因为当初那边是那样写的,所以这边没办法才只能这么写。
(沉思)把当初那边改好很麻烦,影响也很大,不如就这样吧。
(沉思)这里这么写是因为你看着里是这样的, 然后这里有这个逻辑,然后这里。。。(来回来去翻n个类之后)。。。 所以你看我这里虽然写的比较诡异,但是完全没问题的!(得意ing)
(沉思)这边做的这么奇怪是因为有个bug,通过这么写,这个就bug没了,我也不知道怎么回事。。。所以你看我在这边注释,这行不能删了。。。
(沉思)我觉得这个功能很酷,你们虽然现在不需要,不过有总比没有好吧,将来如果……%¥……&%&……%*7&%……*%…(我没听懂)的话,这个就很有用!!
...
一次一次被我打回去重写,后来总算简化成大概5k行了;临走时候跟我说:你这样编程也太难了。。。
再后来由于一些额外复杂的代码造成我们实现新东西会很复杂,我又重写了一遍,总共大概不到1k行代码。
这里边有几件事情我想说:
然后我又想问几个问题:
我们所在的部门,所在的组,公司,它们的文化,到底是关心作出了一个东西,还是关心做好了一个东西。一个总是给系统添加垃圾,留坑给后人,但是能很快做出能跑起来的系统的程序员,我们到底认为他是做了好事还是做了坏事?我们到底认为他很强,还是他很弱?用超过必要而为了突显技术实力(或者练手)的复杂工具,技术框架搭建系统,做完跑路,在一个组,一个部门,一个公司,那里的文化,到底应该是鼓励还是抑制这种行为?我们又应该如何在一个环境中,去倡导推崇什么样的文化,相遇什么样的人?
人与文化,决定了什么人留在这里,什么人离开,什么人吸引什么人,什么人成长成什么样子。而设计/技术这些枝末细节则必顺应此中的人与文化而自然变化,或自愈,或走向毁灭;哪怕在恶劣的环境中,向下引导,向上规谏,潜移默化,最终改天换日,此为编程之大道也!
下边是定理证明(雾) ( ̄∇ ̄)
最小垃圾存在定律:定义垃圾为系统的总复杂度减去系统的本质复杂度;那么得到:如存在多种方法可以设计与实现一个系统或功能,存在且只存在一种实现会引入最少的垃圾;
垃圾与复杂度正比定律:根据定义可得,系统存在的垃圾越多,系统越复杂;
垃圾倍增定律:基于已有垃圾量a的现状来演化,进化此系统,增加的新垃圾量与已有垃圾量a成正比;
系统腐败定律:当基于垃圾量a来实现新功能的cost大于新功能本身的价值时,系统腐败,需要重构;
战斗人员负战力定律:如果程序员a引入的垃圾,在n次迭代中经过倍增所造成的成本,大于其所清扫的垃圾经过倍增所获得的机会成本,和其实现的新功能价值之和。此时,我们称此程序员战力为负值,其战力绝对值与其引入垃圾的能力和其清扫垃圾的能力的差值成正比
以一敌百存在定律:由负战力定律可知,对所有的自然数n,一个正战力的战斗人员的战力 > (负战力战斗员1+负战力战斗员2+ … 负战力战斗员n)的战力和
系统本质复杂度不可知定律与系统表征复杂度无限接近本质定律:取决于战斗人员的知识量,经验,天赋等,对于任何战斗人员n,都必定存在一个战斗人员m(考虑历史长河)使得战斗人员n观察系中的纯净无垃圾系统(复杂度总为1)是战斗人员m观察系中的含垃圾系统(复杂度为1+x),这使得在所有观察系中(包含外星生物),系统的表征复杂度(或者说观察复杂度)无限趋近与本质复杂度。然而我们只能通过观察来感知事物的本质复杂度,却永远无法得知我们离本质复杂度还有多远。(感谢评论区 @尤睿 提出表征复杂度这个概念来纠正此定律)
以有限的生命去追求可以无限的提升的净化方法与视野,我们称之程序艺术家,也就是SDA(Software Development Artist)
… it's extraordinarily important that we in computer science keep fun in computing…
——— Alan J. Perlis (April 1, 1922-February 7, 1990) 《SICP》
打星际… 哦,不, 错了重来… 写程序,你快乐嘛?
写在最后,看到大家最关心的是他拿到正式录取资格了么?还有也许通过我的描述关于他的这个侧面,你会觉得他很不称职。其实不是的,他代码写的绝对是平均值往上的水平,他的问题在于:
他其实有非常强的解决问题的能力,想法天马行空,通过自己设计算法来猜函数可能需要的数组大小就可见一斑,还有一个从s3(专业词汇)读数据的需求,他不是简单调api完了,而是写了一个环状buffer(专业词汇),使得网络,硬盘,app可以在理论上最大效率的适应程序当时的场景(为了协调异步,他自己发明了一个很笨拙的promise(专业词汇)),这非常厉害,一般的实习生哪怕sde1可能都写不出来(可惜的是场景会随业务逻辑激烈变化,今天的优化可以是明日的累赘,这就叫做过度优化,过度优化是一种强耦合,会把你的系统死死的钉死在当前版本)。他只是不明白简单是美这件事情而已。如果能有人帮他斧正,日后必成大器。
他最终拿到了正式录取资格,这其中还有个小波折,终审的bar raiser(amazon内部的一个可以一票否决招聘结果的角色)看到他在代码复查系统里跟我的各种激辩,觉得这人不能留。好说歹说才给了正式录取资格。不过最后人家没接,去读博啦。
最最后:在一个相对干净的环境写程序,不断找出新的本来以为不是垃圾的垃圾,对我来说,是一件非常愉快的事情。然而帮别人打扫他本就不该制造的垃圾则是非常痛苦的一件事。
写程序,本应是多么快乐的一件事啊!
====== 小尾巴 =====
编程难度不在技术上,而是在你想模拟的系统上,也就是难在系统分析。
实践一个简单的算法,或是问题时,会感觉很不错,然而面临复杂的现实世界,你会发现无论通过什么方式去描述,一但只要你想描述,必然就会出现缺陷。
正所谓道可道,非常道。
任何一种思维方式都是对现实的抽象,抽象便是提取特征忽略细节。
人的大脑是有限的,直觉认识并不能完美认识一个复杂系统,所以出现了很多理论工具可以用于修正,然而这些理论工具本身也是抽象的经验集合。
比如说,你这个代码模拟行星运动,将行星看成质点,于是简洁的公式便能描述,而这个是有代价的,每个行星上的石头运动实际上被忽视的。
这种忽视不重要的因素,看起来挺有道理,然而现实是你并不知道其实应该忽视什么。
所以整个系统分析,最难事是难在取舍上,没有银弹说白了就是并不存在完美的模拟现实世界的理论方法。
慢慢体会吧。
在不同的行业中,决定人类的工程的复杂度上限的因素是不同的。在制造业中,这个因素主要是成品造价、安全性等因素。而在软件行业中,这个因素是人的脑力极限。所以,编程难,就在于只有软件行业真正触及到了人脑的极限。
看过评论,我还是要强调「只有」。其它行业里,设计人员不是不会触及到智力极限,但是在产品真正投入生产之前,就已经被各种投资方和审核方以成本和安全因素把复杂度降下去了。所以其它行业对智力上限的触及是暂时的。
难在心里的恐惧,以及循循善诱的老师,前者尤甚。
首先,如果编程的标准是要成为特别牛逼的程序员,那么,难。但如果只是写代码来提升自己的工作效率,来用机器代替重复工作,那么,一点都不难。
不过很多人都被吓住了,跟以前的我一样。
先说心里的恐惧:
我在掌握了编程思想,并且能够灵活运用之后,跟很多人建议过让他们学编程。无一例外,都被拒绝了,原因是太难了,学不会。
只有一个朋友,有一次想学一个excel作图,让我教他。我说这得用VBA,他一听是编程,马上拒绝了。不过最后在我的反复劝说之下,还是决定尝试一下。整个过程也就半个小时,我就教会他了。
这并不意味着他以后可以自己写程序了,不过类似的问题,他可以通过修改小小的参数和语句,独立完成了。这个朋友之前成绩很差,而且数学更是一窍不通,不过他在整个学习过程中,没有任何问题。
其实只要克服了心理恐惧,编程并没有那么难。这有点像宜家的家具,绝大多数情况下,你不需要自己砍木头,打铁,冶炼,锻造,成型,然后组装。你只需依照宜家的说明书,把运送过来的几个大部件简单的组装好就可以使用了。
绝大多数情况下,你不需要去自己写算法,写最基础的函数,这些东西已经在那里了,你需要的,只是按照说明书用就可以了。比如R,比如python,甚至VBA,里面有大量现存的包,你直接调用,然后一行代码解决所有问题,没有那么恐怖。
另外VBA的录制提供了一个很好的学习方法,它在录制宏的同时,会显示相应的代码。如果你哪个命令不会,直接录制宏,看看系统是怎么显示的就可以,也省去了你上网找的时间。
再说循循善诱的老师:
平心而论,我大学本科的老师也不差,自己还跟清华的老师合作编写教材什么的,业内也也算小有名气。然而他讲课真是不行,四个字概括就是讲不明白。所以我虽然C++及格,但是我仍然什么也不懂,心里也像其他人一样存在着对编程的恐惧。
后来到读研究生,法国的一个编程的老师,只用了6节课,就把所有的思想都讲清楚了。不一定100%准确,不过这对于以后学习其他语言,起到了很大的帮助。
他说简单来看,编程就是无数个函数的组成。任何复杂的函数,其实都是由简单函数组成的。先尝试完成最基本的功能,最小的函数,然后再一点一点把小功能组合起来,形成大功能。
比如画一幅马赛克的图,先学会怎么画一条线,然后画四条,组成正方形,然后染色,组成小的色块,之后再用循环和条件,把这些色块排列起来。
有了这种思想,之后遇到的新语言学习,我基本没遇到什么困难,本质上就是函数,循环,条件。至于每种语言的表述方式,记都不用记,直接网上找文档就可以。虽然现在他讲的java我已经忘干净了。不过他的方法,以及编程的思想,应该是我得到的最大的财富了吧。
如果是题主所说的这个事嘛。
难度在于诗篇是给人类看的,人类可以容忍各种各样的不精确描述,就算作者真的没写清楚,也总会有读者通过强行解释给圆回来。
而给计算机看的东西,你必须事无巨细的展现出所有的细节,每个细节全都写得清清楚楚。
所以「做一个登录页面」这种在人类语言中一句话能解决的东西,编程做起来,那也是远远大于这一句话的工作量。
简要的说,程序员要做的事情是把人类语言翻译成计算机可以理解的语言。
编程语言是给计算机看的,也是给人看的,但最终还是给计算机看的。
程序员真正做的事情其实并不是「生成程序」本身,而是「向计算机描述自己想要一个什么样的程序」,然后,真正的程序其实是计算机自己生成的。
虽然程序其实是计算机生成,但「向计算机描述自己想要一个什么样的程序」这件事情比一般人想象的要繁琐得多。
我写了大概三四年的代码,觉得编程最难的地方在于:知识和能力的积累并不是一个线性增长的过程。编程能力跟语文和文史类的东西还是有一些不同,并不是说今天背了一篇课文,知识积累5%,明天背了一篇课文,知识累计10%。绝大多数时候,都是今天遇到一个bug,不知道怎么解决,知识积累0%,明天碰到一个很难的算法题,做不出来,知识积累0%。然后经过很多天,似乎就明白了怎么去处理bug。知识积累70%。然后又是某一天,可能悟透了某一类算法,知识积累80%。
所以中间有很多时间会觉得痛苦不堪,感觉自己做出了非常大的努力,但是好像没办法提高。这个感觉就像什么呢,好像金庸笔下的武侠世界,勤勤恳恳,终日习武,却总是在某一个地方卡住。在武学的门口彷徨踱步,却又总是不得门路。但于某日,突然之间,茅塞顿开,任督二脉打通,便一日千里,功力大涨。编程也是这么个事,刚接触编程时,环境不会配置,代码不知如何运行,看着VC6.0的界面,佶屈聱牙,不知道如何是好。若渡了这劫,又过许久,能写几行代码,但是各种error纷至沓来。变量未定义,库函数找不到,重定义,段错误,诸如此般,铺天盖地砸来,心态稍不坚定,便举手投降,直言此生再也不写代码。亦有心志坚定者,过了此关,后续却又遭大难,在动态规划贪心搜索等算法迷雾之中徘徊不得前,于自动微分梯度下降等深度学习混乱之林中茫然不能出,陷于线程锁cache一致访存效率等性能优化之路上恍惚不能进。诸多艰辛困难,于是再也不提计算机理论,专注于业务逻辑。虽也有所成就,但回首往事,总是有些许苦楚,暗自忧愁。编程之难,大抵如此。
至于在此路上攻坚克难。个人愚见,用以抛砖引玉。主要是三个方面,又或者算是三个阶段。第一阶段,先看理论,倒不用太过于深入,科普类为佳,没有代码示例的最好。例如计算机原理,操作系统,计算机网络等知识,先用文科的角度切入,多看多读多理解。例如操作系统,分为几块内容,进程管理,内存管理,文件管理。进程管理中,进程是个啥,线程是个啥,进程之间如何交互,交互遇到的问题如何解决。这时不用看具体代码,当作八股文章,记住即可。先大概明白这本书在说一个什么事情,在解决什么具体的问题,建立一个感性的认识。第二阶段,理论联系实践,多写代码。例如,搞清楚了操作系统在干个什么事情之后。就得做点实验,动手写一下,实现一下相关功能,例如实现一下线程的结构,实现一下多线程的交互。或者,难度上升一些,写个简单的操作系统。相关资料见下方repo。
这一步务必困难,十分艰辛,但完成之后将收益巨大,而后也能真正地深入理解线程进程、线程交互等相关概念。再到第三阶段,实践回归理论,撸完了代码,再看看理论知识,这个东西是不是理论说的这个样子,什么会这么实现,后续还会遇到什么问题,应该如何解决。如果觉得还是模糊不清,是不是要继续深入深入。随后再回到第一个阶段,找更深入的书籍,看更新的论文,不断重复上述的三个阶段。
谢邀。
关于这个问题,这个人已经回答的很好了:
他在1986年的一次茶话会上发了一篇受邀论文(论文的开头就是“谢邀”),题目就是《没有银弹-软件工程中的根本和次要问题》。 这个人就是IBM大型机之父,Frederick Brooks. 整个论文不太长,有兴趣的同学可以找来读读。在他的经典书《人月神话》中也有收录。 在30多年后的今天,我们在知乎上讨论这个问题,他却早就回答了。他的答案,至今看来,都没有一丝一毫需要修改的地方,毫无瑕疵。
布鲁克斯把软件开发中的困难分为两类: essence,可以译为本质困难或者主要问题,指的是软件开发中不可规避的问题,就是软件本身在概念建构上存先天的困难,也就是如何从问题领域,发展出具体的解决方案。 Accident,可以译为次要因素或次要问题,指的是把解决方案实施到电脑上,所遇到的困难。
他认为软件开发中无法规避的四个特性是: 复杂度; 一致性; 可变性; 不可见性。
他还归纳了在次要问题上我们取得的进步: 高级语言; 分时系统; 统一开发环境。
次要问题我们就不展开了,相信大家都可以理解。 下面所有就都是摘录了,分别描述了4个主要问题。
复杂度。 规模上,软件实体可能比任何由人类创造的其他实体要复杂,因为没有任何两个软件部分是相同的(至少是在语句的级别)。如果有相同的情况,我们会把它们合并成供调用的子函数。在这个方面,软件系统与计算机、建筑或者汽车大不相同,后者往往存在着大量重复的部分。 数字计算机本身就比人类建造的大多数东西复杂。计算机拥有大量的状态,这使得构思、描述和测试都非常困难。软件系统的状态又比计算机系统状态多若干个数量级。 同样,软件实体的扩展也不仅仅是相同元素重复添加,而必须是不同元素实体的添加。大多数情况下,这些元素以非线性递增的方式交互,因此整个软件的复杂度以更大的非线性级数增长。 软件的复杂度是必要属性,不是次要因素。因此,抽掉复杂度的软件实体描述常常也去掉了一些本质属性。数学和物理学在过去三个世纪取得了巨大的进步,数学家和物理学家们建立模型以简化复杂的现象,从模型中抽取出各种特性,并通过试验来验证这些特性。这些方法之所以可行——是因为模型中忽略的复杂度不是被研究现象的必要属性。当复杂度是本质特性时,这些方法就行不通了。 上述软件特有的复杂度问题造成了很多经典的软件产品开发问题。由于复杂度,团队成员之间的沟通非常困难,导致了产品瑕疵、成本超支和进度延迟;由于复杂度,列举和理解所有可能的状态十分困难,影响了产品的可靠性;由于函数的复杂度,函数调用变得困难,导致程序难以使用;由于结构性复杂度,程序难以在不产生副作用的情况下用新函数扩充;由于结构性复杂度,造成很多安全机制状态上的不可见性。 复杂度不仅仅导致技术上的困难,还引发了很多管理上的问题。它使全面理解问题变得困难,从而妨碍了概念上的完整性;它使所有离散出口难以寻找和控制;它引起了大量学习和理解上的负担,使开发慢慢演变成了一场灾难。
一致性。
并不是只有软件工程师才面对复杂问题。物理学家甚至在非常“基础”的级别上,面对异常复杂的事物。不过,物理学家坚信必定存在着某种通用原理,或者在夸克中,或者在统一场论中。爱因斯坦曾不断地重申自然界一定存在着简化的解释,因为上帝不是专横武断或反复无常的。 软件工程师却无法从类似的信念中获得安慰,他必须控制的很多复杂度是随心所欲、毫无规则可言的,来自若干必须遵循的人为惯例和系统。它们随接口的不同而改变,随时间的推移而变化,而且,这些变化不是必需的,仅仅由于它们是不同的人——而非上帝——设计的结果。 某些情况下,因为是开发最新的软件,所以它必须遵循各种接口。另一些情况下,软件的开发目标就是兼容性。在上述的所有情况中,很多复杂性来自保持与其他接口的一致,对软件的任何再设计,都无法简化这些复杂特性。
可变性。
软件实体经常会遭受到持续的变更压力。当然,建筑、汽车、计算机也是如此。不过,工业制造的产品在出厂之后不会经常地发生修改,它们会被后续模型所取代,或者必要更改会被整合到具有相同基本设计的后续产品系列。汽车的更改十分罕见,计算机的现场调整时有发生。然而,它们和软件的现场修改比起来,都要少很多。 其中部分的原因是因为系统中的软件包含了很多功能,而功能是最容易感受变更压力的部分。另外的原因是因为软件可以很容易地进行修改——它是纯粹思维活动的产物,可以无限扩展。日常生活中,建筑有可能发生变化,但众所周知,建筑修改的成本很高,从而打消了那些想提出修改的人的念头。 所有成功的软件都会发生变更。现实工作中,经常发生两种情况。当人们发现软件很有用时,会在原有应用范围的边界,或者在超越边界的情况下使用软件。功能扩展的压力主要来自那些喜欢基本功能,又对软件提出了很多新用法的用户们。 其次,软件一定是在某种计算机硬件平台上开发,成功软件的生命期通常比当初的计算机硬件平台要长。即使不是更换计算机,则有可能是换新型号的磁盘、显示器或者打印机。软件必须与各种新生事物保持一致。 简言之,软件产品扎根于文化的母体中,如各种应用、用户、自然及社会规律、计算机硬件等等。后者持续不断地变化着,这些变化无情地强迫着软件随之变化。
不可见性。
软件是不可见的和无法可视化的。例如,几何抽象是强大的工具。建筑平面图能帮助建筑师和客户一起评估空间布局、进出的运输流量和各个角度的视觉效果。这样,矛盾变得突出,忽略的地方变得明显。同样,机械制图、化学分子模型尽管是抽象模型,但都起了相同的作用。总之,都可以通过几何抽象来捕获物理存在的几何特性。 软件的客观存在不具有空间的形体特征。因此,没有已有的表达方式,就像陆地海洋有地图、硅片有膜片图、计算机有电路图一样。当我们试图用图形来描述软件结构时,我们发现它不仅仅包含一个,而是很多相互关联、重叠在一起的图形。这些图形可能描绘控制流程、数据流、依赖关系、时间序列、名字空间的相互关系等等。它们通常不是有较少层次的扁平结构。实际上,在上述结构上建立概念控制的一种方法是强制将关联分割,直到可以层次化一个或多个图形2。 除去软件结构上的限制和简化方面的进展,软件仍然保持着无法可视化的固有特性,从而剥夺了一些具有强大功能的概念工具的构造思路。这种缺憾不仅限制了个人的设计过程,也严重地阻碍了相互之间的交流。
我的公众号dingshukai666,教编程,欢迎关注~
编程不难,或者准确来说,是写代码不难。
对特定领域特定需求给出合理的针对性设计并完美实现,且可以维护,很难。
对于已经探索的比较成熟,需求非常明确,需求重复性高,需求的要求不高的特定领域应用,比如规模不大的网页浏览,现在就已经很简单了,有大量框架可以用。
对于需求复杂,没有直接可以使用的完美吻合需求的框架的,就更难; 如一些比较新的业务方向的。
对于还没怎么被探索过的,需求不十分明确,全新的领域,就十分之难。
真正难的部分在于,你不知道怎么做。
程序是解决问题的。现实世界的问题的相当复杂的。编程就是把现实世界的问题抽象成计算机能解释运行的语言的过程。所以编程的难度源于现实世界的复杂性。