《重构-改善既有代码的设计》系列读书笔记(七、处理概括关系(继承关系))

处理概括关系(继承关系)

Pull Up Field(字段上移)

两个子类拥有相同的字段

动机
  • 本重构从两方面减少重复,首先去除了重复的数据声明,其次去除了重复的行为。

Pull Up Method (函数上移)

有些函数,在各个子类中产生完全相同的结果

动机
  • 如果系统中出现重复,意味着以后你修改一处代码时需要同时修改另一个。
  • 子类函数覆盖了超类的函数,确做的相同的工作。

Pull Up Constructor Body(构造函数本体上移)

你在各个子类中拥有一些构造函数,他们的本体几乎完全一致

动机
  • 构造函数不能继承,所以如果有相同的构造逻辑,需要将具体逻辑上移到父类的构造函数中。
做法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// before
class Employee...
protected String _name;
protected String _id;
class Manager extends Employee...
public Manager(String name,String id,int grade) {
_name = name;
_id = id;
_grade = grade;
}
private int _grade;
//after
class Employee...
public Employee(String name,String id) {
_name = name;
_id = id;
}
protected String _name;
protected String _id;
class Manager extends Employee...
public Manager(String name,String id,int grade) {
super (name,id);
_grade = grade;
}
private int _grade;

Pull Down Method(函数下移)

超类中某个函数只与部分子类有关

动机
  • 使用完提炼子类后,你可能会需要用到这个手段。

Pull Down Field(字段下移)

超类中的某个字段只被部分子类用到

动机
  • 同上面的函数下移

Extract Subclass (提炼子类)

类中的某些特性只被某些实例用到(这个有点勉强吧,不可能每个实例都会用到所有特性,当然,将关联性比较强的特性组成一个新的类是可以的)

Extract Superclass (提炼超类)

两个类有相似特性

动机
  • 重复代码的危害。

Extract Interface (提炼接口)

若干客户使用类接口中的同一个子集,或者有部分接口相同。

动机
  • 分离出公用的接口可以使系统的用法更清晰,同时也更容易看清楚系统的责任划分。
  • 接口或者多继承可以实现这种模块分离需求。

Collapse Hierarchy(折叠集成关系)

超类和子类之间无太大区别

动机
  • 消除无谓的集成关系,简化代码结构。

Form Template Method(塑造模板函数)

你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作的细节上有所不同

动机
  • 将执行操作的顺序移至超类,并借多态保证各操作仍得以保持差异性,这样的函数称为模板函数。
做法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// before
class Customer...
public String statement() {
String result = textHeaderString();
while(...){
result += textEachRentalString();
}
result += textFooterString();
return result;
}
public String htmlStatement() {
String result = htmlHeaderString();
while(...){
result += htmlEachRentalString();
}
result += htmlFooterString();
return result;
}
// step 1
class Statement {}
class TextStatement extends Statement {}
class HtmlStatement extends Statement {}
class Customer...
public String statement() {
return new TextStatement().value(this);
}
public String htmlStatsment() {
return new HtmlStatement().value(this);
}
class TextStatement {
public String value(Customer aCustomer) {
String result = headerString(aCustomer);
while(...){
result += eachRentalString();
}
result += footerString(aCustomer);
return result;
}
}
class HtmlStatement {
public String value(Customer aCustomer) {
String result = headerString(aCustomer);
while(...){
result += eachRentalString();
}
result += footerString(aCustomer);
return result;
}
}
// step 2
class Statement {
public String value(Customer aCustomer) {
String result = headerString(aCustomer);
while(...){
result += eachRentalString();
}
result += footerString(aCustomer);
return result;
}
}
abstract String headerString(Customer aCustomer)
abstract String eachRentalString()
abstract String footerString(Customer aCustomer)

Replace Inheritance with Delegation(以委托取代继承)

某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

动机
  • 当你从超类中继承了一堆并不需要的东西时,传达出的信息与你的意图南辕北辙,这是一种混淆,你应该将它去除。

  • 所用委托取代继承时,你可以有目的的选择需要的接口。

Replace Delegation with Inheritance(以继承取代委托)

你的两个子类之间使用委托关系,并且经常是全托。。。

动机
  • 委托需要编写大量的委托代码,如果是全托,会增加大量的代码量。
  • 如果你没有使用受委托类的所有接口,那就不需要修改,如果需要使用所有的接口,请使用继承代替委托。