C言語でGoFのBuilderパターンを実装します。
目次
Builderパターンを確認する
まずはBuilderパターンを確認しましょう。TECHSCOREさんのBuilderパターンの解説記事をご参照ください。
https://www.techscore.com/tech/DesignPattern/Builder.html/
Builderパターンで設計する
TECHSCOREさんのサンプルコードをC言語に移植します。クラス図は次のようになります。
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を渡してあげれば、別の溶液も作ることができますね。