隐藏委托关系

动机
封装性太弱,如果委托关系发生变化,下层不再由assistant实现而改为其他实现,那么上层的调用代码manager.getAssistant().manage()
可能也要修改。
做法
- 对每一个委托关系中的函数,在服务对象端
Manager
建立一个简单的委托函数。
fun manage() {
return this._assistant.manage()
}
- 调整客户,使其只调用服务对象提供的函数。(可以查看
getAssistant()
的usage,找到这些地方,将manager.getAssistant().manage()
改为manager.manage()
) - 直到不再有任何客户需要取用受托类,可以移除相关访问函数
getAssistant()
。 - 如果调用者和服务提供者不在同一个模块或包,可以修改访问可见性(kotlin
internal
模块内可见,java默认包可见性)
移除中间人
和隐藏委托关系相反的操作
动机
随着受托类的特性越来越多,服务类会变成一个中间人,这时或许可以让客户直接调用受托类以减少中间人里面不是那么必要的冗长代码。
做法
- 在服务类
Manager
中增加一个函数用来获得受托对象。
fun getAssistant() {
return this._assistant
}
- 对于每个委托函数,find usage,把客户调用它的地方转为调用受托对象。
manager.manage()
变为manager.getAssistant().manage()
- 委托函数变成灰色,删除它。
使用时机
什么时候应该隐藏委托关系,什么时候应该移除中间人?这是一个度的问题,随着系统变化,合适的隐藏程度这个尺度也相应改变,只需要灵活使用隐藏委托关系和移除中间人来重构即可。
系统的变化包括类的规模,如果随着受托类功能越来越多,受托类的数目也越来越多,服务类中的委托函数使得服务类变得非常巨大,那无疑需要考虑将一些受托类可以直接暴露给客户。将哪些受托类暴露给客户,需要考虑呈现给客户的服务的模型尽量简洁便于理解。也包括服务类的功能改变,使得一些功能可以被归类并提炼类。
更适宜隐藏委托关系的情形
- 受托类的功能较少,例如只有
init()
和release()
- 受托类有不同实现,例如有的实现需要
init()
和release()
而有的实现不需要,这种实际上可能已经不算委托关系了。
更适宜移除中间人的情形
服务类的委托函数名字已经可以看出委托关系的。例如staff.getDepartment().getName()
改为staff.getDepartmentName()
就没啥必要,通过这个名字已经可以看出委托关系了。看似隐藏委托关系后,减少了耦合,委托关系发生变化后,上层调用代码不需要改也可以,实则这个方法名已经不再贴切了,得把方法名改了,然后改了上层调用的代码,并没有减少耦合。
网友评论