企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
反向重构:内联类(186) ![](https://box.kancloud.cn/4263079d51dfc941d3bcfd88c2a33f03_565x192.jpeg) ``` class Person {  get officeAreaCode() {return this._officeAreaCode;}  get officeNumber() {return this._officeNumber;} ``` ![](https://box.kancloud.cn/a3bed334e2e1f6d1a46c5039deb25af9_91x152.jpeg) ``` class Person {  get officeAreaCode() {return this._telephoneNumber.areaCode;}  get officeNumber() {return this._telephoneNumber.number;} } class TelephoneNumber {  get areaCode() {return this._areaCode;}  get number() {return this._number;} } ``` ### 动机 你也许听过类似这样的建议:一个类应该是一个清晰的抽象,只处理一些明确的责任,等等。但是在实际工作中,类会不断成长扩展。你会在这儿加入一些功能,在那儿加入一些数据。给某个类添加一项新责任时,你会觉得不值得为这项责任分离出一个独立的类。于是,随着责任不断增加,这个类会变得过分复杂。很快,你的类就会变成一团乱麻。 设想你有一个维护大量函数和数据的类。这样的类往往因为太大而不易理解。此时你需要考虑哪些部分可以分离出去,并将它们分离到一个独立的类中。如果某些数据和某些函数总是一起出现,某些数据经常同时变化甚至彼此相依,这就表示你应该将它们分离出去。一个有用的测试就是问你自己,如果你搬移了某些字段和函数,会发生什么事?其他字段和函数是否因此变得无意义? 另一个往往在开发后期出现的信号是类的子类化方式。如果你发现子类化只影响类的部分特性,或如果你发现某些特性需要以一种方式来子类化,某些特性则需要以另一种方式子类化,这就意味着你需要分解原来的类。 ### 做法 - 决定如何分解类所负的责任。 - 创建一个新的类,用以表现从旧类中分离出来的责任。 > 如果旧类剩下的责任与旧类的名称不符,为旧类改名。 - 构造旧类时创建一个新类的实例,建立“从旧类访问新类”的连接关系。 - 对于你想搬移的每一个字段,运用搬移字段(207)搬移之。每次更改后运行测试。 - 使用搬移函数(198)将必要函数搬移到新类。先搬移较低层函数(也就是“被其他函数调用”多于“调用其他函数”者)。每次更改后运行测试。 - 检查两个类的接口,去掉不再需要的函数,必要时为函数重新取一个适合新环境的名字。 - 决定是否公开新的类。如果确实需要,考虑对新类应用将引用对象改为值对象(252)使其成为一个值对象。 ### 范例 我们从一个简单的`Person`类开始。 ##### class Person... ``` get name()   {return this._name;} set name(arg) {this._name = arg;} get telephoneNumber() {return `(${this.officeAreaCode}) ${this.officeNumber}`;} get officeAreaCode()   {return this._officeAreaCode;} set officeAreaCode(arg) {this._officeAreaCode = arg;} get officeNumber() {return this._officeNumber;} set officeNumber(arg) {this._officeNumber = arg;} ``` 这里,我可以将与电话号码相关的行为分离到一个独立的类中。首先,我要定义一个空的`TelephoneNumber`类来表示“电话号码”这个概念: ``` class TelephoneNumber { } ``` 易如反掌!接着,我要在构造`Person`类时创建`TelephoneNumber`类的一个实例。 ##### class Person... ``` constructor() { this._telephoneNumber = new TelephoneNumber(); } ``` ##### class TelephoneNumber... ``` get officeAreaCode() {return this._officeAreaCode;} set officeAreaCode(arg) {this._officeAreaCode = arg;} ``` 现在,我运用搬移字段(207)搬移一个字段。 ##### class Person... ``` get officeAreaCode() {return this._telephoneNumber.officeAreaCode;} set officeAreaCode(arg) {this._telephoneNumber.officeAreaCode = arg;} ``` 再次运行测试,然后我对下一个字段进行同样处理。 ##### class TelephoneNumber... ``` get officeNumber() {return this._officeNumber;} set officeNumber(arg) {this._officeNumber = arg;} ``` ##### class Person... ``` get officeNumber() {return this._telephoneNumber.officeNumber;} set officeNumber(arg) {this._telephoneNumber.officeNumber = arg;} ``` 再次测试,然后再搬移对电话号码的取值函数。 ##### class TelephoneNumber... `get telephoneNumber() {return `(${this.officeAreaCode}) ${this.officeNumber}`;}`##### class Person... `get telephoneNumber() {return this._telephoneNumber.telephoneNumber;}`现在我需要做些清理工作。“电话号码”显然不该拥有“办公室”(office)的概念,因此我得重命名一下变量。 ##### class TelephoneNumber... ``` get areaCode() {return this._areaCode;} set areaCode(arg) {this._areaCode = arg;} get number() {return this._number;} set number(arg) {this._number = arg;} ``` ##### class Person... ``` get officeAreaCode() {return this._telephoneNumber.areaCode;} set officeAreaCode(arg) {this._telephoneNumber.areaCode = arg;} get officeNumber() {return this._telephoneNumber.number;} set officeNumber(arg) {this._telephoneNumber.number = arg;} ``` `TelephoneNumber`类上有一个对自己(telephone number)的取值函数也没什么道理,因此我又对它应用函数改名(124)。 ##### class TelephoneNumber... `toString() {return `(${this.areaCode}) ${this.number}`;}`##### class Person... `get telephoneNumber() {return this._telephoneNumber.toString();}`“电话号码”对象一般还具有复用价值,因此我考虑将新提炼的类暴露给更多的客户端。需要访问`TelephoneNumber`对象时,只须把`Person`类中那些`office`开头的访问函数搬移过来并略作修改即可。但这样`TelephoneNumber`就更像一个值对象(Value Object)\[mf-vo\]了,因此我会先对它使用将引用对象改为值对象(252)(那个重构手法所用的范例,正是基于本章电话号码例子的延续)。