软件重构v2(精品).ppt
《软件重构v2(精品).ppt》由会员分享,可在线阅读,更多相关《软件重构v2(精品).ppt(156页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、1软件重构2报告人任甲林高级咨询师主要从事CMMI的培训、咨询和评估工作联系方式:Mobile:13301172719E-mail:Blog:http:/ Code 重复的代码Long Method 过长方法Large Class 过大类Long Parameter List 过长参数列表Divergent Change 发散式变化Shotgun Surgery 霰弹式修改Feature Envy 依恋情结Data Clumps 数据泥团Primitive Obsession 基本类型偏执Switch Statements Switch语句Parallel Inheritance Hierar
2、chies 平行继承层次Lazy Class 多余的类Speculative Generality 不确定的一般性Temporary Field 临时字段Message Chains 消息链Middle Man 二传手Inappropriate Intimacy 过度亲密Alternative Classes with Different Interfaces 异曲同工的类Incomplete Library Class 不完整的库类Data Class 数据类Refused bequest被拒绝的馈赠Comments 过多的注释9重复的代码症状:容易形式:两个代码段看上去几乎相同困难形式:两
3、个代码段都拥有几乎相同的作用措施:抽取方法抽取类替换算法说明:一次,仅仅一次原则10重复的代码 protected void queryBtn(object sender,EventArgs e)/如果项目编号不为空时 if(this.xmbhText.Text.ToString().Trim().Equals()=false)/对输入的查询条件项目编号是否合法进行校验 Byte MyBytes=System.Text.Encoding.Default.GetBytes(this.xmbhText.Text.ToString().Trim();if(MyBytes.Length 10)Mess
4、ageBox.Alert(项目编号不能超过10个字节!);this.xmbhText.Focus();return;/如果项目名称不为空时if(this.xmmcText.Text.ToString().Trim().Equals()=false)/对输入的查询条件项目名称是否合法进行校验 Byte xmmccheck=System.Text.Encoding.Default.GetBytes(this.xmmcText.Text.ToString().Trim();if(xmmccheck.Length 40)MessageBox.Alert(项目名称不能超过40个字节!);this.xmm
5、cText.Focus();return;/实现数据库绑定 GridView1_DataBind();11过长的方法症状:存在大量的代码行(只要看到超过N(如10行)代码的方法,立即检查是否可以重构之。长度是一个警告信号,并不表示一定有问题。)措施:绝大多数场合下,可以采取抽取方法的重构手法来把方法变小每当需要以注释来说明点什么时,我们就可以把需要说明的东西写进一个独立方法中以其用途(而非实现方法)命名。其他在现代的开发工具中,方法调用的增多不会影响性能。长方法难以理解。12过大类症状:存在大量实例变量存在大量方法存在大量代码行措施:抽取类抽取子类抽取接口13过长参数列表症状:方法的参数多于1
6、个或2个后果:难以理解难以记忆容易造成前后不一致措施:将参数替换为方法保持对象完整性引入参数对象14发散式变化症状:有多种变化的原因导致类的改变措施:如果类既要找到对象,又要对其做某些处理,则令调用者查找对象,并将该对象传入,或者令类返回调用者所用的值采用抽取类,为不同决策抽取不同的类如果多个类共享相同类型的决策,则可以合并这些新类。至少这些类可以构成一层。15霰弹式修改症状:发生一次改变时,需要修改多个类的多个地方措施:找出一个应对这些修改负责类,这可能是一个现有的类,也可能需要通过抽取类来创建一个新类。使用移动字段(move field)和移动方法(move method)将功能置于所选的
7、类中。如果未选中类足够简单,则可以使用内联类将该类除去。16魔法数症状:代码中出现了常量措施:对于特殊值,采用将魔法数替换为符号常量如果值是串,则可以将其置于某种映射功能方法中17依恋情结症状:一个方法似乎过于强调处理其他类的数据,而不是处理自己的数据。措施:使用移动方法将动作置于适当的类中(可能必须先采用抽取方法分离出位置不当的部分)18数据泥团症状:同样的两至三项数据频繁地一起出现在类和参数表中代码声明了某些字段,并声明了处理这些字段的方法,然后又声明了更多的字段和更多的方法,如此继续。各组字段名以类似的子串开头或结束措施:如果项是类中的字段,则使用抽取类将其取至一个新类中如果值共同出现在
8、方法的签名中,则使用引入参数对象(introduce parameter object)以抽取新的对象查看由新对象传递这些项的调用,以确定是否可以代之以使用保持对象完整(preserve whole object)查看这些项的使用:通常可以利用移动方法等重构技术,从而将这些使用移至新的对象中。19基本类型偏执症状:使用了基本类型或近基本类型(int、float、string等等)存在表示小整数的常量或枚举存在表示字段名的串常量措施:将数据值替换为对象将类型码替换为类将类型码替换为子类将类型码替换为状态/策略将数组替换为对象其他说明:用类的眼光来看待一切20Switch语句症状:代码使用了一个s
9、witch语句,尤其是对一个类型字段代码在某一行上存在多个If语句,特别是对同一个值进行比较时代码使用了instanceof或其等价形式来确定所处理的是何类型措施:如果针对相同的一条switch语句在多处出现,它通常会使用一个类型码;将其代之以多态内置于对象中。要完成这种改变,需要采用一系列重构技术:1 抽取方法。抽出每个分支上的代码。2 移动方法。将相关代码移动到适当的类。3 将类型码替换为子类或将类型码替换为状态/策略。建立继承体系结构4 将条件式替换为多态。去除条件式。如果条件式出现在一个类中,可以通过将参数替换为显示方法或引入Null对象来取代条件逻辑21平行继承层次症状:在一个继承体
10、系中建立了一个新的子类,缺发现还需要在另外一个继承体系中创建一个相关的类。可能发现两个继承体系中子类有相同的前缀(命令可以反映出协调继承体系的需求)措施:使用移动字段和移动方法类重新分配字段,从而可以去除某个继承体系。22多余的类症状类并没有做什么工作,似乎是由其父类、子类或者调用者完成了所有相关的工作,而在此类中却没有足够的行为,以至于对其是否继续存在会产生质疑。措施:如果一个类的父类或者子类更适合于完成该类的行为,则通过折叠继承体系将该类与其父类或子类合并。否则,通过内联类将其行为合并至其调用者。23不确定的一般性症状:存在未用的类、方法、字段、参数等。它们可能没有客户,或者仅有测试作为客
11、户对于当前实现的需求,代码过于复杂措施:对于一个不必要的类,可以:折叠继承体系内联类对于一个不必要的方法,使用内联方法或移除方法对于一个不必要的字段,确保没有对它的任务引用,删除之对于一个不必要的参数,使用移除参数(remove parameter)说明:不要过度设计24临时字段症状:字段仅在某些时候得到设置,而在其余时间内为null(或未用)措施:抽取类,移除字段以及所有相关代码25消息链症状:可能看到如下的调用形式:a.b().c().d()措施:如果处理实际上属于目标对象(即消息链最末的对象),则使用抽取方法和移动方法将处理置于该对象中。使用隐藏委托使方法仅依赖于一个对象(因此,不采用a
12、.b().c().d()的形式,而是将一个d()方法置于a对象中。这可能还需要为b()和c()对象增加一个d()方法)。26二传手症状:类的大多数方法都是在调用另一个对象的同一个(或类似的)方法:F()delegate.f();措施:可以通过令客户直接调用委托来移除中间人如果委托由中间人所有,或者是不可变的,而且中间人还有需要增加的行为,那么此中间人可以看作是委托的一个示例,如此则可以采用将委托替换为继承。27 过度亲密症状一个类访问了另一个类的内部(本应是私有的)部分。措施如果两个独立的类彼此“纠缠”。使用移动方法和移动字段,将适当的部分置于适当的类中。如果纠缠的部分看上去是一个被遗漏的类,
13、则使用抽取类和隐藏委托引入此新类如果类互相指向对方,则采用将双向引用改为单向引用,使之成为一种单向依赖如果子类以一种非受控的方式访问其父类的字段,则使用自封装字段如果父类可以定义一个通用算法,而子类能够插入其中,则使用构建模板方法如果父类和子类需要进一步解耦合,则采用将继承替换为委托28异曲同工的类症状:两个类看上完成相同的工作,但却使用了不同的方法名措施:协调各个类,使之取得一致,从而可以删除某个类采用重命名方法,使方法名类似使用移动方法、增加参数和方法参数化,从而使方法的协议(方法签名和实现途径)相似如果2个类只是相似而非相同,那么一旦对他们进行了很好的协调,就可以使用抽取超类如果可能的话
14、,删除多余的类29不完整的库类症状:你正在使用一个库类,而且希望在该类上有某个特性,但是却未能如愿。如果这是一个正常的类,就可以加以修改;但是,由于这是库的一部分,因此可能无法修改也不希望对它有所改变。措施:查看类或库的所有者是否考虑将来增加你所需的支持。如果仅仅是一两个方法,则可以对库类的客户应用引入外来方法(Introduce Foreign Method)如果存在多个方法需要增加,则要应用引入本地扩展(Introduce Local Extension)。再进一步使用这个新的扩展类。可能会决定引入一层来覆盖这个库30数据类症状:类仅有字段构成,或者只有简单的赋值方法和取值方法构成措施:采
15、用封装字段阻止对字段直接访问(仅允许通过赋值方法和取值方法进行访问)对可能的方法尽量采用移除设置方法(remove setting methods)采用封装集合(encapsulate collection)去除对所有集合类型字段的直接访问。查看对象的各个客户,如果客户试图对数据做同样的工作,则对客户采用抽取方法,然后将方法移到该类中。在完成上述工作后,可能发现类中存在多出相似的方法,使用诸如重命名方法、抽取方法、增加参数或者移除参数等重构技术,以协调签名,并消除重复对字段的大多数访问都不再需要,因为所移动的方法涵盖了其实际应用。因此可以使用隐藏方法来消除对赋值方法和取值方法的访问。31拒收的
16、馈赠症状subclass应该继承superclass的方法和字段,但是subclass只使用了superclass的部分方法和字段如果subclass复用了superclass的行为(实现),却又没有支持superclass的接口继承没有实际意义;子类并非父类的一个例子措施如果不会导致混淆,可以顺其自然如果找不出原因来共享某个关系,则采用将继承替换为委托如果父-子类确实有意义,则可以通过抽取子类、下移字段和下移方法来创建一个新的子类。令此类有非拒绝行为,并将父类的客户修改为该新类的客户,这样父类就不必再提及此特性了。还可以从原来的类以及其父类中去除这些被拒绝的方法。32过多的注释 Commen
17、ts 症状:代码中出现注释符(/或/*)措施:抽取方法重命名方法引入断言其他说明:有些注释是有用的,不能一概删除指出为什么需要以某种方式完成某项工作引用了并非显而易见的算法33案例:多余的注释public class KnockOutServiceBean implements KnockOutService/*The exception message.*/private String message=;/*The ssn and age not exist exception message.*/private static final String SSNORINCOMENOTEXIST
18、_EXCEPTION=ssnOrIncomeNotExistMessage;/*The ssn format error exception message.*/private static final String SSNFORMAT_EXCEPTION=ssnFormatErrorMessage;/*Read ruls file is wrong exception message.*/private static final String READRULES_EXCEPTION=read ruls file is wrong;34重新组织方法35抽取方法 Extract Method 当
19、方法过长时、需要注释才能让人理解其用途时、存在重复代码时,就可以考虑采用抽取方法的重构手法了。只有当抽取出的新方法进行了很好的命名,才能充分新方法的作用以“做什么”来命名方法,而不是以“如何做”来命名方法不要恐惧小方法36案例抽取方法/重构前int main()int a=10;int b=20;int c=a;a=b;b=c;/重构后void swap(int&x,int&y)int nTmp=x;x=y;y=nTmp;int main()/其他代码 int a=10;int b=20;swap(a,b);/此处调用 /其他代码37案例:抽取方法void printOwing(double
20、amount)void printOwing(double amount)printBanner();printBanner();/print details/print detailsSystem.out.println(name:+_name);System.out.println(name:+_name);System.out.println(amount+amount);System.out.println(amount+amount);void printOwing(double amount)void printOwing(double amount)printBanner();p
21、rintBanner();printDetails(amount);printDetails(amount);void printDetails(double amount)void printDetails(double amount)System.out.println(name:+_name);System.out.println(name:+_name);System.out.println(amount+amount);System.out.println(amount+amount);38案例:抽取方法/原方法double getPrice()int basePrice=_quat
22、ity*_itemPrice;double discountFactor;if(basePrice 1000)discountFactor=0.95;else discountFactor=0.98;return basePrice*discountFactor;double getPrice()return basePrice()*discountFactor();int basePrice()return _quatity*_itemPrice;double discountFactor()if(basePrice()1000)return 0.95;/从上次替换中获得好处else ret
23、urn 0.98;39实战演练:抽取方法public class Matcher public Matcher()public boolean match(int expected,int actual,int clipLimit,int delta)/Clip too-large values for(int i=0;i clipLimit)actuali=clipLimit;/Check for length differences if(actual.length!=expected.length)return false;/Check that each entry within ex
24、pected+/-delta for(int i=0;i delta)return false;return true;40方法内联化方法内联化 Inline MethodInline Method 一个小方法如果方法本体(method body)与其名称(method name)同样清楚易懂,则可考虑用方法本体替换掉对方法的调用。非必要的间接性可以去掉。间接层有其价值,但不是所有的间接层都有价值。用Inline可以找出那些是没有用的,同时去掉这些不必要的间接层。清晰简单是目标41案例:方法内联化案例:方法内联化int getRating()int getRating()return(more
25、ThanFiveLateDeliveries()?2:1;return(moreThanFiveLateDeliveries()?2:1;boolean moreThanFiveLateDeliveries()boolean moreThanFiveLateDeliveries()return _numberOfLateDeliveries 5;return _numberOfLateDeliveries 5;int getRating()int getRating()return(_numberOfLateDeliveries 5)?2:1;return(_numberOfLateDeliv
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 软件 v2 精品
限制150内