C言語でAbstractFactoryパターン【オブジェクト指向】

C言語でGoFのAbstractFactoryパターンを実装します。

AbstractFactoryパターンを確認する

まずはAbstractFactoryパターンを確認しましょう。TECHSCOREさんのAbstractFactoryパターンの解説記事をご参照ください。

 

https://www.techscore.com/tech/DesignPattern/AbstractFactory.html/

 

AbstractFactoryパターンで設計する

TECHSCOREさんのサンプルコードをC言語に移植します。C言語に移行するにあたり、クラス図は下記のようになりました。

 

AbstractFactoryパターンのクラス図

 

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);
}

 

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

 

目次:C言語でGoFのデザインパターン【オブジェクト指向】

ソースコード:https://github.com/yuksblog/c_gof_design_pattern