21.5 案例:对具体类的良性依赖
良性依赖可以是对抽象基类的依赖,也可以是对具体类的依赖。其实,这种对具体类的良性依赖的例子是很多的,比如《设计模式》和《敏捷软件开发》中Facade模式的相关例子。下面,笔者举一个基于组件开发的例子,在“组件重用”这种“黑盒重用”日益盛行的今天,也许更具现实意义。
好的组件库,都恪守接口隔离原则(Interface-Segregation Principle),以达到“不应该强迫客户依赖于它们不用的方法”的目的。这其中包含了基于角色的设计(Role-based Design)的思想:协作被定义为“多个对象为了完成某种目标而进行的交互”;角色被定义为“特定协作中的对象的抽象”,它“仅定义了对象特征的一个对某协作有意义的子集”;协作和角色的概念和现实世界很接近,我们很容易通过已有角色的组合来构造新的协作,以完成新的功能。
好了,看我们的需求:在多个XML文件中,查找那些包含特定名字的元素的文件。比如,我们可能希望知道哪些XML文件中包含名为book的元素,这在XML网页越来越多的今天,对搜索引擎无疑是很有用。我们使用微软的DOM组件来实现:CXmlFileSearcher代表一个高层模块,它的职责就是上面描述的需求;而具体的打开XML文件的工作,它交给IXMLDOMDocument来做;具体的读取XML文件中的每个元素的名字的工作,交给IXMLDOMNode来做;它是个典型的Facade模式,如图21-8所示。
|
外观(Facade)模式 关键字:子系统 高层接口 支持变化:它实现了子系统与客户之间的松耦合关系,而子系统内部的功能部件往往是紧耦合的。松耦合关系使得子系统的组件变化不会影响到它的客户。 |

图21-8 基于Façade模式的设计
值得说明的是,按照依赖倒置原则(Dependency-Inversion Principle),应当做到“高层模块不应该依赖于低层模块,二者都应该依赖于抽象”。就是说,我们应当为CXmlFileSearcher提供一个抽象基类,供外部的高层模块client调用,这在当前的需求之下,未免有过度设计(Over-engineering)之嫌。这也正好体现了良性依赖原则的“务实”优点。
当然,我们并不是一味地回避使用抽象基类的“抽象耦合”。这不,没过多久,新需求出现了,不仅要搜索元素名,还要搜索属性名和文本格式的内容。还是首先仅做重构,引入抽象基类CSearcher,原先在CXmlFileSearcher中实现的搜索策略移到具体类CEleNameSearcher中,如图21-9所示。需要特别说明的是,现在引入抽象和上一步引入抽象,是完全不同的两回事;现在引入抽象是基于实实在在的需求,而上一步引入抽象是基于猜测,其接口很可能不能满足刚刚提出来的新需求。

图21-9 引入抽象基类CSearcher
重构完毕,可以增加新功能了。运用策略模式,将搜索属性名和文本格式的内容分别实现作具体类CAttrSearcher和CTextSearcher,如图21-10所示。

图21-10 进一步引入策略模式






