重新组织数据
1、SelfEncapsulateField(自封装字段)
动机:你直接访问一个字段,但与字段直接的耦合关系逐渐变得笨拙。
做法:为这个字段建立取值/设置函数,并且只以这些函数来访问字段。
2、ReplaceDatavaluewithObject(以值对象取代数据值)
动机:比如说电话号码,一开始你可能会用一个字符串来表示“电话号码”概念,但是随后你就会发现,电话号码需要”格式化“、”抽取区号“之类的特殊行为。
3、ChangeValuetoReference(将值对象改为引用对象)
动机:有时候,你会从一个简单的值对象开始,在其中保存少量不可修改的数据。而后,你可能会希望给这个对象加入一些可修改数据,并确保对任何一个对象的修改都能影响到所有引用到此一对象的地方。这时候你就需要将这个对象变成一个引用对象。
值对象和引用对象:值对象是不可变的。引用对象时可变的。
4、ChangeReferencetoValue(将引用对象改为值对象)
动机:你有一个引用对象,很小且不可变,而且不易管理。
5、ReplaceArraywithObject(以对象取代数组)
动机:你有一个数组,其中的元素各自代表不同的东西。
做法:以对象替换数组。对于数组中的每个元素,以一个字段来表示。
代码1:
String[]row=newString[3];
row[0]="Liverpool";
row[1]="15";
代码2:
Performancerow=newPerformance();
row.setName("Liverpool");
row.setWins("15");
6、DuplicateObservedData(复制“被监视数据”)
动机:你有以下领域数据置身于GUI控件中,而领域函数需要访问这些数据。
做法:将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象中的重复数据。
7、ChangeUnidirectionalAssociationtoBidirectional(将单向关联改为双向关联)
动机:两个类都需要使用对方特性,但其间只有一条单向连接。
做法:添加反向指针,并使修改函数能够同时更新两条连接。
下面是一段简单程序,其中有两个类:表示“订单”的Order和表示“客户“的Customer,Customer并没有引用Order;
代码1:
classOrder...
Customer_customer;
CustomergetCustomer(){
return_customer;
}
voidsetCustomer(Customerarg){
_customer=arg;
}
代码2:由于一个客户可以拥有多分订单,所以我们在Customer增加一个集合字段。
ClassCustomer{
PrivateSet_orders=newHashSet();
SetfriendOrders(){
Return_orders;
}
而Order需要修改setCustomer函数,令它同时更新反向指针:
classOrder...
VoidsetCustomer(Customerarg){
If(_customer!=null)_customer.friendOrders().remove(this);
_customer=arg;
If(_customer!=null)_customer.friendOrders().add(this);
}
现在,我需要决定由哪一个类负责控制关联关系。我比较喜欢让单个类操控,因为这样就可以将所有处理关联关系的逻辑集中安置于一地。我将按照下列步骤做出这一决定。
1、如果两者都是引用对象,而其间的关联是”一对多“关系,那么就由”拥有单一引用“的那一方承担”控制者”角色。以本例而言,如果一个客户拥有多个订单,那么就由Order类(订单)来控制关联关系。
2、如果某个对象时组成另一对象的部件,那么由后者负责控制关联关系。
3、如果两者都是引用对象,而其间的关联“多对多”关系,那么随便其中哪个对象来控制关联关系,都无所谓。
如果你希望在Customer中也能修改连接,就让它调用控制函数:
ClassCustomer....
VoidaddOrder(Orderarg){
arg.setCustomer(this);
}
如果一份订单也可以对应多个客户,那么你所面临的就是一个“多对多”的情况,重构后的函数可能是下面这样:
ClassOrder...
VoidaddCustomer(Customerarg){
arg.friendOrders().add(this);
}
VoidremoveCustomer(Customerarg){
arg.friendOrders().remove(this);
_customers.remove(arg);
}
ClassCustomer...
VoidaddOrder(Orderarg){
arg.addCustomer(this);
}
VoidremoveOrder(Orderarg){
arg.removeCustomer(this);
}
8、ChangeBidirectionalAssociationtoUnidirectional(将双向关联改为单向关联)
9、ReplaceMagicNumberwithSymbolicConstant(以字面常量取代魔法数)
所谓魔法数是指拥有特殊意义,却不能明确表现出这种意义的数字。如果你需要在不同的地点引用同一个逻辑数,魔法数会让你烦恼不已,因为一旦这些数发生改变,你就比必须在程序中找到所有魔法数,并将它们全部修改一遍,这简直是一场噩梦。就算你不需要修改,要准确指出每个魔法数的用途,也会让你破费脑筋。
代码1:
doublepotentialEnergy(doublemass,doubleheight){
returnmass*9.81*height;
}
代码2:
staticfinaldoubleGRAVITATIONAL_CONSTANT=9.81;
doublepotentialEnergy(doublemass,doubleheight){
returnmass*GRAVITATIONAL_CONSTANT*height;
}
10、EncapsulateField(封装字段)
11、EncapsulateCollection(封装集合)
动机:取值函数不该返回集合自身,因为这会让用户得以修改集合内容而集合拥有者却一无所悉。
做法:让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。
代码1:
classPerson...
privateSet_courses;
publicSetgetCourses(){
return_courses;
}
publicvoidsetCourses(Setarg){
_courses=arg;
}
Personkent=newPerson();
Sets=newHashSet():
s.add(newCourse("AppreciatingSingleMalts"));
kent.setCourses(s);
Assert.equals(2,kent.getCourses().size());
Courserefact=newCourse("BrutalSarcasm");
kent.getCourses().add(refact);
kent.getCourses().remove(refact);
classPerson
privateSet_courses;
//初始化集合
publicvoidinitializeCourses(Setarg){
Assert.isTrue(_courses.isEmpty());
_courses.addAll(arg);
}
//添加元素
publicvoidaddCourse(Coursearg){
_courses.add(arg);
}
//移除元素
publicvoidremoveCourse(Coursearg){
_courses.remove(arg);
}
//返回集合副本
publicSetgetCourses(){
returnCollections.unmodifiableSet(_courses);
}
publicintnumberOfCourses(){
return_courses.size();
}
Personkent=newPerson();
kent.addCourse(newCourse("SmalltalkProgramming"))
代码3:字符串数组封装
String[]_skills;
String[]getSkills(){
return_skills;
}
voidsetSkills(String[]arg){
_skills=arg;
}
kent.getSkills()[1]="Refactoring";
voidsetSkill(intindex,StringnewSkill){
_skill[index]=newSkill;
}
voidsetSkills(String[]arg){
_skills=newString[arg.length];
for(inti=0;i<arg.length;i++)
setSkill(i,arg[i]);
}
//返回副本
String[]getSkills(){
String[]result=newString[_skills.length];
System.arraycopy(_skills,0,result,0,_skills.length);
returnresult;
}
kent.setSkill(1,"Refactoring");
总结:其他数组封装也是类似,总而言之,将元素的操作封装到对象中,并提供获取数据副本的函数。
12、ReplaceRecordwithDataClass(以数据类取代记录)
13、ReplaceTyeCodewithClass(以类取代类型码)
14、ReplaceTypeCodewithSubclasses(以子类取代类型码)
工厂类函数
staticfinalintENGINEER=0;
staticfinalintSALESMAN=1;
staticfinalintMANAGER=2;
staticEmployeecreate(inttype){
switch(type){
caseENGINEER:
returnnewEngineer();
caseSALESMAN:
returnnewSalesman();
caseMANAGER:
returnnewManager():
default:
thrownewIllegalArgumentException("Icorrecttypecodevalue");
}
}