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);
}
今回は水炊きしか作っていませんが、他にキムチ鍋やすき焼きを作りたくなった場合でも、ごそっと入れ替えることができますね。