2 什么样的代码可以重构/怎么重构

重复代码

  • 同一个类两处相同代码,提炼到外部方法
  • 互为兄弟的子类含相同代码,可以使用模板设计模式将相同代码和不同代码隔离。
  • 两个不相干的类出现重复代码,将重复代码提取到独立类中。使用类调用它

过长函数

程序越长越难理解。应该积极分解函数。尽量遵循:每当感觉需要以注释来说明点什么时,就需要把需要说明的东西写在独立函数中,并说明其用途。我们可以对一组甚至一行代码做这件事。

若参数和临时变量过多,怎么提取到外部方法

  • 使用第6部分的 以查询取代临时变量 部分。将临时变量提炼到一个独立函数中。
  • 以一个对象取代这些参数
  • 若你的参数是一个对象取出部分属性传递到方法中,则改为传递整个对象
  • 使用 第6部分的 以函数对象取代函数 的做法来减少参数过多的问题

过长参数列

第一:使用“明确函数取代参数的方式”方式解决,可获得编译期检查的好处,也使接口更明确

public static int _height;
public static int _width;

void setValue(String name, int value){
    if (name.equals("height")){
        _height = value;
        return;
    }
    if (name.equals("width")){
        _width = value;
        return;
    }
}

//明确函数取代参数的方式
// ======================》》》》

void setHeight(int arg){
    _height = arg;
}
void setWidth(int arg){
    _width = arg;
}

第二:保持对象完整:使用一个完整的对象而不把对象属性提取出来

第三:用参数对象代替参数

发散式变化

如果发现某个类做了两个类做的事,比如一个人,即包含了名字,也包含了工作信息,就可以把工作信息从人的类中提取出来(提炼类):

散弹式修改

如果遇到某个变化,你必须在不同类中做出修改。这时就需要把这些修改的代码放到同一个类中。

1 搬移函数:在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或直接将其删除

2 搬移字段:在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段。类之间移动状态和行为,在重构过程中是必不可少的措施。

依恋情节

对象技术的全部要点:将数据和对这些数据的行为包装在一起。

问题:有时候我们厂看到某个函数为了计算某个值,从另一个对象中调用几乎半打的取值函数。

疗法:把这个函数移至另一个地点。原则:判断哪个类拥有最多被此函数使用的数据,然后将这个函数和数据摆在一起。

数据泥团

我们常常可以在多处地方看到相同的数据:两三个类相同的字段、许多函数签名中相同的参数。这些总绑在一起出现的数据应该拥有它们自己的对象。

第一步:[提炼类]将一起出现的数据提炼到独立对象

第二步:[引入参数对象]/[改为传递整个函数]

评判办法:删掉众多数据中的一项。若其他数据没有收到影响,那代表你应该将它们生成新对象。

基本类型偏执

使用小对象——如结合数值和币种的money类、由一个起始值和结束值组成的range类、特殊字符串组成的电话号码和邮政编码等等。

使用【以对象取代数据值】将特殊存在的数据值替换为对象

若要替换的数据值是类型码,使用【以类取代类型码】

public class Person {
    public static final int O=0;
    public static final int A=1;
    public static final int B=2;
    public static final int AB=3;

    private int _bloodGroup;

    public int get_bloodGroup() {
        return _bloodGroup;
    }

    public void set_bloodGroup(int _bloodGroup) {
        this._bloodGroup = _bloodGroup;
    }
}

===============================》》》》

public class Person{
  private BloodGroup _bloodGroup;
  public BloodGroup get_bloodGroup() {
      return _bloodGroup;
  }
  public void set_bloodGroup(BloodGroup _bloodGroup) {
      this._bloodGroup = _bloodGroup;
  }
}
public class BloodGroup {
    public static final BloodGroup O = new BloodGroup(0);
    public static final BloodGroup A = new BloodGroup(1);
    public static final BloodGroup B = new BloodGroup(2);
    public static final BloodGroup AB = new BloodGroup(3);

    private final int _code;
    private BloodGroup(int code) {
        _code = code;
    }

    private int getCode() {
        return _code;
    }

    @Override
    public String toString() {
        return String.valueOf(_code);
    }
}

若与类型码相关的条件表达式:封装集合--将行为移动到这个类中。

如果在参数列表中看到基本型数据:那就 引入参数对象

如果正在数组中挑选数据:那就换成对象:

switch惊悚现身

1 用子类替代类型码

2 以多态取代条件表达式

/**
 * 以多态取代条件表达式
 */
public class Employee1 {
    /**
     * 以多态取代条件表达式
     */
    public static final int ENGINERR = 0;
    public static final int SALESMAN = 1;
    public static final int MANAGER = 2;

    private static int _monthlySalary = 3000;
    private static int _commision = 1000;
    private static int _bonus = 500;
	//省略get方法
    public Employee1() {
    }

