代码味道. 原著石一槛(1分)

  • 主题发起人 主题发起人 曹晓钢
  • 开始时间 开始时间

曹晓钢

Unregistered / Unconfirmed
GUEST, unregistred user!
(说明,这是erptao.org,refactoring论坛中关于Code Smell的一个贴子,以后我会把相关内容继续转贴到这里)
什么时候需要Refactoring?
代码的味道是高水平程序员对“好程序“的一种感觉,他们具备一种能力,即使不涉及程序代码的具体实现就能看出你的设计是否合理。
如果代码有“异味“,那么你需要进行Refactorying.
1.重复代码(Duplicate Code)
即使是一两句代码的重复也需要refactoring,有时候重复不是那么明显,你需要首先进行其他的refactoring才能看到代码重复。排除代码重复是OO软件工程最重要的研究课题之一
2.长方法(Long Method)
来自于面向过程的思路,即使能够在一页内能够显示的方法也可能太长。
3.大类(Large Class)
一个类含有太多的责任和行为
4.参数太多(Long Parameter List)
对象含有状态,不再需要太多的参数。
5.不一致的变化(Divergent Change)
不要把变化速度不同的东西放在一起。不要把一个方法对每个子类的变化的部分和不变化的部分放在一起。不要把对象中每秒都在变化的实例变量和一个月才变化一次的势力变量放在一起...等等。
6.Shotgun Surgery
改变影响到太多的类和方法
7.特性羡慕(Feature Envy)
对其他对象中的数据太感兴趣了
8.数据从(Data Clumps )
一块数据到处一起使用,他们应该有自己的类
9.原始类型困扰(Primitive Obsession)
用类代替原始数据类型
10.开关语句(Switch Statement)
面向对象由其他办法来处理这些依赖于类型的方法。
11.并行继承层次(Parallel Inheritance Hierarchies )
有时候有用但有时候不必要
12.惰类(Lazy Class)
不足以自己成为一个类,应该排除
13.投机通则(Speculative Generality )
不要太多考虑为将来而建立的灵活性
14.消息链(Message Chain )
硬性把客户和导航结构相耦合
15.中间人(Middle Man )
如果他所有的事情就是在做分派,那么应当删除。
16.不合适的亲密(Inappropriate Intimacy)
限制对其他类内部结构的知识和了解。
17.不完整的库类(Incomplete Library Class )
某些时候必须扩展一增加所需的功能
18.数据类(Data Class )
应当添加任务和行为来处理它的数据
19.被拒绝的遗产(Refused Bequest )
子类很少利用父类给予它们的东西
20.注释(Comments )
注释是说明why而不是what的好地方。

转贴附注:
原作为pdf格式,请参见:http://www.erptao.org/download.php?op=viewsdownload&sid=6
 
大侠出手,味道就是不一样。
 
曹老大,拜托换换行!^_^
 
与魔鬼词典比起来,还是差了那么一点点哦~~~
 
>20.注释(Comments )
>注释是说明why而不是what的好地方。
这一点看起来很有高手的味道.
 
呵呵,
经验是不错,
不过囫囵吞枣好象不好
以上代码的除"异味",不适合中等经验的程序员以下人
经验不足,无法完全体会,勉强去用,会画虎类狗
还有就是这种经验对于开发来说不是很全面,大家自己玩味吧
 
软件工程不是学出来的
而是在编码过程中积累经验而来的
上面的总结的经验都很好
不知有没有人能全部理解。(我是每一项都能理解个皮毛,但每一项在实际过程中
你要选择正确的方向,这很难。而且还要根据具体情况选择放弃上面 20条中
的某几条)
不容易啊
 
[:)],听课
 
Refactoring现在已成为我懒惰的借口,
以前我总是希望第一次就把代码写好,所以每次写第一句之前都想很详细。
接触了Refactoring后,简直乱写代码,以后再Refactoring吧,每次都是这
样想.但写完后又想Refactoring的时间足够我再写一个新软件了。
所以我偏向一开始就做好规划,边写边优化,尽量让异味小点。
哪位高人能讲讲Refactorying的技巧,Refactorying是不是与语言相关性很强的。
 
我对此文不懂?呢否讲述时用的
 
xalndy,我不知道什么华宇,也不认识你。
对于你的言论,我会要求管理员严肃处理。
 
很抱歉,我今天在贵论坛上对“曹晓钢(南京华宇的)”说了很多不该说的话。
我对我的过激言论表示道歉!希望你们能谅解,能谅解一个刚刚毕业就被人在心口
狠狠的扎上一刀的人,见到如此对待自己的那个人的愤慨和痛苦!真的很对不起!请原谅!
 
To xalndy
你可能认错人了吧
 
不大懂,可能我水平太差,艾...
 
致曹先生:
文章我已打印下來,不過,在你所說的原出出僅看到兩個PDF的文章,即你提到的第一節和第二節,
可否告知有後續章節嗎?如果有的話,請問,在什麼地方?我希望拿到完整的20個章節!
再次表達真誠的謝意和感激!
 
