簡易ファクトリパターンの概要
簡易ファクトリパターンは、オブジェクトの生成ロジックをクライアントコードから分離する創造型デザインパターンの一つです。このパターンでは、特定のインターフェースを実装するオブジェクトを生成するための専用クラス(ファクトリクラス)を定義します。クライアントは生成したいオブジェクトの具体的なクラス名を知らなくても、ファクトリにパラメータを渡すことで必要なインスタンスを取得できます。
例えば、異なる種類の通知手段(メール、SMS、プッシュ通知)を持つシステムにおいて、クライアントは通知タイプを指定するだけで、適切な通知オブジェクトを受け取ることができます。これにより、新しい通知手段を追加する際にも、クライアント側のコードを変更せずに済む可能性があります。
主な利用場面
- オブジェクト生成の統一: 複数の場所で同じような生成ロジックが重複している場合、ファクトリに集約することで保守性を高めます。
- 結合度の低下: クライアントコードが具体的なクラスに依存するのを防ぎ、抽象化に依存させることでシステム全体の結合度を下げます。
- 設定による柔軟性: 設定ファイルや環境変数に基づいて生成するクラスを切り替える際に有効です。
パターンの利点と欠点
利点:
- クライアントは生成ロジックの詳細を知る必要がなく、インターフェースのみを意識すれば済みます。
- 複雑なインスタンス化プロセスを一元管理できるため、コードの重複を防げます。
- パラメータを変更するだけで異なる実装を取得できるため、拡張性が向上します。
欠点:
- ファクトリクラスにすべての生成ロジックが集中するため、クラスが肥大化しやすいです。
- 新しい製品クラスを追加する際、ファクトリの条件分岐を変更する必要があり、開放閉鎖原則に完全に準拠しません。
- システム内にクラスが増えるため、全体構造の理解コストが上がります。
実装ステップ
- 共通のインターフェースまたは抽象クラスを定義し、製品が実すべきメソッドを宣言します。
- インターフェースを実具する具体的な製品クラスを複数作成します。
- ファクトリクラスを作成し、入力パラメータに基づいて適切な製品インスタンスを生成・返却するメソッドを実装します。
- クライアントはファクトリクラスを呼び出してインスタンスを取得し、インターフェースを通じて操作を行います。
各言語における実装例
1. C# での実装
C# ではインターフェースとクラスを用いて、通知システムを例に実装します。
using System;
// 通知インターフェース
public interface INotification
{
void Send(string message);
}
// 具体的な通知クラス:メール
public class EmailNotification : INotification
{
public void Send(string message)
{
Console.WriteLine($"メールを送信:{message}");
}
}
// 具体的な通知クラス:SMS
public class SmsNotification : INotification
{
public void Send(string message)
{
Console.WriteLine($"SMS を送信:{message}");
}
}
// ファクトリクラス
public class NotificationFactory
{
public INotification CreateNotification(string type)
{
switch (type.ToLower())
{
case "email":
return new EmailNotification();
case "sms":
return new SmsNotification();
default:
throw new ArgumentException("未知の通知タイプ");
}
}
}
// クライアントコード
public class Program
{
public static void Main()
{
var factory = new NotificationFactory();
var notifier = factory.CreateNotification("email");
notifier.Send("こんにちは");
}
}
2. Java での実装
Java ではドキュメント生成を例に、インターフェースと実装クラスを分離します。
public interface Document {
void generate();
}
public class PdfDocument implements Document {
@Override
public void generate() {
System.out.println("PDF ドキュメントを生成");
}
}
public class WordDocument implements Document {
@Override
public void generate() {
System.out.println("Word ドキュメントを生成");
}
}
public class DocumentFactory {
public Document getDocument(String format) {
if (format.equalsIgnoreCase("PDF")) {
return new PdfDocument();
} else if (format.equalsIgnoreCase("WORD")) {
return new WordDocument();
}
throw new IllegalArgumentException(" unsupported format");
}
}
public class Client {
public static void main(String[] args) {
DocumentFactory factory = new DocumentFactory();
Document doc = factory.getDocument("PDF");
doc.generate();
}
}
3. JavaScript での実装
JavaScript ではクラス構文を用いて、UI ボタンの生成ファクトリを作成します。
// ボタンインターフェースの役割を果たす基底クラス
class Button {
render() {
console.log(`ボタンを描画`);
}
onClick() {
console.log(`クリックイベント`);
}
}
class WindowsButton extends Button {
render() {
console.log(`Windows スタイルのボタン描画`);
}
}
class MacButton extends Button {
render() {
console.log(`Mac スタイルのボタン描画`);
}
}
// ファクトリ関数
function createButton(osType) {
if (osType === 'windows') {
return new WindowsButton();
} else if (osType === 'mac') {
return new MacButton();
}
throw new Error('Unsupported OS');
}
// 使用例
const btn = createButton('mac');
btn.render();
btn.onClick();
4. C++ での実装
C++ では抽象クラスとポインタを用いて、ログ出力システムを構築します。
#include <iostream>
#include <string>
#include <memory>
class ILogger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~ILogger() {}
};
class FileLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "ファイルにログ出力:" << msg << std::endl;
}
};
class ConsoleLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "コンソールにログ出力:" << msg << std::endl;
}
};
class LoggerFactory {
public:
static std::unique_ptr<ILogger> createLogger(const std::string& type) {
if (type == "file") {
return std::make_unique<FileLogger>();
} else if (type == "console") {
return std::make_unique<ConsoleLogger>();
}
throw std::invalid_argument("Invalid logger type");
}
};
int main() {
auto logger = LoggerFactory::createLogger("console");
logger->log("システム起動");
return 0;
}
5. Python での実装
Python ではデータベース接続オブジェクトの生成を例に示します。
class DBConnection:
def connect(self):
pass
class MySQLConnection(DBConnection):
def connect(self):
return "MySQL に接続しました"
class PostgreSQLConnection(DBConnection):
def connect(self):
return "PostgreSQL に接続しました"
class ConnectionFactory:
@staticmethod
def get_connection(db_type):
if db_type == "mysql":
return MySQLConnection()
elif db_type == "postgres":
return PostgreSQLConnection()
else:
raise ValueError("Unknown database type")
# 実行
if __name__ == "__main__":
conn = ConnectionFactory.get_connection("mysql")
print(conn.connect())
6. Go での実装
Go 言語ではインターフェースと構造体を用いて、決済処理システムを例に実装します。
package main
import "fmt"
type Payment interface {
Process(amount float64)
}
type CreditCard struct{}
func (c CreditCard) Process(amount float64) {
fmt.Printf("クレジットカードで %.2f 円決済\n", amount)
}
type PayPal struct{}
func (p PayPal) Process(amount float64) {
fmt.Printf("PayPal で %.2f 円決済\n", amount)
}
func NewPayment(method string) Payment {
switch method {
case "card":
return CreditCard{}
case "paypal":
return PayPal{}
default:
panic("unsupported payment method")
}
}
func main() {
p := NewPayment("card")
p.Process(1000.0)
}
7. PHP での実装
PHP では図形の描画を例に、インターフェースと静的メソッドを利用します。
<?php
interface Renderable {
public function render();
}
class Circle implements Renderable {
public function render() {
echo "円を描画します\n";
}
}
class Square implements Renderable {
public function render() {
echo "正方形を描画します\n";
}
}
class ShapeFactory {
public static function make($type) {
if ($type === 'circle') {
return new Circle();
} elseif ($type === 'square') {
return new Square();
}
throw new Exception("Invalid shape");
}
}
// 利用
$shape = ShapeFactory::make('circle');
$shape->render();
?>