Skip to main content

GoFデザインパターン - 生成パターン(Creational Patterns)

生成パターンは、オブジェクトの生成メカニズムを抽象化し、システムをより柔軟で再利用可能にするためのパターンです。

1. Singleton(シングルトン)

概要

クラスのインスタンスが1つしか存在しないことを保証し、グローバルなアクセスポイントを提供します。

使用場面

  • 設定管理
  • ロガー
  • データベース接続プール
  • キャッシュマネージャー

サンプルコード

// スレッドセーフなシングルトン実装
public sealed class ConfigurationManager
{
private static readonly Lazy<ConfigurationManager> _instance =
new Lazy<ConfigurationManager>(() => new ConfigurationManager());

private Dictionary<string, string> _settings;

// プライベートコンストラクタ
private ConfigurationManager()
{
_settings = new Dictionary<string, string>
{
{ "ApiUrl", "https://api.example.com" },
{ "Timeout", "30" }
};
}

public static ConfigurationManager Instance => _instance.Value;

public string GetSetting(string key)
{
return _settings.TryGetValue(key, out var value) ? value : null;
}

public void SetSetting(string key, string value)
{
_settings[key] = value;
}
}

// 使用例
class Program
{
static void Main()
{
var config1 = ConfigurationManager.Instance;
var config2 = ConfigurationManager.Instance;

Console.WriteLine(ReferenceEquals(config1, config2)); // True

string apiUrl = config1.GetSetting("ApiUrl");
Console.WriteLine($"API URL: {apiUrl}");
}
}

2. Factory Method(ファクトリメソッド)

概要

オブジェクト生成のためのインターフェースを定義し、どのクラスをインスタンス化するかをサブクラスに決定させます。

使用場面

  • ログ出力先の切り替え(ファイル、コンソール、データベース)
  • 支払い方法の選択(クレジットカード、PayPal、銀行振込)
  • データベース接続の抽象化

サンプルコード

// 製品インターフェース
public interface ILogger
{
void Log(string message);
}

// 具象製品
public class FileLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[FILE] {DateTime.Now}: {message}");
// 実際にはファイルに書き込む処理
}
}

public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[CONSOLE] {DateTime.Now}: {message}");
}
}

public class DatabaseLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[DATABASE] {DateTime.Now}: {message}");
// 実際にはデータベースに保存する処理
}
}

// Creator(抽象クラス)
public abstract class LoggerFactory
{
// Factory Method
public abstract ILogger CreateLogger();

public void LogMessage(string message)
{
ILogger logger = CreateLogger();
logger.Log(message);
}
}

// 具象Creator
public class FileLoggerFactory : LoggerFactory
{
public override ILogger CreateLogger()
{
return new FileLogger();
}
}

public class ConsoleLoggerFactory : LoggerFactory
{
public override ILogger CreateLogger()
{
return new ConsoleLogger();
}
}

// 使用例
class Program
{
static void Main()
{
LoggerFactory factory;

// 環境に応じてファクトリを切り替え
string environment = "production";

if (environment == "production")
{
factory = new FileLoggerFactory();
}
else
{
factory = new ConsoleLoggerFactory();
}

factory.LogMessage("Application started");
}
}

3. Abstract Factory(抽象ファクトリ)

概要

関連する、または依存しあうオブジェクト群を、具象クラスを指定せずに生成するためのインターフェースを提供します。

使用場面

  • UI要素のテーマ切り替え(Windows、Mac、Linux)
  • データベースの切り替え(SQL Server、PostgreSQL、MySQL)
  • クロスプラットフォーム対応

サンプルコード

// 抽象製品A
public interface IButton
{
void Render();
void Click();
}

// 抽象製品B
public interface ITextBox
{
void Render();
string GetText();
}

// 具象製品 - Windows
public class WindowsButton : IButton
{
public void Render()
{
Console.WriteLine("Rendering Windows style button");
}

public void Click()
{
Console.WriteLine("Windows button clicked");
}
}

public class WindowsTextBox : ITextBox
{
public void Render()
{
Console.WriteLine("Rendering Windows style textbox");
}

public string GetText()
{
return "Windows textbox content";
}
}

// 具象製品 - Mac
public class MacButton : IButton
{
public void Render()
{
Console.WriteLine("Rendering Mac style button");
}

public void Click()
{
Console.WriteLine("Mac button clicked");
}
}

public class MacTextBox : ITextBox
{
public void Render()
{
Console.WriteLine("Rendering Mac style textbox");
}

public string GetText()
{
return "Mac textbox content";
}
}

// 抽象ファクトリ
public interface IGUIFactory
{
IButton CreateButton();
ITextBox CreateTextBox();
}

// 具象ファクトリ
public class WindowsFactory : IGUIFactory
{
public IButton CreateButton()
{
return new WindowsButton();
}

public ITextBox CreateTextBox()
{
return new WindowsTextBox();
}
}

