C言語でGoFのFactoryMethodパターンを実装します。
目次
FactoryMethodパターンを確認する
まずはFacotryMethodパターンを確認しましょう。TECHSCOREさんのFacotryMethodパターンの解説記事をご参照ください。
https://www.techscore.com/tech/DesignPattern/FactoryMethod.html/
FactoryMethodパターンで設計する
TECHSCOREさんのサンプルコードをC言語に移植します。クラス図にするよ次のようになります。
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のおかげでサブクラスで好きなオブジェクトが使えるようになりましたね。