質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

6593閲覧

デザインパターン Factory Method の使い方(C++)

sin_250

総合スコア112

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

オブジェクト

オブジェクト指向において、データとメソッドの集合をオブジェクト(Object)と呼びます。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2019/05/22 13:14

いつもお世話になります。

デザインパターンのFactory MethodをC++で実装しようとしている中で
疑問がございます。

サンプルコード

ファイル数多いですが、ご容赦ください。以下があります。

  • 抽象ファクトリクラス(abstract_factory.hpp)
  • 具体ファクトリクラス(concrete_factory.hpp/cpp)
  • 生成されるクラスの抽象(product.hpp)
  • 生成される具体クラス(pen.hpp/cpp)
  • 上記の使い手(main.cpp)
  • Makefile

cpp

1/* 1. abstract_factory.hpp */ 2#ifndef ABSTRACT_FACTORY_HPP_ 3#define ABSTRACT_FACTORY_HPP_ 4 5#include <string> 6class Product; 7 8class Factory { 9 public: 10 Product* create(std::string type) { 11 return createInstance(type); 12 } 13 private: 14 virtual Product* createInstance(std::string type) = 0; 15}; 16#endif

cpp

1/* 2. concrete_factory.hpp */ 2#ifndef CONCRETE_FACTORY_HPP_ 3#define CONCRETE_FACTORY_HPP_ 4 5#include "abstract_factory.hpp" 6 7class ConcreteFactory : public Factory { 8 private: 9 Product* createInstance(std::string type); 10}; 11#endif

cpp

1/* 3. concrete_factory.cpp */ 2#include <string> 3#include "concrete_factory.hpp" 4#include "pen.hpp" 5 6Product* ConcreteFactory::createInstance(std::string type) { 7 if (type == "Pen") { 8 return new Pen(); 9 } 10}

cpp

1/* 4. product.hpp */ 2#ifndef PRODUCT_HPP_ 3#define PRODUCT_HPP_ 4 5class Product { 6 public: 7 virtual void sayName() = 0; 8}; 9#endif

cpp

1/* 5. pen.hpp */ 2#ifndef PEN_HPP_ 3#define PEN_HPP_ 4 5#include "product.hpp" 6 7class Pen : public Product { 8 public: 9 void sayName(); 10}; 11#endif

cpp

1/* 6. pen.cpp */ 2#include <iostream> 3#include "pen.hpp" 4 5void Pen::sayName() { 6 std::cout << "I am a pen!" << std::endl; 7}

cpp

1/* 7. main.cpp */ 2#include "product.hpp" 3#include "concrete_factory.hpp" // IS THIS CORRECT ??? 4 5int main(void) { 6 7 Factory* factory = new ConcreteFactory(); 8 Product* product = factory->create("Pen"); 9 10 product->sayName(); // expects "I am a pen!" 11 12 return 0; 13}

Makefile

1# 8. Makefile 2test: main.cpp concrete_factory.cpp pen.cpp 3 g++ -std=c++11 $^

質問

main.cppにおいて、concrete_factory.hppをインクルードしているのはFactory Methodの正しい実装でしょうか?

Productについては、pen.hppではなくproduct.hppをインクルードしているので
抽象化できているなと納得できるのですが、Factory側については具体クラスの方をインクルードせざるを得ないように思うのですが、
使い手側がファクトリの具体クラスのヘッダに依存してしまって良いのか、それが正しいやり方なのかを知りたいです。

その他

一応、有名な本2,3冊を読みつつ考えているのですが、使い手側のサンプルコードがあまり載っておらず
よく分からない状態です。

よろしくお願い申し上げます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

.NET でデータベースを扱う各種クラスが Factory Method パターンで実装されているので、これが参考になるのではないかと思います。

まず、DbProviderFactories というクラスに静的メソッド GetFactory があります。これが Factory Method です。このメソッドに文字列を渡すと、それに応じた Factory を返します。ユーザーは自分で Factory を実装して DbProviderFactories に登録することもできるので、既存のコードを全く変更せずに新しく Factory を追加することもできます。

このメソッドから返された Factory は DbProviderFactory というクラスを継承し、ユーザーは具体的な Factory の詳細を知らなくても扱うことができます。Factory は接続のための Connection オブジェクトや、SQL を実行する Command オブジェクトを生成し、たとえば Oracle を OLEDB で扱う場合でも、CSV を ODBC で扱う場合でも、DbProviderFactories に渡す文字列以外は全く同じコードが使えます。

つまり、使用するデータベースを変更したくなった時には、コードを書き換えてコンパイルし直さなくても、実行時に読み込む設定の変更のみで対応できるということです。

これが Factory Method パターンの利点だと私は思います。

この利点を享受するには、具体的なクラスをインクルードしてコンパイルし直す戦略では意味がないのではないでしょうか?

concrete_factory は隠蔽し、あくまで抽象クラスのみを扱うコードを書くことをお勧めします。

投稿2019/05/23 03:30

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sin_250

2019/05/26 05:25

