【C++】アブストラクトファクトリを実装したよ

アブストラクトファクトリについて、お話しします。一言でいうと、

関連するオブジェクト群を具象クラスを明確化せずに生成するためのパターン。

です。調べてみると、GUI部品の生成ではよくこのパターンが使われているそうですね。GUIの部品は、ダイアログやボタン、スクロールバーなど様々な種類があります。WindowsやUnixなどの異なる環境によって、表示や動作が違います。そのため、GUIを利用したアプリケーションは、OSのGUI部品にすぐに切り替えられるようにクライアントを特定のGUI部品に意識させずに、GUI部品群の生成を一か所にまとめる必要があるみたいですね。

想定

今回は、ある工場の製品を想定します。見た目は一緒だけど、材料だったり、焼成温度、焼成時間が違うとします。

材料(Material 焼成温度(Temparature 焼成時間(Duration
製品A (ProductA 1000 10分
製品B (ProductB 2000 20分

だとします。例えば、製品Bを作るのに間違えて、鉄の代わりに銀を入れちゃったら大変ですよね。これを防ぐのが、アブストラクトファクトリです。

適用前

まずはパターン適用前。ProductFactoryで生成していくパターンでやってみます。

 


using namespace std;


class Material {
	;
};

class MaterialA :public Material {
public:
	MaterialA() { cout << "MaterialA\n"; }
};


class MaterialB :public Material {
public:
	MaterialB() { cout << "MaterialB\n"; }
};

class Temparature {
	;
};

class TemparatureA :public Temparature {
public:
	TemparatureA() { cout << "temparatureA\n"; }
};

class TemparatureB :public Temparature {
public:
	TemparatureB() { cout << "temparatureB\n"; }
};


class Duration {
	;
};
class DurationA:public Duration{
public:
	DurationA() { cout << "DurationA\n"; }
}; 

class DurationB:public Duration{
public:
	DurationB() { cout << "DurationB"; }
};



class PruductFactory {
public:
	MaterialA* createMaterialA() {
		return new MaterialA();
	}
	MaterialB* createMaterialB() {
		return new MaterialB();
	}
	TemparatureA* createTemparatureA() {
		return new TemparatureA();
	}
	TemparatureB* createTemparatureB() {
		return new TemparatureB();
	}
	DurationA* createDurationA() {
		return new DurationA();
	}
	DurationB* createDurationB() {
		return new DurationB();
	}

};


int main() {
	PruductFactory product_factory;
	MaterialA* material_a;
	MaterialB* material_b;
	TemparatureA* temparature_a;
	TemparatureB* temparature_b;
	DurationA* duration_a;
	DurationB* duration_b;

	int make_p = 0;
	if (make_p == 0) {
		material_a = product_factory.createMaterialA();
		temparature_a = product_factory.createTemparatureA();
		duration_a = product_factory.createDurationA();
	}
	else if (make_p == 1) {
		material_b = product_factory.createMaterialB();
		temparature_b = product_factory.createTemparatureB();
		duration_b = product_factory.createDurationB();
	}
	

}

こちらのコードでも動きますが、いくつかの課題があります。
・柔軟性

新しく製品Cを作りたい場合、その製品を作るためのメソッドを生成する必要がある。

また、クライアント側もそれに対応してmainの中身を書き換える必要がある。

・クライアント側の意識

この場合、クライアント側が製品の組み合わせを理解する必要がある。仮に間違えてクライアントが製品Aに対して、材料Bを入れてしまう可能性も無きにしも非ず。

スポンサードサーチ

アブストラクトファクトリを適用

アブストラクトファクトリを使うことで、ProductFactoryとそこで作る材料を抽象化します。そうすることでクライアント側は特に具体的な生成方法を意識する必要がなくなり、どんな部品でも対応できます。


using namespace std;


class Material {
public:
	virtual void makeMaterial() const = 0;
};

class MaterialA :public Material {
public:
	MaterialA() { cout << "MaterialA\n"; }
	virtual void makeMaterial() const {cout << "Make MaterialA \n";}
};


class MaterialB :public Material {
public:
	MaterialB() { cout << "MaterialB\n"; }
	virtual void makeMaterial() const {cout << "Make MaterialB \n";}
};

class Temparature {
public:
	virtual void makeTemparature() const = 0;
};

class TemparatureA :public Temparature {
public:
	TemparatureA() { cout << "temparatureA\n"; }
	virtual void makeTemparature() const { cout << "Make TemparatureA\n"; }
};

class TemparatureB :public Temparature {
public:
	TemparatureB() { cout << "temparatureB\n"; }
	virtual void makeTemparature() const {cout << "Make TemparatureB\n";}
};


class Duration {
public:
	virtual void makeDuration() const = 0;
};

class DurationA:public Duration{
public:
	DurationA() { cout << "DurationA\n"; }
	virtual void makeDuration() const {	cout << "Make DurationA\n";}

}; 

class DurationB:public Duration{
public:
	DurationB() { cout << "DurationB"; }
	virtual void makeDuration() const {cout << "Make DurationB\n";} 
}; 

class PruductFactory { 
public: 
  virtual Material* createMaterial() =0; 
  virtual Temparature* createTemparature() = 0; 
  virtual Duration* createDuration() = 0; 
}; 
class ProductAFactory:public PruductFactory
{ 
public:
  Material* createMaterial() { return new MaterialA(); } 
  Temparature* createTemparature() { return new TemparatureA(); }
  Duration* createDuration() { return new DurationA(); } 
}; 
class ProductBFactory :public PruductFactory 
{ 
public: 
  Material* createMaterial() { return new MaterialB(); } 
  Temparature* createTemparature() { return new TemparatureB(); } 
  Duration* createDuration() { return new DurationB(); } 
}; 
int main() { 
  PruductFactory* product_factory; 
  Material* material; 
  Temparature* temparature; 
  Duration* duration; 
  product_factory = new ProductAFactory(); 
  material = product_factory->createMaterial();
	temparature = product_factory->createTemparature();
	duration = product_factory->createDuration();
	material->makeMaterial();
	temparature->makeTemparature();
	duration->makeDuration();


	delete product_factory;
	delete material;
	delete temparature;
	delete duration;

	product_factory = new ProductBFactory();
	material = product_factory->createMaterial();
	temparature = product_factory->createTemparature();
	duration = product_factory->createDuration();
	material->makeMaterial();
	temparature->makeTemparature();
	duration->makeDuration();
}



 


MaterialA
temparatureA
DurationA
Make MaterialA
Make TemparatureA
Make DurationA
MaterialB
temparatureB
DurationBMake MaterialB
Make TemparatureB
Make DurationB

見るとわかる通り、クライアントが意識するのは、ProductFactoryだけであって、

  • Material
  • Temparature
  • Duration

の中身を意識する必要がありません。

スポンサードサーチ

注意点

ProductC、Dと追加するのは非常に容易ですが、新しい項目を追加する際大幅な編集が必要です。例えば、ProductCにはさらにメッキ塗装が必要だとします。

変更前

材料(Material 焼成温度(Temparature 焼成時間(Duration
製品A (ProductA 1000 10分
製品B (ProductB 2000 20分

変更後

材料 焼成温度 焼成時間 メッキ塗装
製品A 1000 10分
製品B 2000 20分
製品C 500 30分 必要

アブストラクトファクトリで注意するのは、追加項目です。

メッキ塗装が必要ない製品A、Bに対してもなにかしらの記述が必要です。このように何か項目を追加する際に膨大な編集が必要になります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です