犯错之后,方法反反复复复的好多遍

论文发表、论文指导
周一至周五
9:00&22:00
善于原谅学生的错误是教师的美德
  摘 要: “问题生”是班主任工作中的重要对象,也是班级建设中的难点,学会并善于原谅他们的过错,是教育工作者,尤其是班主任的职业要求。如果教师能做到恩威并用,宽严有度,真心关爱,多看长处,那么,人类的花园中一定会多开出许多鲜艳的花朵。 中国论文网 /9/view-976054.htm  关键词: 问题生 班主任 转变      中小学教育工作者,尤其是从事班主任工作的教师总是习惯说:我是允许学生犯错的,但我绝不允许你同样的错误犯两遍。然而,基于二十多年高中班主任工作的实践,我的感悟却是:学生同样的错误不仅会犯两遍,而且有可能一而再,再而三地重犯。“问题生”的缺点和不良行为,大多不是一朝一夕和一种原因形成的。(吴沛发,2001)反反复复犯错误正是“问题生”的“问题”之所在,因而使他们改掉不良习惯,重新扬起前进的风帆,绝不是一蹴而就的事情。在转化“问题生”的过程中,教师,特别是班主任,不仅要有热心、真心、耐心,而且要有技巧,讲究方法,要学会善于原谅学生的错误。   本文以上海市初级中学黄静华老师为例具体探讨这一棘手的问题。黄老师有三十多年的教学一线任教和班主任工作中的经验,她坚持德育探索,培育了一批又一批合格的人才,先后获得了全国先进工作者、全国师德标兵、全国三育人先进个人、上海市劳动模范等许多荣誉称号。下面是一则她的育人案例(宋运来2008):   一天清晨,黄老师接到了学生小伟的母亲打来的电话,说孩子又在网吧里通宵未归。原来,小伟沉溺于电子游戏,平时不写作业,好几门课不及格,黄老师骑上自行车,在小伟常去的一家网吧里找到了他。黄老师二话不说,把一脸疲惫的小伟拉进了小饭店。小伟吃完了饭见老师并没有训斥他的意思,便松了一口气,说是妈妈把家里的电脑锁了起来。黄老师告诉他:以后想打游戏了就到办公室来找我,顺便也教教老师怎么用电脑。吃完早饭她把孩子小伟送回家,并叮嘱小伟的父母亲不要再打骂孩子。   为了收住小伟的心,黄老师又想了一招。她向小伟求助说,学校的走廊有点黑,老师晚上一个人走害怕,你能不能一边做作业一边等等我?这一等就等到了毕业。在每天结伴回家的路上,黄老师都要和小伟谈谈心。小伟的学习习惯一天天好起来。后来,他考上了一所国家级重点中专,还当上了班长。   从上述案例可以看出:“问题生”小伟被黄静华老师成功转化。但这一转化过程并非一帆风顺,也非一日之功。中间是一个反反复复的过程,经过多次改正,犯错,再改正,再犯错,直至最终不再犯错误的过程。   《学记》说:“教也者,长善而救其失者也。”长善救失,就是要求教育者能正面引导学生,通过老师的教育,同学的配合帮助,使“问题生”逐渐积累积极向上的因素,告别和抛弃懒散的消极的因素,最终实现“旧我”向“新我”的转变。在“问题生”的转化过程中,教师应该充满善意和智慧。本文提出以下四种策略,以期对班主任工作有所裨益。   一、恩威并用   “问题生”大都有一种自卑心理。他们由于经常犯错误,挨批评,受处分,可能会遭老师的白眼,被同学孤立,因此他们也往往与老师和集体处于对立状态。如果老师,特别是班主任能主动接近他们,甚至通过赠送小礼物,或很“仗义”地替他们承担一两次责任的话,就会很容易让他们产生感激之情,使他们从内心里感受到老师的关怀,产生一种自豪感,在脑海里留下美好的回忆,从而为下一步的努力和成功建立起信心。就像上述案例之中的小伟,黄老师把他从网吧找到之后,不仅请他吃饭,而且主动让他到老师办公室玩游戏,在这样情况下,他尽力去按老师的要求做事情,时间一长,不良习惯就慢慢改掉了。   二、宽严有度   “问题生”往往定力较弱,教师和家长千万别指望通过一次深刻的谈话或一顿打骂就能让他们转变过来。如果一次校正就能让其转变过来,那么所有的学校也就没有“问题生”了。针对“问题生”在转化过程中易反复犯错的实际情况,广大教师一定要有足够的心理准备,为他们的转化预留较大的空间,对他们的要求要比正常学生宽一些,只要他们再次犯错的时间向后推迟了,也就是两次犯错的时间间隔拉长了,这就是进步。比如原来每周都犯错的学生,经过教育之后,改为两周或三周才犯错了;你再次教育之后,他改为一个月之后再次犯错;当你第三次教育之后,曾经的“浪子”可能已经迷途知返了。   三、真心关爱   著名女诗人席慕容说:“当你用怜惜的双手小心翼翼地捧住一粒石子并虔诚地祈求它成为钻石的时候,那石子就当真生出了钻石的心。”无论如何,以诚取信于学生是每个班主任开展工作的第一步。(王莉,2006)教师对“问题生”的关心和爱护必须是发自真心的,因为一些“问题生”的内心异常敏感,对老师故意装出的“关心”和虚假的所谓“理解”有着本能的反感,很多“问题生”最后之所以下定决心痛改前非,往往是因为他们不想让自己最心爱的老师失望。古语说:“看书求理,须令自家胸中头点;与人说理,须令人家胸中点头。”在知恩图报心理的支配下,他们才一步步从“旧我”的深渊中跋涉而出,蜕变为老师和社会希望的“新我”。   四、多看长处   每个人都有自己的长处,每个学生的世界也都是多姿多彩的。前苏联教育家马卡连柯说:“教育儿童的最好方法在于鼓励孩子们的好作为。”按照教育教学的一般规律来讲,那些“问题生”经过自身的积极努力,在各方面出现进步的可能性更大,其潜力隐藏也更为巨大,更具有挖掘的意义。“问题生”是人类百花园中的一朵迟开的花儿。他们也有长处,只是他们的长处和自身过多的缺点比,易于被人们忽略罢了。这就要求教师细心地发现,学会用放大镜放大他的优点。班主任可以有意识地找一两件“问题生”感兴趣,并且是他们擅长的事情交给他们去做,然后在全班学生面前十分郑重地给予表扬和肯定,这样不仅能让他们体味到成功之后的自豪和快乐,而且有助于他们克服自卑心理,并以此事为契机,把他们的注意力引向正确的发展轨道。   总之,“问题生”的转化不是一蹴而就的,作为教育工作者,我们一定要有耐心、有真心、有爱心。在法国作家拉?封丹的寓言故事《北风和南风比威力》中,北风和南风总是互不服气,于是他们决定打赌,看谁的力量更强大,决定比谁能把行人的大衣脱掉。北风无论怎样猛烈地吹,行人只是将衣服裹得更紧;而南风只是轻轻拂动,人们就热得脱掉了大衣。对于问题学生,只要我们多些宽容和理解,多些耐心和真情,就能像春风融雪一样,开启他们曾经对良知紧闭的心扉。      参考文献:   [1]吴沛发.中学“问题生”的识别及转化[J]铜仁师专学报,2001,(2).   [2]宋运来.影响教师一生的100个好习惯[M].南京:江苏人民出版社,2008,1.   [3]李明高.教师最关键的18项修炼[M].南京:江苏人民出版社,2008,9.   [4]高长海.教育方法与艺术全书[M].北京:中国物资出版社,1998,2.   [5]王莉.因为有了爱[J].天津教育,2006,(1).
