设为首页 加入收藏

TOP

3.4.3 "有一个"与"是一个"的区别
2013-10-07 15:33:56 来源: 作者: 【 】 浏览:68
Tags:3.4.3 " 一个 区别

3.4.3  "有一个"与"是一个"的区别

在现实中,区分对象之间的"有一个"与"是一个"关系相当容易。没人会说桔子有一个水果-- 桔子是一种水果。在代码中,有时候并不会这么明显。

考虑一个代表哈希表的假想类,哈希表是高效地将键映射到值的一种数据结构。例如,保险公司使用Hashtable类将成员ID映射到名称,从而给定一个ID就可以方便地找到对应的成员名称。成员ID是键,成员名称是值。

在标准哈希表实现中,每个键都有一个值。如果ID 14534映射到名称"Kleper,Scott",就不能再映射到成员名称"Kleper,Marni"。在大多数实现中,如果对一个已经有值的键添加第二个值,第一个值就会消失。换句话说,如果ID 14534映射到"Kleper,Scott",然后又将ID 14534分配给"Kleper,Marni",那么Scott将被遗弃,下面的序列两次调用了假想哈希表enter()行为,并给出了每次调用结束后哈希表的内容。hash.enter用到了超前一点的C++(www.cppentry.com)对象语法,可以将其当作"使用hash对象的enter行为"。

  1. hash.enter(14534, "Kleper, Scott"); 

< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

14534

"Kleper, Scott" [字符串]

  1. hash.enter(14534, "Kleper, Marni"); 

14534

"Kleper, Marni" [字符串]

不难想象类似于哈希表但是允许一个键有多个值的数据结构的使用。在保险公司示例中,一个家庭可能有多个名称对应于同一个ID。由于这种数据结构非常类似于哈希表,因此可以用某种方式使用哈希表的功能。哈希表的键只能有一个值,但是这个值是任意类型。除了字符串之外,这个值还可以是一个包含多个键值的集合(例如数组或者列表)。当向已有ID添加新的成员时,可以将名称加入到集合中。运行方式如下所示:

  1. Collection collection;                                                  // Make a new collection.  
  2. collection.insert("Kleper, Scott");         // Add a new element to the collection.  
  3. hash.enter(14534, collection);                      // Enter the collection into the table.  

14534

{Kleper, Scott}[集合]

  1. Collection collection = hash.get(14534);// Retrieve the existing collection.  
  2. collection.insert(“Kleper, Marni”); // Add a new element to the collection.  
  3. hash.enter(14534, collection); // Replace the collection with the updated one.  

14534

{Kleper, Scott, Kleper,

Marni}  [集合]


使用集合而不是字符串有些繁琐,并且需要大量重复代码。在一个单独的类中封装多值功能应该会比较好,可以将这个类叫做MultiHash。MultiHash类的运行与Hashtable类似,只是暗地里将每个值作为字符串的集合而不是单个字符串存储。很明显,MultiHash与Hashtable有某种联系,因为它仍然使用哈希表存储数据。不明显的是,这是"是一个"关系还是"有一个"关系。

先考虑"是一个"关系。假定MultiHash是Hashtable的子类,它必须重写在表中添加项的行为,从而既可以创建集合并添加新的元素,又可以获取已有集合并添加新的元素。此外还必须重写获取值的行为。例如,可以将给定键的所有值集中到一个字符串。这好像是一个相当合理的设计。即使子类重写了超类所有的行为,仍然可以在子类中使用原始行为,从而使用超类的行为。这种方法如图3-5所示。

 
图  3-5
现在考虑"有一个"关系。MultiHash属于自己的类,但是包含了Hashtable对象。这个类的接口可能与Hashtable非常相似,但是并不需要相同。在幕后,当用户向MultiHash添加项时,会将这个项封装到一个集合并送入Hashtable对象。这也很合理,如图3-6所示。

 
图  3-6

那么,哪个方案是正确的?没有明确的答案,本书的作者之一认为这是"有一个"关系,他编写了一个MultiHash类供产品使用。主要原因是允许修改公开的接口而不需要担心维护哈希表的功能。例如,图3-6中get行为变成了getAll,清楚表明将获取MultiHash中某个特定键所有的值。此外,在"有一个"关系中,不需要担心哈希表功能会渗透。例如,如果哈希表类提供了获取值的总数的方法,只要MultiHash不重写这个方法,就可以用这个方法报告集合的数目。

这就是说,MultiHash实际上是一个具有新功能的Hashtable这一说法是让人信服的,因此应该是"是一个"关系。关键在于有时候这两种关系之间的差别很小,您需要考虑使用类的方式,还需要考虑您创建的类只是利用了其他类的一些功能,还是在其他类的基础上修改或者添加新功能。

表3-3给出了关于MultiHash两种方法的支持以及反对意见。

表  3-3

 

是一个

有一个

支持的原因

  基本上,这是具有不同特征的同一抽象

  这个类的行为与Hashtable(几乎)相同

  MultiHash可以拥有任何有用的行为,

而不需要考虑Hashtable拥有什么行为

  可以不采用Hashtable实现方式,

同时不需要改变公开的行为

反对的原因

  根据定义,哈希表一个键对应一个值,

MultiHash当作哈希表是错误的

  MultiHash将哈希表的两个行为全部重写,

这有力地说明这个设计是错误的

  Hashtable未知的或者不正确的属性以

及行为会“渗透”到MultiHash

  在某种意义上,MultiHash

过提出新行为进行了重造

  Hashtable的一些其他属性

以及行为可能是有用的

反对"是一个"关系的理由在这种情况下非常有力。实际上,根据作者多年的经验,如果要选择的话建议采用"有一个"关系而不是"是一个"关系。

注意,在这里使用Hashtable和MultiHash说明了"有一个"和"是一个"关系的不同。在代码中,建议使用标准哈希表类而不是自己写一个。C++(www.cppentry.com)11的标准库中提供了一个unordered_map类,用来代替Hashtable,此外还提供了一个unordered_multimap类,可以用来代替MultiHash类。在第12章将讨论这两个标准类。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇3.2.5 综合考虑 下一篇3.4.2 "是一个"关系(继..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Python中文网 - 人生 (2025-12-24 18:49:47)
·【整整648集】这绝对 (2025-12-24 18:49:44)
·Python超详细一条龙 (2025-12-24 18:49:42)
·【超详细】JDK 下载 (2025-12-24 18:19:32)
·Java_百度百科 (2025-12-24 18:19:29)