としおの読書生活

田舎に住む社会人の読書記録を綴ります。 主に小説や新書の内容紹介と感想を書きます。 読書の他にもワイン、紅茶、パソコン関係などの趣味を詰め込んだブログにしたいです。

カテゴリ: C++

3753817_s


本記事では、デザインパターンの名著である結城浩さんの『Javaで学ぶデザインパターン入門』を参考にFactory Method(ファクトリ・メソッド)をC++で実装していきます。




Factory Methodとは


Factory Methodとは、Template Methodをインスタンス生成の場面に適用したものです。

Factoryは、工場という意味です。

要するにFactory Methodとは、インスタンスを生成するための工場のようなデザインパターンだと思ってください。






Factory Methodの登場人物


Factory Methodは以下のようなクラス構成になっています。


factoryMethod


Product


Factory Methodで生成されるインスタンスが持つべきAPIを定義する抽象クラスです。

APIの具体的な実装は、ConcreateProductでクラスで行います。


Creator


Productを生成する抽象クラスです。

Crateorクラスは、Createメソッドで実際に生成する、ConcreteProductについては何も知りませんが、インスタンスを生成するメソッドを作ることでこの問題を解決しています。


ConcreteProduct


ConcreteProductは実際に生成される具体的な製品の役です。


ConcreteCreator


ConcreteCreatorは実際にConcreteProductを生成する役です。



C++による実装


今回は、IDカードを製造する工場というイメージでプログラムをFactory Methodを使用して実装していきます。

クラス構成は以下の通りです。


factoryMethodSample



Productクラス


Productクラスは抽象クラスとして実装していきます。


ヘッダファイル

#ifndef PRODUCT_H
#define PRODUCT_H

class Product{
public:
    virtual void use()=0;
};
#endif // PRODUCT_H



Factoryクラス


createメソッドでは、インスタンスを生成するcreateProductMethodを呼び出し、生成したインスタンスをregisterProductで登録するという処理を行います。

createメソッド以外の具体的な実装は子クラスで行います。今回の場合は、IDCardFactoryクラスが子クラスにあたります。

createMethodは継承先でオーバーライドされないようにfinalを使って宣言します。

ヘッダファイル

#ifndef FACTORY_H
#define FACTORY_H

#include <iostream>

class Product;

