ファクトリパターン
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 55/61。
Factoryパターンは、オブジェクトの生成を単一の関数に集約します。プログラムの至る所に malloc や初期化コードを分散させる代わりに、何を生成するかを決定し、すぐに使用可能なオブジェクトを返す1つの関数を呼び出します。
このパターンは、ポリモーフィズムと組み合わせることで特に強力になります。ファクトリは、共通のベースポインタを介して異なる具体的な型を返すことができ、呼び出し元から作成の詳細を隠蔽します。呼び出し元は単に「図形」を要求し、ファクトリが円、長方形、またはその他のものを構築するかどうかを決定します。
一般的なファクトリの実装例を以下に示します:
typedef enum {
SHAPE_CIRCLE,
SHAPE_RECTANGLE
} ShapeType;
Shape* create_shape(ShapeType type) {
switch (type) {
case SHAPE_CIRCLE:
return (Shape*)create_circle(5.0);
case SHAPE_RECTANGLE:
return (Shape*)create_rectangle(4.0, 3.0);
default:
return NULL;
}
}ファクトリは識別子(通常は列挙型)を受け取り、switch文を使用して呼び出すコンストラクタを決定します。実際に作成された型に関係なく、Shape*を返します。呼び出し側は Circle や Rectangle について知る必要はありません。単に基本型を操作するだけです:
Shape* s = create_shape(SHAPE_CIRCLE);
s->draw(s); // ポリモーフィックな呼び出し
free_shape(s);このパターンにより、新しい型の追加が容易になります。新しいenum値と、ファクトリ内の新しいcaseを追加するだけです。残りのコードは変更されません。
チャレンジ
簡単単一のファクトリ関数を通じて異なる種類のペットを作成するシステム、Pet Factoryを構築しましょう。作成ロジックをコード全体に分散させる代わりに、入力識別子に基づいてどの種類のペットを作成するかを決定する1つの場所に集約します。
コードは以下の3つのファイルに分けて構成します:
pet.h:PET_DOG、PET_CAT、PET_BIRDの値を持つPetType列挙型を定義します。speak用の関数ポインタ(const Pet*を受け取り、何も返さない)を含むベースとなるPet構造体を作成します。PetTypeを受け取りPet*を返すファクトリ関数create_petと、クリーンアップ用のfree_pet関数を宣言します。pet.c: 3つの具体的なペット構造体 —Dog、Cat、Bird— を定義し、それぞれ最初のメンバとしてPetを埋め込みます。各タイプに対して speak 関数を実装します:- Dogs は
Woof!と出力します。 - Cats は
Meow!と出力します。 - Birds は
Tweet!と出力します。
create_petファクトリを実装します。PetTypeに対する switch 文を使用して正しいコンストラクタを呼び出し、その結果をPet*として返します。無効なタイプの場合はNULLを返します。割り当てられたメモリを解放するためにfree_petを実装します。- Dogs は
main.c: ペットのタイプを表す整数(0は dog、1は cat、2は bird)を読み取ります。ファクトリを使用して適切なペットを作成し、ベースのPet*ポインタを通じてポリモーフィックにそのspeak関数を呼び出し、その後ペットを解放します。
プログラムは1つの入力を受け取ります:どのペットを作成するかを示す整数です。
入力が 0 の場合の出力例:
Woof!入力が 1 の場合の出力例:
Meow!入力が 2 の場合の出力例:
Tweet!Factory パターンの素晴らしさは、main.c が Dog、Cat、Bird 構造体について知る必要がないことです。ベースの Pet 型のみを扱います。後で新しいペットのタイプを追加する場合、ペットを使用するコードを変更することなく、新しい列挙値とファクトリ内の新しい case を追加するだけで済みます。
チートシート
Factoryパターンは、オブジェクトの生成を単一の関数に集約し、生成の詳細を呼び出し元から隠蔽します。
Factory関数は、識別子(通常は列挙型)を受け取り、switch文を使用して呼び出すコンストラクタを決定し、基本型のポインタを返します:
typedef enum {
SHAPE_CIRCLE,
SHAPE_RECTANGLE
} ShapeType;
Shape* create_shape(ShapeType type) {
switch (type) {
case SHAPE_CIRCLE:
return (Shape*)create_circle(5.0);
case SHAPE_RECTANGLE:
return (Shape*)create_rectangle(4.0, 3.0);
default:
return NULL;
}
}呼び出し元は基本型のみを扱い、ポリモーフィックな振る舞いを可能にします:
Shape* s = create_shape(SHAPE_CIRCLE);
s->draw(s); // polymorphic call
free_shape(s);利点:新しい型を追加するには、新しい列挙値とswitchケースを追加するだけで済み、Factoryを使用する既存のコードを変更する必要はありません。
自分で試してみよう
#include <stdio.h>
#include "pet.h"
int main(void) {
int type;
scanf("%d", &type);
// TODO: ファクトリを使用して適切なペットを作成する
// ヒント: intをPetTypeにキャストする
// TODO: Pet*ポインタを介してポリモーフィックにspeak関数を呼び出す
// TODO: ペットを解放する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。