第八章 重新组织数据
本章将介绍几个能让你更轻松处理数据的重构手法。很多人或许会认为Self Encapsulate Field有点多余,但是关于“对象应该直接访问其中的数据,抑或应该通过访问函数来访问”这一问题,争论的声音从来不曾停止。有时候你确实需要访问函数,此时就可以通过Self Encapsulate Field得到它们。通常我会选择“直接访问”方式,因为我发现,只要我想做,任何时候进行这项重构都是很简单的。
面向对象语言有一个很有用的特征:除了允许使用传统语言提供的简单数据类型,它们还允许你定义新类型。不过人们往往需要一段时间才能习惯这种编程方式。一开始你常会使用一个简单数值来表示某个概念。随着对系统的深入了解,你可能会明白,以对象表示这个概念,可能更合适。Replace Value With Object让你可 以将“哑”数据变成善表达的对象。如果你发现程序中有太多地方需要这一类对象,也可以使用Change Value to Reference将它们变成引用对象。
魔法数——也就是带有特殊含义的数字——从来都是个问题。我还淸楚记得,一开始学习编程的时候,老师就告诉我不要使用魔法数。但它们还是不时出现。因此,只要弄淸楚魔法数的用途,我就运用Replace Magic Number with Symbolic Constant将它们除掉,以绝后患。
对象之间的关联可以是单向的,也可以是双向的。单向关联比较简单,但有时为了支持一项新功能,你需要使用Change Unidirectional Association to Bidirectional将它变成双向关联。Change Bidirectional Association to Unidirectional则恰恰相反:如果你发现不再需要双向关联,可以使用这项重构将它变成单向关联。
我常常遇到这样的情况:GUI类竟然去处理不该它们处理的业务逻辑。为了把这些处理业务逻辑的行为移到合适的领域类去,你需要在领域类中保存这些逻辑的相关数据,并运用Duplicate Observed Data提供对GUI的支持。一般来说,我不喜欢重复的数据,但这是一个例外,因为这里的重复数据通常是不可避免的。
面向对象编程的关键原则之一就是封装。如果一个类公开了任何public数据,你就应该使用Encapsulate Collection将它郑重地包装起来。如果被公开的数据是个集合,就应该使用Encapsulate Collection,因为集合有其特殊协议。如果一整条记录都被裸露在外,就应该使用Replace Record with Data Class。
需要特别对待的一种数据是类型码(type code):这是一种特殊数值,用来指出“与实例所属之类型相关的某些东西”。类型码通常以枚举形式出现,并且通常以static final整数实现。如果这些类型码用来表现某种信息,并且不会改变所属类型的行为,你可以运用Replace Type Code With Class将它们替换掉,这项重构会为你提供更好的类型检査,以及一个更好的平台,使你可以在未来更方便地将相关行为添加进去。另一方面,如果当前类型的行为受到类型码的影响,你就应该尽可能使用Replace Type Code With SubClasses。如果做不到,就只好使用更复杂(同时也更灵活)的Replace Type Code with State/Strategy。