class Factory{
public:
    virtual Product* create(std::string ownerfinal;
    virtual Product* createProduct(std::string owner)=0;
    virtual void registerProduct(Product* product)=0;
};
#endif // FACTORY_H


ソースファイル

#include "Factory.h"
#include "Product.h"

Product* Factory::create(std::string owner){
    Productp = createProduct(owner);
    registerProduct(p);
    return p;
}






IDCardクラス


IDCardクラスはProductクラスを継承して作る、具体的な製品のクラスです。

今回は、useメソッドでカードを使い、getOwnerメソッドでカードの持ち主の名前を返します。

ヘッダファイル

#ifndef IDCARD_H
#define IDCARD_H

#include <iostream>
#include "Product.h"

class IDCard : public Product{
public:
    IDCard(std::string owner);
    ~IDCard();
    void use() final;
    std::string getOwner();
    
private:
    std::string m_owner;
};
#endif // IDCARD_H


ソースファイル

#include <iostream>
#include "IDCard.h"

IDCard::IDCard(std::string owner)
m_owner(owner)
{
    std::cout << owner << "のカードを作ります。" << std::endl;
};

IDCard::~IDCard(){};

void IDCard::use(){
    std::cout << m_owner << "のカードを使います。" << std::endl;
}

std::string IDCard::getOwner(){
    return m_owner;
}



IDCardFactoryクラス


IDCardFactoryクラスではcreateProductメソッドの具体的な実装を行っています。

このメソッドではIDCardのインスタンスを生成します。

また、registerOwnerでは生成したインスタンスを登録します。

ヘッダファイル

#ifndef IDCARDFACTORY_H
#define IDCARDFACTORY_H

#include <iostream>
#include <list>
#include "Factory.h"

class Product;

class IDCardFactory : public Factory{
public:
    IDCardFactory();
    ~IDCardFactory();
    Product* createProduct(std::string owner);
    void registerProduct(Product* product);
    std::list<std::stringgetOwners();
    
private:
    std::list<std::stringm_owners;
};
#endif // IDCARD_H


ソースファイル

#include "IDCardFactory.h"
#include "Product.h"
#include "IDCard.h"

IDCardFactory::IDCardFactory(){};

IDCardFactory::~IDCardFactory(){};

Product* IDCardFactory::createProduct(std::string owner){
    return new IDCard(owner);
}

void IDCardFactory::registerProduct(Product* product){
    m_owners.push_back(dynamic_cast<IDCard*>(product)->getOwner());
}

std::list<std::stringIDCardFactory::getOwners(){
    return m_owners;
}



main関数


今まで作成してきたクラスを実際に使っていきます。

ソースファイル

#include "Factory.h"
#include "Product.h"
#include "IDCardFactory.h"
#include "IDCard.h"

int main(void){
    Factory *factory = new IDCardFactory();
    Product *card1 = factory->create("としお");
    Product *card2 = factory->create("ひろし");
    Product *card3 = factory->create("花子");
    card1->use();
    card2->use();
    card3->use();

    return 0;
}


実行結果

としおのカードを作ります。
ひろしのカードを作ります。
花子のカードを作ります。  
としおのカードを使います。
ひろしのカードを使います。
花子のカードを使います。




まとめ


今回は、Template Methodを応用したFactory MethodはTemplate Methodを実装していきました。

似たようなクラスを複数個生成するときなどに便利なデザインパターンでした。

有効に活用できる場面を分析すれば、とても役にたつのでぜひ使ってみてください。









3753817_s

本記事では、デザインパターンの名著である結城浩さんの『Javaで学ぶデザインパターン入門』を参考にテンプレートメソッド(Template Method)をC++で実装していきます。




テンプレートメソッドとは


テンプレートメソッドとは、テンプレートの機能を持つメソッドが親クラスで定義されており、そのメソッドの中の中小メソッドを子クラスで実装する方法です。

例えばRPGの戦闘をテンプレートメソッドを使って実装するとします。

ここでは戦闘は以下の流れで行うとします。
  1. 移動
  2. 攻撃
移動と攻撃の方法はRPGでいう戦士や魔法使いの役職で変わるとします。

そこで、親クラスで先ほどの戦闘の流れを呼ぶためのテンプレートメソッドと移動と攻撃の抽象メソッドを定義します。

そして、子クラスとして戦士クラスや魔法使いクラスを用意して、移動や攻撃の具体的な実装を行っていきます。


このようにスーパークラスで処理の枠組みを定め、サブクラスでその具体的な内容を定めるデザインパターンをテンプレートメソッドパターン(Template Method)と呼びます。



テンプレートメソッドの登場人物


テンプレートメソッドパターンは以下のようなクラス構成になっています。


テンプレートメソッド



AbstractClass(抽象クラス)


AbstractClass役はテンプレートメソッドを実装します。

また、テンプレートメソッドの他にテンプレートメソッドで使用している抽象メソッドも宣言します。

これらの抽象メソッドの具体的な実装は子クラスであるConcreateClassによって行われます。


ConcreateClass(具象クラス)


AbstractClassで宣言された抽象メソッドを具体的に実装する役です。

ここで実装したメソッドがAbstractClassのテンプレートメソッドにより、呼び出されます。





C++による実装


今回は、文字列や文字を5回表示するといったプログラムをテンプレートメソッドパターンを使用して実装していきます。

クラス構成は以下の通りです。


テンプレートメソッド_sample


AbstractDisplayクラス


今回のサンプルプログラムではAbstractDisplayクラスがテンプレートクラスにあたります。

具体的な実装は、displayメソッドだけ行います。

displayメソッドは、「open → printを5回呼ぶ → close」という流れのメソッドになります。

■ヘッダファイル

#ifndef ABSTRACT_DISPLAY_H
#define ABSTRACT_DISPLAY_H

class AbstractDisplay{
public:
    virtual ~AbstractDisplay(){};
    virtual void open()=0;
    virtual void print()=0;
    virtual void close()=0;
    virtual void display() final;
};
#endif // ABSTRACT_DISPLAY_H


■ソースファイル

#include "AbstractDisplay.h"

void AbstractDisplay::display(){
    open();

    for(int i=0i<5; ++i){
        print();
    }

    close();
}



CharDisplayクラス


CharDisplayクラスの各メソッドは以下のような処理を行います。

メソッド名処理
open文字列"<<"を表示する
printコンストラクタで与えられていた文字を表示する
close文字列">>"を表示する


コンストラクタで'H'という文字列が与えられた場合、AbstractDisplayクラスで定義したdisplayメソッドを呼び出すと、

<<HHHHH>>

のように出力されます。


■ヘッダファイル

#ifndef CHAR_DISPLAY_H
#define CHAR_DISPLAY_H

#include "AbstractDisplay.h"

class CharDisplay : public AbstractDisplay{
public:
    CharDisplay(char ch);
    virtual ~CharDisplay();
    void open() override;
    void print() override;
    void close() override;

private:
    char m_ch;
};
#endif // CHAR_DISPLAY_H


■ソースファイル

#include <iostream>
#include "CharDisplay.h"

CharDisplay::CharDisplay(char ch) : m_ch(ch){}

CharDisplay::~CharDisplay(){};

void CharDisplay::open(){
    std::cout << "<<";
}

void CharDisplay::print(){
    std::cout << m_ch;
}
 
void CharDisplay::close(){
    std::cout << ">>" << std::endl;



StringDisplayクラス


StringDisplayクラスの各メソッドは以下のような処理を行います。

メソッド名処理
open文字列"+-----+"を表示する
printコンストラクタで与えられていた文字列を"|"と"|"ではさんで表示する
close文字列"+-----+"を表示する


コンストラクタで'Hello'という文字列が与えられた場合、AbstractDisplayクラスで定義したdisplayメソッドを呼び出すと、

+-----+
|Hello|
|Hello|
|Hello|
|Hello|
|Hello|
+-----+

のように出力されます。


■ヘッダファイル

#ifndef STRING_DISPLAY_H
#define STRING_DISPLAY_H

#include <iostream>
#include "AbstractDisplay.h"

class StringDisplay : public AbstractDisplay{
public:    
    StringDisplay(std::string string);
    virtual ~StringDisplay();
    void open() override;
    void print() override;
    void close() override;

private:
    std::string m_string;
};
#endif // STRING_DISPLAY_H


■ソースファイル

#include <iostream>
#include "StringDisplay.h"

StringDisplay::StringDisplay(std::string string) : m_string(string){}

StringDisplay::~StringDisplay(){};

void StringDisplay::open(){
    std::cout << "+";
    for(int i=0im_string.length(); ++i){
        std::cout << "-";
    }
    std::cout << "+" << std::endl;
}

void StringDisplay::print(){
    std::cout << "|" << m_string << "|" << std::endl;
}

void StringDisplay::close(){
    std::cout << "+";
    for(int i=0im_string.length(); ++i){
        std::cout << "-";
    }
    std::cout << "+" << std::endl;



main関数


今まで作成してきたクラスを実際に使っていきます。


■ソースファイル

#include "AbstractDisplay.h"
#include "CharDisplay.h"
#include "StringDisplay.h"

int main(void){
    AbstractDisplay *d1 = new CharDisplay('H');
    AbstractDisplay *d2 = new StringDisplay("Hello, world.");

    d1->display();
    d2->display();

    delete d1;
    delete d2;

    return 0;
}


■出力結果

<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+





まとめ


テンプレートメソッドパターンでは、テンプレートメソッドでアルゴリズムが定義されているため、子クラス側ではアルゴリズムの流れを気にせずに実装することができました。

テンプレートメソッドでアルゴリズムが共通化されているため、仮にアルゴリズムが間違えていたとしてもテンプレートメソッドクラスだけを修正すればよいので、効率的にプログラムを修正することができます。

プログラムで不具合を少なくするためには、同じ処理を何度も書かないということが大切ですので、せひテンプレートメソッドを使用してプログラムの共通化を行っていきましょう。






3753817_s


本記事では、デザインパターンの名著である結城浩さんの『Javaで学ぶデザインパターン入門』を参考にアダプターパターン(Adapter)をC++で実装してみました。



アダプターパターン(Adapter)とは


アダプターパターンとは、「すでに提供されているもの」と「必要なもの」の間にある「ずれ」を埋めて再利用するためのデザインパターンです。

提供されたAPIなどを手直しするために使われます。

アダプターパターンには、以下の2種類の実装方法があります。

  • クラスによるAdapterパターン(継承を使ったもの)
  • インスタンスによるAdapterパターン(委譲を使ったもの)



クラスによるAdapterパターン(継承を使ったもの)


継承を使ったアダプターパターンは以下のようなクラス図になります。


アダプターパターン_継承



継承のAdapterパターンは、Adaptee役のメソッドがほぼそのまま使えそうなときに使われます。


Target(対象)


今必要となっているメソッドを定める役で、インタフェースとなっています。

C++では、二重継承を非推奨としているため、継承を使ったアダプターパターンでは、Targetは省略されることが多いです。


Client(依頼者)


Target役(省略する際はAdapter役)のメソッドを使って仕事をする役です。

main関数のようなAPIを呼び出すやつだと思ってもらえば大丈夫です。


Adaptee(適合される側)


すでに用意しているメソッドをもっている役です。要するに使いたいAPIを持っているクラスのことですね。

Target役が使いたいAPIがAdaptee役ですでに実装していたら、Adapter役は必要ないんですけどね…。


Adapter


Adapterパターンの主役です。Adaptee役のメソッドを使用して、Targetが求めているメソッドを作り出すやくですね。



インスタンスによるAdapterパターン(委譲を使ったもの)


委譲を使ったアダプターパターンは以下のようなクラス図になります。


アダプターパターン_委譲


委譲を使ったAdapterパターンは、Adaptee役のメソッドを呼び出す前に手を加えたいことが多い場合に有効です。

Adaptee役のメソッドをそのまま呼び出したい場合は、メソッドを再定義する必要があり、コード量が増えるというデメリットもあります。

C++では多重継承(インタフェースと親クラスの同時継承)が推奨されていないこともあり、委譲を使ったAdapterパターンのほうがよくつかわれるような気がします。



C++で継承をつかったアダプターパターンを実装


今回は、与えられた文字列を

(Hello)
*Hello*

のように表示する簡単なものをAdapterパターンを使用して実装していきます。

クラス構成は以下の通りです。


アダプターパターン_サンプル1



二重継承をしないようにTargetクラスを省略した実装にしています。


Bannerクラス


Bannerクラスは、事前に用意されていたAPIクラスだと思ってください。

■ヘッダファイル

#ifndef BANNER_H
#define BANNER_H

#include <iostream>

class Banner{
public:
    Banner(std::string string);
    
    virtual ~Banner();

    void showWithParen();

    void showWithAster();

private:
    std::string m_string;
};
#endif // BANNER_H


■ソースファイル

#include "Banner.h"

Banner::Banner(std::string string)
m_string(string
{

}

Banner::~Banner(){}

void Banner::showWithParen(){
    std::cout << "(" << m_string << ")" << std::endl;
}

void Banner::showWithAster(){
    std::cout << "*" << m_string << "*" << std::endl;
}



PrintBannerクラス


PrintBannerクラスがアダプターの役目を果たします。

今回はm単純な実装のためBannerクラスのAPIをそのまま呼び出すだけになります。

■ヘッダファイル

#ifndef PRINTBANNER_H
#define PRINTBANNER_H

#include "Banner.h"

class PrintBanner : public Banner{
public:
    PrintBanner(std::string string);

    ~PrintBanner();

    void printWeak();
    
    void printStrong();

};
#endif // PRINTBANNER_H


■ソースファイル

#include "PrintBanner.h"

PrintBanner::PrintBanner(std::string string)
Banner(string)
{

}

PrintBanner::~PrintBanner(){}

void PrintBanner::printWeak(){
    showWithParen();
}

void PrintBanner::printStrong(){
    showWithAster();
}



main関数


ここまで作ってきたAdapter役のPrintBannerクラスを使って、"Adapter Sample"という文字列を括弧付きと*ではさんで表示します。

■ソースファイル

#include "PrintBanner.h"

int main(void){
    PrintBanner printBanner("Adapter Sample");
    
    printBanner.printWeak();
    printBanner.printStrong();

    return 0;
}


■実行結果

(Adapter Sample)
*Adapter Sample*






C++で委譲をつかったアダプターパターンを実装


委譲を使ったアダプターパターンのサンプルプログラムのクラス構成は以下の通りです。


アダプターパターン_サンプル2


Bannerクラス


■ヘッダファイル

#ifndef BANNER_H
#define BANNER_H

#include <iostream>

class Banner{
public:
    Banner(std::string string);
    
    virtual ~Banner();

    void showWithParen();

    void showWithAster();

private:
    std::string m_string;
};
#endif // BANNER_H


■ソースファイル

#include "Banner.h"

Banner::Banner(std::string string)
m_string(string
{

}

Banner::~Banner(){}

void Banner::showWithParen(){
    std::cout << "(" << m_string << ")" << std::endl;
}

void Banner::showWithAster(){
    std::cout << "*" << m_string << "*" << std::endl;
}



Printクラス


JavaでいうインタフェースはC++では抽象クラスとして実装していきます。

■ヘッダファイル

#ifndef PRINT_H
#define PRINT_H

class Print{
protected:
    virtual ~Print(){};

    virtual void printWeak()=0;
    
    virtual void printStrong()=0;
};
#endif // PRINT_H



PrintBannerクラス


■ヘッダファイル

#ifndef PRINTBANNER_H
#define PRINTBANNER_H

#include <iostream>
#include "Print.h"

class Banner;

class PrintBanner : public Print{
public:
    PrintBanner(std::string string);

    virtual ~PrintBanner();

    void printWeak() override;
    
    void printStrong() override;

private:
    Banner *m_Banner;

};
#endif // PRINTBANNER_H



■ソースファイル

#include "PrintBanner.h"
#include "Banner.h"
#include "Print.h"

PrintBanner::PrintBanner(std::string string)
{
    m_Banner = new Banner(string);
}

PrintBanner::~PrintBanner(){
    delete m_Banner;
}

void PrintBanner::printWeak(){
    m_Banner->showWithParen();
}

void PrintBanner::printStrong(){
    m_Banner->showWithAster();
}



main関数


■ソースファイル

#include "PrintBanner.h"

int main(void){
    PrintBanner printBanner("Adapter Sample");
    
    printBanner.printWeak();
    printBanner.printStrong();

    return 0;
}


■実行結果

(Adapter Sample)
*Adapter Sample*




まとめ


個人開発ではなく、会社などで集団で開発する場合は、いきなりAPIの使用が変更することが多々あります。

いきなりの変更でも困らないようにアダプターパターンを使って、修正箇所ができる限り少なくなるようなプログラムを作っていきましょう。







647498_s


windowsでtimeGetTime関数を使用して時間計測のプログラムを作ろうとしたろころ、コンパイルの段階で

undefined reference to `__imp_timeGetTime'

というエラーが現れて少し苦戦したので対処法を残します。



コンパイルしたいコード


コンパイルしたいと思ったコードは、timeGettime関数を使って実行時間を計測するコードです。
ソースファイル名はsample.cppとします。

#include <iostream>
#include <windows.h>

int main(void){
    DWORD start_tm = timeGetTime();

    for(int i=0i<1000; ++i){
        for(int j = 0j<10000000; ++j){}
    }

    DWORD end_tm = timeGetTime();
    std::cout << end_tm - start_tm << std::endl

    return 0;
}



コンパイル失敗


何も考えずにコンパイルすると…

g++ -o sample sample.cpp

以下のメッセージがでて、そんなもの定義していないぞと怒られてしまいます。

undefined reference to `__imp_timeGetTime'




`__imp_timeGetTime'の対処法


エラーについて調べてみるとtimeGetTime関数を使用するためにはwinmm.libをリンクする必要があるということが分かりました。

なのでコンパイルコマンドを以下に直すと

g++ -o sample sample.cpp -lwinmm

コンパイルが成功しました。



まとめ


windows.hで定義しているAPIを使用するためには、必要に応じてライブラリをリンクする必要があるということが分かりました。

普段Visual Studioばかりつかっていたら、そのあたりをそこまで意識しなくていいので気をつけなければなりませんね…。




3753817_s


本記事では、デザインパターンの名著である結城浩さんの『Javaで学ぶデザインパターン入門』を参考にIteratorパターンをC++で実装してみました。

C++のSTLでは、すでにイテレータは実装されているので、改めて実装することはないのですが、今回は設計思想を学ぶために実装していきます。




イテレータパターンとは


イテレータパターンとは、何かが同じようなものがたくさん集まっているときに、それを順番に指し示していき全体をスキャンする処理を行うためのものです。

例えば、本棚から本の名前を順番に表示するといったときに使うことができます。

ちなみにイテレータ(Iterator)とは、日本語で繰り返すという意味です。



イテレータパターンの登場人物


イテレータパターンは以下のようなクラス構成になっています。



イテレータパターン


Iterator


要素を順番にスキャンしていくAPIを定める役。

次の要素を得るためのhasNextメソッドや次の要素を得るためのnextメソッドなどを定めます。


ConcreateIterator


Iterator役が定めたAPIを実際に実装する役。

この役はスキャンをするのに必要な情報をもっている必要があります。


Aggregate


Iterator役をCreateするためのAPIを定める役。

今回の場合、IteratorがIteratorを作成する、メンバ関数になります。


ConcreateAggregate


Aggregate役が定めたAPIを実際に実装する役。

ConcreateAggregateが自信をサーチしてもらうために必要な具体的なIteratorであるConcreateIteratorを作成します。





C++による実装


今回は、本棚から本の名前を順番に表示するといったイテレータパターンを使ったサンプルプログラムを実装していきます。

クラス構成は以下の通りです。


イテレータパターン (2)



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++では既存の実装があるため、実装する必要のないイテレータパターンを作成することでイテレータの動きが以前より分かるようになりよかったです。

今後も勉強がてらデザインパターンを実装していきます。







↑このページのトップヘ