としおの読書生活

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

2021年03月

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.|
+-------------+





まとめ


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

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

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






PXL_20210227_032531375


瀬尾まいこさんの『君が夏を走らせる』を読みました。

たった一ヶ月の短い夏を描いている作品ですが、二歳に満たない鈴香と高校生の大田君の成長を見ることができ、読んでいるだけで癒されるし、ところどころ感動するシーンもある良作でした。

以下、あらすじと感想になります。



『君が夏を走らせる』のあらすじ


中学三年生のころ駅伝に出場して以来、何かに夢中になることの楽しさをしった大田。

高校生になったら不良をやめて堅実に生きようと思っていた矢先、大田は目指していた高校の受験に失敗し、地域の不良が集まる学校に進学することになった。

周りとの温度の違いもあり、大田は徐々に高校から足が遠のいていった。

新しく夢中になることができるものを見つけることができず、日々をやり過ごしていた大田のもとに、中学のときに仲良くしていた先輩から一本の電話が入った。

その電話の内容は、妻が入院することになったので一ヶ月ほど、一歳の娘である鈴香の子守をしてくれないかというものだった。

高校生の自分が人の子守なんてすることができないと思った大田は、先輩からの頼みを断ろうとしたが、断り切れず一か月の子守を引き受けることにした。

子守を初めたはいいが、鈴香は泣き止まないし、ご飯も大人しく食べてくれない。

小さな鈴香に振り回された大田は、振り回されながらも鈴香とともに過ごす時間を幸せに過ごし、徐々に何かに夢中になるということを思いだすことになる。

二度と戻らぬ記憶に温かい、涙あふれるひと夏の奮闘記。



感想(ネタバレあり)


鈴香ちゃんと大田君の成長を描いているとても良い作品でした。

鈴香ちゃんの世話を頼まれたばかりの頃の大田君は、断り切れなくてしかたなく世話をしてあげている感がすごく強かったです。しかし、時間がたつにつれて鈴香ちゃんとともに過ごすことの楽しさを知った大田君を見ていると心がほっかりしました。

二人が仲良くなるにつれて別れの時が少しずつ迫っている様子が切なく感じました。

もちろん鈴香は先輩の娘なので二度と会えなくなるというわけではないのですが、二人で今までのような濃密な時間を過ごすことはもうないんでしょうね…。


大田君の成長


私は『君が夏を走らせる』のテーマは”成長”だと思っています。

一方大田君は、高校受験に失敗してしまい、物語の序盤では自分が成長することはないと感じながら自堕落な生活を送っていて、自分が成長することはできないと思い込んでいるような様子でした。

しかし、鈴香との出会いをきっかけに大田君は、どんな経験からでも人は成長できるということをしります。

  • 大田君が話しているのを聞いて「すげー」という言葉を使えるようになる
  • 大田君のまねをしておもちゃのフライパンで料理をする
  • 大田君が買ってきてくれた積み木で遊ぶ鈴香

鈴香は、まだ二歳なのでどんな些細な経験でもすべてを糧にして成長していきます。
その様は、大田君を通して学習をしているとも言えます。

そんな鈴香の成長を見てきた大田君はラストシーンで以下のように思います。

記憶のどこにも残っちゃいないけれど、俺にも鈴香と同じように、すべてが光り輝いて見えたときがあったのだ。もちろん、今だってすべてが光を失っているわけじゃない。こんなふうに俺に「がんばって」と声を送ってくれるやつがいるのだから。俺はまだ十六歳だ。「もう十分」なんて、言ってる場合じゃない。
『君が夏を走らせる』より

上記の大田君の想いから、大田君が閉じこもっていた殻をやぶって、これからも成長していくだろうということが分かりますね。





子育ての難しさ


大田君が鈴香に苦戦する様子や公園で出会ったママさんたちの様子を見ていると、子育てって大変なんだなと感じました。

それと同時に子育てには、子どもに好きなことをさせてあげることと、親が子どもが学ぶことができる環境を整えてあげることが大切なんだと本作から学びました。

大田君は基本的には鈴香ちゃんのしたいことに対して否定することはなく、適切な遊び方で遊ばせてあげていました。

また、大田君と鈴香ちゃんの様子を見ていると将来自分に子どもができたら、レトルト食品ばかり与えるのではなく、大田君のように子どもが満足するような料理を作ってあげたいなとも感じました。

その他にも鈴香ちゃんは昼食後必ず大田君に買ってきてもらった絵本を読んでもらうのが習慣だという場面がありました。

このシーンを読んで子どもが自ら絵本を読んでもらいだがるような環境を作りが必要だと感じさせられました。

とりあえず、将来子育てをするときは大田君の子育てを参考にすればなかなか良い子が育つような気がしました。



まとめ


『そして、バトンは渡された』を読んだことをきっかけに『君が夏を走らせる』を読みましたがとても面白く大満足でした。

大田君や鈴香のように常に自分は成長することができるということを忘れずに今後の人生を歩んでいきたいです。

余談になりますが、あとがきで、本作には『あと少し、もう少し』という前作があったということを知りました。

こちらでは、中学時代の駅伝のときの大田君の様子などを描いているみたいなので是非読んでみようと思います。






↑このページのトップヘ