C言語でGoFのTemplateMethodパターンを実装します。
目次
TemplateMethodパターンを確認する
まずはTemplateMethodパターンを確認しましょう。TECHSCOREさんのTemplateMethodパターンの解説記事をご参照ください。
https://www.techscore.com/tech/DesignPattern/TemplateMethod.html/
TemplateMethodパターンで設計する
TECHSCOREさんのサンプルコードをC言語に移植します。クラス図にすると下記のようになります。
TemplateMethodパターンで実装する
Cuttableを作る
まずはCuttableを作りましょう。ここでは空の構造体として作り、マーカーとしての役目だけを持つようにしました。本来であればcut()メソッドなどがあるべきでしょうね。
Cuttable.h
typedef struct Cuttable { } Cuttable;
Woodを作る
続いてCuttableをimplementsするWoodを作りましょう。今回は単に寸法を持つだけの構造体としました。
Wood.h
typedef struct WoodStruct Wood; extern Wood *Wood_create(uint16_t width, uint16_t height, uint16_t length); extern void Wood_destroy(Wood *wood);
Wood.c
typedef struct WoodStruct { Cuttable intereface; uint16_t width; uint16_t height; uint16_t length; } WoodStruct; Wood *Wood_create(uint16_t width, uint16_t height, uint16_t length) { Wood *wood = (Wood*)malloc(sizeof(Wood)); wood->width = width; wood->height = height; wood->length = length; return wood; } void Wood_destroy(Wood *wood) { free(wood); }
WoodCutPrintを作る
テンプレートであるWoodCutPrintを作ります。今回私は、draw()、cut()、print()と抽象メソッドが3つもあって少し多いと感じたので、仮想関数テーブルを使う方式で作りました。もちろん、仮想関数テーブルを使わない方式でもまったく問題ありません。
WoodCutPrint.h
typedef struct WoodCutPrintStruct WoodCutPrint; extern void WoodCutPrint_createWoodCutPrint(WoodCutPrint *wcp);
WoodCutPrintPrivate.h
typedef struct WoodCutPrintInterface { void (*draw)(WoodCutPrint *wcp, Wood *wood); void (*cut)(WoodCutPrint *wcp, Wood *wood); void (*print)(WoodCutPrint *wcp, Wood *wood); } WoodCutPrintInterface; typedef struct WoodCutPrintStruct { WoodCutPrintInterface *vtable; } WoodCutPrintStruct;
WoodCutPrint.c
void WoodCutPrint_createWoodCutPrint(CutPrint *wcp) { Wood *wood = Wood_create(100, 500, 200); wcp->vtable->draw(wcp, (Cuttable*)&wood); wcp->vtable->cut(wcp, (Cuttable*)&wood); wcp->vtable->print(wcp, (Cuttable*)&wood); Wood_destroy(wood); }
TanakasWoodCutPrintを作る
テンプレートで作れたので、田中君の木版画を作ってみましょう。ヘッダファイルは次のようにします。
TanakasWoodCutPrint.h
typedef struct TanakasWoodCutPrintStruct TanakasWoodCutPrint; extern TanakasWoodCutPrint *TanakasWoodCutPrint_create(); extern void TanakasWoodCutPrint_destroy(TanakasWoodCutPrint *tanaka);
中身は次のように作ります。
TanakasWoodCutPrint.c
static void Tanakas_draw(WoodCutPrint *wcp, Wood *wood) { printf("draw the girl friend\n"); } static void Tanakas_cut(WoodCutPrint *wcp, Wood *wood) { printf("cut the wood\n"); } static void Tanakas_print(WoodCutPrint *wcp, Wood *wood) { printf("print dynamically\n"); } static WoodCutPrintInterface interface = { Tanakas_draw, Tanakas_cut, Tanakas_print }; typedef struct TanakasWoodCutPrintStruct { WoodCutPrint super; uint16_t age; } TanakasWoodCutPrintStruct; TanakasWoodCutPrint *TanakasWoodCutPrint_create() { TanakasWoodCutPrint *tanaka = (TanakasWoodCutPrint*)malloc(sizeof(TanakasWoodCutPrint)); tanaka->super.vtable = &interface; tanaka->age = 15; return tanaka; } void TanakasWoodCutPrint_destroy(TanakasWoodCutPrint *tanaka) { free(tanaka); }
田中君は、draw()、cut()、print()だけを作れば良いだけです。createWoodCutPrint()を丸ごと田中君に任せるより、手順の間違いがないですし、田中君の作業も楽になりますね。