本記事では、デザインパターンの名著である結城浩さんの『Javaで学ぶデザインパターン入門』を参考にIteratorパターンをC++で実装してみました。
C++のSTLでは、すでにイテレータは実装されているので、改めて実装することはないのですが、今回は設計思想を学ぶために実装していきます。
【目次】
〇イテレータパターンとは
〇イテレータパターンの登場人物
・Iterator
・ConcreateIterator
・Aggregate
・ConcreateAggregate
〇C++による実装
・Aggregateインタフェース
・Iteratorインタフェース
・Bookクラス
・BookShelfクラス
・BookShelfIteratorクラス
・main関数
〇まとめ
イテレータパターンとは
イテレータパターンとは、何かが同じようなものがたくさん集まっているときに、それを順番に指し示していき全体をスキャンする処理を行うためのものです。
例えば、本棚から本の名前を順番に表示するといったときに使うことができます。
ちなみにイテレータ(Iterator)とは、日本語で繰り返すという意味です。
イテレータパターンの登場人物
イテレータパターンは以下のようなクラス構成になっています。
Iterator
要素を順番にスキャンしていくAPIを定める役。
次の要素を得るためのhasNextメソッドや次の要素を得るためのnextメソッドなどを定めます。
ConcreateIterator
Iterator役が定めたAPIを実際に実装する役。
この役はスキャンをするのに必要な情報をもっている必要があります。
Aggregate
Iterator役をCreateするためのAPIを定める役。
今回の場合、IteratorがIteratorを作成する、メンバ関数になります。
ConcreateAggregate
Aggregate役が定めたAPIを実際に実装する役。
ConcreateAggregateが自信をサーチしてもらうために必要な具体的なIteratorであるConcreateIteratorを作成します。
C++による実装
今回は、本棚から本の名前を順番に表示するといったイテレータパターンを使ったサンプルプログラムを実装していきます。
クラス構成は以下の通りです。
Aggregateインタフェース
JavaでいうインタフェースはC++では抽象クラスとして実装していきます。
#ifndef AGGREGATE_H
#define AGGREGATE_H
#include <iostream>
class Iterator;
class Aggregate{
public:
virtual Iterator* iterator() = 0;
virtual ~Aggregate(){};
};
#endif // AGGREGATE_H
Iteratorインタフェース
こちらもAggregateインタフェースと同様に抽象クラスでの実装です。
#ifndef ITERATOR_H
#define ITERATOR_H
class Book;
class Iterator{
public:
Iterator(){};
virtual bool hasNext() = 0;
virtual Book next() = 0;
virtual ~Iterator(){};
};
#endif // ITERATOR
Bookクラス
Bookクラスは本を表すクラスです。
今回の場合できることは、本の名前を設定することと取得することだけです。
■ヘッダーファイル
#ifndef BOOK_H
#define BOOK_H
#include <iostream>
class Book{
public:
Book();
virtual ~Book();
void setName(std::string name);
std::string getName();
private:
std::string m_name;
};
#endif // BOOK_H
■ソースファイル
#include "Book.h"
Book::Book()
: m_name("")
{
}
Book::~Book(){
}
void Book::setName(std::string name){
m_name = name;
}
std::string Book::getName(){
return m_name;
}
BookShelfクラス
BookShelfクラスは本棚を表すクラスです。
こちらのクラスが先ほどのBookクラスの集合体になります。
■ヘッダーファイル
#ifndef BOOKSHELF_H
#define BOOKSHELF_H
#include <iostream>
#include "Aggregate.h"
class Book;
class Iterator;
class BookShelf : public Aggregate{
public:
BookShelf(int maxsize);
virtual ~BookShelf();
Book getBookAt(int index);
void appendBook(Book book);
int getLength();
Iterator* iterator();
private:
Book *m_books;
int m_last;
};
#endif // BOOKSHELF_H
■ソースファイル
#include <iostream>
#include <unistd.h>
#include "BookShelf.h"
#include "Book.h"
#include "BookShelfIterator.h"
BookShelf::BookShelf(int maxsize)
: m_last(0)
{
m_books = new Book[maxsize];
}
BookShelf::~BookShelf(){
delete m_books;
}
Book BookShelf::getBookAt(int index){
return m_books[index];
}
void BookShelf::appendBook(Book book){
m_books[m_last] = book;
m_last++;
}
int BookShelf::getLength(){
return m_last;
}
Iterator* BookShelf::iterator(){
return new BookShelfIterator(this);
}
BookShelfIteratorクラス
BookShekfクラスのスキャンを行うクラスです。
■ヘッダーファイル
#ifndef BOOKSHELFITERATOR_H
#define BOOKSHELFITERATOR_H
#include "Iterator.h"
#include "BookShelf.h"
//class BookShelf;
class Book;
class BookShelfIterator : public Iterator{
public:
BookShelfIterator(BookShelf* bookShelf);
virtual ~BookShelfIterator();
bool hasNext() override;
Book next() override ;
private:
BookShelf *m_bookShelf;
int m_index;
};
#endif // BOOKSHELFITERATOR_H
■ソースファイル
#include "BookShelfIterator.h"
#include "BookShelf.h"
#include "Book.h"
BookShelfIterator::BookShelfIterator(BookShelf *bookShelf)
: m_index(0)
{
m_bookShelf = bookShelf;
}
BookShelfIterator::~BookShelfIterator(){
}
bool BookShelfIterator::hasNext(){
if (m_index < m_bookShelf->getLength()){
return true;
}
else{
return false;
}
}
Book BookShelfIterator::next(){
Book book = m_bookShelf->getBookAt(m_index);
m_index++;
return book;
}
main関数
今まで作成してきたクラスを使って実際に小さな本棚を作っていきます。
■ソースファイル
#include <iostream>
#include "BookShelf.h"
#include "Book.h"
#include "BookShelfIterator.h"
int main(void){
BookShelf bookShelf(4);
Book book1;
book1.setName("Around the World in 80 days");
bookShelf.appendBook(book1);
Book book2;
book2.setName("Bible");
bookShelf.appendBook(book2);
Book book3;
book3.setName("Cinderella");
bookShelf.appendBook(book3);
Book book4;
book4.setName("Daddy-Long-Legs");
bookShelf.appendBook(book4);
Iterator *it = bookShelf.iterator();
while(it->hasNext()){
Book book = it->next();
std::cout << book.getName() << std::endl;
}
delete it;
return 0;
}
■実行結果
Around the World in 80 days
Bible
Cinderella
Daddy-Long-Legs
まとめ
C++では既存の実装があるため、実装する必要のないイテレータパターンを作成することでイテレータの動きが以前より分かるようになりよかったです。
今後も勉強がてらデザインパターンを実装していきます。