異なるインスタンスを使いたくなった際に文字列を変えるのか、Factoryの種類を変えるのか、の違いに本質的な違いがあるかだと思いました。 Factory側も抽象クラスでインターフェースは切られているので、どのFactory具体クラスを使っていてもメソッド類は共通で使えるはずです。 ただおっしゃるように文字列の場合は確かに設定で読み込んでそれを渡す、ということができるのは違いがありますね。 それはおそらくFactoryパターンの本質より少し表面的な話なのかもしれず、それをやるためにはasmさんが書かれたような関数を用意すればいいということなのですかね。。。 引き続き勉強してみます。ご回答ありがとうございました!
Zuishin

2019/05/26 05:27

何のために使うのかということですね。メリットがわからないまま使っても意味がないと思います。
guest

0

こんにちは。

main.cppにおいて、concrete_factory.hppをインクルードしているのはFactory Methodの正しい実装でしょうか?

各種のFactory Methodの解説記事を見る限り、正しい実装と思います。

抽象化できているなと納得できるのですが、Factory側については具体クラスの方をインクルードせざるを得ないように思うのですが、使い手側がファクトリの具体クラスのヘッダに依存してしまって良いのか

ナンセンスですよね? 私もそう思います。
Factory Methodパターンは、「欲しいクラスを直接生成する」代わりに『「欲しいクラスを生成する」クラス(concrete_factory)を生成し、それに欲しいクラスを作らせる』パターンです。プログラマは「欲しいクラスを生成するクラス」がどれなのか知っている必要があります。そんな面倒なことしないで直接欲しいクラスを作ればよいだけですから、メリットは存在しないように感じます。

あ、でもパラメータを受け取って、そのパラメータに適合したクラスのインスタンスを生成し、その抽象型へのポインタを返却するパターンは有用ですよ。(残念ながら名前は付いていないようですが...)

投稿2019/05/23 02:47

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sin_250

2019/05/26 05:19

いつもお世話になっております。 引き続きさらに調べてみたのですが、やっぱりクライアント側でFactoryの具体クラスを持っているのはプラクティスとして特段間違ってはいないようです。 パターンの真価を実感するには、もっとオブジェクト生成の種類や条件が複雑なプログラムを扱って 苦しまないとダメってことですかね笑 ご回答ありがとうございました。
Chironian

2019/05/26 06:08

> 引き続きさらに調べてみたのですが、やっぱりクライアント側でFactoryの具体クラスを持っているのはプラクティスとして特段間違ってはいないようです。 その通りです。回答にも記載したように「Factory Methodパターン」の理解としては間違っていないと思います。 つまり、私は「Factory Methodパターン」自体がクソと言っています。 他のクラスを生成するための専用クラスの存在がそもそもナンセンスですから。(普通はコンストラクタで十分ですし、そのためのコンストラクタです。)
guest

0

ベストアンサー

ケースバイケースですね。

Factoryのデストラクタをvirtualにしておき

cpp

1#include <concrete_factory.hpp> 2#include <なんとか_factory.hpp> 3 4Factory* MakeFactory(int param){ 5 switch(param){ 6 case CONCREATE: 7 return new ConcreteFactory(); 8 } 9 return nullptr; 10} 11 12void CloseFactory(Factory* p){ delete p; }

みたいなFactory作成関数・ヘッダを作るのが適切な場合もありますし
具体的なFactoryと密に結びついても問題ない場合もあります。

投稿2019/05/22 13:48

編集2019/05/22 23:42
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sin_250

2019/05/22 13:56

ご回答有り難うございます。 確かに抽象クラスは基本的にデストラクタをvirtualにしておくべきでした、サンプルコードについては忘れていました。 申し訳ないです。 ケースバイケースということは、サンプルのmain.cppでconcrete_factory.hppをincludeしているのは 明らかな間違いというわけではないのでしょうか。(例えば、もしmain.cppでpen.hppをインクルードしたらFactory Methodとしては、明らかな間違いだと思います) この場合のMakeFactoryはクラスではなく単なる関数という理解で良いのでしょうか。 質問ばかりで申し訳ありません・・・いつもありがとうございます。
asm

2019/05/22 23:43 編集

クラスにしてしまうとFactoryを作るAbstractFactoryが出てきたりと 複雑になるだけなので、単なる関数にしてみました。 **誤った認識があったので一部編集
asm

2019/05/23 00:30

> concrete_factory.hppをインクルードしているのは間違いではないか main.cppと「concrete_factory」との結合が密であるか疎であるかは重要ではありません。 あくまでも、派生クラスがオブジェクトの生成のみに責任を持つTemplate MethodパターンがFactory Methodパターンです。
sin_250

2019/05/26 05:13

重ねてのご回答、ありがとうございます。 さらにいろいろ調べてみましたが、同じような疑問を持っている方もいるようで、 感覚的にパターンの意味を理解するのはもう少し経験が必要なところなのだなとわかりました。 クライアント側が実際に使いたいインスタンスの種類を「全く」知らないということは有り得ないので、 結局、何かしらの方法で具体的に使いたいものを指定する方法が必要な際に、ファクトリに渡すstringの内容を変えてファクトリ側でstringの内容に応じて生成インスタンスを切り替えるのか、 それとも使用するファクトリそのものを変えるのかが、単なるFactoryとFactoryMethodパターンの違いなのかなと理解しました。 何となく、stringの実引数を変えるのは問題ないけどFactoryの種類を変えることに抵抗を感じたのは 単なる経験不足というか本質が肌で分かってないだけかもしれません。 ご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問