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()を丸ごと田中君に任せるより、手順の間違いがないですし、田中君の作業も楽になりますね。