LoginSignup
0
0

生成に関するパターン(FactoryMethodパターン)

Last updated at Posted at 2023-10-16

背景

デザインパターンには大きく分けて以下のようなパターンがあるが、

  • 生成に関するパターン
  • 構造に関するパターン
  • 振る舞いに関するパターン

よく使うのは振る舞いに関するパターンなのでそちらを重点的に見ていたが、
生成に関するパターンもしっかり理解しておきたい。

FactoryMethodパターンの適用可能性:

  1. クラスが、生成しなければならないオブジェクトのクラスを事前に知ることができない場合
  2. サブクラス化により、生成するオブジェクトを特定化する場合
  3. クラスが責任をいくつかのサブクラスの中の一つに委譲するときに、どのサブクラスに委譲するのかに関する知識を局所化したい場合

参考文献

題材

肩トレするときに、どんなジムにどんなマシン・器具があるか、
また、高重量・低repトレーニングをしたい場合と、低重量・高repトレーニングをしたい場合で、
同じ肩トレであってもメニューは変わってくると思う。

ただ、具体的なジム・マシンの情報は切り離し、できるだけ抽象的な情報を使って実現させる方法を考えてみた。

パターン適用前

純粋仮想関数pressを持つクラス「Shoulder」を継承する形で
「dumbbell」クラスと「smith」クラスがある。
これらのクラスをMainから直接生成するのが最初の例。

スクリーンショット 2023-10-16 15.47.07.png

サンプルコード

Shoulder.hpp
#pragma once
#include <iostream>
using namespace std;

class Shoulder{
    public:
        virtual void press() = 0;
        Shoulder(){}
        ~Shoulder(){}
};
Concrete.hpp
#pragma once
#include <iostream>
#include "Shoulder.hpp"
using namespace std;

class dumbbell : public Shoulder{
    public:
        void press();
        dumbbell(){}
        ~dumbbell(){}
};

class smith : public Shoulder{
    public:
        void press();
        smith(){}
        ~smith(){}
};
Concrete.cpp
#pragma once
#include <iostream>
#include "Concrete.hpp"

/* Concrete */
void dumbbell::press(){
    cout << "dumbbell press!\r\n";
}

void smith::press(){
    cout << "smith press!!\r\n";
}
main.cpp
#include <iostream>
#include "Shoulder.hpp"
#include "Concrete.hpp"

#define HEAVEY 1
#define LOW 2

int main(){
    Shoulder *anytimefitness;
    Shoulder *goldgym;
    int kibun;
    cout << "How do you feel?(1:HEAVEY, 2:LOW)\r\n";
    cin >> kibun;

    if (kibun == LOW){
        /* 低負荷の気分ならエニタイムに行き、ダンベルしかないのでダンベルで肩トレをする */
        anytimefitness = new dumbbell();
        anytimefitness->press();
    }else if(kibun == HEAVEY){
        /* 高負荷の気分なら、ゴールドジムに行きスミスマシンを使って肩トレをする */
        goldgym = new smith();
        goldgym->press();
    }
}
shota@ before % ./a.out                  
How do you feel?(1:HEAVEY, 2:LOW)
1
smith press!!
shota@ before % ./a.out
How do you feel?(1:HEAVEY, 2:LOW)
2
dumbbell press!

適用前の問題

  • Mainと具象クラス(dumbbell、smith)が依存関係にある
  • 具象クラスの生成に必要なことがあれば、いちいちMainを修正しないといけない
  • 具象クラスの情報を知っている人が局所化できていない

パターン適用後

適用前はMainと直接依存関係があった箇所を、ファクトリークラス「gymFactory」を経由させるように改善した。
具象クラスの生成は、このファクトリークラスを継承したサブクラスに責務を持たせる。
今回の例だと、広島にあるジムを知っている「hiroshimaGym」というファクトリークラスの具象クラスを用意した。
そうすることによって、Mainは具体的なクラスを知らなくても良くなり、依存関係を局所化することができる。

スクリーンショット 2023-10-16 15.47.21.png

サンプルコード

gymFactory.hpp
#pragma once
#include <iostream>
#include "Shoulder.hpp"
using namespace std;

