はじめに
「C言語でトライ! デザインパターン」。間が空きましたが再開します。
今回はFactory Methodパターン。Abstruc Factoryと似たように生成部分を抽象化する形ですが、生成を行うFactory自体が抽象化されていない点がAbstruc Factoryとは異なる点ですかね。
デザインパターン一覧
作成したライブラリパッケージの説明
公開コードはこちら
Factory Methodパターン
Wikipediaの説明はこちらです。
Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。
TECHSCORE(テックスコア)さんのクラス図も乗せておきます。
ポイントはfactoryMethodを利用してインスタンスを生成するという点ですね。
インスタンスを生成がポイントなのはAbstruc Factoryと同様。
Abstruc Factoryは生成メソッドを持つクラス自体も抽象化することで色々な部分が差し替えやすく拡張性をぐっと高めていました。
その反面、適用するためのコード量がどうしても増え、元々オブジェクト指向言語でないCでやろうなんてのはハードルがちょっと高いかな~という印象でした。
こちらのFactory Methodは、工場はここ!って決めてしまえるような状況で気軽にクラス生成を抽象化出来る、導入ハードルの低く適用しやすいパターンだな~と感じました。
Abstruc Factoryとの違いについては、こちらの方の記事がわかりやすかったです。
一言解説部分を抜粋
Factory Method
[オブジェクト生成] の抽象化にポイントを置いたパターン
Abstract Factory
[関連するオブジェクト群をまとめて生成するための手順] の抽象化
常日頃から「拡張するのがわかってるのに、newする部分を抽象化しないと意味ないよな~」と思うケースが多々あったので、このパターンは自分には使いやすそうです。
個人的に利用するとしたら、まずはFactory Methodで生成を抽象化
→Factoryに対して拡張性が欲しくなってきたらAbstruc Factoryに切り替える。なんてのが良さそうかな。
サンプル
私は以前の記事で「Cでのクラス表現は前方宣言によるカプセル化と集約でいいかな」なんて言ってましたが、その考えを改める日が来ました。
FactoryMethodを活用するなら、継承表現を活用すると面白いですね。
//product側の定義
struct product_t;
typedef struct product_t *Product;
struct product_t {
void (*show_name)(Product this);
};
//継承して拡張できるよう、define定義しておく
#define PRODUCT_CLASS \
void (*show_name)(Product this);
//factory側のインターフェース定義
typedef struct factory_t {
Product (*product_factory)(void);
void (*product_free)(Product this);
} *Factory;
//工場の実体としてトヨタ、日産を用意
Factory toyota_factory_new();
Factory nissan_factory_new();
実体側。2018/10/06現在ではプリウスが一番上に出てたのでプリウス工場にしましたが、トヨタさんの製品が入れ替わればtoyota_factoryを変えれば利用者への影響はなし!いいですね。日産も同じような感じ。
#include <stdio.h>
#include <stdlib.h>
#include "factory.h"
//継承も出来る
typedef struct car_t {
PRODUCT_CLASS
char * name;
char * grade;
} *Car;
static void show_carname(Product this) {
Car instance = (Car)this;
printf("Toyota:%s(grade %s)\n", instance->name, instance->grade);
}
static Product toyota_factory(void) {
Car instance = calloc(1, sizeof(*instance));
instance->name = "prius";
instance->grade = "Apremium";
instance->show_name = show_carname;
return (Product)instance;
}
static void toyota_factory_free(Product this) {
free(this);
}
Factory toyota_factory_new() {
Factory instance = calloc(1, sizeof(*instance));
instance->product_factory = toyota_factory;
instance->product_free = toyota_factory_free;
return instance;
}
実際の利用者側はこんな感じ。Productの扱いは同じなので、Factoryの実体がトヨタでも日産でも、Factory生成後の処理に影響しないのがいいですね。
#include <stdlib.h>
#include "factory.h"
static void do_product(Factory factory) {
Product product = factory->product_factory();
product->show_name(product);
factory->product_free(product);
}
int main() {
Factory factory;
factory = toyota_factory_new();
do_product(factory);
free(factory);
factory = nissan_factory_new();
do_product(factory);
free(factory);
return 0;
}
実行時もこんな感じにちゃんとそれぞれの処理が実行されます。
$ ./test
Toyota:prius(grade Apremium)
Nissan:leaf
サンプルコードは以下に格納しています。
https://github.com/developer-kikikaikai/design_patter_for_c_appendix/tree/master/factory_method
感想
これは滅茶苦茶使いやすいですね!
今まで設計の際に悩んでいた「生成に対する抽象化」。今まではinstanceを管理するマネージャーを作って隠ぺい→実体はgetみたいな感じにしたりしてたけど、この形式はわかりやすいし使いやすいですな!
前回のAbstruct Factoryについては「いい表現だけど、ここまで抽象化したくなるケースに出会ったことないな~、難しいな~」という印象が強かったんですが、こちらについては適用ケースがかなり思い浮かびます。
Cのオブジェクト指向表現についても、こちらが活用できるケースに対してはガンガン継承を使いたいですね。便利な道具を使いこなせるよう、柔軟な設計を心がけていきたい。
参考
Factory Method と Abstract Factory の違いを順に理解する
デザインパターン「Factory Method」