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

《重构--改善既有代码的设计》--重新组织数据(8)

2015-07-29 09:24 856 查看
一、Self Encapsulate(自封装字段)

你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。为这个字段建立取值/设值函数,并且只以这些函数来访问字段。



1、为待封装字段建立取值/设值函数。

2、找出该字段的所有引用点,将它们全部改为调用取值/设值函数。

3、将该字段声明为private。

4、复查,确保找出所有引用点。

5、编译、测试。

class IntRange
{
private int _low, _high;

bool Includes(int arg)
{
return arg >= _low && arg <= _high;
}

void Grow(int factor)
{
_high = _high * factor;
}

IntRange(int low, int high)
{
_low = low;
_high = high;
}
}

把字段_low,_high变为属性。

class IntRange
{
private int _low, _high;

bool Includes(int arg)
{
return arg >= Low && arg <= High;
}

void Grow(int factor)
{
_high = _high * factor;
}

IntRange(int low, int high)
{
_low = low;
_high = high;
}

int Low
{
get { return _low; }
set { _low = value; }
}

int High
{
get { return _high; }
set { _high = value; }
}
}


一般认为设值函数在对象被创建后才使用,也可以单独另建一个初始化函数,如下:

IntRange(int low, int high)
{
Initialize(low, high);
}

void Initialize(int low, int high)
{
_low = low;
_high = high;
}


一旦拥有子类,上述动作的价值就体现出来了。

二、Replace Data Value With Object(以对象取代数据值)

你有一个数据项,需要与其他数据和行为一起使用才有意义。

将数据项变成对象。



1、为待替换数值新建一个类,在其中声明一个final字段,其类型一样,添加get属性、构造函数。

2、编译。

3、将数值字段类型改为新建的类。

4、修改set属性。

5、如果源类中构造函数有这个字段,在新类对这个字段进行赋值。

6、在源类中设值函数,在新类创建一个实例。

7、编译、测试。

8、新类使用Change Value to Reference

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

你从一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象。

将这个值对象变成引用对象。



1、使用Replace Constructor With Factory Method

2、编译、测试。

3、决定由什么对象负责提供访问新对象的途径。

4、预行创建或动态创建。

5、修改工厂函数,令它返回引用对象。

6、编译、测试。

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

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

将它变成一个值对象。



1、检查重构目标是否为不可变对象,或是否可修改为不可变对象。

2、建立equals()和hashCode()。

3、编译、测试。

4、考虑是否可以删除工厂函数,并将构造函数声明为public。

五、Replace Array With Object(以对象取代数组)

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

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

1、新建一个类表示数组所拥有的信息。

2、修改数组用户,让它们改用新类的实例。

3、编译、测试。

4、逐一为数组添加get/set。根据元素用途来命名访问属性名。

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

你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据。

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



1、修改展现类,使其成为领域类的Observer。

2、针对GUI类中的领域数据,使用Self Encapsulate Field。

3、编译、测试。

4、在事件处理函数中调用设值函数,直接更新GUI组件。

5、编译、测试。

6、在领域类中定义数据及其相关访问函数。

7、修改展现类中访问函数,将它们的操作对象改为领域对象(而非GUI组件)。

8、修改Observer的update(),使其从相应的领域对象中将所需数据复制给GUI组件。

9、编译、测试。

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



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

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

1、在被引用类中增加一个字段,用以保存反向指针。

2、决定由哪个类---引用端还是被引用端----控制关联关系。

3、在被控端建立一个辅助函数,其命名应该清楚指出它的有限用途。

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

两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。去除不必要的关联。



1、找出保存“你想去除的指针”的字段,检查它的每一个用户,判断是否可以去除该指针。

2、编译、测试。

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

你有一个字面数值,带有特别含义。创造一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。



1、声明一个常量,令其值为原本的魔法数值。

2、找出这个魔法数的所有引用点。

3、检查是否可以使用这个新声明的常量来替换该魔法数。

4、编译。

5、所有魔法数都被替换完毕后,编译并测试。

十、Encapsulate Field(封装字段)

你的类中存在一个public字段,将它声明为private,并提供相应的访问函数。

十一、Encapsulate Collection(封装集合)

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

1、加入为集合添加/移除元素的函数。

2、将保存集合的字段初始化为一个空集合。

3、编译。

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

你需要面对传统编程环境中的记录结构。为该记录创建一个“哑”数据对象。

1、新建一个类,表示这个记录。

2、建立对应的private字段,并提供其get/set属性。

十三、Replace Type Code with Class(以类取代类型码)

类之中有一个数值类型码,但它并不影响类的行为。以一个新的类替换该数值类型码。



1、为类型码建立一个类。

2、修改源类实现,让它使用上述新建的类。

3、编译、测试。

4、对于源类中每一个使用类型码的函数,相应建立一人函数,让新函数使用新建的类。

5、逐一修改源类用户,让它们使用新接口。