class gymFactory{
    public:
        virtual Shoulder *gymCreate(int kibun) = 0;
        gymFactory(){}
        ~gymFactory(){}
};
hiroshimaGym.hpp
#pragma once
#include <iostream>
#include "gymFactory.hpp"

#define HEAVEY 1
#define LOW 2

/* Concrete */

class hiroshimaGym : public gymFactory{
    public:
        Shoulder *gymCreate(int kibun);
        hiroshimaGym(){}
        ~hiroshimaGym(){}
};
hiroshimaGym.cpp
#pragma once
#include <iostream>
#include "gymFactory.hpp"
#include "hiroshimaGym.hpp"
#include "Concrete.hpp"

/* Concrete */
Shoulder *hiroshimaGym::gymCreate(int kibun){
    if (kibun == LOW){
        /* 低負荷の気分ならエニタイムに行き、ダンベルしかないのでダンベルで肩トレをする */
        return new dumbbell();
    }else if(kibun == HEAVEY){
        /* 高負荷の気分なら、ゴールドジムに行きスミスマシンを使って肩トレをする */
        return new smith();
    }
}
main.cpp
#include <iostream>
#include "Shoulder.hpp"
#include "Concrete.hpp"
#include "hiroshimaGym.hpp"

int main(){
    /* 具象クラスは知らなくて良い。*/
    /* Shoulder *anytimefitness; */
    /* Shoulder *goldgym; */

    hiroshimaGym _hiroshimaGym; /* ファクトリメソッドの具象クラスは変える必要あり */
    Shoulder *_Shoulder;
    int kibun;
    cout << "How do you feel?(1:HEAVEY, 2:LOW)\r\n";
    cin >> kibun;

    _Shoulder = _hiroshimaGym.gymCreate(kibun);

    /* 具体的なジム・器具は知らんけど、ショルダープレスができる! */
    _Shoulder->press();
}
shota@ after % ./a.out                                   
How do you feel?(1:HEAVEY, 2:LOW)
1
smith press!!
shota@ after % ./a.out
How do you feel?(1:HEAVEY, 2:LOW)
2
dumbbell press!

結論

今回の例だと、適用可能性の「1.クラスが、生成しなければならないオブジェクトのクラスを事前に知ることができない場合」「3.クラスが責任をいくつかのサブクラスの中の一つに委譲するときに、どのサブクラスに委譲するのかに関する知識を局所化したい場合」に該当するのではないかと思った。
先にMainを作り込みたいとか、Mainは大きくなりそうなので、なるべく具象クラスとの依存関係は疎にして、
変更する必要のない部品にしておきたい、というときに使うと良いのかと感じた。

こうしておけば、広島でない場所に転勤した場合などに、生成部分を切り出したFactoryのクラスを修正すれば良く、影響範囲を狭くすることができるかと思う。

逆に、転勤しないことがわかっている(具象クラスが変わる可能性が低い)とき、
生成に伴うコスト(生成時に何か処理をしないといけないときなど)が変わらない場合は、無理に使う必要はないと思う、Mainから呼んでしまっても大差ないかなと。
使いどきを見極めるのが難しいので、考え方がおかしい・もっとこう考えた方が良い・などアドバイスあればどなたか教えてください。

と、ここまで記載してAbstract Factoryパターンとの違いが気になった。
以前Abstract Factoryパターンの例も作って見たものと見比べると、「同じじゃね!?」と思ってしまった。。
復習した結果、二つのパターンの違いは以下と認識した。

  • FactoryMethodパターンは、「メソッド」に単一のオブジェクト生成の責務を委譲する(Template Methodと同じ)
  • Abstract Factoryパターンは、「オブジェクト」に複数のオブジェクト生成の責務を委譲する

つまり、単一のオブジェクトを作りたいが、中身はよくわからん場合、TemplateMethodパターンと同じようにスーパークラスにメソッドを用意しておけば良い。

ちなみに、筋肥大のためにはプレス系で重量を扱うことが大事ということで、ダンベルであっても重量を扱うように心がけている。
トレーニングの最初はプレス系で重量を扱い、最後はレイズ系でしっかり追い込む。
肩は追い込まないとなかなか筋肉痛がこないので、翌日筋肉痛が来たら嬉しいですよね。
みなさんも筋トレライフを楽しんでいきましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0