重新组织数据
自封装字段
你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。
简而言之就是为类的某个成员变量(字段)添加 get 方法,而避免直接使用其字段名来取值。
// 书中的示例
private int low, hight;
boolean includes (int arg) {
return arg >= low && arg <= high;
}
// 重构后
private int low, hight;
boolean includes(int arg) {
return arg >= getLow() && arg <= getHigh();
}
int getLow(){
return low;
}
int getHight(){
return hight;
}
今天编程中,使用 get 和 set 方法的操作已经成为了一种习惯,并且 IDE 工具也可以自动生成。get 方法比直接读取成员变量(字段)的可控性更高一些,可以进行某过滤操作等。
以对象取代数据值
在开发的初其阶段,我们很多字段都是固定的,这时我们可能不会为一个字段去封装一个对象,而是直接使用一个变量来表示,但是后面如果字段增加的话,就要再继续增加变量。
书中提倡的是将变量也封装成对象。
// 书中的示例
class Order {
private String customer;
public Order(String customer) {
this.customer = customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
public String getCustomer(String customer) {
return customer;
}
}
// 重构后
class Order {
private Customer customer;
public Order(String name) {
this.customer = new Customer(name);
}
public String getCustomer() {
return customer.getName();
}
public void setCustomer(String name) {
this.customer = new Customer(name);
}
}
class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
上面的例子,就是将原本只是一个成员变量表示的 customer 封装成了一个对象 Customer,然后再提供方法从 Customer 对象中获取值。
将值对象改为引用对象
// 值对象和引用对象,书中的理解与我们现在的理解不太相同。这也属于正常,毕竟这是一本 9x 年的书。
将引用对象改为值对象
// 与上面的操作相反
以对象取代数组
// 书中示例
String[] row = new String[];
row[0] = "Liverpool";
row[1] = "15";
// 重构后
Performance row = new Performance();
row.setName("Liverpool");
row.setAge("15");
这个很简单,用一个数组不同的下标来表示多个数据的情况下,使用一个对象来表示更合适。其实现在写代码已经没有人会用数组来存多个数据了。
复制“被监视的数据”
本质上是观察者设计模式,书中的代码示例使用了 JDK 提供的 Observer 接口和 Observable 抽象类。
将单向关联改为双向关联
两个类都需要使用对方特性,但其间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接。
将双向关联改为单向关联
以字面常量取代魔法数
创建一个常量,根据其意义为字命名,将将上述的字面数值替换为这个常量。
封装字段
本质就是将一个公开的 public 的成员变量,修改为调用其 get 和 set 方法。
public String name;
// 重构后
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
封装集合
让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。
如果在一个封装类中,包含有 集合 类型,对其提供了 get 和 set 方法,那么将 get 方法返回一个只读的副本,要向集合中添加元素时,再单独提供 add 方法进行添加 ,而不能是通过 get 方法获取到集合对象,然后调用集合对象的 API 。
class xxx {
....
// 示例代码
public Set<Order> getOrder(){
retrun this.orders;
}
// 重构后
public Set<Order> getOrder(){
return Collections.unmodifiableSet(courses);
}
}
让代码各有所属,重构还有很多的手法,需要结合起来进行综合性的使用。
以数据类取代记录
这已经是我们现在的常用手段了,在编写代码的过程中,我们经常使用到的是 XxxEntity 或者称为的 POJO 对象。
以类取代类型码
现在我们的常用手段是使用枚举类来替换类型码。
以子类取代类型码
其本质是对一种特殊进行处理,如果是特殊情况下,单独抽取出来形成一个子类。
以 State/Strategy 取代类型码
这是两种设计模式,状态/策略。状态模块和策略模式其实也挺像的,在正常的过程中我们可以
以字段取代子类
// 书中的示例
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 boolean isMale;
private char code;
static Person createMale() {
return new Person(true, 'M');
}
static Person createFemale() {
return new Person(false, 'F');
}
protected Person (boolean isMale, char code) {
this.isMale = isMale;
this.code = code;
}
boolean isMale() {
return thid.code;
}
char getCode() {
return this.code;
}
}
参考资料:
[1]: book.douban.com/subject/426… "重构-改善既有代码的设计"