5 minutes note

日記、技術メモ、勉強記録など。

【C++】デザインパターン:Iteratorパターン

集合の各要素に順次アクセスする仕組みを作るパターン。

クラスに集合の役割をもたせ、イテレータを提供させることで、順次アクセス可能にする。

集合の要素を直接参照するのではなく、イテレータを介してアクセスすることで、集合の具体的な実装方法(配列とかListとかVectorとか)に依らず要素を数え上げられるようになることがメリット。

イテレータの振る舞いも好きに実装できるので、例えばある条件に適合する要素だけイテレーションする(部分集合のイテレーション)、みたいなことができると思う。

ソースコード

C++11で記述。

本棚クラスを本クラスの集合として扱う。

本棚クラスにイテレータを提供させ、イテレータを介して本クラスのメンバ関数にアクセスしている。

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Iterator;
class Book;

// クラスをイテレータをもつ集合として扱うためのインタフェース
class Aggregate {
public:
    virtual std::unique_ptr<Iterator> GetIterator() = 0;
};

// イテレータとしての役割を与えるインタフェース
class Iterator {
public:
    virtual ~Iterator(){};
    virtual bool HasNext() = 0;
    virtual void* Next() = 0;
};

// 本を表すクラス (集合の要素)
class Book {
public:
    void SetName(const std::string name) {
        this->name_ = name;
    }

    std::string GetName() {
        return this->name_;
    }
private:
    std::string name_;
};

// 本棚を表すクラス (集合)
class BookShelf : public Aggregate {
public:
    BookShelf(const int maxSize)
    : bookCount_(0) {
        books_.resize(maxSize);
    }

    Book* GetBookAt(const int index) {
        return &(this->books_[index]);
    }

    void AppendBook(const Book book) {
        this->books_[bookCount_] = book;
        bookCount_++;
    }

    int GetLength() {
        return bookCount_;
    }

    std::unique_ptr<Iterator> GetIterator() override;

private:
    std::vector<Book> books_;
    int bookCount_;
};

// イテレータ
class BookShelfIterator : public Iterator {
public:
    BookShelfIterator(const BookShelf bookShelf) :
        bookShelf_(bookShelf), index_(0) {}

    bool HasNext() override {
        if(index_ < bookShelf_.GetLength()) {
            return true;
        } else {
            return false;
        }
    }

    void* Next() override {
        Book *pBook = bookShelf_.GetBookAt(index_);
        index_++;
        return pBook;
    }

private:
    BookShelf bookShelf_;
    int index_;
};

std::unique_ptr<Iterator> BookShelf::GetIterator() {
    return std::unique_ptr<BookShelfIterator>(new BookShelfIterator(*this));
}

int main() {

    Book book1;
    Book book2;
    Book book3;
    Book book4;
    book1.SetName("Inherit the Stars");
    book2.SetName("2001: A Space Odyssey");
    book3.SetName("The Door into Summer");
    book4.SetName("Do Androids Dream of Electric Sheep?");

    BookShelf bookShelf(4);
    bookShelf.AppendBook(book1);
    bookShelf.AppendBook(book2);
    bookShelf.AppendBook(book3);
    bookShelf.AppendBook(book4);

    auto it = bookShelf.GetIterator();

    while(it->HasNext()) {
        Book *book = reinterpret_cast<Book*>(it->Next());
        std::cout << book->GetName() << std::endl;
    }
}

参考

以下の書籍、Webページを参考にしました。

myenigma.hatenablog.com