public class MacFactory : IGUIFactory
{
public IButton CreateButton()
{
return new MacButton();
}

public ITextBox CreateTextBox()
{
return new MacTextBox();
}
}

// クライアントコード
public class Application
{
private IButton _button;
private ITextBox _textBox;

public Application(IGUIFactory factory)
{
_button = factory.CreateButton();
_textBox = factory.CreateTextBox();
}

public void Render()
{
_button.Render();
_textBox.Render();
}
}

// 使用例
class Program
{
static void Main()
{
IGUIFactory factory;
string os = Environment.OSVersion.Platform.ToString();

// OSに応じてファクトリを選択
if (os.Contains("Win"))
{
factory = new WindowsFactory();
}
else
{
factory = new MacFactory();
}

Application app = new Application(factory);
app.Render();
}
}

4. Builder(ビルダー)

概要

複雑なオブジェクトの構築プロセスを表現から分離し、同じ構築プロセスで異なる表現を生成できるようにします。

使用場面

  • 複雑なオブジェクトの段階的な構築
  • 不変オブジェクトの生成
  • テストデータの作成
  • SQLクエリの構築

サンプルコード

// 製品クラス
public class EmailMessage
{
public string To { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public List<string> Cc { get; set; } = new List<string>();
public List<string> Bcc { get; set; } = new List<string>();
public List<string> Attachments { get; set; } = new List<string>();
public bool IsHtml { get; set; }
public Priority Priority { get; set; }

public void Display()
{
Console.WriteLine($"To: {To}");
Console.WriteLine($"From: {From}");
Console.WriteLine($"Subject: {Subject}");
Console.WriteLine($"Body: {Body}");
if (Cc.Any()) Console.WriteLine($"Cc: {string.Join(", ", Cc)}");
if (Bcc.Any()) Console.WriteLine($"Bcc: {string.Join(", ", Bcc)}");
if (Attachments.Any()) Console.WriteLine($"Attachments: {string.Join(", ", Attachments)}");
Console.WriteLine($"HTML: {IsHtml}");
Console.WriteLine($"Priority: {Priority}");
}
}

public enum Priority
{
Low,
Normal,
High
}

// Builderインターフェース
public interface IEmailBuilder
{
IEmailBuilder SetTo(string to);
IEmailBuilder SetFrom(string from);
IEmailBuilder SetSubject(string subject);
IEmailBuilder SetBody(string body);
IEmailBuilder AddCc(string cc);
IEmailBuilder AddBcc(string bcc);
IEmailBuilder AddAttachment(string attachment);
IEmailBuilder SetIsHtml(bool isHtml);
IEmailBuilder SetPriority(Priority priority);
EmailMessage Build();
}

// 具象Builder
public class EmailBuilder : IEmailBuilder
{
private EmailMessage _email = new EmailMessage();

public IEmailBuilder SetTo(string to)
{
_email.To = to;
return this;
}

public IEmailBuilder SetFrom(string from)
{
_email.From = from;
return this;
}

public IEmailBuilder SetSubject(string subject)
{
_email.Subject = subject;
return this;
}

public IEmailBuilder SetBody(string body)
{
_email.Body = body;
return this;
}

public IEmailBuilder AddCc(string cc)
{
_email.Cc.Add(cc);
return this;
}

public IEmailBuilder AddBcc(string bcc)
{
_email.Bcc.Add(bcc);
return this;
}

public IEmailBuilder AddAttachment(string attachment)
{
_email.Attachments.Add(attachment);
return this;
}

public IEmailBuilder SetIsHtml(bool isHtml)
{
_email.IsHtml = isHtml;
return this;
}

public IEmailBuilder SetPriority(Priority priority)
{
_email.Priority = priority;
return this;
}

public EmailMessage Build()
{
return _email;
}
}

// Director(オプション)
public class EmailDirector
{
public EmailMessage CreateWelcomeEmail(IEmailBuilder builder, string recipient)
{
return builder
.SetTo(recipient)
.SetFrom("noreply@example.com")
.SetSubject("Welcome to Our Service")
.SetBody("<h1>Welcome!</h1><p>Thank you for joining us.</p>")
.SetIsHtml(true)
.SetPriority(Priority.Normal)
.Build();
}

public EmailMessage CreateUrgentNotification(IEmailBuilder builder, string recipient)
{
return builder
.SetTo(recipient)
.SetFrom("admin@example.com")
.SetSubject("URGENT: Action Required")
.SetBody("Please take immediate action on this matter.")
.SetPriority(Priority.High)
.AddAttachment("details.pdf")
.Build();
}
}

// 使用例
class Program
{
static void Main()
{
// 直接ビルダーを使用
var builder = new EmailBuilder();
var email1 = builder
.SetTo("user@example.com")
.SetFrom("sender@example.com")
.SetSubject("Hello")
.SetBody("This is a test email")
.AddCc("cc@example.com")
.AddAttachment("document.pdf")
.SetPriority(Priority.High)
.Build();

email1.Display();
Console.WriteLine("\n---\n");

// Directorを使用
var director = new EmailDirector();
var email2 = director.CreateWelcomeEmail(new EmailBuilder(), "newuser@example.com");

email2.Display();
}
}

5. Prototype(プロトタイプ)

概要

生成するオブジェクトの種類を原型となるインスタンスで指定し、それをコピーすることで新しいオブジェクトを生成します。

使用場面

