简单易懂读《重构》 - Primitive Obesession (基本类型偏执)

  • 含义:
    以类代替原本单独存在的数值
  • 坏处:
    单独存在的数值不易于理解,也不符合面向对象的思想。
  • 目标:
    使数值尽量用类代替,就像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

本文涉及的重构方法

Introduce Parameter Object(引入参数对象)
Extract Class(提取类)
Replace Data Value with Object (以对象取代数据值)
  • 作用:
    消除那些与其他数据和方法在一起使用才有意义的数据项
  • 步骤:
  1. 为这个数值新建一个类,类型跟这个数值的类型一样,添上构造函数和get方法。
  2. 将原来类中的那个数值替换为前面新建的类,然后修改这个类里对原数值的调用为那个get函数。如果这个数值在原来的代码中还要支持修改,则在这个新类中添加set函数。修改原来类中的设值部分为新类的set函数。
  3. 编译,测试
  4. 这个新类如果产生了过多实例,想合成为一个,考虑使用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(将值对象改为引用)
  • 作用:
    从类中创建了过多的实例,而这些实例实际上可以用同一个对象代替
  • 步骤:
  1. 使用Replace Constructor with Factory Method (使用工厂函数替代构造器)
  2. 编译,测试
  3. 考虑这个类由什么对象提供访问新对象的途径(比如:静态字典或者注册表对象,也可以用多个对象作为访问点)
  4. 考虑这个对象中引用的对象哪些是需要提前创建好的,哪些需要动态创建的。
  5. 修改工厂函数,使它返回引用对象
  6. 编译,测试
  • 代码实例
//以上文中列举的例子为基础进行改进
//首先把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 (以子类替代类型码)
  • 作用:
    消除代码中的不可变的那些类型码
  • 步骤:
  1. 把这个类型码变量使用 Self Encapsulate Field(自封装字段) 封装起来, 如果该类型码传给了构造函数,需要将构造函数换成工厂函数。
  2. 根据类型码对应的变量建立相应的子类,对子类里获取类型码的变量的函数进行覆写。
  3. 把父类中保存类型码字段删除,将类型码的获取函数声明为抽象函数。
  4. 编译,测试
  • 代码实例
//根据水果类型码构建不同的水果,创建水果对象时根据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");
  • 重构思路:
    以对象取代数组,把数组中的元素改为对象中的属性。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值