重构之代码的坏味道(一)
2023-09-16 01:12:26
在软件开发过程中,我们常常会遇到各种各样的代码坏味道。这些坏味道会使代码难以理解、难以维护,并且容易出错。本文介绍了10种常见的代码坏味道,以及如何通过重构来消除这些坏味道。
1. 神秘命名
神秘命名是指使用一些晦涩难懂的变量名、函数名或类名。这样的命名方式会使代码难以理解,也容易造成错误。例如,以下代码中的变量名就非常神秘:
int x = 10;
int y = 20;
int z = x + y;
这段代码中,变量名x、y和z都没有任何意义,读者很难理解这段代码的含义。为了消除这个坏味道,我们可以将这些变量名改成更有意义的名称,例如:
int width = 10;
int height = 20;
int area = width + height;
这样,读者就可以很容易地理解这段代码的含义了。
2. 重复代码
重复代码是指在代码中出现多处相同的代码段。这样的代码会使代码难以理解、难以维护,并且容易出错。例如,以下代码中就存在重复代码:
if (x > 0) {
// do something
} else {
// do something else
}
if (y > 0) {
// do something
} else {
// do something else
}
这段代码中,if语句的结构完全相同,只是变量x和y不同。为了消除这个坏味道,我们可以使用循环来代替重复的if语句:
for (int i = 0; i < 2; i++) {
int x = i == 0 ? x : y;
if (x > 0) {
// do something
} else {
// do something else
}
}
这样,代码就更加简洁和易于理解了。
3. 过长函数
过长函数是指代码行数过多的函数。这样的函数难以理解、难以维护,并且容易出错。一般来说,函数的代码行数最好不要超过100行。如果一个函数的代码行数超过了100行,那么就应该考虑将其分解成多个更小的函数。
4. 全局数据
全局数据是指在程序中所有函数都可以访问的数据。这样的数据会使代码难以理解、难以维护,并且容易出错。例如,以下代码中的全局变量x就可以被程序中的任何函数访问:
int x = 10;
void f1() {
x++;
}
void f2() {
x--;
}
这段代码中,函数f1()和函数f2()都可以修改全局变量x的值。这样,如果一个函数不小心修改了全局变量x的值,那么就会影响到其他函数的运行。为了消除这个坏味道,我们可以使用局部变量来代替全局变量。例如,以下代码中的局部变量x只能被函数f1()访问:
void f1() {
int x = 10;
x++;
}
void f2() {
int x = 20;
x--;
}
这样,函数f1()和函数f2()就不会互相影响了。
5. 发散式变化
发散式变化是指当修改代码时,需要在多个地方进行修改。这样的代码难以理解、难以维护,并且容易出错。例如,以下代码中的类Customer需要在多个地方进行修改,才能支持新的地址类型:
public class Customer {
private String name;
private String address;
public Customer(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
为了消除这个坏味道,我们可以使用继承或组合来将代码组织成更小的模块。例如,以下代码中的类CustomerAddress将地址信息封装成一个单独的模块,这样当需要支持新的地址类型时,只需要修改这个模块就可以了:
public class Customer {
private String name;
private CustomerAddress address;
public Customer(String name, CustomerAddress address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public CustomerAddress getAddress() {
return address;
}
public void setAddress(CustomerAddress address) {
this.address = address;
}
}
public class CustomerAddress {
private String street;
private String city;
private String state;
private String zip;
public CustomerAddress(String street, String city, String state, String zip) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public String getZip() {
return zip;
}
public void setStreet(String street) {
this.street = street;
}
public void setCity(String city) {
this.city = city;
}
public void setState(String state) {
this.state = state;
}
public void setZip(String zip) {
this.zip = zip;
}
}
这样,代码就更加简洁和易于理解了。
6. 霰弹式修改
霰弹式修改是指当修改代码时,需要在多个地方进行小的修改。这样的代码难以理解、难以维护,并且容易出错。例如,以下代码中的类Customer需要在多个地方进行小的修改,才能支持新的客户类型:
public class Customer {
private String name;
private String address;
private String phone;
private String email;
public Customer(String name, String address, String phone, String email) {
this.name = name;
this.address = address;
this.phone = phone;
this.email = email;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public String getPhone() {
return phone;
}
public String getEmail() {
return email;
}
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEmail(String email) {
this.email = email;
}
}
为了消除这个坏味道,我们可以使用组合或继承来将代码组织成更小的模块。例如,以下代码中的类CustomerPersonalInfo将客户的个人信息封装成一个单独的模块,这样当需要支持新的客户类型时,只需要修改这个模块就可以了:
public class Customer {
private String name;
private CustomerPersonalInfo personalInfo;
public Customer(String name, CustomerPersonalInfo personalInfo) {
this.name = name;
this.personalInfo = personalInfo;
}
public String getName() {
return name;
}
public CustomerPersonalInfo getPersonalInfo() {
return personalInfo;
}
public void setPersonalInfo(CustomerPersonalInfo personalInfo) {
this.personalInfo = personalInfo;
}
}
public class CustomerPersonalInfo {
private String address;
private String phone;
private String email;
public CustomerPersonalInfo(String address, String phone, String email) {
this.address = address;
this.phone = phone;
this.email = email;
}
public String getAddress() {
return address;
}
public String getPhone() {
return phone;
}
public String getEmail() {
return email;
}
public void setAddress(String address) {
this.address = address;
}