  • オブジェクトの生成コストが高い場合
  • 既存のオブジェクトの設定を引き継ぎたい場合
  • 複雑な初期化が必要なオブジェクト
  • ゲーム開発でのエンティティ複製

サンプルコード

// プロトタイプインターフェース
public interface IPrototype<T>
{
T Clone();
}

// 具象プロトタイプ
public class Employee : IPrototype<Employee>
{
public string Name { get; set; }
public string Department { get; set; }
public Address Address { get; set; }
public List<string> Skills { get; set; }
public decimal Salary { get; set; }

public Employee()
{
Skills = new List<string>();
}

// シャローコピー
public Employee ShallowClone()
{
return (Employee)this.MemberwiseClone();
}

// ディープコピー
public Employee Clone()
{
var clone = (Employee)this.MemberwiseClone();

// 参照型のディープコピー
clone.Address = new Address
{
Street = this.Address.Street,
City = this.Address.City,
ZipCode = this.Address.ZipCode
};

clone.Skills = new List<string>(this.Skills);

return clone;
}

public void Display()
{
Console.WriteLine($"Name: {Name}");
Console.WriteLine($"Department: {Department}");
Console.WriteLine($"Address: {Address.Street}, {Address.City}, {Address.ZipCode}");
Console.WriteLine($"Skills: {string.Join(", ", Skills)}");
Console.WriteLine($"Salary: {Salary:C}");
}
}

public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}

// プロトタイプレジストリ(オプション)
public class EmployeeRegistry
{
private Dictionary<string, Employee> _prototypes = new Dictionary<string, Employee>();

public void RegisterPrototype(string key, Employee prototype)
{
_prototypes[key] = prototype;
}

public Employee GetPrototype(string key)
{
return _prototypes.ContainsKey(key) ? _prototypes[key].Clone() : null;
}
}

// 使用例
class Program
{
static void Main()
{
// オリジナルのEmployeeオブジェクトを作成
var originalEmployee = new Employee
{
Name = "John Doe",
Department = "Engineering",
Address = new Address
{
Street = "123 Main St",
City = "Tokyo",
ZipCode = "100-0001"
},
Salary = 80000m
};
originalEmployee.Skills.Add("C#");
originalEmployee.Skills.Add(".NET");

Console.WriteLine("Original Employee:");
originalEmployee.Display();
Console.WriteLine("\n---\n");

// ディープコピーを作成
var clonedEmployee = originalEmployee.Clone();
clonedEmployee.Name = "Jane Smith";
clonedEmployee.Department = "Marketing";
clonedEmployee.Address.City = "Osaka";
clonedEmployee.Skills.Add("SQL");

Console.WriteLine("Cloned Employee:");
clonedEmployee.Display();
Console.WriteLine("\n---\n");

Console.WriteLine("Original Employee (変更されていない):");
originalEmployee.Display();
Console.WriteLine("\n---\n");

// レジストリパターンの使用
var registry = new EmployeeRegistry();

var seniorDevPrototype = new Employee
{
Department = "Engineering",
Address = new Address { City = "Tokyo" },
Salary = 100000m
};
seniorDevPrototype.Skills.AddRange(new[] { "C#", ".NET", "Azure", "SQL" });

registry.RegisterPrototype("SeniorDeveloper", seniorDevPrototype);

// プロトタイプから新しいインスタンスを作成
var newDeveloper = registry.GetPrototype("SeniorDeveloper");
newDeveloper.Name = "Mike Wilson";
newDeveloper.Address.Street = "456 Tech Ave";
newDeveloper.Address.ZipCode = "100-0002";

Console.WriteLine("New Developer from Prototype:");
newDeveloper.Display();
}
}

まとめ

生成パターンの比較:

パターン主な目的使用場面
Singletonインスタンスを1つに制限設定管理、ロガー、キャッシュ
Factory Methodサブクラスが生成するクラスを決定ログ出力先の切り替え、プラグイン
Abstract Factory関連オブジェクト群の生成UI要素のテーマ切り替え
Builder複雑なオブジェクトの段階的構築不変オブジェクト、複雑な設定
Prototypeオブジェクトのクローン生成コストが高い、複雑な初期化

選択のガイドライン

  • インスタンスが1つだけ必要 → Singleton
  • 生成するクラスを実行時に決定 → Factory Method
  • 関連するオブジェクト群をまとめて生成 → Abstract Factory
  • 複雑なオブジェクトを段階的に構築 → Builder
  • 既存のオブジェクトをコピーして生成 → Prototype