转载请注明来源。原文地址:
【xzbu】郑重声明:本网站资源、信息来源于网络,完全免费共享,仅供学习和研究使用,版权和著作权归原作者所有,如有不愿意被转载的情况,请通知我们删除已转载的信息。
xzbu发布此信息目的在于传播更多信息,与本网站立场无关。xzbu不保证该信息(包括但不限于文字、数据及图表)准确性、真实性、完整性等。异类大明星 二 第237章【韩硕又要出对联了!】 都市娱乐,大家读书院 - 网络文学门户
-& 二 第237章【韩硕又要出对联了!】
| 『&提示:本系统支持键盘左右方向键[←][→]翻页&』
二 第237章【韩硕又要出对联了!】
&&&&韩硕的粉丝们对于郭明那是不遗余力地嘲讽,大有把对方十八辈祖宗全给问候个遍的趋势!&&&&郭明的粉丝那边根本就承受不住韩硕这百万雄师的破口大骂,往往刚刚反驳一句,那恢复就被淹没在了大海里,而那成为众之矢之的郭明,此时更是被这种吓人的阵仗给吓得脸色苍白如纸,他战战兢兢地用颤抖的手指,在键盘上敲出了简短的几句话,“韩前辈,我承认之前对你有所冒犯,请您大人不记小人过,原谅我。”&&&&他也想把这道歉的话给编的长一点儿,但是他那哆哆嗦嗦的手指不听话啊,就这么几句简简单单的话,他还是反反复复删改了好多遍才最终成了型!&&&&坐在电脑前的韩硕,见到这郭明服软了,心里大呼一声痛快,叫你丫的敢冒犯小爷,今个儿要是不把你收拾地服服帖帖得,起王中王倒着写的时候,韩硕倒是心里有了一点儿想法,当然,这个想法也只是想法而已,现如今还不是把这个想法付诸于实践的时候。&&&&再说郭明道歉这事,韩硕虽然心里极其特别非常的痛快,但是表面上还是要装作风轻云淡的样子,于是他便发表了一个比较装逼的回复:“没关系的,小辈们总是会犯错误的,记住教训以后就不会再犯同样的错误了!”&&&&韩硕这么说,俨然把他自己放在了一个至高无上的地位上,而那郭明,则被他说成了一个犯了错的后辈,大人不记小人过的韩硕,自然是不会把这种鸡毛蒜皮的小事给放在心里的。&&&&而那郭明看到韩硕如此数落自己。却偏偏哑巴吃黄连,有苦说不出,心里要多郁闷就有多郁闷!&&&&网友们对于两人的话那是抱有相当大的热情。&&&&“道高一尺魔高一丈!”&&&&“哈哈,要真这么说的话,那咱们的韩硕老师就成了不折不扣的大魔头了啊!”&&&&“是啊,那法力简直是无边无际。寻常人等在他面前就是被秒杀的份儿!”&&&&“那可不,虽然这郭明不属于寻常人,属于那种比较有本事的人,但还是被韩硕老师那无边无际的法力给打的尸骨无存!”&&&&“岂止是逆天啊!简直是太逆天了!”&&&&“韩硕老师,我......此时无声胜有声!”&&&&“逐字逐句地看完韩硕老师这个微薄以后,我的心久久不能平静,震撼啊!为什么会有如此好的对联!我纵横网络各大对联论坛多年,自以为再也不会有任何对联能打动我,没想到今天看到了如此精妙绝伦的对联!韩硕老师。是你让我深深地理解了“人外有人,天外有天”这句话。谢谢侬!在看完这微薄以后,我没有立即回复,因为我生怕我庸俗不堪的回复会玷污了这网上少有的微薄。但是我还是回复了,因为觉得如果不能在如此精彩的微博下面留下自己的网名,那我死也不会瞑目的!能够在如此精彩的微薄下面留下自己的网名是多么骄傲的一件事啊!韩硕,请原谅我的自私!我知道无论用多么华丽的辞藻来形容楼主您对联的精彩程度都是不够的,都是虚伪的。所以我只想说一句:您的对联太好看了!我愿意一辈子看下去!”&&&&... ...&&&&冒犯韩硕的那些人,已经被韩硕送进了“坟墓”。现在,是个举国欢庆的日子,韩硕作为这场争论的最大赢家,理应担当活跃气氛的职责,想前想后,他觉得在这个氛围下。还是多出一些比较有趣的对联为妙,于是便用灵活的手指飞快地在键盘上敲击出一段话并通过微薄给发送了出来:&&&&“既然大家对于对联这么的有兴致,那我就再出几个对联分享一下,时间是一个小时,如果全部对上来了。我会给那人颁发奖品的,当然,若是人数太多,那我会抽取一部分幸运儿颁发奖品,至于奖品是什么,可以是签名也可以签名,但是千万不能提钱,因为谈钱伤感情!”&&&&网友们一听,性致顿时就来了!&&&&“哇咔咔!”&&&&“又有对联?”&&&&“不过韩硕老师,你丫的也太抠了吧,连个像样的奖品都舍不得拿出来?用几个签名来糊弄人?”&&&&“韩硕老师,你是孙猴子请来的逗比,专门来活跃气氛来的吗?”&&&&“签名就签名,就要挫挫你这气氛!看你以后还敢不敢这么抠了!”&&&&“就是,大家都认真点儿,如果聚在一起的,可以相互讨论,一定要把韩硕老师出的对联给对上,咱不能让他再这么坑咱们了!”&&&&“我又又又又又又又又又又又又吃了一片镇静剂,呵呵,这么激动人心的场面,怎么少的了我镇静剂哥撑场面?韩硕老师,您尽管放马过来,对对联这是我的强项,当然,只要您别太坑人,专出寂寞寒窗空守寡那种达到千古奇联程度的对联就好了!”&&&&电脑前,郭明和李尤看到韩硕这厮又要出对联了,再次吐出三生老血,去你妹啊,还来?原来你丫的在和我们较量的时候,根本就没尽全力啊,要不要这么打击人啊?&&&&至于之前那些被韩硕的列锦诗给气的吐血的老学究们,也是在焦灼地等待着韩硕的对联。&&&&还有那些刚刚进入新狼微薄,就被这么大的动静给惊醒的网友们,还有一些大v各路明星,也都参与了进来,所有人都对于韩硕接下来所要出的对联拭目以待!&&&&当然,这部分人大都不是冲着韩硕的签名来的,毕竟韩硕的签名除了在他的粉丝面前兴许还有一些价值,但若是放在他们眼里,那基本就等于无价——没有价钱了。&&&&他们冲的是韩硕对对联以及出对联的本事!&&&&在了解到寂寞寒窗空守寡的难度后,大家都想着对出韩硕的对联,以证明自己的实力!&&&&毕竟人家可是对联专家啊,京城对联协会以及那个对对联研究颇深的畅销书作家郭明在人家面前都不是对手啊!&&&&要是能对出韩硕的对联了,那还不得直接当上对联协会的主席了?(未完待续。。)u
更多精彩小说,欢迎访问大家读书院 http://www.dajiadu.net
本作品《异类大明星》为私人收藏性质,所有作品的版权为原作者 剩斗士 所有!任何人未经原作者同意不得将作品用于商业用途,否则后果自负。编程语言大牛王垠:编程的智慧,带你少走弯路
招聘信息:
作者: 授权本站转载。编程是一件创造性的工作,是一门艺术。精通任何一门艺术,都需要很多的练习和领悟,所以这里提出的“智慧”,并不是号称三天瘦二十斤的减肥药,它并不能代替你自己的勤奋。然而我希望它能给迷惑中的人们指出一些正确的方向,让他们少走一些弯路,基本做到一分耕耘一分收获。反复推敲代码既然“天才是百分之一的灵感,百分之九十九的汗水”,那我先来谈谈这汗水的部分吧。有人问我,提高编程水平最有效的办法是什么?我想了很久,终于发现最有效的办法,其实是反反复复地修改和推敲代码。在IU的时候,由于Dan Friedman的严格教导,我们以写出冗长复杂的代码为耻。如果你代码多写了几行,这老顽童就会大笑,说:“当年我解决这个问题,只写了5行代码,你再回去想想吧……” 当然,有时候他只是夸张一下,故意刺激你的,其实没有人能只用5行代码完成。然而这种提炼代码,减少冗余的习惯,却由此深入了我的骨髓。有些人喜欢炫耀自己写了多少多少万行的代码,仿佛代码的数量是衡量编程水平的标准。然而,如果你总是匆匆写出代码,却从来不回头去推敲,修改和提炼,其实是不可能提高编程水平的。你会制造出越来越多平庸甚至糟糕的代码。在这种意义上,很多人所谓的“工作经验”,跟他代码的质量,其实不一定成正比。如果有几十年的工作经验,却从来不回头去提炼和反思自己的代码,那么他也许还不如一个只有一两年经验,却喜欢反复推敲,仔细领悟的人。有位文豪说得好:“看一个作家的水平,不是看他发表了多少文字,而要看他的废纸篓里扔掉了多少。” 我觉得同样的理论适用于编程。好的程序员,他们删掉的代码,比留下来的还要多很多。如果你看见一个人写了很多代码,却没有删掉多少,那他的代码一定有很多垃圾。就像文学作品一样,代码是不可能一蹴而就的。灵感似乎总是零零星星,陆陆续续到来的。任何人都不可能一笔呵成,就算再厉害的程序员,也需要经过一段时间,才能发现最简单优雅的写法。有时候你反复提炼一段代码,觉得到了顶峰,没法再改进了,可是过了几个月再回头来看,又发现好多可以改进和简化的地方。这跟写文章一模一样,回头看几个月或者几年前写的东西,你总能发现一些改进。所以如果反复提炼代码已经不再有进展,那么你可以暂时把它放下。过几个星期或者几个月再回头来看,也许就有焕然一新的灵感。这样反反复复很多次之后,你就积累起了灵感和智慧,从而能够在遇到新问题的时候直接朝正确,或者接近正确的方向前进。写优雅的代码人们都讨厌“面条代码”(spaghetti code),因为它就像面条一样绕来绕去,没法理清头绪。那么优雅的代码一般是什么形状的呢?经过多年的观察,我发现优雅的代码,在形状上有一些明显的特征。如果我们忽略具体的内容,从大体结构上来看,优雅的代码看起来就像是一些整整齐齐,套在一起的盒子。如果跟整理房间做一个类比,就很容易理解。如果你把所有物品都丢在一个很大的抽屉里,那么它们就会全都混在一起。你就很难整理,很难迅速的找到需要的东西。但是如果你在抽屉里再放几个小盒子,把物品分门别类放进去,那么它们就不会到处乱跑,你就可以比较容易的找到和管理它们。优雅的代码的另一个特征是,它的逻辑大体上看起来,是枝丫分明的树状结构(tree)。这是因为程序所做的几乎一切事情,都是信息的传递和分支。你可以把代码看成是一个电路,电流经过导线,分流或者汇合。如果你是这样思考的,你的代码里就会比较少出现只有一个分支的if语句,它看起来就会像这个样子:if&(...)&{
&&if&(...)&{
&&}&else&{
}&else&if&(...)&{
}注意到了吗?在我的代码里面,if语句几乎总是有两个分支。它们有可能嵌套,有多层的缩进,而且else分支里面有可能出现少量重复的代码。然而这样的结构,逻辑却非常严密和清晰。在后面我会告诉你为什么if语句最好有两个分支。写模块化的代码有些人吵着闹着要让程序“模块化”,结果他们的做法是把代码分部到多个文件和目录里面,然后把这些目录或者文件叫做“module”。他们甚至把这些目录分放在不同的VCS repo里面。结果这样的作法并没有带来合作的流畅,而是带来了许多的麻烦。这是因为他们其实并不理解什么叫做“模块”,肤浅的把代码切割开来,分放在不同的位置,其实非但不能达到模块化的目的,而且制造了不必要的麻烦。真正的模块化,并不是文本意义上的,而是逻辑意义上的。一个模块应该像一个电路芯片,它有定义良好的输入和输出。实际上一种很好的模块化方法早已经存在,它的名字叫做“函数”。每一个函数都有明确的输入(参数)和输出(返回值),同一个文件里可以包含多个函数,所以你其实根本不需要把代码分开在多个文件或者目录里面,同样可以完成代码的模块化。我可以把代码全都写在同一个文件里,却仍然是非常模块化的代码。想要达到很好的模块化,你需要做到以下几点:1. 避免写太长的函数。如果发现函数太大了,就应该把它拆分成几个更小的。通常我写的函数长度都不超过50行,那正好是我的笔记本电脑屏幕所能容纳的代码的行数。这样我可以一目了然的看见一个函数,而不需要滚屏。50行并不是一个很大的限制,因为函数里面比较复杂的部分,往往早就被我提取出去,做成了更小的函数,然后从原来的函数里面调用。所以我写的函数大小一般远远不足50行。有些人不喜欢使用小的函数,因为他们想避免函数调用的开销,结果他们写出几百行之大的函数。这是一种历史遗留的错觉。现代的编译器都能自动的把小的函数内联(inline)到调用它的地方,所以根本不产生函数调用,也就不会产生任何多余的开销。同样的一些人,也爱使用宏(macro)来代替小函数,这也是一种历史遗留的错觉。在早期的C语言编译器里,只有macro是静态“内联”的,所以他们使用宏,其实是为了达到内联的目的。然而能否内联,其实并不是宏与函数的根本区别。宏与函数有着巨大的区别(这个我以后再讲),应该尽量避免使用宏。为了内联而使用宏,其实是滥用了宏,这会引起各种各样的麻烦,比如使程序难以理解,难以调试,容易出错等等。2. 每个函数只做一件简单的事情。有些人喜欢制造一些“通用”的函数,既可以做这个又可以做那个,然后他们传递一个参数来“选择”这个函数所要做的事情。这种“复用”其实是有害的。如果一个函数可能做两种不一样的事情,最好就写成两个不同的函数,否则这个函数的逻辑就不会很清晰,容易出现错误。写可读的代码有些人以为写很多注释就可以让代码更加可读,然而却发现事与愿违。注释不但没能让代码变得可读,反而由于大量的注释充斥在代码中间,让程序变得障眼难读。而且代码的逻辑一旦修改,就会有很多的注释变得过时,需要更新。修改注释是相当大的负担,所以大量的注释,反而成为了妨碍改进代码的绊脚石。实际上,真正优雅可读的代码,是几乎不需要注释的。如果你发现需要写很多注释,那么你的代码肯定是含混晦涩,逻辑不清晰的。其实,程序语言的逻辑表达能力,是远远高于自然语言的。使用大量的自然语言去解释程序的细节,是本末倒置的。有人受到了Donald Knuth提出的所谓“文学编程”(Literate Programming)的误导,认为程序里面注释应该是主要的部分,而代码其次,其实并不是这样的。很多人(包括Knuth自己)使用文学编程,其实并没有写出容易理解的代码。Knuth认为人与人之间交流,必须使用自然语言,而其实如果使用得当,程序语言能够更加清晰精确地在人类之间传递信息。之所以说“如果使用得当”,是因为如果没能合理利用程序语言提供的优势,你会发现程序还是很难懂,以至于需要写注释。所以我现在告诉你一些要点,也许可以帮助你大大减少写注释的必要:1. 使用有意义的函数和变量名字。如果你的函数和变量的名字,能够切实的描述它们的逻辑,那么你就不需要写注释来解释它在干什么。比如://&put&elephant&elephant1&into&fridge&fridge2
putElephantIntoFridge(elephant1,&fridge2);由于我的函数名putElephantIntoFridge已经说明了它要干什么(把大象放进冰箱),所以上面那句注释完全没有必要。2. 把复杂的逻辑提取出去,做成“帮助函数”。有些人写的函数很长,以至于看不清楚里面的语句在干什么,所以他们误以为需要写注释。如果你仔细观察这些代码,就会发现不清晰的那片代码,往往可以被提取出去,做成一个函数,然后在原来的地方调用。由于函数有一个名字,这样你就可以使用有意义的函数名来代替注释。举一个例子:...&...
//&put&elephant&elephant1&into&fridge&fridge2
openDoor(fridge2);
if&(driveElephantIntoFridge(elephan1,&fridge2))&{
&&&feedElephant(new&Treat(),&elephant1);
&&&putBananaIntoFridge(new&Banana(),&fridge2);
&&&waitForElephantEnter(elephant1,&fridge2);
closeDoor(fridge2);
...&...如果你把这片代码提出去定义成一个函数:function&putElephantIntoFridge(elephant,&fridge)&{
&&openDoor(fridge2);
&&if&(driveElephantIntoFridge(elephan1,&fridge2))&{
&&&&feedElephant(new&Treat(),&elephant1);
&&}&else&{
&&&&putBananaIntoFridge(new&Banana(),&fridge2);
&&&&waitForElephantEnter(elephant1,&fridge2);
&&closeDoor(fridge2);
}然后原来的代码就可以改成:...&...
putElephantIntoFridge(elephant1,&fridge2);
...&...注释就没必要了。程序语言相比自然语言,是非常强大而严谨的,它其实已经具有自然语言的主要元素:主语,谓语,宾语,名词,动词,如果,因为,所以,否则,是,不是,…… 所以如果你充分利用了程序语言的表达能力,你完全可以用程序本身来表达它到底在干什么,而不需要自然语言的辅助。有少数的时候,你也许会为了绕过其他一些代码的设计问题,采用一种违反直觉的作法。这时候你就可以使用很短的一条注释,说明为什么要写成那奇怪的样子。这样的情况应该很少出现,否则这意味着整个代码的设计都有问题。写简单的代码现在我提出一些我自己正在使用的代码规范,稍微解释一下为什么它们能让代码更加简单,从而提高代码的质量。1. 避免使用i++和++i。这种自增减操作表达式含义很蹊跷,非常容易搞混淆。而且含有它们的表达式的结果,有可能取决于参数的求值顺序。其实这两个表达式完全可以分解成两步做,把读写操作分开:一步更新i的值,另外一步使用i的值。比如,如果你想写foo(i++),你完全可以把它拆成int t = i += 1; foo(t);。如果你想写foo(++i),可以拆成i += 1; foo(i); 拆开之后的代码,含义完全一致,却清晰很多。到底更新是在取值之前还是之后,非常的明显。有人也许以为i++或者++i的效率比拆开之后要高,这只是一种误解。这些代码经过最基础的编译器优化之后,生成的机器代码是完全没有区别的。i++和++i,只有在两种情况下可以安全的使用。一种是用在for循环语句的update部分,比如for(int i = 0; i < 5; i++),另一种情况是写在单独的一行,比如i++;。这两种情况是完全没有歧义的。但是一定要避免把i++和++i用在复杂的表达式里面,比如foo(i++),foo(++i) + foo(i),…… 没有人应该知道,或者去追究这些是什么意思。2. 永远不要省略花括号。很多语言允许你在某种情况下省略掉花括号,比如C,Java都允许你在if语句里面只有一句话的时候省略掉花括号:if&(...)&
&&action1();咋一看少打了两个字,多好。可是这其实经常引起奇怪的问题。比如,你后来想要加一句话action2()到这个if里面,于是你就把代码改成:if&(...)&
&&action1();
&&action2();为了美观,你很小心的使用了action1()的缩进。咋一看它们是在一起的,所以你下意识里以为它们只会在if的条件为真的时候执行,然而action2()却其实在if外面,它会被无条件的执行。我把这种现象叫做“光学幻觉”(optical illusion),理论上每个程序员都应该发现这个错误,然而实际上却容易被忽视。那么你问,谁会这么傻,我在加入action2()的时候加上花括号不就行了?可是从设计的角度来看,这样其实并不是合理的作法。首先,也许你以后又想把action2()去掉,这样你为了样式一致,又得把花括号拿掉,烦不烦啊?其次,这使得代码样式不一致,有的if有花括号,有的又没有。况且,你为什么需要记住这个规则?如果你不问三七二十一,只要是if-else语句,把花括号全都打上,就可以想都不用想了,就当C和Java没提供给你这个特殊写法。这样就可以保持完全的一致性,减少不必要的思考。有人可能会说,全都打上花括号,只有一句话也打上,多碍眼啊?然而经过实行这种编码规范几年之后,我并没有发现这种写法更加碍眼,反而由于花括号的存在,使得代码界限明确,让我的眼睛负担更小了。3. 合理使用括号,不要盲目依赖操作符优先级。利用操作符的优先级来减少括号,对于1+2*3这样常见的算数表达式,是没问题的。然而有些人如此的仇恨括号,以至于他们会写出2 << 7 - 2 * 3这样的表达式,而完全不用括号。这里的问题,在于移位操作<<的优先级,是很多人不熟悉,而且是违反常理的。由于x << 1相当于把x乘以2,很多人误以为这个表达式相当于(2 << 7) - (2 * 3),所以等于250。然而实际上<<的优先级比加法+还要低,所以这表达式其实相当于2 << (7 - 2 * 3),所以等于4!解决这个问题的办法,不是要每个人去把操作符优先级表给硬背下来,而是合理的加入括号。比如上面的例子,最好直接加上括号写成2 << (7 - 2 * 3)。虽然没有括号也表示同样的意思,但是加上括号就更加清晰,读者不再需要死记<<的优先级就能理解代码。4. 避免使用continue和break。循环语句(for,while)里面出现return是没问题的,但是如果使用了continue或者break,就会让循环的逻辑和终止条件变得复杂,难以确保正确。如果只有一个continue或者break也许还好,但是如果你的循环语句里面出现了多个continue或者break,你就该考虑改写整个循环了。出现continue或者break的原因,往往是对循环要执行的逻辑没有想得很清楚。因为如果你考虑周全了,应该是几乎不需要continue或者break的。改写循环的办法有多种,你也许可以把复杂的部分提取出来,做成函数调用,或者把它变成一个没有continue或者break的循环结构。举一个例子。下面这段代码里面有一个continue:List&goodNames&=&new&ArrayList();
for&(String&name:&names)&{
&&if&(name.contains("bad"))&{
&&goodNames.add(name);
}它说:“如果name含有&#39;bad&#39;这个词,跳过后面的循环代码……” 注意,这是一种“负面”的描述,它不是在告诉你什么时候“做”一件事,而是在告诉你什么时候“不做”一件事。为了知道它到底在干什么,你必须搞清楚continue会导致哪些语句被跳过了,然后脑子里把逻辑反个向,你才能知道它到底想做什么。这就是为什么含有continue和break的循环不容易理解,它们依靠“控制流”来描述“不做什么”,“跳过什么”,结果到最后你也没搞清楚它到底“要做什么”。其实,我们只需要把continue的条件反向,这段代码就可以很容易的被转换成等价的,不含continue的代码:List&goodNames&=&new&ArrayList();
for&(String&name:&names)&{
&&if&(!name.contains("bad"))&{
&&&&goodNames.add(name);
}goodNames.add(name);和它之后的代码全部被放到了if里面,多了一层缩进,然而continue却没有了。你再读这段代码,就会发现更加清晰。因为它是一种更加“正面”地描述。它说:“在name不含有&#39;bad&#39;这个词的时候,把它加到goodNames的链表里面……”再举一个例子:public&boolean&hasBadName(List&names)&{
&&boolean&result&=&
&&for&(String&name:&names)&{
&&&&&&if&(name.contains("bad"))&{
&&&&&&&&&&result&=&
&&&&&&&&&&
}这个函数检查names链表里是否存在一个名字,包含“bad”这个词。它的循环里包含一个break语句。这个函数可以被改写成:public&boolean&hasBadName(List&names)&{
&&for&(String&name:&names)&{
&&&&&&if&(name.contains("bad"))&{
&&&&&&&&&&return&
}改进后的代码,在name里面含有“bad”的时候,直接用return true返回,而不是对result变量赋值,break出去,最后才返回。如果循环结束了还没有return,那就返回false,表示没有找到这样的名字。使用return来代替break,这样break语句和result这个变量,都一并被消除掉了。我曾经见过很多其他使用continue和break的例子,几乎无一例外的可以被消除掉,变换后的代码变得清晰很多。我的经验是,99%的break和continue,都可以通过替换成return语句,或者翻转if条件的方式来消除掉。剩下的1%含有复杂的逻辑,但也可以通过提取一个帮助函数来消除掉。修改之后的代码变得容易理解,容易确保正确。写直观的代码我写代码有一条重要的原则:如果有更加直接,更加清晰的写法,就选择它,即使它看起来更长,更笨,也一样选择它。比如,Unix命令行有一种“巧妙”的写法是这样:command1&&&&command2&&&&command3由于Shell语言的逻辑操作a && b具有“短路”的特性,如果a等于false,那么b就没必要执行了。这就是为什么当command1成功,才会执行command2,当command2成功,才会执行command3。同样,command1&||&command2&||&command3操作符||也有类似的特性。上面这个命令行,如果command1成功,那么command2和command3都不会被执行。如果command1失败,command2成功,那么command3就不会被执行。这比起用if语句来判断失败,似乎更加巧妙和简洁,所以有人就借鉴了这种方式,在程序的代码里也使用这种方式。比如他们可能会写这样的代码:if&(action1()&||&action2()&&&&action3())&{
}你看得出来这代码是想干什么吗?action2和action3什么条件下执行,什么条件下不执行?也许稍微想一下,你知道它在干什么:“如果action1失败了,执行action2,如果action2成功了,执行action3”。然而那种语义,并不是直接的“映射”在这代码上面的。比如“失败”这个词,对应了代码里的哪一个字呢?你找不出来,因为它包含在了||的语义里面,你需要知道||的短路特性,以及逻辑或的语义才能知道这里面在说“如果action1失败……”。每一次看到这行代码,你都需要思考一下,这样积累起来的负荷,就会让人很累。其实,这种写法是滥用了逻辑操作&&和||的短路特性。这两个操作符可能不执行右边的表达式,原因是为了机器的执行效率,而不是为了给人提供这种“巧妙”的用法。这两个操作符的本意,只是作为逻辑操作,它们并不是拿来给你代替if语句的。也就是说,它们只是碰巧可以达到某些if语句的效果,但你不应该因此就用它来代替if语句。如果你这样做了,就会让代码晦涩难懂。上面的代码写成笨一点的办法,就会清晰很多:if&(!action1())&{
&&if&(action2())&{
&&&&action3();
}这里我很明显的看出这代码在说什么,想都不用想:如果action1()失败了,那么执行action2(),如果action2()成功了,执行action3()。你发现这里面的一一对应关系吗?if=如果,!=失败,…… 你不需要利用逻辑学知识,就知道它在说什么。写无懈可击的代码在之前一节里,我提到了自己写的代码里面很少出现只有一个分支的if语句。我写出的if语句,大部分都有两个分支,所以我的代码很多看起来是这个样子:if&(...)&{
&&if&(...)&{
&&&&return&
&&}&else&{
&&&&return&
}&else&if&(...)&{
}使用这种方式,其实是为了无懈可击的处理所有可能出现的情况,避免漏掉corner case。每个if语句都有两个分支的理由是:如果if的条件成立,你做某件事情;但是如果if的条件不成立,你应该知道要做什么另外的事情。不管你的if有没有else,你终究是逃不掉,必须得思考这个问题的。很多人写if语句喜欢省略else的分支,因为他们觉得有些else分支的代码重复了。比如我的代码里,两个else分支都是return true。为了避免重复,他们省略掉那两个else分支,只在最后使用一个return true。这样,缺了else分支的if语句,控制流自动“掉下去”,到达最后的return true。他们的代码看起来像这个样子:if&(...)&{
&&if&(...)&{
&&&&return&
}&else&if&(...)&{
return&这种写法看似更加简洁,避免了重复,然而却很容易出现疏忽和漏洞。嵌套的if语句省略了一些else,依靠语句的“控制流”来处理else的情况,是很难正确的分析和推理的。如果你的if条件里使用了&&和||之类的逻辑运算,就更难看出是否涵盖了所有的情况。由于疏忽而漏掉的分支,全都会自动“掉下去”,最后返回意想不到的结果。即使你看一遍之后确信是正确的,每次读这段代码,你都不能确信它照顾了所有的情况,又得重新推理一遍。这简洁的写法,带来的是反复的,沉重的头脑开销。这就是所谓“面条代码”,因为程序的逻辑分支,不是像一棵枝叶分明的树,而是像面条一样绕来绕去。正确处理错误使用有两个分支的if语句,只是我的代码可以达到无懈可击的其中一个原因。这样写if语句的思路,其实包含了使代码可靠的一种通用思想:穷举所有的情况,不漏掉任何一个。程序的绝大部分功能,是进行信息处理。从一堆纷繁复杂,模棱两可的信息中,排除掉绝大部分“干扰信息”,找到自己需要的那一个。正确地对所有的“可能性”进行推理,就是写出无懈可击代码的核心思想。这一节我来讲一讲,如何把这种思想用在错误处理上。错误处理是一个古老的问题,可是经过了几十年,还是很多人没搞明白。Unix的系统API手册,一般都会告诉你可能出现的返回值和错误信息。比如,Linux的read系统调用手册里面有如下内容:RETURN&VALUE
On&success,&the&number&of&bytes&read&is&returned...
On&error,&-1&is&returned,&and&errno&is&set&appropriately.
EAGAIN,&EBADF,&EFAULT,&EINTR,&EINVAL,&...很多初学者,都会忘记检查read的返回值是否为-1,觉得每次调用read都得检查返回值真繁琐,不检查貌似也相安无事。这种想法其实是很危险的。如果函数的返回值告诉你,要么返回一个正数,表示读到的数据长度,要么返回-1,那么你就必须要对这个-1作出相应的,有意义的处理。千万不要以为你可以忽视这个特殊的返回值,因为它是一种“可能性”。代码漏掉任何一种可能出现的情况,都可能产生意想不到的灾难性结果。对于Java来说,这相对方便一些。Java的函数如果出现问题,一般通过异常(exception)来表示。你可以把异常加上函数本来的返回值,看成是一个“union类型”。比如:String&foo()&throws&MyException&{
}这里MyException是一个错误返回。你可以认为这个函数返回一个union类型:{String, MyException}。任何调用foo的代码,必须对MyException作出合理的处理,才有可能确保程序的正确运行。Union类型是一种相当先进的类型,目前只有极少数语言(比如Typed Racket)具有这种类型,我在这里提到它,只是为了方便解释概念。掌握了概念之后,你其实可以在头脑里实现一个union类型系统,这样使用普通的语言也能写出可靠的代码。由于Java的类型系统强制要求函数在类型里面声明可能出现的异常,而且强制调用者处理可能出现的异常,所以基本上不可能出现由于疏忽而漏掉的情况。但有些Java程序员有一种恶习,使得这种安全机制几乎完全失效。每当编译器报错,说“你没有catch这个foo函数可能出现的异常”时,有些人想都不想,直接把代码改成这样:try&{
}&catch&(Exception&e)&{}或者最多在里面放个log,或者干脆把自己的函数类型上加上throws Exception,这样编译器就不再抱怨。这些做法貌似很省事,然而都是错误的,你终究会为此付出代价。如果你把异常catch了,忽略掉,那么你就不知道foo其实失败了。这就像开车时看到路口写着“前方施工,道路关闭”,还继续往前开。这当然迟早会出问题,因为你根本不知道自己在干什么。catch异常的时候,你不应该使用Exception这么宽泛的类型。你应该正好catch可能发生的那种异常A。使用宽泛的异常类型有很大的问题,因为它会不经意的catch住另外的异常(比如B)。你的代码逻辑是基于判断A是否出现,可你却catch所有的异常(Exception类),所以当其它的异常B出现的时候,你的代码就会出现莫名其妙的问题,因为你以为A出现了,而其实它没有。这种bug,有时候甚至使用debugger都难以发现。如果你在自己函数的类型加上throws Exception,那么你就不可避免的需要在调用它的地方处理这个异常,如果调用它的函数也写着throws Exception,这毛病就传得更远。我的经验是,尽量在异常出现的当时就作出处理。否则如果你把它返回给你的调用者,它也许根本不知道该怎么办了。另外,try { ... } catch里面,应该包含尽量少的代码。比如,如果foo和bar都可能产生异常A,你的代码应该尽可能写成:try&{
}&catch&(A&e)&{...}
}&catch&(A&e)&{...}而不是try&{
}&catch&(A&e)&{...}第一种写法能明确的分辨是哪一个函数出了问题,而第二种写法全都混在一起。明确的分辨是哪一个函数出了问题,有很多的好处。比如,如果你的catch代码里面包含log,它可以提供给你更加精确的错误信息,这样会大大地加速你的调试过程。正确处理null指针穷举的思想是如此的有用,依据这个原理,我们可以推出一些基本原则,它们可以让你无懈可击的处理null指针。首先你应该知道,许多语言(C,C++,Java,C#,……)的类型系统对于null的处理,其实是完全错误的。这个错误源自于最早的设计,Hoare把这个错误称为自己的“”,因为由于它所产生的财产和人力损失,远远超过十亿美元!这些语言的类型系统允许null出现在任何对象(指针)类型可以出现的地方,然而null其实根本不是一个合法的对象。它不是一个String,不是一个Integer,也不是一个自定义的类。null的类型本来应该是NULL,也就是null自己。根据这个基本观点,我们推导出以下原则:1. 尽量不要产生null指针。尽量不要用null来初始化变量,函数尽量不要返回null。如果你的函数要返回“没有”,“出错了”之类的结果,尽量使用Java的异常机制。虽然写法上有点别扭,然而Java的异常,和函数的返回值合并在一起,基本上可以当成union类型来用。比如,如果你有一个函数find,可以帮你找到一个String,也有可能什么也找不到,你可以这样写:public&String&find()&throws&NotFoundException&{
&&if&(...)&{
&&&&return&"found";
&&}&else&{
&&&&throw&new&NotFoundException();
}Java的类型系统会强制你catch这个NotFoundException,所以你不可能像漏掉检查null一样,漏掉这种情况。Java的异常也是一个比较容易滥用的东西,不过我已经在上一节告诉你如何正确的使用异常。2. 不要把null放进“容器数据结构”里面。所谓容器(collection),是指一些对象以某种方式集合在一起,所以null不应该被放进Array,List,Set等结构,不应该出现在Map的key或者value里面。把null放进容器里面,是一些莫名其妙错误的来源。因为对象在容器里的位置一般是动态决定的,所以一旦null从某个入口跑进去了,你就很难再搞明白它去了哪里,你就得被迫在所有从这个容器里取值的位置检查null。你也很难知道到底是谁把它放进去的,代码多了就导致调试极其困难。解决方案是:如果你真要表示“没有”,那你就干脆不要把它放进去(Array,List,Set没有元素,Map根本没那个entry),或者你可以指定一个特殊的,真正合法的对象,用来表示“没有”。需要指出的是,类对象并不属于容器。所以null在必要的时候,可以作为对象成员的值,表示它不存在。比如:class&A&{
&&String&name&=&
}之所以可以这样,是因为null只可能在A对象的name成员里出现,你不用怀疑其它的成员因此成为null。所以你每次访问name成员时,检查它是否是null就可以了,不需要对其他成员也做同样的检查。3. 函数调用者:明确理解null所表示的意义,尽早检查和处理null返回值,减少它的传播。null很讨厌的一个地方,在于它在不同的地方可能表示不同的意义。有时候它表示“没有”,“没找到”,有时候它表示“出错了”,“失败了”…… 你必须理解每一个null的意义,不能给混淆起来。如果你调用的函数有可能返回null,那么你应该在第一时间对null做出“有意义”的处理。比如,上述的函数find,返回null表示“没找到”,那么调用find的代码就应该在它返回的第一时间,检查返回值是否是null,并且对“没找到”这种情况,作出有意义的处理。“有意义”是什么意思呢?我的意思是,使用这函数的人,应该明确的知道在拿到null的情况下该怎么做,承担起责任来。他不应该只是“向上级汇报”,把责任踢给自己的调用者。如果你违反了这一点,就有可能采用一种不负责任,危险的写法:public&String&foo()&{
&&String&found&=&find();
&&if&(found&==&null)&{
&&&&return&
}当看到find()返回了null,foo自己也返回null。这样null就从一个地方,游走到了另一个地方。如果你不假思索就写出这样的代码,最后的结果就是代码里面随时随地都可能出现null。到后来为了保护自己,你的每个函数都会写成这样:public&void&foo(A&a,&B&b,&C&c)&{
&&if&(a&==&null)&{&...&}
&&if&(b&==&null)&{&...&}
&&if&(c&==&null)&{&...&}
}4.&函数作者:明确声明不接受null参数,当参数是null时立即崩溃。不要试图对null进行“容错”,不要让程序继续往下执行。如果调用者使用了null作为参数,那么调用者(而不是函数作者)应该对程序的崩溃负全责。上面的例子之所以成为问题,就在于人们对于null的“容忍态度”。上面这种“保护式”的写法,试图“容错”,试图“优雅的处理null”,其结果是让调用者更加肆无忌惮的传递null给你的函数。到后来,你的代码里出现一堆堆nonsense的情况,null可以在任何地方出现,都不知道到底是哪里产生出来的。谁也不知道出现了null是什么意思,该做什么,所有人都把null踢给其他人。最后这null像瘟疫一样蔓延开来,到处都是,成为一场噩梦。正确的做法,其实是强硬的态度。你要告诉函数的使用者,我的参数全都不能是null,如果你给我null,程序崩溃了该你自己负责!至于调用者代码里有null怎么办,他自己该知道怎么处理(参考以上几条),不应该由函数作者来操心。5. 使用@NotNull和@Nullable标记。IntelliJ提供了@NotNull和@Nullable两种标记,加在类型前面,这样可以比较可靠地防止null指针的出现。IntelliJ本身会对含有这种标记的代码进行静态分析,指出运行时可能出现NullPointerException的地方。在运行时,会在null指针不该出现的地方产生IllegalArgumentException,即使那个null指针你从来没有deference。这样你可以在尽量早期发现并且防止null指针的出现。扩展话题:关于Optional类型和Union类型有些语言,比如Java 8和Swift,提供了一种叫“Optional类型”的东西。比如在Java 8里面,你可以使用Optional来表示“可能是String,可能没有”。很多人以为有了Optional类型,就可以完美的解决null指针的问题,然而它并不是想象的那样完美。因为你看到的类型是Optional,而不是String,所以类型系统不允许你直接把它当String来用。这多出来的一层关卡,可以防止你不问三七二一就取它的值,你总要想一下。然而这并不能从根本上解决问题。Optional并不能完全阻止你产生跟NullPointerException等价的运行时错误。因为你仍然可以写这样的代码:Optional&x&=&Optional.empty();
String&y&=&x.get();没有检查x.isPresent()就使用x.get(),结果出现NoSuchElementException。这其实等价于没有检查null就在dereference它。只不过现在出现的不是NullPointerException,而是NoSuchElementException。两个都是运行时错误,换汤不换药,程序照样崩溃。所以你看到了,Optional只是一种善意的“提示”,它使你不会在完全不知情的情况下犯错误。可是如果你忽略这种提示,照样可以犯一样的错误。Optional并没有任何强制性的力量。Swift的Optional类型跟Java的是一样的问题,Swift的手册里指出:“Using the ! operator to unwrap an optional that has a value of nil results in a runtime error.” 所以,Swift并不能静态地阻止你对一个值为nil的Optional进行!操作。如果你做了,就会产生“运行时错误”。另外,Optional类型会导致程序变得复杂。Optional和null指针,在结构上有一个很大的差别。Optional比null指针多了一层数据结构。Optional把需要的值放在了另外一个对象里面。你必须用x.get()来得到里面这个值,这跟使用null的时候很不一样。当你判断了一个String不可能是null,你不需要再做一次get把内容给取出来。比如:String&found&=&find();
if&(found&!=&null)&{
&&total&+=&found.length();
}判断found不是null之后,我们可以直接用found.length()得到它的长度,而不需要先使用found.get()。这个例子貌似小事,然而如果Optional类型被放进另外的结构或者容器里面,或者包含了另外类型,你就知道它的繁琐和痛苦了。Optional的这个问题,跟Haskell的Maybe类型的问题一样,经常导致类型嵌套层数太多,太烦。相比之下,union类型系统可以完全静态地防止NullPointerException,而不导致类型的过度嵌套。Union类型可以完全的涵盖Optional类型的功能,非常的简单,而且有很多其它的好处。这种类型系统已经存在于语言(一个Scheme的后代),还没有面世的Yin语言也实现了union类型。的类型推导系统里面也具有union类型。Union类型系统非常强大,它不但可以完全静态地消灭NullPointerException,而且可以取代Java等语言的exception机制。它让错误处理变得非常严密,却又非常方便。不过需要注意的是,就算你有了union类型系统,完全静态地防止了NullPointerException,上面提到的几条对待null的原则仍然是有用的。在有union类型的语言里面,一个容易犯的错误是不假思索的扩展union类型,把什么可能性都加进去,结果最后得到很大的union类型。这导致很多变量和参数具有union类型,每个变量都有可能是好多种东西,以至于你需要做好几个判断才能通过类型检查。这种现象跟null指针的泛滥的问题并没有本质的区别,因为你没能有效地控制住“可能性”。这个“可能性爆炸”的问题,程序语言也许不能给你很好的帮助。只有靠自己,遵循上面的原则,尽早排除union类型或者减少其中的可能性,你才能避免这种混乱。防止过度工程人的脑子真是奇妙的东西。虽然大家都知道过度工程(over-engineering)不好,在实际的工程中却经常不由自主的出现过度工程。所以我觉得必须分析一下过度工程出现的信号和兆头,在初期的时候就避免它。过度工程即将出现的一个重要信号,就是当你过度的思考“将来”,考虑一些还没有发生的事情,还没有出现的需求。比如,“如果我们将来有了上百万行代码,有了几千号人,这样的工具就支持不了了”,“将来我可能需要这个功能,所以我现在就把代码写来放在那里”,“将来很多人要扩充这片代码,所以现在我们就让它变得可重用”……这就是为什么很多软件项目如此复杂。实际上没做多少事情,却为了所谓的“将来”,加入了很多不必要的复杂性。眼前的问题还没解决呢,就被“将来”给拖垮了。人们都不喜欢目光短浅的人,然而在现实的工程中,有时候你就是得看近一点,把手头的问题先搞定了,再谈以后扩展的问题。另外一种过度工程的来源,是过度的关心“代码重用”。很多人“可用”的代码还没写出来呢,就在关心“重用”。为了让代码可以重用,最后被自己搞出来的各种框架捆住手脚,最后连可用的代码就没写好。如果可用的代码都写不好,又何谈重用呢?很多一开头就考虑太多重用的工程,到后来被人完全抛弃,没人用了,因为别人发现这些代码太难懂了,自己从头开始写一个,反而省好多事。过度地关心“测试”,也会引起过度工程。有些人为了测试,把本来很简单的代码改成“方便测试”的形式,结果引入很多复杂性,以至于本来一下就能写对的代码,最后复杂不堪,出现很多bug。世界上有两种“没有bug”的代码。一种是“没有明显的bug的代码”,另一种是“明显没有bug的代码”。第一种情况,由于代码复杂不堪,加上很多测试,各种coverage,貌似测试都通过了,所以就认为代码是正确的。第二种情况,由于代码简单直接,就算没写很多测试,你一眼看去就知道它不可能有bug。你喜欢哪一种“没有bug”的代码呢?根据这些,我总结出来的防止过度工程的原则如下:先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。先写出可用的代码,反复推敲,再考虑是否需要重用的问题。先写出可用,简单,明显没有bug的代码,再考虑测试的问题。
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量4556点击量2428点击量2074点击量2043点击量1885点击量1809点击量1738点击量1727点击量1654
&2018 Chukong Technologies,Inc.
京公网安备89

我要回帖

更多关于 反反复复那个什么歌曲 的文章

 

随机推荐