我不是个伟大的程序员;我只是个有着一些优秀习惯的好程序员而己
本人比较直接,不说虚的,直接上干货。
目录
Duplicated Code(重复的代码)
Long Method(过长函数)
Long Parameter List(过长参数列)
Large Class(过大类)
提前总结就是四招:
一、重复的代码提炼成函数
二、把过长的函数变小
三、参数列太长或变化太频繁,参数对象化
四、大招:类的代码行数太多,要考虑提炼子类。
第一招 重复的代码提炼成函数
第一种情况是:同一个class内的两个函数含有相同表达式(expression)。优化的思路1
1、在各个subclass 中分解目标函数,把有差异的部分变成入参,封装成一个模板函数。
2、把模板函数放到父类中。
3、子类根据需要输入不同的入参,得到需要的结果。
*********************************************************************************
如果是相似的表达式,差异的地方不好抽共性,则用模板函数设计模式来处理。
这里用到了JAVA的两个特性,继承和覆写(overrides)。
优化的思路2
1、在各个subclass 中分解目标函数,使分解后的各个函数要不完全相同,要不完全不同。
2、父类有一个主函数包含完全相同的函数和完全不同的函数:相同的函数,抽到父类中,不相同的函数在父类中定义一个函数。
3、子类继承父类,然后覆写完全不同的函数,再调用主函数可得到期望的结果。
第二招 把过长的函数变小
百分之九十九的场合里,要把函数变小,只需使用Extract Method(第一招)。找到函数中适合集在一起的部分,将它们提炼出来形成一个新函数。
如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。这时就要用Replace Temp with Query来消除这些临时变量
Replace Temp with Query(以查询取代临时变量)
优化思路
1、找出只被赋值一次的临时变量。
2、将该临时变量声明为final
3、编译:这可确保该临时变量的确只被赋值一次。
4、将临时变量等号右侧部分提炼到一个独立函数中;
5、首先将函数声明为private。日后你可能会发现有更多class需要使用 它,彼时你可再放松对它的保护。
6、编译,测试:确保提炼出来的函数无任何连带影响(副作用),结果不变;
7、把临时变量全替换成独立出来的函数;
以上,over!
例子:未优化代码
通过以上的优化,一个大函数,已经变成了多个小函数,重点是代码的可读性提高了,顺带的代码量变少。
第三招 参数对象化
当你看到一个函数的入参有四,五个,甚至更多时,且好几个函数都使用这组入参,这时就要用参数对象化来优化代码。这些函数可能隶属同一个class,也可能隶属不同的classes 。这样一组参数就是所谓的Date Clump (数据泥团)」。这时用一个对象封装这些参数,再用对象取代它们。
优化思路
1、入参有四,五个,甚至更多时,就要着手优化;
2、用一个新的class封装入参,并把这些参数设置为private严格保护起来,写这些参数的get方法和set方法。
3、原函数的入参变成这个新的class对象,函数里的参数用class对象对应的属性替换。
4、编译测试;
5、将原先的参数全部去除之后,观察有无适当函数可以运用Move Method 搬移到参数对象之中。
例子:未优化的代码
@Autowired private AddressService addressService; public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,String addressName,String mobile,String zipCode,String consignee){ return addressService.inquireAddressList(pageNum,pageSize,addressName,mobile,zipCode,consignee); }
优化
@Autowired private AddressService addressService; public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,InquireAddressListInput output){ return addressService.inquireAddressList(pageNum,pageSize,output); } public class InquireAddressListInput(){ private String addressName; private String mobile; private String zipCode; private String consignee; public String getConsignee() { return consignee; } public void setConsignee(String consignee) { this.consignee = consignee; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getZipCode() { return zipCode; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } public String getAddressName() { return addressName; } public void setAddressName(String addressName) { this.addressName = addressName; } }
第四招 大招-提炼类和提炼子类
如果想利用单一class做太多事情,其内往往就会出现太多instance变量。一旦如此,Duplicated Code也就接踵而至了。
Extract Class 是Extract Subclass 之外的另一种选择,两者之间的抉择其实就是委托(delegation)和继承(inheritance)之间的抉择。
情况一:某个class做了应该由两个classes做的事。(Extract Class)
优化思路1
1、明确每个class所负的责任,该做什么事情;
2、建立一个新class,用以表现从旧class中分离出来的责任;
3、建立「从旧class访问新class」的连接关系;
4、每次搬移后,编译、测试。
5、决定是否让新的class曝光。
例子:未优化的代码情况二:class 中的某些特性(features)只被某些(而非全部)实体(instances)用到。Extract Subclass(提炼子类)
优化思路2
1、为source class 定义一个新的subclass
2、为这个新的subclass 提供构造函数。
简单的作法是:让subclass 构造函数接受与superclass 构造函数相同的参数,并通过super 调用superclass 构造函数;
3、找出调用superclass 构造函数的所有地点。如果它们需要的是新建的subclass , 令它们改而调用新构造函数。
如果subclass 构造函数需要的参数和superclass 构造函数的参数不同,可以使用Rename Method 修改其参数列。如果subclass 构造函数不需要superclass 构造函数的某些参数,可以使用Rename Method 将它们去除。
如果不再需要直接实体化(具现化,instantiated)superclass ,就将它声明为抽象类。
4、逐一使用Push Down Method 和 Push Down Field 将source class 的特性移到subclass 去。
5、每次下移之后,编译并测试。
例子:未优化代码
--用来决定当地修车厂的工作报价:
class JobItem ... public JobItem (int unitPrice, int quantity, boolean isLabor, Employee employee) { _unitPrice = unitPrice; _quantity = quantity; _isLabor = isLabor; _employee = employee; } public int getTotalPrice() { return getUnitPrice() * _quantity; } public int getUnitPrice(){ return (_isLabor) ? _employee.getRate(): _unitPrice; } public int getQuantity(){ return _quantity; } public Employee getEmployee() { return _employee; } private int _unitPrice; private int _quantity; private Employee _employee; private boolean _isLabor; class Employee... public Employee (int rate) { _rate = rate; } public int getRate() { return _rate; } private int _rate;
class LaborItem extends JobItem { public LaborItem (int unitPrice, int quantity, boolean isLabor, Employee employee) { super (unitPrice, quantity, isLabor, employee); } public LaborItem (int quantity, Employee employee) { super (0, quantity, true, employee); } public Employee getEmployee() { return _employee; } } //因为_employee 值域也将在稍后被下移到LaborItem ,所以我现在先将它声明为protected。 class JobItem... protected Employee _employee;
使用某项值域的函数全被下移至subclass 后,我就可以使用 Push Down Field 将值域也下移。
最后的结果就是:
public class JobItem { protected JobItem (int unitPrice, int quantity) { _unitPrice = unitPrice; _quantity = quantity; } public int getTotalPrice() { return getUnitPrice() * _quantity; } public int getUnitPrice(){ return _unitPrice; } public int getQuantity(){ return _quantity; } private int _unitPrice; private int _quantity; } // public class LaborItem extends JobItem { private Employee _employee; public LaborItem(int quantity, Employee employee) { super(0, quantity); _employee = employee; } public Employee getEmployee() { return _employee; } public int getUnitPrice() { return _employee.getRate(); } } //public class Employee { public Employee(int rate) { _rate = rate; } public int getRate() { return _rate; } private int _rate; }
一个class如果拥有太多代码,也适合使用Extract Class和Extract Subclass。
想重构代码,直接把以上四招看情况用上,更多精彩内容,请等待后续更新。
***************************************************************************
作者:小虚竹
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员而己