Factoryパターン
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 53/70。
Factoryパターンは、オブジェクトの生成を別のメソッドやクラスに委譲する生成に関するパターンです。コード内で直接 new を使用する代わりに、ファクトリにオブジェクトの生成を依頼します。これにより、生成ロジックが集中化され、コードの柔軟性が向上します。
さまざまな種類のドキュメントを作成する必要があるシナリオを考えてみましょう。ファクトリがない場合、コードは特定のクラスに密結合してしまいます:
// Factoryなし - 生成ロジックが散在している
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();ファクトリは、この意思決定を1か所にカプセル化します:
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}これで、クライアントコードは具体的なクラスを意識することなく、必要なものをリクエストするだけで済むようになります。
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // PDFを開いていますFactoryパターンは、後で新しい型を追加する必要がある場合に真価を発揮します。SpreadsheetDocumentを追加する場合、変更が必要なのはファクトリ内だけであり、ドキュメントが作成されるすべての場所を変更する必要はありません。この分離により、コードベースの保守とテストが容易になります。
チャレンジ
簡単Factory パターンを使用して通知システムを構築しましょう。コードの至る所で異なる通知タイプを直接作成する代わりに、単純な文字列識別子に基づいてどの通知を作成するかを決定するファクトリクラスに作成ロジックを集中させます。
コードは以下の3つのファイルに分けて構成します:
Notification.cs:Notifications名前空間内に、文字列を返す単一のメソッドSend(string message)を持つINotificationインターフェースを定義します。次に、このインターフェースを実装する3つのクラスを作成します:EmailNotification-"Email: {message}"を返しますSmsNotification-"SMS: {message}"を返しますPushNotification-"Push: {message}"を返します
NotificationFactory.cs: 同じ名前空間内にNotificationFactoryクラスを作成し、型文字列に基づいて適切なINotificationを返すCreate(string type)メソッドを実装します:"email"はEmailNotificationを返します"sms"はSmsNotificationを返します"push"はPushNotificationを返します
EmailNotificationを返します。Program.cs: ファクトリのインスタンスを作成し、入力に基づいて通知を作成することで、すべてをまとめます。ファクトリは、どの具体的なクラスをインスタンス化するかについてのすべての意思決定を処理します。
2つの入力を受け取ります:
- 通知タイプ (例:
sms) - 送信するメッセージ (例:
Your order has shipped)
ファクトリを使用して適切な通知タイプを作成し、その Send メソッドをメッセージとともに呼び出して、結果を出力してください。
例えば、入力が push と Meeting in 5 minutes の場合、出力は以下のようになります:
Push: Meeting in 5 minutesメインコードで new EmailNotification() などを使用していないことに注目してください。単にファクトリに必要なものを要求しているだけです。後で SlackNotification を追加する場合、通知を作成するすべての場所ではなく、ファクトリを更新するだけで済みます!
チートシート
Factoryパターンは、オブジェクトの生成を別のメソッドやクラスに委譲する生成に関するパターンであり、生成ロジックを一元化し、コードの柔軟性を高めます。
ファクトリがない場合、オブジェクトの生成は分散し、密結合になります。
// ファクトリなし - 分散した生成ロジック
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();ファクトリを使用すると、生成ロジックは1か所にカプセル化されます。
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}クライアントコードは、具体的なクラスを知ることなくオブジェクトを要求します。
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // Opening PDFFactoryパターンを使用すると、新しい型の追加が容易になります。変更はファクトリ内に限定され、コードベース全体に分散することはありません。
自分で試してみよう
using System;
using Notifications;
class Program
{
public static void Main(string[] args)
{
// 入力を読み込む
string type = Console.ReadLine();
string message = Console.ReadLine();
// TODO: NotificationFactory のインスタンスを作成する
// TODO: ファクトリを使用して適切な通知を作成する
// TODO: Send メソッドを呼び出し、結果を出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。