“Use-In-The-Implementation(在实现中使用)”关系增强了设计者抽象表达逻辑依赖的能力。这个符号表示一个逻辑实体将在其实现中使用另一个逻辑实体(即使没有在其接口中使用),在分析一个设计的底层结构时,可能非常有用。和“Uses-In-The-Interface”关系一样,“Uses-In-The-Implementation”关系表明两个逻辑实体之间的一种物理依赖。当设计师细化高层设计并把它们放入离散的物理组件中时,可以充分地利用这个信息。
如果在一个函数的定义中涉及了一个类型,那么就说在这个函数的实现中使用了该类型。
请看下面的自由函数operator==的实现。其中,假设迭代器总是以相同的顺序返回等价IntSet对象的成员:
上面的实现创建了两个迭代器,我们为每个IntSet参数都创建了一个迭代器。只有当两个迭代器都涉及有效集合元素时,才会进入for循环体。集合中相应位置上的整数通过循环的每次迭代比较。如果所有这样的比较都失败,那么就立刻认为集合是不相等的。要从for循环退出,以下两个条件都必须为真:
(1)至少有一个迭代器已经到达了集合的结尾并且当前无效。
(2)没有发现相应的集合项不相等。
当且仅当此时这两个迭代器都无效时,两个IntSet对象才会相等。
要注意的是operator==(const IntSet&, const IntSet&)不是类IntSet的一个友元。因此这个运算符的任何有效实现都必须利用类IntSetIter。在operator==的实现与类IntSetIter之间的使用关系产生了一种operator==对类IntSetIter的隐含依赖关系。因为是在这个运算符的实现中(而不是在它的逻辑接口中)使用了IntSetIter,所以我们采用一种稍有不同的符号来表示这种关系:
也就是说,就是在类B的实现中使用了类A。
图1-19再次为我们展示了带有两种使用关系的intset组件逻辑视图。特别是,我们看到:
在其接口中使用类IntSet,并在其实现中使用类IntSetIter。尽管operator!=与operator==对称,但是实际上operator!=有可能根据operator==实现。
如果在一个函数的接口中使用了一个对象,就自动地认为在该函数的实现中使用了该对象。因此我们看到符号??就可以推断它所指示的使用不会用在接口中。例如,我们可以从图1-19直接推断出operator!=没有在它的接口中使用IntSetIter。
如果一个类型:(1)被用在类的成员函数中,(2)在类的数据成员的声明中被涉及,或(3)是类的私有基类,那么这个类的实现中就使用了这个类型。
一个类可以以多种方式在其实现中使用另一个类型。正如我们将在3.4节所看到的,类使用一个类型的特定方式,不仅会影响类所依赖的类型,也会影响类的客户端被迫依赖于那个类型的程度。目前,我们简单地展示类在其实现中使用一个类型的方式:
“Uses-In-The-Implementation”关系的特定类型: