其实很多重构手法都是成对出现的,需要根据使用场景进行正向或逆向的重构,以达到提高代码可阅读性、增强代码复用性和可扩展性的目的
你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用 在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除 重构前的代码:
class Account{ private AccountType _type; private int _daysOverdrawn; ... double overdraftCharge(){ if(_type.isPremium){ double result = 10; if(_daysOverdrawn > 7){ result += (_daysOverdrawn - 7) * 0.85; } return result; }else{ return _daysOverdrawn * 1.75; } } double bankCharge(){ double result = 4.5; if(_daysOverdrawn > 0){ result += overdraftCharge(); return result; } } }计费函数overdraftCharge()应该移动到账户类型里,这样多种账户类型就可以对应多种计费规则,重构后的代码:
class Account{ private AccountType _type; private int _daysOverdrawn; ... double bankCharge(){ double result = 4.5; if(_daysOverdrawn > 0){ result += _type.overdraftCharge(_daysOverdrawn); return result; } } } class AccountType{ ... double overdraftCharge(int daysOverdrawn){ if(isPremium()){ double result = 10; if(daysOverdrawn > 7){ result += (daysOverdrawn - 7) * 0.85; } return result; }else{ return daysOverdrawn * 1.75; } } }你的程序中,某个字段被其所驻类之外的另一个类更多地用到。 在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段 重构前的代码:
class Account{ private AccountType _type; private double _interestRate; double interestForAmount_days(double amount, int days){ return _interestRate * amount * days / 365; } }利率_interestRate应该跟账户AccountType类相关性更强,不同种类的账户有不同利率。 重构后的代码:
class Account{ private AccountType _type; double interestForAmount_days(double amount, int days){ return _type.getInterestRate() * amount * days / 365; } } class AccountType{ private double _interestRate; void setInterestRate(double interestRate){ _interestRate = interestRate; } double getInterestRate(){ return _interestRate; } }某个类做了应该由两个类做的事。 建立一个新类,将相关的字段和函数从旧类搬移到新类。 重构前的代码:
class Person{ private String _name; private String _officeAreaCode; private String _officeNumber; public String getName(){ return _name; } public String getTelephoneNumber(){ return "(" + _officeAreaCode + ")" + _officeNumber; } String getOfficeAreaCode(){ return _officeAreaCode; } void setOfficeAreaCode(String officeAreaCode){ _officeAreaCode = officeAreaCode; } String getOfficeNumber(){ return _officeNumber; } void setOfficeNumber(String officeNumber){ _officeNumber = officeNumber; } }可以把与电话号码相关的行为提炼到一个独立的电话号码类里。 重构后的代码:
class Person{ private String _name; private TelephoneNumber _officeTelephone = new TelephoneNumber(); public String getName(){ return _name; } public String getTelephoneNumber(){ return _officeTelephone.getTelephoneNumber(); } TelephoneNumber getOfficeTelephone(){ return _officeTelephone; } } class TelephoneNumber{ private String _number; private String _areaCode; public String getTelephoneNumber(){ return "(" + _areaCode + ")" + _number; } String getAreaCode(){ return _officeAreaCode; } void setAreaCode(String areaCode){ _areaCode = areaCode; } String getNumber(){ return _number; } void setNumber(String number){ _number = number; } }如果一个类不再承担足够责任、不再有单独存在的理由,就应该把这个类的所有特性搬移到另一个类中,然后移除原类 此例代码和Extract Class(提炼类)的例子完全相反。
重构前的代码:
class Person{ Department _department; public Department getDepartment(){ return _department; } public void setDepartment(Department department){ _department = department; } } class Department{ private String _chargeCode; private Person _manager;//经理 public Department(Person manager){ _manager = manager; } public Person getManager(){ return _manager; } }如果想知道某人的经理是谁,则需要得到Department对象后再获取经理:
Person manager = xiaoming.getDepartment().getManager();针对这种委托关系,应该隐藏掉中间细节
重构后的代码:
class Person{ Department _department; public void setDepartment(Department department){ _department = department; } public Person getManager(){ return _departmant.getManager(); } } class Department{ private String _chargeCode; private Person _manager;//经理 public Department(Person manager){ _manager = manager; } public Person getManager(){ return _manager; } } Person manager = xiaoming.getManager();某个类做了过多的简单委托动作,做法就是去掉委托,直接调用. 和Hide Delegate(隐藏“委托关系”)的例子完全相反。
为一个无法修改的类增加一个函数 重构前的代码:
previousEnd是Date对象,实现下一天的Date对象:
Date newStart = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate()+1);重构后的代码:
Date newStart = nextDay(previousEnd); private static Date nextDay(Date date){ return new Date(date.getYear(), date.getMonth(), date.getDate()+1); }同样是为一个无法修改的类增加一个函数 比如为Date类增加一个下一天的函数:
class MfDateSub extends Date{ public MfDateSub (String dateString){ super(dateString); } public MfDateSub(Date date){ super(date.getTime()); } Date nextDay(){ return new Date(getYear(), getMonth(), getDate()+1); } }就是把一些变量用get/set方法封装起来,可以对数据更好的管理
你有一个数据项,需要与其他数据和行为一起使用才有意义,这时就需要把数据项变成对象 以字段代表一个客户,重构前的代码:
class Order{ String _customer public Order(String customer){ _customer = customer; } public String getCustomer(){ return _customer; } public void setCustomer(String customer){ _customer = customer } }把字段改为客户对象,对代码逻辑及以后的功能扩展都大有帮助,重构后的代码:
class Order{ private Customer _customer; public Order(String customerName){ _customer = new Customer(customer); } public String getCustomerName(){ return _customer.getName(); } public void setCustomerName(String customerName){ _customer = new Customer(customerName) } } class Customer{ private final String _name; public Customer(String name){ _name = name; } public String getName(){ return _name; } }为改变上面例子里Order对象与Customer对象的一对一关系,采用Hashtable存储多个Customer对象,这样Customer就可以对应多个Order对象
class Customer{ private static Dictionary _instances = new Hashtable(); private final String _name; private Customer(String name){ _name = name; } public String getName(){ return _name; } public void store(){ _instance.put(this.getName, this); } public static Customer getNamed(String name){ return (Customer) _instances.get(name); } }你有一个引用对象,很小且不可变,而且不易管理,这时可以执行与Change Value to Reference(将值对象改为引用对象)相反的操作,将引用对象改为值对象
你有一个数组,其中的元素各自代表不同的东西,这时就要用对象来代替数组。 重构前的代码:
String[] person = new String[3]; person[0] = "xiaoming"; person[1] = "15"; person[2] = "man";重构后的代码:
Person person = new Person(); person.setName("xiaoming"); person.setOld("15"); person.setSex("man");把UI类里的数据逻辑部分分离到一个独立的类里 重构前的代码:
public class IntervalWindow extends Frame { java.awt.TextField _startField; java.awt.TextField _endField; java.awt.TextField _lengthField; private SymFocus symFocus; public IntervalWindow(String title, int width, int height) { super(title); super.setSize(width, height); super.setLayout(new BorderLayout()); _startField = new TextField(); _endField = new TextField(); _lengthField = new TextField(); symFocus = new SymFocus(); _startField.setBounds(100, 20, 400, 30); _startField.setText("0"); super.add(_startField, BorderLayout.NORTH); _endField.setBounds(100, 60, 400, 30); _endField.setText("0"); super.add(_endField, BorderLayout.CENTER); _lengthField.setBounds(100, 100, 400, 30); _lengthField.setText("0"); super.add(_lengthField, BorderLayout.SOUTH); _startField.addFocusListener(symFocus); _endField.addFocusListener(symFocus); _lengthField.addFocusListener(symFocus); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { IntervalWindow intervalWindow = new IntervalWindow("IntervalWindow", 500, 400); intervalWindow.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } class SymFocus extends java.awt.event.FocusAdapter { public void focusLost(java.awt.event.FocusEvent event) { Object object = event.getSource(); if (object == _startField) { StartField_FocusLost(event); } else if (object == _endField) { EndField_FocusLost(event); } else if (object == _lengthField) { LengthField_FocusLost(event); } } private void LengthField_FocusLost(FocusEvent event) { if (isInteger(_lengthField.getText())) { calculateEnd(); } else { _lengthField.setText("0"); } } private void EndField_FocusLost(FocusEvent event) { if (isInteger(_endField.getText())) { calculateLength(); } else { _endField.setText("0"); } } private void StartField_FocusLost(FocusEvent event) { if (isInteger(_startField.getText())) { calculateLength(); } else { _startField.setText("0"); } } private void calculateEnd() { try { int start = Integer.parseInt(_startField.getText()); int length = Integer.parseInt(_lengthField.getText()); int end = start + length; _endField.setText(String.valueOf(end)); } catch (NumberFormatException e) { throw new RuntimeException("Unexpected Number Format Error"); } } private void calculateLength() { try { int start = Integer.parseInt(_startField.getText()); int end = Integer.parseInt(_endField.getText()); int length = end - start; _lengthField.setText(String.valueOf(length)); } catch (NumberFormatException e) { throw new RuntimeException("Unexpected Number Format Error"); } } private boolean isInteger(String text) { for (int i = 0; i < text.length(); i++) { if (!Character.isDigit(text.charAt(i))) { return false; } } return true; } } }重构后的代码:
public class IntervalWindow extends Frame implements Observer { java.awt.TextField _startField; java.awt.TextField _endField; java.awt.TextField _lengthField; private SymFocus symFocus; private Interval _interval; public IntervalWindow(String title, int width, int height) { super(title); super.setSize(width, height); super.setLayout(new BorderLayout()); _startField = new TextField(); _endField = new TextField(); _lengthField = new TextField(); symFocus = new SymFocus(); _startField.setBounds(100, 20, 400, 30); _startField.setText("0"); super.add(_startField, BorderLayout.NORTH); _endField.setBounds(100, 60, 400, 30); _endField.setText("0"); super.add(_endField, BorderLayout.CENTER); _lengthField.setBounds(100, 100, 400, 30); _lengthField.setText("0"); super.add(_lengthField, BorderLayout.SOUTH); _startField.addFocusListener(symFocus); _endField.addFocusListener(symFocus); _lengthField.addFocusListener(symFocus); _interval = new Interval(); _interval.addObserver(this); update(_interval, null); } String getEnd() { return _interval.getEnd(); } void setEnd(String arg) { _interval.setEnd(arg); } String getStart() { return _interval.getStart(); } void setStart(String arg) { _interval.setStart(arg); } String getLength() { return _interval.getLength(); } void setLength(String arg) { _interval.setLength(arg); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { IntervalWindow intervalWindow = new IntervalWindow("IntervalWindow", 500, 400); intervalWindow.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } @Override public void update(Observable o, Object arg) { _startField.setText(_interval.getStart()); _endField.setText(_interval.getEnd()); _lengthField.setText(_interval.getLength()); } class SymFocus extends java.awt.event.FocusAdapter { public void focusLost(java.awt.event.FocusEvent event) { Object object = event.getSource(); if (object == _startField) { StartField_FocusLost(event); } else if (object == _endField) { EndField_FocusLost(event); } else if (object == _lengthField) { LengthField_FocusLost(event); } } private void LengthField_FocusLost(FocusEvent event) { setLength(_lengthField.getText()); if (_interval.isInteger(getEnd())) { _interval.calculateEnd(); } else { setLength("0"); } } private void EndField_FocusLost(FocusEvent event) { setEnd(_endField.getText()); if (_interval.isInteger(getEnd())) { _interval.calculateLength(); } else { setEnd("0"); } } private void StartField_FocusLost(FocusEvent event) { setStart(_startField.getText()); if (_interval.isInteger(getStart())) { _interval.calculateLength(); } else { setStart("0"); } } } }独立出一个Interval类用来处理数据逻辑部分
public class Interval extends Observable { private String _end = "0"; private String _start = "0"; private String _length = "0"; String getEnd() { return _end; } void setEnd(String arg) { _end = arg; setChanged(); notifyObservers(); } String getStart() { return _start; } void setStart(String arg) { _start = arg; setChanged(); notifyObservers(); } String getLength() { return _length; } void setLength(String arg) { _length = arg; setChanged(); notifyObservers(); } void calculateEnd() { try { int start = Integer.parseInt(getStart()); int length = Integer.parseInt(getLength()); int end = start + length; setEnd(String.valueOf(end)); } catch (NumberFormatException e) { throw new RuntimeException("Unexpected Number Format Error"); } } void calculateLength() { try { int start = Integer.parseInt(getStart()); int end = Integer.parseInt(getEnd()); int length = end - start; setLength(String.valueOf(length)); } catch (NumberFormatException e) { throw new RuntimeException("Unexpected Number Format Error"); } } boolean isInteger(String text) { for (int i = 0; i < text.length(); i++) { if (!Character.isDigit(text.charAt(i))) { return false; } } return true; } }两个类都需要使用对方特性,但其间只有一条单向连接 例如之前订单和客户的关系
重构前的代码:
class Order{ Customer _customer; Customer getCustomer(){ return _customer; } void setCustomer(Customer arg){ _customer = arg; } }重构后的代码:
class Order{ private Set _customer = new HashSet(); void addCustomer(Customer arg){ arg.friendOrders().add(this); _customer.add(arg); } void removeCustomer(Customer arg){ arg.friendOrders().remove(this); _customer.remove(arg); } } class Customer{ private Set _orders = new HashSet(); Set friendOrders(){ return _orders; } void addOrder(Order arg){ arg.addCustomer(this); } void removeOrder(Order arg){ arg.removeCustomer(this); } }两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性,和Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)相反的操作
在代码里应避免直接使用数字,应该创建一个值为该数字的常量,并根据其意义命名
重构前的代码:
double potentialEnergy(double mass, double height){ return mass * 9.81 * height; }重构后的代码:
static final double GRAVITATIONAL_CONSTANT = 9.81; double potentialEnergy(double mass, double height){ return mass * GRAVITATIONAL_CONSTANT * height; }把public类型的字段封装为private,并提供相应的get/set方法
重构前的代码:
public String name;重构后的代码:
private String name; public String getName(){ return name; } public void setName(String arg){ name = arg; }有个函数返回一个集合,改为让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数
重构前的代码:
class Order{ private Set _customer = new HashSet(); public setCustomers(Set arg){ _customer.removeAll(); _customer.addAll(arg); } public getCustomers(){ return _customer; } }重构后的代码:
class Order{ private Set _customer = new HashSet(); public setCustomers(Set arg){ _customer.removeAll(); _customer.addAll(arg); } public getCustomers(){ Set result = new HashSet(); result.addAll(_customer) return retult; } public addCustomer(Customer obj){ _customer.add(obj); } public removeCustomer(Customer obj){ _customer.remove(obj); } }对一些数据变量用数据类进行封装
重构前的代码:
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 Person(int bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(int arg){ _bloodGroup = arg; } public int getBloodGroup(){ return _bloodGroup; } }重构后的代码:
public class Person { private BloodGroup _bloodGroup; public Person(BloodGroup bloodGroup){ _bloodGroup = bloodGroup; } public void setBloodGroup(BloodGroup arg){ _bloodGroup = arg; } public BloodGroup getBloodGroup(){ return _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; } }一个类以几种不可变类型码表示该类的类型时,应该以子类取代这些类型码 重构前的代码:
class Employee{ private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee(int type){ _type = type; } }重构后的代码:
abstract class Employee{ static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; abstract int getType(); static Employee create(int type){ switch(type){ case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect type code value"); } } } class Engineer extends Employee{ int getType(){ return Employee.ENGINEER; } } class Salesman extends Employee{ int getType(){ return Employee.SALESMAN; } } class Manager extends Employee{ int getType(){ return Employee.MANAGER; } }一个类以几种可变类型码表示该类的类型时,以状态对象取代类型码 重构前的代码:
class Employee{ private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; static final int MONTHLY_SALARY = 5000; static final int COMMISSION = 3000; static final int BONUS = 4000; Employee(int type){ _type = type; } int payAmount(){ switch(_type){ case ENGINEER: return MONTHLY_SALARY; case SALESMAN: return MONTHLY_SALARY + COMMISSION; case MANAGER: return MONTHLY_SALARY + BONUS; default: throw new RuntimeException("Incorrect Emplayee"); } } }重构后的代码:
//声明一个状态类 abstract class EmployeeType{ static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; abstract int getTypeCode(); static EmployeeType newType(int type){ switch(type){ case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect type code value"); } } } class Engineer extends EmployeeType{ int getTypeCode(){ return EmployeeType.ENGINEER; } } class Salesman extends EmployeeType{ int getTypeCode(){ return EmployeeType.SALESMAN; } } class Manager extends EmployeeType{ int getTypeCode(){ return EmployeeType.MANAGER; } } class Employee{ private EmployeeType _type; static final int MONTHLY_SALARY = 5000; static final int COMMISSION = 3000; static final int BONUS = 4000; int getType(){ return _type.getTypeCode(); } void setType(int code){ _type = EmployeeType.newType(code); } int payAmount(){ switch(_type){ case EmployeeType.ENGINEER: return MONTHLY_SALARY; case EmployeeType.SALESMAN: return MONTHLY_SALARY + COMMISSION; case EmployeeType.MANAGER: return MONTHLY_SALARY + BONUS; default: throw new RuntimeException("Incorrect Emplayee"); } } }子类的唯一差别只在“返回常量数据”的函数身上时,应该让它们返回超类中的某个(新增)字段,然后销毁子类 重构前的代码:
abstract class Person{ abstract boolean isMale(); abstract char getCode(); } class Male extends Person{ boolean isMale(){ return true; } char getCode(){ return "M"; } } class Female extends Person{ boolean isMale(){ return false; } char getCode(){ return "F"; } }重构后的代码:
class Person{ private final boolean _isMale; private final char _code; boolean isMale(){ return _isMale; } char getCode(){ return _code; } private Person(boolean isMale, char code){ _isMale = isMale; _code = code; } static Person createMale(){ return new Person(true, 'M'); } static Person createFemale(){ return new Person(false, 'F'); } }