重构类

1. 搬移函数

每一个方法应该放在它最适合的位置,不能随便乱放,所以很多时候你需要考虑,一个方法在这里是不是最适合的。

class Class1 {
    aMethod();
}

class Class2 {
}
更改为↓
class Class1 {
}

class Class2 {
    aMethod();
}
做法:
检查源类中被原函数所使用的一切特性(字段,函数),考虑是否该被迁移。某个特性只被打算迁移的那个函数使用,就该将它一并迁移,如果还有其他函数使用了它,可以将使用该特性的所有函数一并迁移。
检查源类的子类和超类,看看是否有该函数的其他声明。如果出现其他声明,或许无法迁移,除非目标类也同样表现出多态性。
在目标类中声明这个函数。
将原函数的代码复制到目标函数中,调整后者,使其能够正常运行。如果目标函数使用了源类中的特性,需要决定如何从目标函数引用源对象。如果目标类中没有相应的引用机制,就把源对象的引用当做参数,传给新建立的目标函数。如果原函数包含异常处理,需判断逻辑上该由哪个类来处理这一异常,如果该由源类负责,就把异常处理留在源类。
编译目标类。
决定如何从原函数正确引用目标函数。可能会有一个现成的字段或函数来取得目标对象。如果没有,看能否建立这样的函数。还是不行,就在源类中建立一个字段来保存目标对象,这可能是一个永久性修改,也可以让它是暂时的,因为后继的其他重构项目可能会把这个新建字段去掉。
编译源代码,使之成为一个纯委托函数保留下来。要在源对象中引用目标函数,将原函数作为委托函数保留下来。
要移除原函数,将源类中对原函数的所有调用。替换为目标函数的调用。
范例:
class Account{
		  //观察每一项特性
		  double overdraftCharge(){
//			  if(_type.isPremium()){
//				  double result = 10;
//				  //会变化,不移动
//				  if(_daysOverDrawn >7) result +=(_daysOverDrawn - 7)* 0.85;
//				  return result;
//			  }
//			  else return _daysOverDrawn * 1.75;
			  return _type.overdraftCharge(_daysOverDrawn);
		  }
		  double bankCharge(){
			  double result = 4.5;
			  //if(_dayOverDrawn >0) result += overdraftCharge();
			  if(_dayOverDrawn >0) result += _type.overdraftCharge(_daysOverDrawn);
			  return result;
		  }
		  private AccountType _type;
		  private int _daysOverdrawn;
	  }
	  
	  class AccountType{
		  /**
		   * 1、去掉type
		   * 2、想办法得到依旧需要的Account类特性,
		   *  (1)将这个特性也移到目标类,(2)建立或使用一个从目标类到源类的引用关系,(3)将源对象当做参数
		   *  	传给目标函数,(4)如果所需特性是个变量,将它当做参数传给目标函数
		   */
		  double overdraftCharge(int daysOverDrawn){
			  if(isPremium()){
				  double result = 10;
				  //会变化,不移动
				  if(daysOverDrawn >7) result +=(daysOverDrawn - 7)* 0.85;
				  return result;
			  }
			  else return daysOverDrawn * 1.75;
		  }
	  }	

2. 搬移字段

一个类中的字段被另一个类中的字段用到,在目标类新建一个字段,修改源字段的所有用户,令他们使用新字段。

class Class1 {
    aField;
}

class Class2 {
}
更改为↓
class Class1 {
}

class Class2 {
    aField;
}
做法:
字段访问级是public的,可以将它封装起来。
在目标类中建立与源字段相同的字段并同时建立相应的设值/取值函数。
编译。
源对象中引用目标对象,首先看是否有现成字段或函数来得到目标对象,再看能否轻易建立这样一个函数,不行就在源类中新建一个字段来存放目标对象。
删除源字段。
将所有对源字段的引用替换为对某个目标函数的引用。如果需要读取该变量,把对源字段的引用替换为对目标取值函数的调用;如果对该变量赋值,就把对源字段的引用替换为对设置函数的调用;如果源字段不是private,就必须在源类的所有子类中查找源字段的引用点,并进行相应替换。
范例:
class Account{
		 private AccountType _type;
		 private double _interestRate;
		 double interestForAmount_days (double amount,int days){
			 //return _interestRate * amount * days /365;
			 return _type.getInterestRate() * amount * days /365;
		 }
	 }
	 
	 class AccountType{
		 private double _interestRate;
		 void setInterestRate(double arg){
			 _interestRate = arg;
		 }
		 double getInterestRate(){
			 return _interestRate;
		 }
	 }

自我封装
	
class Account{
		 private AccountType _type;
		 private double _interestRate;
		 double interestForAmount_days (double amount,int days){
			 //return _interestRate * amount * days /365;
			 return getInterestRate() * amount * days /365;
		 }
		 private void setInterestRate(double arg){
			 //_interestRate = arg;
			 _type.setInterestRate(arg);
		 }
		 private double getInterestRate(){
			 //return _interestRate;
			 return _type.getInterestRate();
		 }
	 }
	 class AccountType {


	 }
3. 提炼一个新类


将不属于这个类中的字段和方法提取到一个新的类中。
class Person {
    private String name;
    private String officeAreaCode;
    private String officeNumber;

