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

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

FactoryMethodパターンを確認する

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

 

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

 

FactoryMethodパターンで設計する

TECHSCOREさんのサンプルコードをC言語に移植します。クラス図にするよ次のようになります。

 

FactoryMethodパターンのクラス図

 

FactoryMethodパターンで実装する

Cuttableを作る

まずはCuttableを作りましょう。TemplateMethodの時と同様に単なるマーカーとして使います。

 

Cuttable.h

typedef struct Cuttable {
} Cuttable;

 

Potatoを作る

今川君が版材として使うポテトを作りましょう。CuttableインタフェースをImplementsしています。

 

Potato.h

typedef struct PotatoStruct Potato;

extern Potato *Potato_create(uint16_t size);

extern void Potato_destroy(Potato *potato);

 

Potato.c

typedef struct PotatoStruct {
    Cuttable intereface;
    uint16_t size;
} PotatoStruct;

Potato *Potato_create(uint16_t size) {
    Potato *potato = (Potato*)malloc(sizeof(Potato));
    potato->size = size;

    return potato;
}

void *Potato_destroy(Potato *potato) {
    free(potato);
}

 

CutPrintを作る

木材専用だったWoodCutPrintを改造して、Cuttableであれば何でも使える新しいCutPrintを作りましょう。

まずはヘッダファイルです。ここは名前を変えただけです。

 

CutPrint.h

typedef struct CutPrintStruct CutPrint;

extern void CutPrint_createCutPrint(CutPrint *cp);

 

次にPrivateとしているヘッダファイルです。

FactoryMethodパターンを適用し、版材を作るためのcreateCuttable()メソッドが追加されてところがポイントです。また、C言語はGCがないため、このままだとメモリリークしてしまいます。そのためdestroyCuttable()メソッドも用意しておきます。

 

CutPrintPrivate.h

typedef struct CutPrintInterface {
    void (*draw)(CutPrint *cp, Cuttable *hanzai);
    void (*cut)(CutPrint *cp, Cuttable *hanzai);
    void (*print)(CutPrint *cp, Cuttable *hanzai);
    Cuttable *(*createCuttable)(CutPrint *cp);
    void (*destroyCuttable)(CutPrint *cp, Cuttable *hanzai);
} CutPrintInterface;

typedef struct CutPrintStruct {
    CutPrintInterface *vtable;
} CutPrintStruct;

extern Cuttable *CutPrint_createCuttable(CutPrint *cp);

extern void CutPrint_destroyCuttable(CutPrint *cp, Cuttable *hanzai);

 

このCutPrintの実装は次のようになります。

 

CutPrint.c

Cuttable *CutPrint_createCuttable(CutPrint *cp) {
    return (Cuttable*)Wood_create(100, 500, 200);
}

void CutPrint_destroyCuttable(CutPrint *cp, Cuttable *hanzai) {
    Wood_destroy((Wood*)hanzai);
}

void CutPrint_createCutPrint(CutPrint *cp) {
    Cuttable *c = cp->vtable->createCuttable(cp);

    cp->vtable->draw(cp, c);
    cp->vtable->cut(cp, c);
    cp->vtable->print(cp, c);

    cp->vtable->destroyCuttable(cp, c);
}

 

ImagawasCutPrintを作る

CutPrintを継承し、ポテトを版材とするImagawasCutPrintを作りましょう。

 

ImagawasCutPrint.h

typedef struct ImagawasCutPrintStruct ImagawasCutPrint;

extern ImagawasCutPrint *ImagawasCutPrint_create();

extern void ImagawasCutPrint_destroy(ImagawasCutPrint *imagawa);

 

ImagawasCutPrint.c

static void Imagawas_draw(CutPrint *wcp, Cuttable *hanzai) {
    printf("draw a manga\n");
}

static void Imagawas_cut(CutPrint *wcp, Cuttable *hanzai) {
    printf("cut with dexterity\n");
}

static void Imagawas_print(CutPrint *wcp, Cuttable *hanzai) {
    printf("print with my blood\n");
}

static Cuttable *Imagawas_createCuttable(CutPrint *cp) {
    return (Cuttable*)Potato_create(100);
}

static void Imagawas_destroyCuttable(CutPrint *cp, Cuttable *hanzai) {
    Potato_destroy((Potato*)hanzai);
}


static CutPrintInterface interface = {
        Imagawas_draw,
        Imagawas_cut,
        Imagawas_print,
        Imagawas_createCuttable,
        Imagawas_destroyCuttable
};

typedef struct ImagawasCutPrintStruct {
    CutPrint super;
    uint16_t age;
} ImagawasCutPrintStruct;

ImagawasCutPrint *ImagawasCutPrint_create() {
    ImagawasCutPrint *imagawa = (ImagawasCutPrint*)malloc(sizeof(ImagawasCutPrint));
    imagawa->super.vtable = &interface;
    imagawa->age = 15;

    return imagawa;
}

void ImagawasCutPrint_destroy(ImagawasCutPrint *imagawa) {
    free(imagawa);
}

 

FactoryMethodのおかげでサブクラスで好きなオブジェクトが使えるようになりましたね。

 

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

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