6、每修改一个用户,编译并测试。

7、删除使用类型的旧接口,并删除保存旧类型的静态变量。

8、编译、测试。

class Person
{
public static readonly int O = 0;
public static readonly int A = 1;
public static readonly int B = 2;
public static readonly int AB = 3;

private int _bloodGroup;

public Person(int bloodGroup)
{
_bloodGroup = bloodGroup;
}

public int BloodGroup
{
get { return _bloodGroup; }
set { _bloodGroup = value; }
}
}


建立一个BloodGroup类,并在这个实例中保存原本的类型码数值:

class BloodGroup
{
private static int _code;

public static readonly BloodGroup O = new BloodGroup(0);
public static readonly BloodGroup A = new BloodGroup(1);
public static readonly BloodGroup B = new BloodGroup(2);
public static readonly BloodGroup AB = new BloodGroup(3);

private static readonly BloodGroup[] _values = { O, A, B, AB };

private BloodGroup(int code)
{
_code = code;
}

public int Code
{
get { return _code; }
set { _code = value; }
}
}


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

你有一个不可变的类型码,它会影响类的行为。以子类取代这个类型码。



1、使用Self Encapsulate Field将类型码自我封装起来。

2、为类型码建立相应的子类。在子类中覆写类型码函数。

3、每建立一个新子类,编译并通过。

4、从父类中删除掉保存类型码的字段,并声明为抽象函数。

5、编译、测试。

十五、Replace Type Code With State/Strategy(以State/Strategy取代类型码)

你有一个类型码,它会影响类的行为,但你无法通过继承手法消除它,以状态对象取代类型码。



1、使用Self Encapsulate Field将类型码自我封装起来。

2、新建一个类,根据类型码用途命名。即状态对象。

3、在新类下创建子类,每个子类对应一种类型码。

4、在超类中建立一个抽象的查询函数,用以返回类型码。在子类覆写该函数,返回确切的类型。

5、编译。

6、在源类新建一个字段,用以保存新建的状态对象。

7、调整源类中查询类型码函数,将查询动作转发给状态对象。

8、调整源类中类型码函数,将一个状态对象子类赋值给第6步骤中那个字段。

9、编译、测试。

class Employee
{
private int _type;
static readonly int ENGINEER = 0;
static readonly int SALESMAN = 1;
static readonly int MANAGER = 2;

Employee(int type)
{
_type = type;
}
}


以下创建父类与子类:

abstract class EmployeeType
{
abstract int GetTypeCode();
}


class Engineer : EmployeeType
{
public int GetTypeCode()
{
return Employee.ENGINEER;
}
}


class Salesman:EmployeeType
{
public int GetTypeCode()
{
return Employee.SALESMAN;
}
}


class Manager:EmployeeType
{
public int GetTypeCode()
{
return Employee.MANAGER;
}
}


现在修改源类Employee:

class Employee
{
public static readonly int ENGINEER = 0;
public static readonly int SALESMAN = 1;
public static readonly int MANAGER = 2;

private EmployeeType _type;

public void SetType(int arg)
{
switch (arg)
{
case 0:
_type = new Engineer();
break;
case 1:
_type = new Salesman();
break;
case 2:
_type = new Manager();
break;
default:
throw new Exception("Incorrect Employee Code.");
}
}
}


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

你的各个子类的唯一差别只在“返回常量数据”的函数身上。修改这些函数,使它们返回父类中的某个(新增)字段,然后销毁子类。



1、对所有子类使用Replace Constructor with Factory Method。

2、如果有任何代码引用子类,改为引用父类。

3、针对常量,声明一个readonly字段。

4、父类声明一个protected构造函数,初始化这些字段。

5、新建或测试子类构造函数,调用父类构造函数。

6、编译、测试。

7、在父类实现所有常量函数,令它们返回相应字段,然后将函数从子类删除。

8、每删除一个函数,编译、测试。

9、子类常量函数被删除,使用Inline Method将子类构造函数内联到父类的工厂函数中。

10、编译、测试。

11、将子类删除。

12、编译、测试。

13、重复“内联构造函数,删除子类”过程,直到所有子类删除。

abstract class Person
{
abstract bool IsMale();
abstract char GetCode();
}


class Male:Person
{
bool IsMale()
{
return true;
}

char GetCode()
{
return 'M';
}
}


class Female:Person
{
bool IsMale()
{
return false;
}

char GetCode()
{
return 'F';
}
}


重构后代码为:

class Person
{
private readonly bool _isMale;
private readonly char _code;

protected Person(bool isMale, char code)
{
_isMale = isMale;
_code = code;
}

public static Person CreateMale()
{
return new Person(true, 'M');
}

static Person CreateFemale()
{
return new Person(false, 'F');
}
}


class Female:Person
{
Female()
: base(false, 'F')
{ }
}


class Male:Person
{
Male()
: base(true, 'M')
{ }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: