含义: 以类代替原本单独存在的数值坏处: 单独存在的数值不易于理解,也不符合面向对象的思想。目标: 使数值尽量用类代替,就像java中的基本类型那样。实现方法:
单独存在的数据值,考虑使用Replace Data Value with Object (以对象取代数据值) 如果想要替换的数据值是类型码,而这些类型码并不影响行为,则可以运用Replace Type Code with Class (以类取代类型码) 如果有与类型码相关的条件表达式,可运用Replace Type Code with Subclass (以子类替代类型码) 或 Replace Type Code with State/Strategy(以状态/策略取代类型码) 。 如果有一组总是放在一起的字段,可运用Extract Class(提取类) 如果你在参数列中看到基本型数据,试试Introduce Parameter Object
本文涉及的重构方法
Replace Data Value with Object (以对象取代数据值)
作用: 消除那些与其他数据和方法在一起使用才有意义的数据项步骤:
为这个数值新建一个类,类型跟这个数值的类型一样,添上构造函数和get方法。 将原来类中的那个数值替换为前面新建的类,然后修改这个类里对原数值的调用为那个get函数。如果这个数值在原来的代码中还要支持修改,则在这个新类中添加set函数。修改原来类中的设值部分为新类的set函数。 编译,测试 这个新类如果产生了过多实例,想合成为一个,考虑使用Change Value to Reference(将值对象改为引用)
//假设有个代表用户的类User, 里面有个数值叫address,我希望改用一个对象来表示地址,以后就更好扩展
//原代码如下:
class User {
private String address;
public User(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
//现在我们把address这个数值搬出来作为一个类
//修改后代码如下
class User {
private Address address;
public User(String address) {
this.address = new Address(address);
}
public String getAddress() {
return address.getDetail();
}
public void setAddress(String address) {
this.address = new Address(address);
}
}
class Address {
private String detail;
public Address(String detail) {
this.detail = detail;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
重构思路: 把函数中那些数值尽量用类去替代之。类中写好构造函数,set/get方法,然后把原代码中对这个数值的操作部分改为这个类。重构完后,继续使用Change Value to Reference(将值对象改为引用) 继续优化代码。
Change Value to Reference(将值对象改为引用)
作用: 从类中创建了过多的实例,而这些实例实际上可以用同一个对象代替步骤:
使用Replace Constructor with Factory Method (使用工厂函数替代构造器) 编译,测试 考虑这个类由什么对象提供访问新对象的途径(比如:静态字典或者注册表对象,也可以用多个对象作为访问点) 考虑这个对象中引用的对象哪些是需要提前创建好的,哪些需要动态创建的。 修改工厂函数,使它返回引用对象 编译,测试
//以上文中列举的例子为基础进行改进
//首先把Address类的构造方法改为工厂
class Address {
private String detail;
//声明构造函数为私有
private Address(String detail) {
this.detail = detail;
}
//建立工厂函数
public static Address create (String detail) {
return new Address(detail);
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
//改变原来User类引用的地方改为调工厂函数
class User {
private Address address;
public User(String address) {
this.address = Address.create(address);
}
public String getAddress() {
return address.getDetail();
}
public void setAddress(String address) {
this.address = new Address(address);
}
}
//如果引用的地方的对象需要预先创建好,方便调用,可以修改程序为以下
class Address {
//建立静态变量储存需要预先实例化的对象
private static HashMap<Address> instances = new HashMap<Address>();
private String detail;
//声明构造函数为私有
private Address(String detail) {
this.detail = detail;
}
static void preloadAddresses () {
new Address("北京路").store();
new Address("南京路").store();
new Address("东京路").store();
new Address("西京路").store();
}
private void store() {
instances.put(this.getDetail(), this);
}
//建立工厂函数
//优化代码,返回已经创建好的实例
//由于实际上此方法已经不是create的意思,修改方法名为getNamed
public static Address getNamed (String detail) {
return instances.get(detail);
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}
重构思路: 可以结合上一个案例来看,前一个案例中,每次实例化都会new 一个 address,在setAddress时也会new,造成大量实例,这些实例,实际上可以用同一个代替,采用工厂方法,并预先实例化大量需要用的实例就能解决此问题。
Replace Type Code with Class (以类取代类型码)
重构思路: 此方法现在已经过时,思想是以类代替代码中的各类类型码,实际上现在可以使用枚举类实现此重构手法。故不作赘述。
Replace Type Code with Subclass (以子类替代类型码)
把这个类型码变量使用 Self Encapsulate Field(自封装字段) 封装起来, 如果该类型码传给了构造函数,需要将构造函数换成工厂函数。 根据类型码对应的变量建立相应的子类,对子类里获取类型码的变量的函数进行覆写。 把父类中保存类型码字段删除,将类型码的获取函数声明为抽象函数。 编译,测试
//根据水果类型码构建不同的水果,创建水果对象时根据type确定水果类型
class Fruit {
private int type;
static final int APPLE = 0;
static final int PEAR = 1;
static final int BANANA = 2;
Fruit(int type) {
this.type = type;
}
}
//重构后
class Fruit {
static final int APPLE = 0;
static final int PEAR = 1;
static final int BANANA = 2;
abstract int getType();
static Fruit create(int type) {
switch (type) {
case APPLE : return new Apple();
case PEAR : return new Pear();
case BANANA : return new Banana();
default: throw new IllegalArgumentException("参数不正确!");
}
}
}
class Apple extends Fruit {
int getType() {
return Fruit.APPLE;
}
}
//其他水果类略...
重构思路: 我个人觉得意义不大,本重构法同样可以用枚举代替,枚举类内写上通过类型码获取对应实例就好。本身这样的写法也只是徒增了类的数量,实际代码的整洁度感觉不太明显,有点牵强地强行套用面向对象的思想。
Replace Type Code with State/Strategy(以状态/策略取代类型码)
重构思路: 参考状态模式和策略模式,以增加类数量的代价实现代码的简洁。
Replace Array with Object (以对象取代数组)
//重构前
String[] row = new String[3];
row[0] = "语文";
row[1] = "K-14";
//重构后
Classroom cr = new Classroom();
cr.setName("语文");
cr.setNum("K-14");
重构思路: 以对象取代数组,把数组中的元素改为对象中的属性。