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();
}
----------------待补充----------------