    int payAmount(int type) {
        switch (type) {
            case ENGINERR:
               return _monthlySalary;
            case SALESMAN:
                return _monthlySalary + _commision;
            case MANAGER:
                return _monthlySalary + _bonus;
            default:
                throw new IllegalArgumentException("Incorect type code value.");
        }
    }

}

我们需要使用多态的方式替换int payAmount()方法,首先定义一个Employee1Type,作为子类中的父类

abstract class Employee1Type {
    abstract int getTypeCode();

    abstract int payAmount(Employee1 emp);
}

建立子类,继承父类,这样在调用时:

class Engineer1 extends Employee1Type {

    int getTypeCode() {
        return Employee1.ENGINERR;
    }

    int payAmount(Employee1 emp) {
        return emp.getMonthlySalary();
    }
}

class Manager1 extends Employee1Type {

    int getTypeCode() {
        return Employee1.MANAGER;
    }

    int payAmount(Employee1 emp) {
        return emp.getMonthlySalary()+emp.getCommision();
    }
}

class SalesMan1 extends Employee1Type {

    int getTypeCode() {
        return Employee1.SALESMAN;
    }
    int payAmount(Employee1 emp) {
        return emp.getMonthlySalary()+emp.getBonus();
    }
}

然后重新修改Employee1类,由于Employee1Type中的方法payAmount代替了Employee1的payAmount。所以可以注释掉Employee1中的方法。

测试一下:

class Test{
    public static void main(String[] args) {
        Employee1Type employee1 = new SalesMan1();
        Employee1 emp = new Employee1();
        System.out.println(employee1.payAmount(emp));
    }
}

3 若只是在单一函数中有选择的事例,多态就有点杀鸡用牛刀了。可以用【以明确函数取代参数】的方式解决。

平行继承体系

是散弹式修改的特殊情况,如果你为某个类增加一个子类,必须也在另一个类相应增加一个子类。如果这两个继承体系的名称前缀完全相同,就是平行继承体系了。

消除方法可以参考【散弹式修改】

合并臃肿没用的继承类

如果一个类对不起他的身价,就让他消失。

使用【折叠继承结构】将它们合并为一个类:

夸夸其谈未来性--删去多余的抽象类或抽象方法

如果一个类对于现在的用处太过于抽象,太放眼将来,令抽象类难以理解,就应该把它丢掉。使用【折叠继承结构】、或移除对现在没有意义的方法、或更改更易于理解的类名。

令人迷惑的暂时字段

某个实例变量仅为某种特定情况而设。这样的代码让人不解,如果不使用,通常我们会认为设计这个变量是用来干嘛的。

1 可以给这些变量搬家,搬到一个相关的类中。

2 可以引入“空对象”,避免写出条件式的代码。

public class Customer {
    private String name = "1111";
    private String history = "111";
    private int salary = 111;
   //省略set、get
}

class SiteDemo{
    private Customer customer;

    public void demo(){
        Customer customer = this.getCustomer();

        System.out.println("name = " + name);
      
      	String name = "0";
        if (customer == null)
            name = customer.getSalary();
        else
            name = "0";
      
        //省略一些代码
        int salary = 1;
        if (customer == null)
            salary = new Employee1().getBonus();
        else
            salary = customer.getSalary();
        System.out.println("salary = " + salary);

        //省略一些代码
        String history = "";
        if (customer != null)
           history = customer.getHistory();

        System.out.println("history = " + history);
    }
}

上面程序中有三处判断customer,可以看到上面代码让人感觉很乱,第一眼看去不知道是啥。现在要把这些根据customer是否等于null的条件去赋值的代码去掉。

首先创建一个NullCustomer继承Customer,并在Customer中添加isNull()方法

Customer中添加

public class Customer {
  //.....
    public boolean isNull() {
        return false;
    }
  static Customer newNull(){
      return new NullCustomer();
  }
}
class NullCustomer extends Customer{
    @Override
    public boolean isNull() {
        return true;
    }

    @Override
    public String getName() {
        return "0";
    }

    public int getSalary() {
        return new Employee1().getBonus();
    }

    @Override
    public String getHistory() {
        return null;
    }
}

在所有创建Customer的地方加上:

public Customer getCustomer() {
    return (customer == null)? Customer.newNull():customer;
}

根据多态中子类会有不同的行为,这时候demo方法就变得很简单。

 public void demo(){
        Customer customer = this.customer.getCustomer();
        String name = customer.getName();
   
        //省略一些代码
        int salary = customer.getSalary();
        System.out.println("salary = " + salary);

        //省略一些代码
        String history = customer.getHistory();

        System.out.println("history = " + history);
    }

测试一下

 public static void main(String[] args) {
        Customer customer = null;
        SiteDemo demo = new SiteDemo();
        demo.setCustomer(customer);    //该方法在Customer 中添加普通set一个即可
        demo.demo();
}

----------------待补充----------------

posted @ 2020-01-14 10:32  星记事  阅读(349)  评论(0编辑  收藏  举报