引子
我是突然从被窝里爬出来重新打开电脑写这个帖子...
在我121028的博客里,我总结了一堆凌乱的关于编程考虑的问题...
我一直试图,引起一种讨论,关于编程,关于C语言,就关于C语言在单片机上的编程,不是那种纯粹的想偷懒,而是关于某些编程问题的深入讨论。
我写了 代码大全 的笔记。我深感欣慰的是,在早期的确引起了一些挺有深度的讨论,但它们也只是到了那个地步。
如同那篇博客,我觉得我总结出的最有价值的结论是:
“只有面对真实的问题的编码,遇到的问题才是最多的,也最实在”
在写那篇博客前后的时间里,我曾有的计划是——
在原来这种写 代码大全 这类笔记 的基础上,我自己总结一些常遇到的典型过程,问题,抽象成几大类,加以概括,这样积累下来,有助于后期的使用。
然而,我最终进而发现,天天以编程为工作的我们,遇到的问题,却绝对不只是 写代码 中遇到的问题,这其中涉及的问题太过于复杂,以至于,以至于,是的,我们都不知道该如何去讨论它,面对它,解析它,解决它。
睡下后,我又在琢磨这件事情,突然我想到了这个方式。
用一种类似于故事的形式,去除掉那些与具体工作内容有关的敏感,真实地叙述属于我们程序员自己的故事。
我不仅仅是为了叙述故事,更没任何在精神层面引起共鸣的打算。
我只是觉得,在真实意境里的讨论,也许更具代表性。
当然,我的故事只是我的故事,我只是一个,只有不到两年开发经验的新手程序员。
我希望,你能为我,为我们,叙述属于你的故事,分享属于你的问题。
在这个帖子里,没有观众。
下面是第一个小节——没有安全感的代码
我会争取简洁,以后也如此,但愿能如愿——
1 不断增加的新功能
你天天坐在电脑前写代码。
这几天你一直在做上星期,上司指派给你的那个功能。
他是这样说的:
“我们现在要实现的一个功能是.......”
然后,他告诉你:
“我们以前的做法大概是,用一个数组代表这24个位置的状态.......”
和以往一样,你发现,他的第二句话,那个建议,几乎成了你的实现方向,这很好。
不过,你慢慢发现,和你已经完成的部分相比,要把这个功能融合进去,似乎要做一些改动,而非仅仅只是加入新代码。
比方说,你要增加一个 状态,以区分这个新功能和原有功能的,但是原来的设计当然不可能考虑这个增加的功能——因为你的上司没告诉你。
是的,你们没有什么前期的系统规划,没有什么问题定义,需求分析,更没有完整的结构定义。
见鬼——有时候你会觉得,上司好像是什么时候突然想起来要增加这个功能,于是他说“小X啊,是这样的,我们还要有这个功能.....”
如果不是现在突然想起,那么就是给客户交出测试版本,你满心欣慰的想:总算完成了一个项目!他却突然在电话里要你增加一个新功能——你除了开始琢磨增加这个新功能要对原有代码做出什么改变以外,你还会再次生出一种感觉:项目总是做不完的,没完没了,除非哪天,他被客户逼到实在没时间了,于是他决定,这个功能暂时不做,留到下次升级再增加。
——有一个兼顾市场和客户的上司是不幸的。
2 没有安全感的代码
不管如何,你接到了新的功能,于是,你接着思考如何实现,并集成到原有代码里去。
前面说过,这不仅是一个 仅仅 增加 而无需改动任何原有代码的过程——假如真是这样,那该是一件非常幸运而简单的事情啊!
于是,你开始重新修正原来设计的状态——
你的程序从大的框架来说,一般都是以状态机驱动的,你设置了一个总的全局的状态变量,你为它定义了一系列的宏,每个宏都代表了一个特定的状态,要运行的子函数——或者说,要执行的动作。
现在,因为要嵌入新功能,你也许要改变其中一个状态的内部实现,甚至更不幸的是,你发现,你不得不增设一个状态,我的意思是,你要定义一个新的宏
#define XX_NEW_STATUS 0~255
接下来你除了要开动脑筋实现新功能的子函数,你还随时保持警惕,它的引入会不会打乱原有的状态切换过程,会不会带来什么影响,应该如何测试——天啊,想到测试你会疯掉的——这意味着过去你所测试过的所有功能你都得重新测试一次!
没办法,你心里很清楚,就像那些大道理一样,为什么一开始不告诉我有这个功能呢?
好让我从设计这个状态之初就可以一并考虑,也就不用现在我重新再来设计一次状态切换机制......
那是一种理想状况,现实是残酷的,所以,你总是在一种感觉下工作——那种感觉的名字叫“没安全感”。
因为前面叙述的种种新功能引入带来的麻烦,你不得不一边迈开新步子往前进,又频频回头,不断梳理,总结原有代码的结构,新引入的部分和它如何融合,如何避免冲突.......
你也许会认为我的这种不安全感有点杞人忧天。
是的,因为你不曾试过,我曾经几次引入一个新功能以后,不断被客户反馈这个功能有诸多bug,以至于我的上司差点想吃了我——
在我排除这些错误的时候,我唯一总结出的一个结论就是:
它和原有功能的融合不够妥当,总是有一些地方疏忽了,以至于某个状态切换或者标志变量没有妥当设置好,诸如此类......
于是从那天起,我最怕听到的一句话就是——
“小林啊,我们还要增加一个功能.....”
我不得不经常做的一件事情就是
”嗯,这个功能实现了,啊,等等,我还要仔细检查一下和原有模块有没可能冲突?“
”因为今天我又完成了一个部分的功能,我又多了一个地方对那个全局状态做了修改.......“
你那个代码兼容性问题,确实经常出现。编程者能你这样思考,我觉得能减少好多问题。世间最痛苦的莫过于做码农,更可悲的是仅仅局限于码农。
淘宝:https://viiot.taobao.com/Q群243090717
多年专业物联网行业经验,个人承接各类物联网外包项目
回复 4楼 wateras1 的帖子
如果没有什么不方便,我期待更深入了解你所考虑的和所做的事情——
你可以选择其他方式而不只是公开写在帖子里。
qq 邮件 或者 论坛的短消息。
不过如果是qq实时聊的话,我白天可能不能立马反应。
看了你那篇文章,我感觉编程确实需要一种高屋建瓴的感觉,最好从设计架构方面来考虑,一种好的架构设计决定一个好的编码规范,一个好的编码规范决定一个系统的鲁棒性和可移植性
淘宝:https://viiot.taobao.com/Q群243090717
多年专业物联网行业经验,个人承接各类物联网外包项目
差点忘了~~
qq
755087287
邮箱是qq邮箱
回复 6楼 wateras1 的帖子
最可悲的莫过于,实际工作的内容不宜讨论,然而,绝大多数精力却在那里。
要想绕开它,编写另一个规模,程度能与之相比的代码(天啊,难道我在从事两份工作?)
不过我昨晚倒是想到了一个 很是类似的东西,简单描述有点像 大富翁游戏的街机版
——当然,我边琢磨边这东西要做成街机版真的各种不合适。
当然,如果只是讨论亦无不可。
至于能不能只是在伪代码设计的基础上深入讨论,还是最后会被迫实际去做这样一个东西,以及花费的时间,这个,,,,,,呵呵,再说吧。
就像我昨晚深夜写了这个帖子,早上起来在公车上琢磨着琢磨着,感觉这个帖子恐怕很难写下去,不过,既然开始了,那就往下成长吧
呵呵,继续写,我时刻关注着,我看看我那种编码思想是否可以解决你的疑惑。
淘宝:https://viiot.taobao.com/Q群243090717
多年专业物联网行业经验,个人承接各类物联网外包项目
回复 9楼 wateras1 的帖子
好吧,其实对你所做的工作也非常感兴趣
3 不幸的现实之 前期规划是必须没有的
前面我试图让你明白为什么我这么怕听到上司要求增加新功能。
告诉你一个不幸的消息:增加新的功能是家常便饭。
这里头的原因主要有:
1 开发过程,用户和开发者都在逐步加深对系统的理解,于是他可能改变了某个功能,很多时候这等同于增加功能,因为你可能发现已完成的实现压根不能满足新要求。
2 如同我说的,你们在开发以前从来就没有所谓的前期规划和结构设计——如果你和你的上司提出这个,最好的情况是他说那你做吧,安排一个时间。
因为随后他一定会经常问你为什么还不开始编码?
尽管McConnell说,这一部分工作经常要占到10~30%……
我相信你不可能在十天以内完成一个七八千行的程序,但我确信如果你连续三天没有编码的话,别说三天,就是一天,你的上司一定会很严肃地问你时间是怎么安排的……
千万不要提到McConnell~因为你和他隔着一个太平洋,而且我想,在他的团队里工作必然轮不到你做这些前期规划工作
而在太平洋彼岸,你却在做着这件事,你一定不会感到自豪,你只不过是暂时改名叫 廖化而已
4 面对现实之 如何应付没时间做全部规划
在以后,我还会和你分享各种残酷的现实,你会看到现实的开发和书里写的压根不是一回事。
如果你因此觉得被生活欺骗了,并打算改行,我,,我一定不会拦你……假如我能想到我除了做码农还能干别的,嗯,估计我就不在这写帖子或者……我还当什么版主……
开玩笑的,虽然我全家有没理想我不知道,但我有理想,所以,现在说回正题。
其实不管是前期规划还是开发任何一个过程,你永远都会感到时间不够。
怎么办?加班……
你加吧,我可不想加~
加班不是好事,这个……不管主观客观我都认为绝对正确无比。
时间不够,怎么办?
凉拌!
一个非常好的方法是 化零为整。
6 拒绝官僚,拥抱xp
首先……如果你以为我在说windows……
那么好吧,出门右拐,查查什么叫 极限编程。
关于极限编程,这是个很大的话题,而且大多数时候,我想至少你很难找到一个人坐你旁边和你结对编程……要记住,现实是残酷的,书里写的是美好的~
不过不管怎么说,作为一种已经存二十几年的颠覆传统的开发模式,它有许多思路和方法我们可以现在就开始学习,然后一点点渗透我们的日常真实工作。
比如化整为零。
一个七八千行的程序,让你花多少时间你才能做好完整的规划呢?
我以我的时间做参考,我刚接触这个平台的第一个项目,前前后后用了将近10个月,当然,后期调试实在很长,如果以首次正式交付,那大概是4个月,按照……按照10~30%,我们悲观地选择小值,那也至少需要 两个星期左右⊙▽⊙
别想了,除非你想你老板天天问你为什么还不编码?
另外,前面说了,你可能随时要接受功能的变化,你不要指望一旦定下来就不会怎么变化了,需求不变只是一个神话。
所以,xp告诉你,不要在一开始就指望做出一个几十上百页的结构设计书,那是传统瀑布式开发过程……
我还没福分在一个以这种典型方式运作的团队里工作,如果你在大公司或者有关部门工作,也许你会有此福分。
不过我听说,反正这玩意对你的主管是福音,对你嘛,没用。
xp的第一条准则就是
从最简单,最小的可运行的原型开始。
这个最小原型很可能只是……点亮一个led,然后你证实,好了现在我能开始操控这个新的单片机了。
7 xp之如何开始你的工作
点亮一个led这种事,请不要介意我阴谋论地认为你已经对我竖起来了中指。
好吧,现在我们来举个例子,它是个真实的故事。
现在让我们来做一个跑马灯,请忘记你在51上玩的那个流水灯,并且绝对不要立马就想到,那个小case了,查表,扔到p1…
然后我说,要做的是,18个灯排成一个圈,然后视觉效果是,一个灯正跑一圈,反跑两圈,再正跑一圈,又反跑,而且加速减速,,,变速不一样,第一圈变得慢,第二圈稍快……
对不起,你晕了没?
反正我是晕了……
你现在在干吗呢?
你是不是已经开始想,怎么变速,怎么控制灯前进,怎么分辨跑第几圈?
很好,我先休息一下眼睛,你想完了叫我一声……
8 增量式前进
你想好了没?
经过一夜的时间,你可能已经调好了,不知道那是一种什么经历呢?
说实话,这个过程的实现并不容易。
让我来告诉你,我会怎么做。
我首先做的事情是,让18个灯闪烁,这样我就清楚的知道
1这灯没坏。
别笑,别以为它们真的是优质产品。
我有过一个经历,一块做好的硬件板拿过来,我测试显示和读键的时候,发现有一个键读不对,由于上面的键是一根线分时扫描的,所以我对此非常迷惑,怀疑是程序的问题,可弄了半天也没见好转。
在我很无奈的时候,我停下来,我不是为什么目的而停下来,我就是没辙了。
无意看了一眼板子,发现每个键都有一根隔离二极管,然后我发现它……见鬼!我马上跳起来,用电烙铁把那根装反的二极管重新焊好,它好了……
我只想告诉你,没有什么是可以不验就能保证它永远正确得。
你在第一次运行你的跑马灯的时候,你是否看到了这里缺一个灯,那里慢了?
我没说一定是因为你的灯也有一根装反了,但是你首先应该自己确认这点。
突然慢了,我并不敢说你的函数也许是忘了清除定时器,或者忘了封顶……
但你应该自己验证。
验证的最简单方法就是,比如仅仅只是让灯全亮,然后全灭。
如果你的延迟函数是已有的,那么在过去的日子里你应该也没验证过?
那么请你现在就做一个最简单的验证,不要考虑什么变速加速,你只要恒速闪烁所有灯就可以了。
9 增量式前进2
前面说的似乎都是测试的内容,和开发本身似乎说不上直接关系。
增量式前进这个标题有点标题党的嫌疑。
实际上,测试正是实际开发的一部分。
增量式前进的基本模式被描述成一个微小的循环,如同前面所说,他做出一个原型,然后测试——
请注意理解测试这个动作的含义。
测试并不是验证你做的没错而已,测试至少还包含一点:
你是否完成了你要实现的功能?
测试经常被用来描述功能。
我们说回那个跑马灯,
前面我们做的测试仅仅是验证性的。
现在我们开始用测试来导向来开发过程。
有一个专业名词叫 测试驱动开发,而且还有一本书专门为嵌入式c开发而准备,百度会告诉你更多。
跑灯,首先我认为我得先让灯跑起来。
前面我们是所有灯在闪烁,现在我们要让灯一个一个往前走,跑起来。
请注意,我们的目的仅仅是让他跑起来,请忘记什么加速减速,匀速就很好,
每一步增加的功能最好是越小越好。
因为越小就越容易实现,越小就越不容易破坏你的已有程序……
你会觉得这跟没意义,甚至很没技术含量,不过别忘了,八千里路第一步就在脚下也很简单,很没什么了不起,但反正我们一步一步走,总会走到的对不对?
那么,没技术含量又有什么关系呢?
好,我们现在让灯跑起来,注意观察是否一个一个位置连续跑过去,有没什么出乎你预期的效果。
没有?很好,再往前走,现在我们要试一下变速了,但确切地说变速可以认为是加速和减速两个,所以我们分别试一下……
到现在,我们的验证性和准备工作都做好了,现在到了设计整个过程控制变速和跑的位置的时候了。
这一部分你可以用你已经用过的方法实现,也许你会发现最终的代码长得和你写的很像。
不过,不知道这样的一个过程是否比你原有的过程更让你欣赏呢?
至于逻辑部分,我们还可以采用其他方法,但是它不在如何开始工作的讨论范围。
感慨啊,加一个新功能,必须把所有功能都重新测一遍