软件系统中不同的层需要不同的抽象,比如:文件系统,网络模型等。如果不同的层实现类似的功能,这是有问题的,本章讨论这个问题如何发生、如何解决,如何重构这样的系统。
透传方法
相邻层有着类似的功能,通常表现为上层直接将方法参数透传下去,调用下层的函数,比如:
class A{
void GetValue() {
return b.GetValue();
}
private:
B b;
};
这种方式表明两个类或者模块功能划分不清晰。
缺点:增加接口复杂度,但是并没有增加功能,同时也增加了模块间的耦合。
如果遇到这样清空,需要考虑这两个类模块所负责的真正职责是什么,对于模块间的共同职责一定要关注。解决方式有两个:1、划分好每个类的职责;2、如果职责无法区分,就合并这两个类。
什么时候接口重复是可以的
上述方式并不是一直都是不好的,关键是模块有没有增加新的功能中,比如类似于工厂模式的设计实现,Dispatcher方法,根据不同的类型,调用下面实现类的Dispatcher方法。
只有提供了不同的功能,提供相同的方法(包括方法名和方法参数)是可以的
装饰器模式
装饰器模式,对外提供一个接口,内部对这个接口有不同的实现。
这个模式本质上是把一个专用模块扩展的更加通用,但是如果使用不同,很容易造成接口透传,造成模块耦合。
在使用装饰器模式之前,考虑以下替代方式:
1、是否可以直接使用底层实现类,而不是装饰器类
2、如果新特性只对某些case生效,那么在使用时,扩展而不是创建一个新的类
3、新的实现在老的类中扩展,而不是创建新的
4、新特性只是一个很小的东西,没必要为了适配装饰器模式,而实现一个特别庞大的接口(组合是可选择的,但是继承是强耦合的)
有时装饰器模式是好用的,但是大多数时候都不是一个好的选择
接口与实现
接口与实现在两个抽象层级,如果抽象层级一样,那么大多数实现的不深
透传变量
相邻层有着类似的功能另一个表现方式是透传变量。
透传变量增加了模块间交互的复杂度,下层模块必须感知到上层模块的参数,而且中间模块必须接受这些参数,尽管这些模块并不需要这些参数(而且很容易造成方法参数很长)
减少参数透传很困难,解决方式:1、创建共享参数类,把所有需要透传的变量封装到共享参数类中;2、使用全局变量(尽量不要使用,弊大于利);3、最常使用的是引入一个context类,在每个层级的构造函数中传入这个类,这样有新的透传参数需要添加时,只需要更改context类即可(没看出1与3的区别)
作者推崇的是方法3,但是方法3远不是完美的解决方案
总结
每实现一个特性都要添加一些模块、类、方法、参数,这就为系统增加了复杂度,因为使用者必须了解这部分,一个完美的状态是,增加了很少复杂度(接口简单),但是实现了很多功能(实现要深)。