C言語でGoFのAbstractFactoryパターンを実装します。
目次
AbstractFactoryパターンを確認する
まずはAbstractFactoryパターンを確認しましょう。TECHSCOREさんのAbstractFactoryパターンの解説記事をご参照ください。
https://www.techscore.com/tech/DesignPattern/AbstractFactory.html/
AbstractFactoryパターンで設計する
TECHSCOREさんのサンプルコードをC言語に移植します。C言語に移行するにあたり、クラス図は下記のようになりました。
TECHSCOREさんのクラス図を2か所異なる点があります。
1点目はFactoryインタフェースにdestroy系のメソッドが追加されています。これはC言語の場合はガベージコレクションがないため、Facotryで作成したインスタンスを自分で削除する必要があるためです。
2点目はListの箇所がVegetableとIngredientになっている点です。今回は配列でVegetableとIngredientを持つようにしたので、クラス図上は直接インタフェース/クラス名を記載しました。
後、TECHSCOREさんのクラス図もそうですが、これ以外にもPotクラスとHotPotクラスも使いますので、お忘れなく。
AbstractFactoryパターンで実装する
今回は部品がたくさんあるので、本質的ではない下記のインタフェース・クラスのソースコードは載せません。GitHubには全て置いておくので、必要だったらダウンロードしてください。
- Pot
- HotPot
- Soup
- Protein
- Vegetable
- Ingredient
- ChickenBoneSoup
- Chicken
- ChineseCabbage
- Leek
- ChrysantThemum
- Tofu
Factoryを作る
まずはFactoryインタフェースを作ります。今回はメソッドが大量にあるので仮想関数テーブルを使った方式にしました。
Factory.h
#define VEGETABLE_SIZE 10 #define INGREDIENT_SIZE 10 typedef struct FactoryStruct Factory; extern Soup *Factory_createSoup(Factory *factory); extern void Factory_destroySoup(Factory *factory, Soup *soup); extern Protein *Factory_createMain(Factory *factory); extern void Factory_destroyMain(Factory *factory, Protein *main); extern Vegetable **Factory_createVegetables(Factory *factory); extern void Factory_destroyVegetables(Factory *factory, Vegetable **vegetables); extern Ingredient **Factory_createOtherIngredients(Factory *factory); extern void Factory_destroyOtherIngredients(Factory *factory, Ingredient **ings);
FactoryPrivate.h
typedef struct FactoryInterface { Soup *(*createSoup)(Factory *factory); void (*destroySoup)(Factory *factory, Soup *soup); Protein *(*createMain)(Factory *factory); void (*destroyMain)(Factory *factory, Protein *main); Vegetable **(*createVegetables)(Factory *factory); void (*destroyVegetables)(Factory *factory, Vegetable **vegetables); Ingredient **(*createOtherIngredients)(Factory *factory); void (*destroyOtherIngredients)(Factory *factory, Ingredient **ings); } FactoryInterface; typedef struct FactoryStruct { FactoryInterface *vtable; } FactoryStruct;
Factory.c
Soup *Factory_createSoup(Factory *factory) { return factory->vtable->createSoup(factory); } void Factory_destroySoup(Factory *factory, Soup *soup) { factory->vtable->destroySoup(factory, soup); } Protein *Factory_createMain(Factory *factory) { return factory->vtable->createMain(factory); } void Factory_destroyMain(Factory *factory, Protein *main) { factory->vtable->destroyMain(factory, main); } Vegetable **Factory_createVegetables(Factory *factory) { return factory->vtable->createVegetables(factory); } void Factory_destroyVegetables(Factory *factory, Vegetable **vegetables) { factory->vtable->destroyVegetables(factory, vegetables); } Ingredient **Factory_createOtherIngredients(Factory *factory) { return factory->vtable->createOtherIngredients(factory); } void Factory_destroyOtherIngredients(Factory *factory, Ingredient **ings) { factory->vtable->destroyOtherIngredients(factory, ings); }
MizutakiFactoryを作る
水炊きの材料を作るMizutakiFactoryを作りましょう。
MizutakiFactory.h
typedef struct MizutakiFactoryStruct MizutakiFactory; extern MizutakiFactory *MizutakiFactory_create(); extern void MizutakiFactory_destroy(MizutakiFactory *factory);
MizutakiFactory.c
typedef struct MizutakiFactoryStruct { Factory interface; } MizutakiFactoryStruct; Soup *MizutakiFactory_createSoup(Factory *factory) { return (Soup*)ChickenBoneSoup_create(200); } void MizutakiFactory_destroySoup(Factory *factory, Soup *soup) { ChickenBoneSoup_destroy((ChickenBoneSoup*)soup); } Protein *MizutakiFactory_createMain(Factory *factory) { return (Protein*)Chicken_create(150); } void MizutakiFactory_destroyMain(Factory *factory, Protein *main) { Chicken_destroy((Chicken*)main); } Vegetable **MizutakiFactory_createVegetables(Factory *factory) { Vegetable **vegetables = (Vegetable**)malloc(sizeof(Vegetable*)*VEGETABLE_SIZE); vegetables[0] = (Vegetable*)ChineseCabbage_create(10); vegetables[1] = (Vegetable*)Leek_create(3); vegetables[2] = (Vegetable*)Chrysanthemum_create(5); for (uint16_t i = 3;i < VEGETABLE_SIZE; i++) { vegetables[i] = NULL; } return vegetables; } void MizutakiFactory_destroyVegetables(Factory *factory, Vegetable ** vegetables) { ChineseCabbage_destroy((ChineseCabbage*)vegetables[0]); Leek_destroy((Leek*)vegetables[1]); Chrysanthemum_destroy((Chrysanthemum*)vegetables[2]); free(vegetables); } Ingredient **MizutakiFactory_createOtherIngredients(Factory *factory) { Ingredient **ings = (Ingredient**)malloc(sizeof(Ingredient*)*INGREDIENT_SIZE); ings[0] = (Ingredient*)Tofu_create(1); for (uint16_t i = 1; i < INGREDIENT_SIZE; i++) { ings[i] = NULL; } return ings; } void MizutakiFactory_destroyOtherIngredients(Factory *factory, Ingredient ** ings) { Tofu_destroy((Tofu*)ings[0]); free(ings); } static FactoryInterface MizutakiFactoryInterface = { MizutakiFactory_createSoup, MizutakiFactory_destroySoup, MizutakiFactory_createMain, MizutakiFactory_destroyMain, MizutakiFactory_createVegetables, MizutakiFactory_destroyVegetables, MizutakiFactory_createOtherIngredients, MizutakiFactory_destroyOtherIngredients }; MizutakiFactory *MizutakiFactory_create() { MizutakiFactory *factory = (MizutakiFactory*)malloc(sizeof(MizutakiFactory)); factory->interface.vtable = & MizutakiFactoryInterface; return factory; } void MizutakiFactory_destroy(MizutakiFactory *factory) { free(factory); }
SampleClassを作る
SampleClassを作り、MizutakiFactoryの使い方を見てみましょう。
SampleClass.c
static Factory *createFactory(uint16_t id) { Factory *factory = NULL; switch (id) { // case 0: // factory = (Factory*)KimuchiFactory_create(); // case 1: // factory = (Factory*)SukiyakiFactory_create(); default: factory = (Factory*)MizutakiFactory_create(); } return factory; } static void destroyFactory(uint16_t id, Factory *factory) { switch (id) { // case 0: // KimuchiFactory_destroy((KimuchiFactory*)factory); // case 1: // SukiyakiFactory_destroy((SukiyakiFactory*)factory); default: MizutakiFactory_destroy((MizutakiFactory*)factory); } } void SampleClass_doMain() { Pot pot; HotPot *hotpot = HotPot_create(&pot); Factory *factory = createFactory(2); HotPot_addSoup(hotpot, Factory_createSoup(factory)); HotPot_addMain(hotpot, Factory_createMain(factory)); HotPot_addVegetables(hotpot, Factory_createVegetables(factory)); HotPot_addOtherIngredients(hotpot, Factory_createOtherIngredients(factory)); // do something Factory_destroySoup(factory, HotPot_getSoup(hotpot)); Factory_destroyMain(factory, HotPot_getMain(hotpot)); Factory_destroyVegetables(factory, HotPot_getVegetables(hotpot)); Factory_destroyOtherIngredients(factory, HotPot_getOtherIngredients(hotpot)); destroyFactory(2, factory); HotPot_destroy(hotpot); }
今回は水炊きしか作っていませんが、他にキムチ鍋やすき焼きを作りたくなった場合でも、ごそっと入れ替えることができますね。