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

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

Builderパターンを確認する

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

 

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

 

Builderパターンで設計する

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

 

Builderパターンのクラス図

 

Builderパターンで実装する

SaltWaterを作る

まずはSaltWaterを作りましょう。このクラスは単に塩と水を持つだけです。

 

SaltWater.h

typedef struct SaltWater {
    double salt;
    double water;
} SaltWater;

 

Builderを作る

Builderパターンのキモの1つであるBuilderインタフェースを作りましょう。このサンプルにおけるBuilderは溶液を作るための機能を持ちます。

 

Builder.h

typedef struct Builder {
    void (*addSolute)(struct Builder *builder, double solute);
    void (*addSolvent)(struct Builder *builder, double solbent);
    void (*abandonSolution)(struct Builder *builder, double solution);
    void *(*getResult)(struct Builder *builder);
} Builder;

extern void Builder_addSolute(Builder *builder, double solute);

extern void Builder_addSolvent(Builder *builder, double solvent);

extern void Builder_abandonSolution(Builder *builder, double solution);

extern void *Builder_getResult(Builder *builder);

 

Builder.c

void Builder_addSolute(Builder *builder, double solute) {
    builder->addSolute(builder, solute);
}

void Builder_addSolvent(Builder *builder, double solvent) {
    builder->addSolvent(builder, solvent);
}

void Builder_abandonSolution(Builder *builder, double solution) {
    builder->abandonSolution(builder, solution);
}

void *Builder_getResult(Builder *builder) {
    return builder->getResult(builder);
}

 

SaltWaterBuilderを作る

BuilderをimplemtnsするSaltWaterBuilderを作りましょう。このBuilderは食塩水を作るためのクラスです。

 

SaltWaterBuilder.h

typedef struct SaltWaterBuilderStruct SaltWaterBuilder;

extern SaltWaterBuilder *SaltWaterBuilder_create();

extern void SaltWaterBuilder_destroy(SaltWaterBuilder * builder);

 

SaltWaterBuilder.s

typedef struct SaltWaterBuilderStruct {
    Builder interface;
    SaltWater saltWater;
} SaltWaterBuilderStruct;

void addSolute(Builder *builder, double solute) {
    SaltWaterBuilder *swb = (SaltWaterBuilder*)builder;
    swb->saltWater.salt += solute;
}

void addSolvent(Builder *builder, double solvent) {
    SaltWaterBuilder *swb = (SaltWaterBuilder*)builder;
    swb->saltWater.water += solvent;
}

void abandonSolution(Builder *builder, double solution) {
    SaltWaterBuilder *swb = (SaltWaterBuilder*)builder;
    double saltDelta = solution *
            (swb->saltWater.salt / (swb->saltWater.salt + swb->saltWater.water));
    double waterDelta = solution *
            (swb->saltWater.water / (swb->saltWater.salt + swb->saltWater.water));
    swb->saltWater.salt -= saltDelta;
    swb->saltWater.water -= waterDelta;
}

void *getResult(Builder *builder) {
    SaltWaterBuilder *swb = (SaltWaterBuilder*)builder;
    return &swb->saltWater;
}

SaltWaterBuilder *SaltWaterBuilder_create() {
    SaltWaterBuilder *builder = (SaltWaterBuilder*)malloc(sizeof(SaltWaterBuilder));
    builder->interface.addSolute = addSolute;
    builder->interface.addSolvent = addSolvent;
    builder->interface.abandonSolution = abandonSolution;
    builder->interface.getResult = getResult;
    builder->saltWater.salt = 0.0;
    builder->saltWater.water = 0.0;

    return builder;
}

void SaltWaterBuilder_destroy(SaltWaterBuilder * builder) {
    free(builder);
}

 

このSaltWaterBuilderは食塩水を作っていますが、別の実装として砂糖水を作るSugarWaterBuilderも作成できますね。

 

Directorを作る

Builderパターンのもう1つのキモであるDirectorクラスを作りましょう。

 

Director.h

typedef struct DirectorStruct Director;

extern Director *Director_create(Builder *builder);

extern void Director_destroy(Director *director);

extern void Director_construct(Director *director);

 

Director.c

typedef struct DirectorStruct {
    Builder *builder;
} DirectorStruct;

Director *Director_create(Builder *builder) {
    Director *director = (Director*)malloc(sizeof(Director));
    director->builder = builder;

    return director;
}

void Director_destroy(Director *director) {
    free(director);
}

void Director_construct(Director *director) {
    Builder_addSolvent(director->builder, 100.0);
    Builder_addSolute(director->builder, 40.0);
    Builder_abandonSolution(director->builder, 70.0);
    Builder_addSolvent(director->builder, 100.0);
    Builder_addSolute(director->builder, 15.0);
}

 

DirectorはBuilderの実装に依存していないことがわかりますね。

Directorは次のように使います。

 

SaltWaterBuilderを使うDirector

SaltWaterBuilder *builder = SaltWaterBuilder_create();
Director *director = Director_create((Builder*)builder);

Director_construct(director);
SaltWater *saltWater = (SaltWater*)Builder_getResult((Builder*)builder);

SaltWaterBuilder_destroy(builder);
Director_destroy(director);

 

SaltWaterBuilder以外のBuilderを渡してあげれば、別の溶液も作ることができますね。

 

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

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