我能理解其中的80~90,甚至更多,太有体会了.关注,
希望找到<九阴真经>的上下部,望曹晓钢告知
 
to keepyea:
在ERPTAO网站上还可以找到几篇续篇:
http://www.erptao.org/download/articles/duplicate.pdf

Duplicate Code
(石一楹)
关于代码重复最著名的单词是Kent Beck 的Once And Only One,也就是说软件操作的任何一个
片断--不管是一个算法,一个常量集合,用于阅读的文档或者其他东西--应当只出现一次。
软件重复出现至少会导致以下问题:
&amp;#8226;
其中的一个版本会过期
&amp;#8226;
代码的责任会四处散开,导致代码难以理解
&amp;#8226;
当你修改代码时,需要重复修改很多地方,一不小心就会遗漏
&amp;#8226;
你不能很好地进行性能优化
我以前的一位老板曾经跟我夸耀过他手下编程的能力:“他只要把一份模版代码拷过去,
稍加修改,就可以完成一个新的模块“。我惊讶这位程序员思路清晰的同时也怀疑这样的程
序除了他自己以外还有谁能维护,我想可能连他自己也无法做到。
重复代码的产生由各种各样的原因,上面的例子就是一个,我经常看到程序员把几行或
一整段代码从这里复制到这里,然后少加修改,就变成了一份新的代码。这里的原因是程序
员可以通过极少的努力就完成代码重用,但是我们可以来看看DavidHooker 提出的7 个软件
开发原则:
1.第一原则:存在的理由(Pattern: TheReason)
一个软件系统存在的理由就是:为它的用户提供价值。你所有的决定都取决于这一点。在指
定一个系统需求,在写下一段系统功能,在决定硬件平台和开发过程之前,问你自己一个问
题,“这样做会为系统增加价值吗?“,如果答案是”yes”,做。如果是”No”,不做。这个原
则是其他原则的原则。
2.第二原则(能简单就简单,愚蠢!)KISS (Pattern: KeepItSimple)
软件设计不是一个轻描淡写的过程。在做任何一个设计时,你必须考虑很多因素。所有设计
应当尽可能简单,但是不要再比这简单了。这样产生的系统才是可以理解和容易维护的。这
并不是说很多由意义的特性,因为这种简单性也要被抛弃。确实很多更优雅的设计往往更简
单,但简单并不意味着“quick and dirty."。事实上,简单是通过许多思考和一次一次的反复
修改才达到的。这些努力的汇报就是更容易维护,代码错误更少。(看看是否违反)
3.第三原则:保持远见(Pattern: MaintainTheVision)
清晰的远见是一个软件项目成功的基础。. 没有这样的远见,项目开发最后就变成天天为一
个不好的设计做补丁。Brooks 说过:
概念的完整性是系统设计中最重要的问题。
Stroustrup 也说:
有一个干净的内部结构识构建一个可理解、可辨识、可维护
、可测试系统的基础。
Booch 则总结道:
只有当你对系统的体系由一个清晰的感觉,才可能去发现通用的抽象和机制。开发
这种通用性最终导致系统更简单,因此更小,更可靠
如果你不断地复制、粘贴、修改代码,最终你将陷入一个大泥潭(the Big Mud),你永远不可能
对系统有一个清晰的认识。
4.第四原则:你制造的,别人会消费(Pattern: WhatYouProduceTheyConsume)
软件系统不是在真空中使用的。其他人会使用、维护、文档你的系统。这依赖于对你系统的
理解。所以,你设计、实现的东西应当能够让别人理解。要记住,你写的代码并非只给计算
机看,你要时时记住,代码还要给人看。(Kent Beck)
如果到处泛滥似是而非的代码,别人如何能够辨别这些代码的相似和不同,如何去理解这些
代码之间具有何种关系。
5.第五原则:对将来开放( Pattern BuildForTodayDesignForTomorrow)
一个成功的软件有很长的生命期。你必须能够使得软件能够适应这样和那样的变化。所以,
一开始就不要软件设计到死角上去。请总是问一下自己“如果这样,那么。。?“这个问题,
你要考虑到各种各样的可能性,而不光光是图省事。复制,粘贴一下即可。
6.第六原则:为重用做好计划
软件模式是重用计划的一种。不断重复的代码显然不是这样的计划。
(See CommentsOnSix)
7.第七原则:思考!
在采取任何动作之前首先做一个清晰、完整的考虑,这样才能产生更好的结果。如果你考虑
了,但还是产生错误的结果,那么这种努力也是值得的。在你学习或研究类似的问题时,更
容易理解和掌握。
这些原则告诉我们轻松地复制、粘贴和修改代码不可能产生好的,也就是容易理解、维护、
重用的代码。但请不要走极端。
我一直认为,一个好的软件系统是各种因素权衡的结果,也就是你如何把握一个度的问题。
重复代码产生的另外一个主要原因就是做得太多,XP 有一个基本原则叫做You Arent Gonna
Need It,它是说“只实现你真正需要的东西,从来不去实现你预期需要的东西“。如果你去
实现你现在认为将来需要的东西,不一定就是你以后真正需要的东西。你处于现在的环境中
可能无法理解你要实现东西究竟是什么样子的。你会浪费大量的时间去构造这样不知道是否
必须的可能性。同时,当你真正实现的时候就可能产生重复代码。
Martin Fowler 在它的Refactoring 一书中有很多用来处理代码重复,包括:
1. 同一个类的两个方法中有相同的表达式,使用Extract method,然后大家都调用该method;
2. 两个兄弟子类之间有相同的表达式,那么在这两个子类中使用Extract Method,接着使用pull up
field,移到共同的超类
3. 如果结构相似而并非完全相同,用Extract method 把相同部分和不同部分分开。然后使用Form
Template method.
4. 如果方法使用不同的算法做相同的事情,那么使用substitute algorithm
5. 如果在两个不相干的类中有重复代码,那么在一个类中使用Extract class,然后在其他类中使用该
class 对象作为元素。
等等。
重复代码需要refactoring 是毫无疑问的,关键在于,你如何找到重复代码,如果所有的重复
代码都是死板的重复,那问题是很容易解决的。但是软件开发的复杂因素可能往往使重复代
码表现为相似性而并非完全的重复。这些相似性可能并非一眼就能看出来。而是需要经过其
它的Refactory 步骤和一定的先见之明。
另一个问题就是排除重复代码的粒度,只有大段的重复代码有价值去排除,还是即使是小小
的2、3 句重复代码就应该去排除。重复代码排除的基本方法是建立自己单独的方法,如果
系统中许许多多的方法都很小,方法之间相互调用的开销就会增加,它同时也增加了维护的
开销。
但是,这些开销是值得的。方法是覆盖的最小粒度,能够被覆盖的粒度越小,能够重用的范
围和成都就愈广。但在这个问题上也不要走极端,只有当一个方法实现一个具体的可以用
Intent Revealing Name(揭示意图的名字)命名时,一段代码才值得称为一个方法,而不是考虑
其代码的多少。
Martin Fowler 在他的refactoring 中描述了很多这样的例子,Kent Beck 则在Smalltalk Best
Practice Pattern 中更基础地揭示了隐含在这些refactoing 下的意图。
下面是一个实际的例子,来自于Martin Fowler 在ACM 上的设计专栏:
class Invoice...
String asciiStatement() {
StringBuffer result = new StringBuffer();
result.append(“Bill for “ + customer + “/n”);
Iterator it = items.iterator();
while(it.hasNext()) {
LineItem each = (LineItem) it.next();
result.append(“/t” + each.product() + “/t/t”
+ each.amount() + “/n”);
}
result.append(“total owed:” + total + “/n”);
return result.toString();
}
String htmlStatement() {
StringBuffer result = new StringBuffer();
result.append(“<P>Bill for <I>” + customer + “</I></P>”);
result.append(“<table>”);
Iterator it = items.iterator();
while(it.hasNext()) {
LineItem each = (LineItem) it.next();
result.append(“<tr><td>” + each.product()
+ “</td><td>” + each.amount() + “</td></tr>”);
}
result.append(“</table>”);
result.append(“<P> total owed:<B>” + total + “</B></P>”);
return result.toString();
} }
asciiStatement 和htmlStatement 具有类似的基础结构,但是它们的实际步骤却有所不同。他
们都完成三件事情:
1. 打印发票头
2. 循环每一个项目,并打印
&amp;#10003;. 打印发票尾部
这种结构的相似性和意图马上上我们使用composed method(也就是Martin Fowler 的Extract
method):
interface Printer {
String header(Invoice iv);
String item(LineItem line);
String footer(Invoice iv);
}
static class AsciiPrinter implements Printer {
public String header(Invoice iv) {
return “Bill for “ + iv.customer + “/n”;
}
public String item(LineItem line) {
return “/t” + line.product()+ “/t/t” + line.amount() +“/n”;
}
public String footer(Invoice iv) {
return “total owed:” + iv.total + “/n”;
}
}
象html 则可以实现htmlPrinter.
class Invoice...
public String statement(Printer pr) {
StringBuffer result = new StringBuffer();
result.append(pr.header(this));
Iterator it = items.iterator();
while(it.hasNext()) {
LineItem each = (LineItem) it.next();
result.append(pr.item(each));
}
result.append(pr.footer(this));
return result.toString();
}
class Invoice...
public String asciiStatement2() {
return statement (new AsciiPrinter());
}
现在,statement 包含一个通用的结构,重复性已经被排除。更重要的是,你可以实现其它
的Printer,XXXPrinter,从而能够轻易地扩展系统。
BTW,Martin Fowler 在这里使用了Dispatched Interpretation 模式,statement 隐瞒了内部的细
节,它隐藏内部的数据和表示,当它需要Printer 做一件事情时,它负责解码内部的数据结
构,然后反过来把消息传给Printer.
参见:Martin Fowler:Refactoring:Improve the design of Existing Code
Kent Beck : Smalltalk Best Pratice Pattern
ACM: Martin Fowler Design column:Reduce repetation
Kent Beck: Extreme Programming Explained
 
后退
顶部