概要
@hyuki 先生著の『Javaで学ぶデザインパターン入門』(2004年、SB Creative)の1章ずつをベースに、サンプルコードをC#で置き換えながら勉強していく記事です。
※著者の @hyuki 先生には適切に書籍への参照を入れれば問題ない旨ご確認いただいています。
本題
Factory Methodパターン
第4回はFactory Methodパターンです。Factory Methodパターンは本の表現を借りると、Template Methodパターン(スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を定める)をインスタンス生成に適用したデザインパターンです。
インスタンスの生成方法をを親クラスで定め、子クラスで実装します。
サンプルコード
早速具体的な事例を見てみましょう。『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に掲載されているコードをC#で(大体)書き換えます。
using System;
using System.Collections.Generic;
namespace FactoryMethodPattern
{
using Framework;
using IDCard;
class Program
{
static void Main(string[] args)
{
Factory factory = new IDCardFactory();
Product card1 = factory.Create("nyanchu");
// => nyanchuのカードを作ります。
Product card2 = factory.Create("toshi0607");
// => toshi0607のカードを作ります。
card1.Use();
// => nyanchuのカードを使います。
card2.Use();
// => toshi0607のカードを使います。
// 実行が一瞬で終わって確認できないので、キーの入力を待ちます
Console.ReadLine();
}
}
}
namespace Framework
{
// Product
public abstract class Product
{
public abstract void Use();
}
// Creator
public abstract class Factory
{
public Product Create(string owner)
{
Product p = CreateProduct(owner);
RegisterProduct(p);
return p;
}
protected abstract Product CreateProduct(string owner);
protected abstract void RegisterProduct(Product product);
}
}
namespace IDCard
{
using Framework;
// ConcreteProduct
public class IDCard : Product
{
public string Owner { get; private set; }
internal IDCard(string owner)
{
Console.WriteLine($"{owner}のカードを作ります。");
this.Owner = owner;
}
public override void Use()
{
Console.WriteLine($"{Owner}のカードを使います。");
}
}
// ConcreteCreator
public class IDCardFactory : Factory
{
private List<string> Owners { get; set; } = new List<string>();
protected override Product CreateProduct(string owner)
{
return new IDCard(owner);
}
protected override void RegisterProduct(Product product)
{
Owners.Add(((IDCard)product).Owner);
}
}
}
効能
- Framework側がIDCard側に依存していないので、Framework側を使いまわしインスタンスの生成を統一することができる。
- Creator(Factory)はnewでインスタンスを生成しない(メソッドを使用する)ことで具体的なクラス名を知らなくてもよい。疎なモジュールになる。
使用上の注意
- クラスを使用する人が設計の意図を理解できるようにすること。これがFactory Methodパターンの場合だと、Factory Methodパターンだと理解されずに本来はテンプレ通り行うべきインスタンス生成時に縦横無尽にnewnewされると意図は伝わっていなさそう。
メモ
インスタンスを生成するメソッドには3種類の書き方がある。
- 抽象メソッドにする
- サンプルはこれ
- デフォルトの実装を用意しておく
- 抽象メソッドにするとサブクラスでの実装が強いられるのに対し、こちらはサブクラスで実装しないとデフォルトの実装を使用する
- スーパークラスを抽象クラスにしておくことはできない
- エラーにする
- 実装が必要なことをエラーによってサブクラスの実装者に知らせるパターン。C#だとFactoryに
throw new NotImplementedException();
とか書いておけばよさそう。
関連しているパターン
- Template Methodパターン
- Singletonパターン
- Compositeパターン
- Iteratorパターン
感想や疑問
- この話って具体的な機能を作るときに意識するというより、
namespace Framework
になってるようにより大きな枠組みを作る人が設計時に意識するとよいことのように感じました。 - リフレクションでインスタンス生成しないといけないかつ似たようなクラスで似たような使い方するときにFactoryはあるとみ通しよくなりそうと思いました。
-
プログラムのコメントや開発の文書の中に、実際に使われているデザインパターンの名称ち意図を記述しておくのはよいことです
って『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に書いてあって確かにって思いました。が、大体暗黙の前提として使用されているので、勉強しないと...っていうのがこの記事の趣旨でもあります。設計者(最初に機能のコード書く人)が当然と思ってる実装でも様々な理由(当然はその人の中の当然でしかない、読む人がひよっことかとか)で意図しない解釈になるようなものだと思います。なので、コードわかりやすくするのはもちろん、書けることは書かないと...って改めて思いました。
C#で学ぶデザインパターン入門
①Iterator
②Adapter
③Template Method
④Factory Method
⑤Singleton
⑥Prototype
⑦Builder
⑧AbstractFactory
⑨Bridge
⑩Strategy
⑪Composite Pattern
⑫Decorator Pattern