重构一 duplicated Code(重复代码)
场景演义:
议题一
同一个类的两个函数有相同代码片段
重构手法(Extract Method)
把一段代码组织在一起并独立,放进一个独立函数中,并让函数名称解析该函数的用途。
演义讲解
public static void main(String[] args) {
A();
System.out.println("B");
System.out.println("C");
}
public static void A(){
System.out.println("A");
}
对上面使用 Extract Method
public static void main(String[] args) {
A();
B();
}
public static void A(){
System.out.println("A");
}
public static void B(){
System.out.println("B");
System.out.println("C");
}
动机
处理复杂函数,过长函数,细粒度函数容易复用,易注释,易覆写。
重构前
viod printOwing(){
Enumeration e=orders.elements();
double outstanding=0.0;
//打印横幅
System.out.println(...............);
System.out.println(...............);
System.out.println(...............);
//计算outstanding值
while(e.hasMoreElements){
Order order=(Order)e.nextElement();
outstanding+=each.getAmout();
}
//打印细节
System.out.println?(...............);
System.out.println?(...............);
}
重构以后
范例一:无局部变量
viod printOwing(){
Enumeration e=orders.elements();
double outstanding=0.0;
//打印横幅
printOwing()
//计算outstanding值
while(e.hasMoreElements){
Order order=(Order)e.nextElement();
outstanding+=each.getAmout();
}
//打印细节
System.out.println(...............);
System.out.println(outstanding);
}
public void printOwing(){
System.out.println(...............);
System.out.println(...............);
System.out.println(...............);
}
范例二:有局部变量
viod printOwing(){
Enumeration e=orders.elements();
double outstanding=0.0;
//打印横幅
printOwing()
//计算outstanding值
while(e.hasMoreElements){
Order order=(Order)e.nextElement();
outstanding+=each.getAmout();
}
//有局部变量的extract method的方式
printdetail(outstanding);
}
public void printdetail(double outstanding){
//打印细节
System.out.println(...............);
System.out.println(outstanding);
}
范例三 对局部变量再赋值
viod printOwing(){
double outstanding=getOutstanding();
//打印横幅
printOwing()
//有局部变量的extract method的方式
printdetail(double outstanding);
}
public double getOutstanding(){
double result=0.0;
Enumeration e=orders.elements();
//计算outstanding值
while(e.hasMoreElements){
Order order=(Order)e.nextElement();
result+=each.getAmout();
}
return result;
}
public void printOwing(){
System.out.println(...............);
System.out.println(...............);
System.out.println(...............);
}
public void printOwing(){
System.out.println(...............);
System.out.println(...............);
System.out.println(...............);
}
public void printdetail(double outstanding){
//打印细节
System.out.println(...............);
System.out.println(outstanding);
}
议题二
两个互为兄弟的子类有相同代码片段
重构手法
extract method(提取重复代码)
Pull UpMethod(推入超类):有些函数在各个子类中产生完全相同的效果,将该函数移至超类中。
动机
避免修改一个而没有修改另一个,有大量冗余的工作要做
如上图所示 我们可以看到两个子类中都有createBill()和chargeFor(Date start,Date end)
这两个方法,遗憾的是只有createBill()方法是相同的,然而chargeFor方法确实不同的,所以我们只能使用pull up method 把createBill(); 提取到超类中,在超类中申明chargeFor方法为抽象方法。
class Customer{
public abstract double chargeFor(Date start,Date end);
}
如上类图所示 createBill已经通过pull up method提升到超类中了,chargeFor也在超类中被申明为了一个抽象类。
FormTemplate Method(模板方法)
一些子类,其中相应的某些函数以相同顺序执行类似操作,但各个操作细节上有所不同。将这些函数都放进独立的函数中,并保持他们都有相同的签名,于是原函数也变得相同了,然后将原函数移至超类。
如上所示。原本有两个子类都有同名函数。可是有些操作却不一样,把这些不同的操作以相同的函数签名用extract method抽取成独立的单元,把原函数pull up mehtod上升到父类中,再把这些不同的函数的公共方法签名作为抽象方法,放进超类中。
动机:两个子类之中有类似的函数,就可以提升至超类中,两个函数执行以大致相近的操作,又不完全相同,可以将完全相同的的顺序移至超类中,并借助多态来保持差异,这样的函数称为模板方法。
重构前范例
public Class Statement{
public String stateString(){
String result=null;
result=result+"你好";
result=result+"是的"
return result;
}
}
public String stateString(){
String result=null;
result=result+"<html><head></hea><body>你好";
result=result+"是的</body></html>"
return result;
}
}
如上代码段所示,是两种不同方式的问好方法,一个是html的方式,一个是普通字符串的方式,但是它们都实现同样的效果。此时我们应该把这两个方法作为两种策略,封装成两个类
第一步 并把两个实现相同功能的方法封装,重新进行方法签名
public class Statement{
}
public class Htmlstatement extends Statement{
public String value(){
String result=null;
result=result+"<h1>欢迎你</h1>";
result=result+"<html><head></hea><body>你好";
result=result+"我是小梅</body></html>"
return result;
}
}
public class Textstatement extends Statement{
public String value(){
String result=null;
result=result+"欢迎你";
result=result+"你好";
result=result+"我是小龙"
return result;
}
}
第二步:提取相同序列中的不同部门,代之以相同的方法签名
public class Statement{
}
public class Htmlstatement extends Statement{
public String value(String customer){
String result=null;
result=result+headString(customer);
result=result+eachRentalString(customer);
result=result+footerString(customer);
return result;
}
public String headString(String customer){
return "<h1>欢迎你</h1>";
}
public String eachRentalString(String rental){
return "<html><head></hea><body>你好";
}
public footerString(String customer){
return "我是小梅</body></html>";
}
}
public class Textstatement extends Statement{
public String value(String customer){
String result=null;
result=result+headString(customer);
result=result+eachRentalString(customer);
result=result+footerString(customer);
return result;
}
public String headString(String customer){
return "欢迎你"+ customer;
}
public String eachRentalString(String rental){
return "你好"+ Rental;
}
public footerString(String customer){
return "我是小龙"+ customer;
}
}
第三步 把相同的序列方法用pull up method 提到超类中,把相同的方法签名在超类中利用抽象方法进行申明。
public class Statement{
public String value(String customer){
String result=null;
result=result+headString(customer);
result=result+eachRentalString(customer);
result=result+footerString(customer);
return result;
}
public abstract String headString(String customer);
public abstract String eachRentalString(String rental);
public abstract String footerString(String customer);
}
子类不做改变:
重构完毕后的类图。
SubstituteAlgorithm(替换算法)
将某个算法替换为另一个更清晰的算法。
议题三
多个毫不相干的类中有相同代码片段
重构手法
ExtractClass 提炼类:某个类做了应该由两个类做的事情,建立一个新类,将相关字段和函数从旧类搬移到新类。
一个类应该是一个清楚的抽象,处理一些明确的责任。
重构前:
public class Person{
private String name;
private String areaCode;
private String areaphomenum;
public void setName(String name){
this.name=name;
}
public String getName(){
retunr this.name;
}
public String getTeleAcreCode(){
return this.areaCode;
}
public String getAreaPhone(){
return this.areaphomenum;
}
}
我们看到跟电话相关的类似乎有点冗余,所以我们应该把它抽取出去
public class TelePhoneNumber{
private String number;
private String code;
public String getNumber(){
return number;
}
public String getCode(){
return code;
}
}
接下来我们把 TelephoneNumber这个类组合进去