    public String getTelephoneNumber() { ..... }
}
提炼到新的类中↓
class TelephoneNumber {
    private String areaCode;
    private String number;

    public String getTelephoneNumber() { ..... }
}

class Person {
    private String name;
    private TelephoneNumber _officeNumber;
}
做法:
决定如何分解类。
建立新类,放旧类的责任。
建立旧类访问新类的联系,可能需要双向连接,但不需要时不要建立。
先迁移较低层次的,再迁移高层次的。
决定是否公开,公开就要判断是否成为引用对象还是不可变的值对象。
范例:
class Persion{
		 private String name;
		 //private String officeAreaCode;
		 //private String officeNumber;
		 //建立连接
		 private TelephoneNumber officeTelephone = new TelephoneNumber();
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public String getOfficeAreaCode() {
			//return officeAreaCode;
			return officeTelephone.getAreaCode();
		}
		public void setOfficeAreaCode(String officeAreaCode) {
			//this.officeAreaCode = officeAreaCode;
			officeTelephone.setAreaCode(officeAreaCode);
		}
		public String getTelephoneNumber() {
//			return ("("+officeAreaCode+")"+officeNumber);
//			return ("("+getOfficeAreaCode()+")"+officeNumber);
			return officeTelephone.getTelephoneNumber();
		}
//		public void setOfficeNumber(String officeNumber) {
//			this.officeNumber = officeNumber;
//		}
		 
	 }
	 
	 class TelephoneNumber{
		 private String areaCode;
		 private String number;


		 public String getTelephoneNumber(){
			 return ("("+areaCode+")"+number);
		 }
		public String getAreaCode() {
			return areaCode;
		}


		public void setAreaCode(String areaCode) {
			this.areaCode = areaCode;
		}


		public String getNumber() {
			return number;
		}


		public void setNumber(String number) {
			this.number = number;
		}
		 
	 }
4. 将类内联化
将这个类所有的特性移到另一个类中,删除原类。
做法:
在目标类身上声明源类的public协议,并将其中所有函数委托至源类。如果以一个独立接口表示源类函数更合适的话,就应该在内联前提炼接口。
修改所有源类的引用点,引用目标类,将源类声明为private,斩断包之外的所有引用,修改源类的名称。
将源类的特性移到目标类。
5.隐藏委托关系
客户通过一个委托类来调用另一个对象,在服务类上建立客户所需的所有函数,用以隐藏委托关系。
做法:
对于每一个委托关系中的函数,在服务对象端建立一个简单的委托函数。
调整客户,只调用服务对象提供的函数,使用者和服务提供者不在一个包,考虑修改委托函数的访问权限。
class Persion{
		 Department department;


		public Persion getDepartment() {
			//return department;
			return department.getManager();
		}


		public void setDepartment(Department department) {
			this.department = department;
		}
	 }
	 
	 class Department{
		 private String chargeCode;
		 private Persion manager;
		public String getChargeCode() {
			return chargeCode;
		}
		public void setChargeCode(String chargeCode) {
			this.chargeCode = chargeCode;
		}
		public Persion getManager() {
			return manager;
		}
		public void setManager(Persion manager) {
			this.manager = manager;
		}
		 
	 }
6. 移除中间人


某个类做了过多的简单委托动作,让客户直接调用委托类。


做法:
建立一个函数,用以获得委托对象。
对于每个委托函数,在服务类中删除该函数,并让需要调用该函数的客户转为调用受托对象。
 范例:
class Persion{
		 Department department;


//		public Persion getDepartment() {
//			return department.getManager();
//		}
		public Department getDepartment(){
			return department;
		}
		public void setDepartment(Department department) {
			this.department = department;
		}
	 }
	 
	 class Department{
		 private String chargeCode;
		 private Persion manager;
		public String getChargeCode() {
			return chargeCode;
		}
		public void setChargeCode(String chargeCode) {
			this.chargeCode = chargeCode;
		}
		public Persion getManager() {
			return manager;
		}
		public void setManager(Persion manager) {
			this.manager = manager;
		}
		 
	 }
7.引入外加函数
为提供服务的类提供一个无法修改的函数
在客户端建立一个函数,并以第一参数形式传入一个服务类实例。
Date newStart = new Date(previousend.getYear(), previousend.getMonth(), previousend.getDay()+1);
		 
		 Date newStart = nextDay(previousend);
		 private static Date nextDay(Date arg){
			 return new Date(previousend.getYear(), previousend.getMonth(), previousend.getDay()+1);
		 }
做法:
在客户类中建立一个函数,这个函数不应该调用客户类的任何特性,如果它需要个值,就把该值传给他。
以服务类实例作为该函数的第一个参数
8.引入本地扩展
需要为服务类提供一些参数,但无法修改这个类,建立一个新类,使他包含这些特性,让这个扩展成为源类的子类或扩展。
做法:
建立一个扩展类,将它作为源类的子类或扩展类。
在扩展类中加入转型构造函数,即接收源对象作为参数的构造函数,如果采用子类化方案,转型构造函数应该调用适当的超类构造函数;采用包装类方法,转型构造函数应该将它得到的传入参数以实例变量的形式保存起来,用作接收委托的源对象。
在扩展类中加入新特性。
根据需要,将源对象替换为扩展对象。
将针对原始类定义的所有外加函数搬移到扩展类中。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值