🙆
Factoryパターン
Simple Factory、Abstract Factory、Factory Method
ソフトウェア設計において、インスタンス生成の責務をクライアントから分離するFactoryパターンは、多くの場面で役立ちます。ここでは、Simple Factory、Abstract Factory、Factory Methodの3つのパターンを紹介し、それぞれの使いどころとメリット・デメリットを解説します。
1.SimpleFactory
Simple Factoryは、インスタンスの生成ロジックをクライアントコードから分離する最もシンプルなパターンです。
クライアントコードは生成ロジックを知らずにインターフェース(もしくは抽象クラス)を通じてインスタンスにアクセスします。
- 基本static
- Factoryクラスで生成しているインスタンスについては、internalを使ってカプセル化しておく
- Factory+生成するInterface(抽象クラス)だけpublicにしておく
- 実装しやすくお手軽
// Simple Factoryの例
public static class NotificationFactory
{
public static INotification CreateNotification(string type)
{
return type switch
{
"Email" => new EmailNotification(),
"SMS" => new SmsNotification(),
_ => throw new ArgumentException("Invalid notification type.")
};
}
}
// 利用例
var notification = NotificationFactory.CreateNotification("Email");
notification.Send("Hello, world!");
2. Abstract Factory
- WindowsUIComponentFactoryとMacUIComponentFactoryの2種類あったとすると、その2種類のFactoryクラスの親クラスとしてインターフェース or 抽象クラスを設けます。2つのFactoryは同じインターフェース or 抽象クラスを継承しているので、利用側でFactoryの切り替えが可能です。
public interface IUIComponentFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
}
public class WindowsUIComponentFactory : IUIComponentFactory
{
public IButton CreateButton() => new WindowsButton();
public ITextBox CreateTextBox() => new WindowsTextBox();
}
public class MacUIComponentFactory : IUIComponentFactory
{
public IButton CreateButton() => new MacButton();
public ITextBox CreateTextBox() => new MacTextBox();
}
// 利用例(IUIComponentFactoryで差し替えが可能)
IUIComponentFactory uiFactory = new WindowsUIComponentFactory();
var button = uiFactory.CreateButton();
button.Render();
3. Factory Method
Factory Methodは、インスタンス生成の責務をサブクラスに委譲することで、共通ロジックを抽象クラスに集約できるのが特徴です。
各サブクラスはFactoryMethodを実装し、状況に応じて異なるインスタンスを生成できます。
抽象クラス(Creator)で共通ロジックを実装し、サブクラスでインスタンス生成を行います。
便利ですが、クラスが増えて可読性が落ちるのがデメリットです。
public interface IReport
{
void Prepare();
void Export();
}
public class PdfReport : IReport
{
public void Prepare() => Console.WriteLine("Preparing PDF Report");
public void Export() => Console.WriteLine("Exporting PDF Report");
}
public class ExcelReport : IReport
{
public void Prepare() => Console.WriteLine("Preparing Excel Report");
public void Export() => Console.WriteLine("Exporting Excel Report");
}
public abstract class ReportGenerator
{
public void GenerateReport()
{
//共通ロジックをかく
Console.WriteLine("Starting report generation...");
var report = CreateReport();
try
{
report.Prepare();
report.Export();
Console.WriteLine("Report generated successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to generate report: {ex.Message}");
}
}
protected abstract IReport CreateReport();
}
public class PdfReportGenerator : ReportGenerator
{
//各サブクラスでインスタンスを生成
protected override IReport CreateReport()
{
Console.WriteLine("Generating a PDF report instance.");
return new PdfReport();
}
}
public class ExcelReportGenerator : ReportGenerator
{
//各サブクラスでインスタンスを生成
protected override IReport CreateReport()
{
Console.WriteLine("Generating an Excel report instance.");
return new ExcelReport();
}
}
// 利用例
ReportGenerator generator = new PdfReportGenerator();
generator.GenerateReport();
generator = new ExcelReportGenerator();
generator.GenerateReport();
基本的には、お手軽さと柔軟性のトレードオフとなります。
適切に使い分けましょう。
Discussion