デザインパターン・Factory(ファクトリー)パターンの実装をご紹介します。
Factoryパターンを使用することで、ソースコードの管理がしやすくなり、機能拡張に強い設計ができます。
Factoryパターンとは
Factory(ファクトリー)パターンとは、下位クラスの各オブジェクトの生成を1つのクラス(Factoryクラス)で実施するデザインパターンです。
用途・どんな時に使えるの?
主に以下のメリットを得るために使用します。
- コード上管理しやすい
- 機能拡張に強く柔軟に対応できる
Factoryクラスを使わない場合オブジェクトを生成する場所があいまいになり、どこで生成しているのか分からなくなります。Factoryクラスを使えば1つのクラスで管理できるためこの問題は解消されます。ただFactoryクラスで管理するオブジェクトは一貫性があるものが望ましいです。次章で説明します。
クラス図
contents_factoryクラスのcreateでcontents_a, contents_b, contents_cオブジェクトを生成します。オブジェクトの生成はcontents_factoryクラスだけで意識します。
機能拡張や削除時のコード変更に対してcontents_factoryクラスだけで抑えることができるため、変更工数を少なくすることができます。
またこの設計であれば機能拡張の際に共通部品として抽象クラスを使用することができます。
派生クラスの拡張
contents_frameworkは抽象クラスであり、contents_a, contents_b, contents_cは派生クラスです。
抽象クラスでは共通の処理、派生クラスでは固有の処理をします。
乗り物で例えると以下のようになります。
抽象クラス:乗り物
→ 「移動する」は共通の処理.
派生クラス:自動車、飛行機、電車
→ 「ペダルを漕ぐ」「操縦機を操作する」「ドアを開く」などは固有の処理.
例えば「戦車」が必要になった場合、「戦車」を派生クラスとして拡張します。「戦車」は「乗り物」クラスを継承しているため、「乗り物」の「移動する」を使用することができます。使用者は今まで通り「乗り物」が公開する「移動する」のIFを使用することで「戦車」を使用することができます。
言い換えるとこれは、使用者は抽象クラスのIFの実装を変更することなく今まで通りに抽象クラスのIFを呼ぶだけで拡張した派生クラスを使用することができます。
派生クラスの一貫性
設計上の注意点としては派生クラスの一貫性を考慮する事です。
これがもし自動車・パソコン・リンゴのような一貫性のないオブジェクトを派生クラスとした場合、パソコンが「移動する」のIFを持つことは意味的におかしいため、一貫性のあるオブジェクトを派生クラスとして管理することを推奨します。
実装の解説
C++で実装したサンプルコードの解説をします。
このサンプルコードはmain関数からFactoryパターンよりcontents_a, contents_b, contents_cのオブジェクトを生成し、それぞれコールします。
使用者であるmain関数では、contents_factoryのcreateでポインタを取得します。contents_factoryクラスではnewすることでオブジェクトを生成し、そのポインタを返すようにしています。
その後、抽象クラスであるcontents_frameworkへ設定し、requestします。
contents_frameworkのact_requestは派生クラスの処理を保証させるため、純粋仮想関数で定義します。requestは外部公開用であり、処理の中でact_requestを呼びオーバーライドで派生クラスが呼ばれます。
■ ExFactory.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include "stdafx.h" #include "logout.h" #include "contents_factory.h" #include "contents_framework.h" #include "define_common.h" using namespace std; using namespace hukusuke; /* main function. */ int main(void) { LOG_OUTPUT("Start."); // Create factory instance. contents_factory factory; // Create contents super class. contents_framework* contents_a = factory.create(REQUEST_ID_A); contents_framework* contents_b = factory.create(REQUEST_ID_B); contents_framework* contents_c = factory.create(REQUEST_ID_C); // Request to contents sub class. if (NULL != contents_a) { contents_a->request(); } else {} if (NULL != contents_b) { contents_b->request(); } else {} if (NULL != contents_c) { contents_c->request(); } else {} // Delete. if (NULL != contents_a) { delete contents_a; contents_a = NULL; } else {} if (NULL != contents_b) { delete contents_b; contents_b = NULL; } else {} if (NULL != contents_c) { delete contents_c; contents_c = NULL; } else {} LOG_OUTPUT("Terminate."); return 0; } |
■ contents_factory.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include "contents_factory.h" namespace hukusuke { contents_factory::contents_factory(){} contents_factory::~contents_factory(){} /* Get contents class*/ contents_framework* contents_factory::create(const REQUEST_ID request_id) { contents_framework* contents = NULL; switch (request_id) { case REQUEST_ID_A: contents = new contents_a(); break; // Create contents_a instance. case REQUEST_ID_B: contents = new contents_b(); break; // Create contents_b instance. case REQUEST_ID_C: contents = new contents_c(); break; // Create contents_c instance. default: LOG_OUTPUT("Error. Not support request id."); break; } return contents; } } |
■ contents_factory.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#pragma once #include "contents_framework.h" #include "logout.h" #include "define_common.h" using namespace std; namespace hukusuke { class contents_factory { public: contents_factory(); ~contents_factory(); // Get contents class. contents_framework* create( const REQUEST_ID request_id ); }; } |
■ contents_framework.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include "contents_framework.h" namespace hukusuke { /*--------------------------------*/ /* contents_framework. */ /*--------------------------------*/ contents_framework::contents_framework(){} contents_framework::~contents_framework(){} /* Public method*/ void contents_framework::request() { this->act_request(); } /* Virtual method*/ void contents_framework::act_request() { LOG_OUTPUT("Not override."); } /*--------------------------------*/ /* contents_a. */ /*--------------------------------*/ contents_a::contents_a() {} contents_a::~contents_a() {} /* Override method*/ void contents_a::act_request() { LOG_OUTPUT("Contents A."); } /*--------------------------------*/ /* contents_b. */ /*--------------------------------*/ contents_b::contents_b() {} contents_b::~contents_b() {} /* Override method*/ void contents_b::act_request() { LOG_OUTPUT("Contents B."); } /*--------------------------------*/ /* contents_c. */ /*--------------------------------*/ contents_c::contents_c() {} contents_c::~contents_c() {} /* Override method*/ void contents_c::act_request() { LOG_OUTPUT("Contents C."); } } |
■ contents_framework.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#pragma once #include "define_common.h" #include "logout.h" using namespace std; namespace hukusuke { /*--------------------------------*/ /* contents_framework. */ /*--------------------------------*/ class contents_framework { public: contents_framework(); ~contents_framework(); void request(); // 外部公開. protected: virtual void act_request() = 0; // 純粋仮想関数. }; /*--------------------------------*/ /* contents_a. */ /*--------------------------------*/ class contents_a : public contents_framework { public: contents_a(); ~contents_a(); private: void act_request(); // 処理. }; /*--------------------------------*/ /* contents_b. */ /*--------------------------------*/ class contents_b : public contents_framework { public: contents_b(); ~contents_b(); private: void act_request(); // 処理. }; /*--------------------------------*/ /* contents_c. */ /*--------------------------------*/ class contents_c : public contents_framework { public: contents_c(); ~contents_c(); private: void act_request(); // 処理. }; } |
サンプルコード
Visual C++ 2017で作成したサンプルコードをGitHubで公開しています。
GitHub – ExFactory
実行結果は以下の通りです。
生成したcontents_a, contents_b, contents_cのそれぞれのact_requestがコールされていることが確認できます。
[ 15] Start. [ 29] Contents A. [ 40] Contents B. [ 51] Contents C. [ 30] Terminate.
参考
以下のサイトを参考にさせていただきました。
TECHSCORE(テックスコア) – 4. FactoryMethod パターン
新品価格 |