C言語でGoFのStrategyパターンを実装します。
目次
Strategyパターンを確認する
まずはStrategyパターンを確認しましょう。TECHSCOREさんのStrategyパターンの解説記事をご参照ください。
https://www.techscore.com/tech/DesignPattern/Strategy.html/
Strategyパターンで設計する
TECHSCOREさんのサンプルコードをC言語に移植します。クラス図にすると次のようになります。
図には出てきませんがcompareの引数に使用するHumanクラスも使います。
Strategyパターンで実装する
Humanを作る
比較対象となるHumanを作りましょう。Humanは名前・身長・体重・年齢を持ちます。今回、メンバ変数はpublicなものとしました。
Human.h
typedef struct Human { char name; int16_t height; int16_t weight; int16_t age; } Human; extern Human *Human_create(char name, uint16_t height, uint16_t weight, uint16_t age); extern void Human_destroy(Human *human);
Human.c
Human *Human_create(char name, uint16_t height, uint16_t weight, uint16_t age) { Human *human = (Human*)malloc(sizeof(Human)); human->name = name; human->age = age; human->height = height; human->weight = weight; return human; } void Human_destroy(Human *human) { free(human); }
Comparatorを作る
Strategyパターンにおける戦略部分のインタフェースComparatorを作りましょう。Human同士の比較ロジックを戦略部分として抜き出します。
Comparator.h
typedef struct Comparator { int16_t (*compare)(struct Comparator *comparator, Human *human1, Human *human2); } Comparator; extern int16_t Comparator_compare(Comparator *comparator, Human *human1, Human *human2);
Comparator.c
int16_t Comparator_compare(Comparator *comparator, Human *human1, Human *human2) { return comparator->compare(comparator, human1, human2); }
AgeComparatorを作る
戦略部分の具体的な実装の1つAgeComparatorを作りましょう。AgeComparatorではHumanの年齢を比較します。
AgeComparator.h
typedef struct AgeComparatorStruct AgeComparator; extern AgeComparator *AgeComparator_create(); extern void AgeComparator_destroy(AgeComparator *comparator);
AgeComparator.c
typedef struct AgeComparatorStruct { Comparator interface; } AgeComparatorStruct; static int16_t compare(Comparator *comparator, Human *human1, Human *human2) { int16_t ret = 0; if (human1->age > human2->age) { ret = 1; } else if (human1->age == human2->age) { ret = 0; } else { ret = -1; } return ret; } AgeComparator *AgeComparator_create() { AgeComparator *comparator = (AgeComparator*)malloc(sizeof(AgeComparator)); comparator->interface.compare = compare; return comparator; } void AgeComparator_destroy(AgeComparator *comparator) { free(comparator); }
同様に、身長を比較するHeightComparator、体重を比較するWeightComparatorも作成できます。冗長なので本記事では省略します。
MyClassを作る
Comparatorを使って比較を行うMyClassを作りましょう。
MyClass.h
typedef struct MyClassStruct MyClass; extern MyClass *MyClass_create(Comparator *comparator); extern void MyClass_destroy(MyClass *myClass); extern int16_t MyClass_compare(MyClass *myClass, Human *human1, Human *Human2);
MyClass.c
typedef struct MyClassStruct { Comparator *comparator; } MyClassStruct; MyClass *MyClass_create(Comparator *comparator) { MyClass *myClass = (MyClass*)malloc(sizeof(MyClass)); myClass->comparator = comparator; return myClass; } void MyClass_destroy(MyClass *myClass) { free(myClass); } int16_t MyClass_compare(MyClass *myClass, Human *human1, Human *human2) { return Comparator_compare(myClass->comparator, human1, human2); }
MyClassではComparatorを呼んでいるだけです。コンストラクタで渡すComparatorを変えることで比較ロジックを簡単に変更することができますね。
MyClassは次のように使います。
AgeComparator *comparator = AgeComparator_create(); MyClass *myClass = MyClass_create((Comparator*)comparator); Human *human1 = Human_create('A', 170, 60, 20); Human *human2 = Human_create('B', 165, 50, 22); CHECK_EQUAL(-1, MyClass_compare(myClass, human1, human2)); Human_destroy(human1); Human_destroy(human2); AgeComparator_destroy(comparator); MyClass_destroy(myClass);