1.5.3 ADT的模板接口
当规范变得更加详细时,就应该选择编程语言了。最终,可以用C++为包中的方法编写原型,并将其组合到实现这个ADT的类的头文件中。
程序清单1-1中的C++头文件包含了关于ADT包的方法,并用注释详细描述了方法的行为。考虑到读者可能不熟悉C++类、模板以及虚方法,C++片段1提供了关于C++类的复习课程,并将讨论C++模板、虚方法以及抽象基类。通过使用模板以及抽象基类,可以利用面向对象设计的封装、多态以及继承的概念开始第一个ADT。
提示:为了强调设计ADT和用数据结构实现ADT的区别,本书在描述ADT的时候使用了模板和抽象基类。我们将包含抽象基类的头文件称为客户接口1。为了在程序中使用某个类,只需要客户接口,因为其中完整地指定了ADT的方法。
当检查程序清单1-1中的接口时,注意针对特殊情况所做的决定,在前面已经提到过这个问题。特别是,add、remove和contains都返回值。注意用来说明方法的注释以@开头,这一内容在附录I中描述。
现在,包中的项是同一类的对象。为了容纳任何同一类型项,包的方法为每个项使用了泛型类型ItemType。为了使标识符ItemType有意义,必须在类的头文件之前编写template<class ItemType>行。当客户选择了实际的数据类型之后,编译器将在出现ItemType的所有位置使用这一数据类型。
BagInterface类是C++抽象基类。C++中的抽象基类(或者简写为抽象类)至少包含一个虚方法,并且没有实现。抽象类无法被实例化,而是只能用作基类。子类必须实现在基类中说明但是没有定义的方法。
程序清单1-1 包含包的C++接口的文件
- /** @file BagInterface.h */
- #ifndef _BAG_INTERFACE
- #define _BAG_INTERFACE
- #include <vector>
- using namespace std;
- template<class ItemType>
- class BagInterface
- {
- public:
- /** Gets the current number of entries in this bag.
- @return The integer number of entries currently in the bag. */
- virtual int getCurrentSize() const = 0;
- /** Sees whether this bag is empty.
- @return True if the bag is empty, or false if not. */
- virtual bool isEmpty() const = 0;
- /** Adds a new entry to this bag.
- @post If successful, newEntry is stored in the bag and
- the count of items in the bag has increased by 1.
- @param newEntry The object to be added as a new entry.
- @return True if addition was successful, or false if not. */
- virtual bool add(const ItemType& newEntry) = 0;
- /** Removes one occurrence of a given entry from this bag,
- if possible.
- @post If successful, anEntry has been removed from the bag
- and the count of items in the bag has decreased by 1.
- @param anEntry The entry to be removed.
- @return True if removal was successful, or false if not. */
- virtual bool remove(const ItemType& anEntry) = 0;
- /** Removes all entries from this bag.
- @post Bag contains no items, and the count of items is 0. */
- virtual void clear() = 0;
- /** Counts the number of times a given entry appears in bag.
- @param anEntry The entry to be counted.
- @return The number of times anEntry appears in the bag. */
- virtual int getFrequencyOf(const ItemType& anEntry) const = 0;
- /** Tests whether this bag contains a given entry.
- @param anEntry The entry to locate.
- @return True if bag contains anEntry, or false otherwise. */
- virtual bool contains(const ItemType& anEntry) const = 0;
- /** Empties and then fills a given vector with all entries that
- are in this bag.
- @return A vector containing all the entries in the bag. */
- virtual vector<ItemType> toVector() const = 0;
- }; // end BagInterface
这些操作的规范是ADT包的各项契约:如果要求这些操作执行,那么就会执行。规范中没有说明如何存储包或如何执行这些操作;而只是说明可以对包做什么。ADT的规范中不包含实现话题,这一点非常重要。对ADT规范的限制可以在ADT实现和使用ADT的程序(也就是客户)之间建立一堵墙。程序只应该依赖于操作的行为。
提示:尽管在实现类之前编写接口并非必要,但是这么做可以以简洁的方式编写规范。然后可以使用接口中的代码作为实际类的大纲。使用接口还可以为ADT提供不依赖于特定类定义的数据类型。第3章和第4章将开发包的不同实现类。类接口代码可以让您方便地用一个包实现替换另一个。