参考书籍《Head First设计模式》
餐馆和咖啡馆的菜单分别是用数组和容器(vector)存储的,有一天餐馆和咖啡店合并了想要打印菜单,必须实现两个不同的遍历(数组用sizeof计算长度,用[]取元素;容器用size取长度,用at()取元素),来处理两种不同的聚合类型,代码类似于下面所示
- for (int i = 0; i<sizeof(dinerMenu.items_) / sizeof(dinerMenu.items_[0]); i++)
- {
- MenuItem item = dinerMenu.items_[i];
- std::cout << item.name <<","<< item.description << ","<< item.price << std::endl;
- }
-
- for (int i = 0; i
size(); i++) - {
- MenuItem item = conffeMenu.items_.at(i);
- std::cout << item.name << "," << item.description << "," << item.price <
- }
如果我们还要遍历另外一种聚合数据,比如map,那么就要再加一种遍历方法。又或者我们有一天咖啡店不用容器存储,改用数组了,那我们的遍历方法又要改写。这就要求客户端对聚合的实现很了解,即针对实现编程(而我们提倡的设计原则是针对接口编程,而不是针对实现编程)。
在上述例子中引起变化的是:由不同的组合类型所造成的遍历。那我们可以封装遍历吗?迭代器模式就是封装了遍历。迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
模式定义
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部表示。
类图如下
代码实现
如上面需求场景中,我们使用迭代器模式,让两个具体的聚合类(餐馆和咖啡馆)都提供一个createIterator的接口,客户端使用该接口返回的iterator遍历聚合体中各元素。这样客户端不需要关系聚合体的具体实现,针对不同聚合体提供的iterator使用方法一样。
先定义迭代器接口(抽象类Iterator)
- class Iterator {
- public:
- virtual bool hasNext() = 0;
- virtual void* next() = 0;
- };
两个具体的迭代器:餐厅菜单迭代器和咖啡店菜单迭代器
- class CoffeMenuIterator :public Iterator {
- public:
- CoffeMenuIterator(std::vector< MenuItem>* items) :items_(items), position_(0)
- {
-
- }
- virtual MenuItem& next()override
- {
- MenuItem& item = items_->at(position_);
- position_++;
- return item;
- }
- bool hasNext()override
- {
- if (position_ >= items_->size())
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- private:
- std::vector< MenuItem>* items_;
- int position_;
- };
-
- class DinerMenuIterator :public Iterator {
- public:
- DinerMenuIterator(MenuItem items[],int length) :items_(items), position_(0),length_(length)
- {
-
- }
- virtual MenuItem& next()override
- {
- MenuItem& item = items_[position_];
- position_++;
- return item;
- }
- bool hasNext()override
- {
- if (position_ >= length_)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- private:
- MenuItem* items_;
- int position_;
- int length_;
- };
再实现两个具体的聚合类,针对该例子即实现餐馆菜单和咖啡店菜单(它们都要实现createIterator接口)。
- class Menu {
- public:
- virtual Iterator* createIterator() = 0;
- };
-
- class DinerMenu :public Menu {
- public:
- DinerMenu()
- {
- items_[0] = { "红烧肉","红烧肉,肥瘦相间,香而不腻",32 };
- items_[1] = { "西红柿炒鸡蛋","西红柿炒鸡蛋,国民妈妈菜",15 };
- items_[2] = { "土豆丝","土豆丝,超便宜",12 };
- }
- virtual Iterator* createIterator() override {
- return new DinerMenuIterator(items_,3);
- }
- private:
- MenuItem items_[3];
- };
-
- class CoffeMenu :public Menu {
- public:
- CoffeMenu()
- {
- items_.push_back({ "拿铁","拿铁,好喝",18 });
- items_.push_back({ "卡布奇诺","卡布奇诺卡布奇诺",18 });
- items_.push_back({ "意式浓缩咖啡","意式浓缩咖啡",22 });
- }
- virtual Iterator* createIterator() override {
- return new CoffeMenuIterator(&items_);
- }
- private:
- std::vector< MenuItem> items_;
- };
运行测试效果如下