💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
反向重构:移除中间人(192) ![](https://box.kancloud.cn/2b8a93f2c8493dd277997d678156e91d_408x421.jpeg) `manager = aPerson.department.manager;`![](https://box.kancloud.cn/a3bed334e2e1f6d1a46c5039deb25af9_91x152.jpeg) ``` manager = aPerson.manager; class Person { get manager() {return this.department.manager;} ``` ### 动机 一个好的模块化的设计,“封装”即使不是其最关键特征,也是最关键特征之一。“封装”意味着每个模块都应该尽可能少了解系统的其他部分。如此一来,一旦发生变化,需要了解这一变化的模块就会比较少——这会使变化比较容易进行。 当我们初学面向对象技术时就被教导,封装意味着应该隐藏自己的字段。随着经验日渐丰富,你会发现,有更多可以(而且值得)封装的东西。 如果某些客户端先通过服务对象的字段得到另一个对象(受托类),然后调用后者的函数,那么客户就必须知晓这一层委托关系。万一受托类修改了接口,变化会波及通过服务对象使用它的所有客户端。我可以在服务对象上放置一个简单的委托函数,将委托关系隐藏起来,从而去除这种依赖。这么一来,即使将来委托关系发生变化,变化也只会影响服务对象,而不会直接波及所有客户端。 ![](https://box.kancloud.cn/5b67640de5018a144691e2baa9ceb651_1231x417.jpeg) ### 做法 - 对于每个委托关系中的函数,在服务对象端建立一个简单的委托函数。 - 调整客户端,令它只调用服务对象提供的函数。每次调整后运行测试。 - 如果将来不再有任何客户端需要取用`Delegate`(受托类),便可移除服务对象中的相关访问函数。 - 测试。 ### 范例 本例从两个类开始,代表“人”的`Person`和代表“部门”的`Department`。 ##### class Person... ``` constructor(name) {  this._name = name; } get name() {return this._name;} get department() {return this._department;} set department(arg) {this._department = arg;} ``` ##### class Department... ``` get chargeCode() {return this._chargeCode;} set chargeCode(arg) {this._chargeCode = arg;} get manager() {return this._manager;} set manager(arg) {this._manager = arg;} ``` 有些客户端希望知道某人的经理是谁,为此,它必须先取得`Department`对象。 ##### 客户端代码... `manager = aPerson.department.manager;`这样的编码就对客户端揭露了`Department`的工作原理,于是客户知道:`Department`负责追踪“经理”这条信息。如果对客户隐藏`Department`,可以减少耦合。为了这一目的,我在`Person`中建立一个简单的委托函数。 ##### class Person... `get manager() {return this._department.manager;}`现在,我得修改`Person`的所有客户端,让它们改用新函数: ##### 客户端代码... `manager = aPerson.department.manager;`只要完成了对`Department`所有函数的修改,并相应修改了`Person`的所有客户端,我就可以移除`Person`中的`department`访问函数了。