您的位置:首页 > 编程语言

重构改善既有代码的设计--重新组织数据

2014-06-13 20:22 537 查看

重新组织数据

1、Self Encapsulate Field(自封装字段)

动机:你直接访问一个字段,但与字段直接的耦合关系逐渐变得笨拙。

做法:为这个字段建立取值/设置函数,并且只以这些函数来访问字段。

2、Replace Data value with Object(以值对象取代数据值)

动机:比如说电话号码,一开始你可能会用一个字符串来表示“电话号码”概念,但是随后你就会发现,电话号码需要”格式化“、”抽取区号“之类的特殊行为。

3、Change Value toReference(将值对象改为引用对象)

动机:有时候,你会从一个简单的值对象开始,在其中保存少量不可修改的数据。而后,你可能会希望给这个对象加入一些可修改数据,并确保对任何一个对象的修改都能影响到所有引用到此一对象的地方。这时候你就需要将这个对象变成一个引用对象。

值对象和引用对象:值对象是不可变的。引用对象时可变的。

4、Change Reference to Value(将引用对象改为值对象)

动机:你有一个引用对象,很小且不可变,而且不易管理。

5、Replace Array with Object(以对象取代数组)

动机:你有一个数组,其中的元素各自代表不同的东西。

做法:以对象替换数组。对于数组中的每个元素,以一个字段来表示。

代码1:

String[] row = new String[3];

row[0] = "Liverpool";

row[1] = "15";

代码2:

Performance row = new Performance();

row.setName("Liverpool");

row.setWins("15");

6、Duplicate Observed Data(复制“被监视数据”)

动机:你有以下领域数据置身于GUI控件中,而领域函数需要访问这些数据。

做法:将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象中的重复数据。

7、Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)

动机:两个类都需要使用对方特性,但其间只有一条单向连接。

做法:添加反向指针,并使修改函数能够同时更新两条连接。

下面是一段简单程序,其中有两个类:表示“订单”的Order和表示“客户“的Customer,Customer并没有引用Order;

代码1:

class Order...

Customer _customer;

Customer getCustomer(){

return _customer;

}

void setCustomer(Customer arg){

_customer = arg;

}

代码2:由于一个客户可以拥有多分订单,所以我们在Customer增加一个集合字段。

Class Customer{

Private Set _orders = new HashSet();

Set friendOrders(){

Return _orders;

}

而Order需要修改setCustomer函数,令它同时更新反向指针:

class Order...

Void setCustomer(Customer arg){

If(_customer !=null) _customer.friendOrders().remove(this);

_customer = arg;

If(_customer !=null) _customer.friendOrders().add(this);

}

现在,我需要决定由哪一个类负责控制关联关系。我比较喜欢让单个类操控,因为这样就可以将所有处理关联关系的逻辑集中安置于一地。我将按照下列步骤做出这一决定。

1、如果两者都是引用对象,而其间的关联是”一对多“关系,那么就由”拥有单一引用“的那一方承担”控制者”角色。以本例而言,如果一个客户拥有多个订单,那么就由Order类(订单)来控制关联关系。

2、如果某个对象时组成另一对象的部件,那么由后者负责控制关联关系。

3、如果两者都是引用对象,而其间的关联“多对多”关系,那么随便其中哪个对象来控制关联关系,都无所谓。

如果你希望在Customer中也能修改连接,就让它调用控制函数:

Class Customer....

Void addOrder(Order arg){

arg.setCustomer(this);

}

如果一份订单也可以对应多个客户,那么你所面临的就是一个“多对多”的情况,重构后的函数可能是下面这样:

Class Order...

Void addCustomer(Customer arg){

arg.friendOrders().add(this);

}

Void removeCustomer(Customer arg){

arg.friendOrders().remove(this);

_customers.remove(arg);

}

Class Customer...

Void addOrder(Order arg){

arg.addCustomer(this);

}

Void removeOrder(Order arg){

arg.removeCustomer(this);

}

8、Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)

9、Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)

所谓魔法数是指拥有特殊意义,却不能明确表现出这种意义的数字。如果你需要在不同的地点引用同一个逻辑数,魔法数会让你烦恼不已,因为一旦这些数发生改变,你就比必须在程序中找到所有魔法数,并将它们全部修改一遍,这简直是一场噩梦。就算你不需要修改,要准确指出每个魔法数的用途,也会让你破费脑筋。

代码1:

double potentialEnergy(double mass, double height){

return mass * 9.81 * height;

}

代码2:

static final double GRAVITATIONAL_CONSTANT = 9.81;

double potentialEnergy(double mass, double height){

return mass * GRAVITATIONAL_CONSTANT * height;

}

10、Encapsulate Field(封装字段)

11、Encapsulate Collection(封装集合)

动机:取值函数不该返回集合自身,因为这会让用户得以修改集合内容而集合拥有者却一无所悉。

做法:让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。

代码1:

class Person...

private Set _courses;

public Set getCourses(){

return _courses;

}

public void setCourses(Set arg){

_courses = arg;

}

Person kent = new Person();

Set s = new HashSet():

s.add(new Course("Appreciating Single Malts"));

kent.setCourses(s);

Assert.equals(2,kent.getCourses().size());

Course refact = new Course("Brutal Sarcasm");

kent.getCourses().add(refact);

kent.getCourses().remove(refact);

class Person

private Set _courses;

//初始化集合

public void initializeCourses(Set arg){

Assert.isTrue(_courses.isEmpty());

_courses.addAll(arg);

}

//添加元素

public void addCourse(Course arg){

_courses.add(arg);

}

//移除元素

public void removeCourse(Course arg){

_courses.remove(arg);

}

//返回集合副本

public Set getCourses(){

return Collections.unmodifiableSet(_courses);

}

public int numberOfCourses(){

return _courses.size();

}

Person kent = new Person();

kent.addCourse(new Course("Smalltalk Programming"))

代码3:字符串数组封装

String[] _skills;

String[] getSkills(){

return _skills;

}

void setSkills(String[] arg){

_skills = arg;

}

kent.getSkills()[1] = "Refactoring";

void setSkill(int index, String newSkill){

_skill[index] = newSkill;

}

void setSkills(String[] arg){

_skills = new String[arg.length];

for(int i = 0; i<arg.length; i++)

setSkill(i, arg[i]);

}

//返回副本

String[] getSkills(){

String[] result = new String[_skills.length];

System.arraycopy(_skills, 0, result, 0, _skills.length);

return result;

}

kent.setSkill(1, "Refactoring");

总结:其他数组封装也是类似,总而言之,将元素的操作封装到对象中,并提供获取数据副本的函数。

12、Replace Record with Data Class(以数据类取代记录)

13、 Replace Tye Code with Class(以类取代类型码)

14、Replace Type Code with Subclasses(以子类取代类型码)

工厂类函数

static final int ENGINEER = 0;

static final int SALESMAN = 1;

static final int MANAGER = 2;

static Employee create(int type){

switch(type){

case ENGINEER :

return new Engineer();

case SALESMAN :

return new Salesman();

case MANAGER :

return new Manager():

default:

throw new IllegalArgumentException("Icorrect type code value");

}

}

15、Replace Type Code with State/Strategy(以State/Strategy取代类型码)

16、Replace Subclass with Fields(以字段取代子类)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: