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

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

Strategyパターンを確認する

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

 

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

 

Strategyパターンで設計する

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

 

Strategyパターンのクラス図

 

図には出てきませんが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);

 

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

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