オブジェクト生成の結合度を下げる
オブジェクト指向設計において、クライアントコードが具体的なクラスを直接インスタンス化すると、結合度が高まり、拡張が困難になります。ファクトリパターンは、このインスタンス生成のロジックをカプセル化し、システムの変更影響範囲を最小限に抑えるための手法です。ここでは、自動車の生成を例に、単純ファクトリとファクトリメソッドの 2 段階の実装アプローチを解説します。
単純ファクトリパターンの実装
最初に、単一の工厂クラスが引数に基づいて異なる種類のオブジェクトを生成する「単純ファクトリ」を実装します。クライアントは生成ロジックの詳細を知る必要はなく、必要な種類を指定するだけで済みます。
まず、共通のインターフェースとなる車両クラスを定義します。
// Vehicle.h
#ifndef VEHICLE_H
#define VEHICLE_H
#include <iostream>
namespace pattern_demo {
class Vehicle {
protected:
bool m_engine_active;
public:
Vehicle() : m_engine_active(false) {}
virtual ~Vehicle() {}
virtual void ignite() = 0;
virtual void shutdown() = 0;
};
} // namespace pattern_demo
#endif
次に、具体的な車種クラスを作成します。ここではベンツ、アウディ、ランボルギーニの 3 種を用意します。
// Benz.h
#ifndef BENZ_H
#define BENZ_H
#include "Vehicle.h"
namespace pattern_demo {
class Benz : public Vehicle {
public:
void ignite() override {
if (m_engine_active) {
std::cerr << "Benz engine already active!" << std::endl;
return;
}
std::cout << "Benz engine starting..." << std::endl;
m_engine_active = true;
}
void shutdown() override {
if (!m_engine_active) {
std::cerr << "Benz engine already stopped..." << std::endl;
return;
}
std::cout << "Benz engine stopping..." << std::endl;
m_engine_active = false;
}
};
} // namespace pattern_demo
#endif
// Audi.h
#ifndef AUDI_H
#define AUDI_H
#include "Vehicle.h"
namespace pattern_demo {
class Audi : public Vehicle {
public:
void ignite() override {
if (m_engine_active) {
std::cerr << "Audi engine already active!" << std::endl;
return;
}
std::cout << "Audi engine starting..." << std::endl;
m_engine_active = true;
}
void shutdown() override {
if (!m_engine_active) {
std::cerr << "Audi engine already stopped..." << std::endl;
return;
}
std::cout << "Audi engine stopping..." << std::endl;
m_engine_active = false;
}
};
} // namespace pattern_demo
#endif
// Lamborghini.h
#ifndef LAMBORGHINI_H
#define LAMBORGHINI_H
#include "Vehicle.h"
namespace pattern_demo {
class Lamborghini : public Vehicle {
public:
void ignite() override {
if (m_engine_active) {
std::cerr << "Lamborghini engine already active!" << std::endl;
return;
}
std::cout << "Lamborghini engine starting..." << std::endl;
m_engine_active = true;
}
void shutdown() override {
if (!m_engine_active) {
std::cerr << "Lamborghini engine already stopped..." << std::endl;
return;
}
std::cout << "Lamborghini engine stopping..." << std::endl;
m_engine_active = false;
}
};
} // namespace pattern_demo
#endif
これらを生成する工廠クラスです。enum で車種を管理し、switch 文でインスタンスを返します。メモリ管理のため、破棄メソッドも用意しています。
// SimpleFactory.h
#ifndef SIMPLE_FACTORY_H
#define SIMPLE_FACTORY_H
#include "Vehicle.h"
#include "Benz.h"
#include "Audi.h"
#include "Lamborghini.h"
namespace pattern_demo {
enum class VehicleType {
TYPE_BENZ = 0,
TYPE_AUDI,
TYPE_LAMBORGHINI
};
class SimpleFactory {
public:
Vehicle* create(VehicleType type) {
switch (type) {
case VehicleType::TYPE_BENZ:
return new Benz();
case VehicleType::TYPE_AUDI:
return new Audi();
case VehicleType::TYPE_LAMBORGHINI:
return new Lamborghini();
default:
return nullptr;
}
}
void dispose(Vehicle* vehicle) {
delete vehicle;
}
};
} // namespace pattern_demo
#endif
クライアントコードでは、工廠を通じてオブジェクトを取得し、利用後は破棄します。
// main_simple.cpp
#include "SimpleFactory.h"
using namespace pattern_demo;
int main() {
SimpleFactory factory;
Vehicle* v1 = factory.create(VehicleType::TYPE_BENZ);
if (v1) {
v1->ignite();
v1->shutdown();
factory.dispose(v1);
}
Vehicle* v2 = factory.create(VehicleType::TYPE_AUDI);
if (v2) {
v2->ignite();
v2->shutdown();
factory.dispose(v2);
}
Vehicle* v3 = factory.create(VehicleType::TYPE_LAMBORGHINI);
if (v3) {
v3->ignite();
v3->shutdown();
factory.dispose(v3);
}
return 0;
}
この実装では、新しい車種を追加する際に SimpleFactory クラス内の switch 文を修正する必要があり、オープン・クローズド原則に違反する可能性があります。
ファクトリメソッドパターンへの移行
拡張性を高めるため、工廠自体を抽象化し、各車種専用の工廠クラスを作成するファクトリメソッドパターンへ変更します。これにより、新しい車種を追加する際は、既存のコードを修正せず新しい工廠クラスを追加するだけで済みます。
抽象工廠クラスを定義します。
// VehicleFactory.h
#ifndef VEHICLE_FACTORY_H
#define VEHICLE_FACTORY_H
#include "Vehicle.h"
namespace pattern_demo {
class VehicleFactory {
public:
virtual ~VehicleFactory() {}
virtual Vehicle* create() = 0;
void dispose(Vehicle* vehicle) {
delete vehicle;
}
};
} // namespace pattern_demo
#endif
各車種に対応した具体的な工廠クラスを実装します。
// BenzFactory.h
#ifndef BENZ_FACTORY_H
#define BENZ_FACTORY_H
#include "VehicleFactory.h"
#include "Benz.h"
namespace pattern_demo {
class BenzFactory : public VehicleFactory {
public:
Vehicle* create() override {
return new Benz();
}
};
} // namespace pattern_demo
#endif
// AudiFactory.h
#ifndef AUDI_FACTORY_H
#define AUDI_FACTORY_H
#include "VehicleFactory.h"
#include "Audi.h"
namespace pattern_demo {
class AudiFactory : public VehicleFactory {
public:
Vehicle* create() override {
return new Audi();
}
};
} // namespace pattern_demo
#endif
// LamborghiniFactory.h
#ifndef LAMBORGHINI_FACTORY_H
#define LAMBORGHINI_FACTORY_H
#include "VehicleFactory.h"
#include "Lamborghini.h"
namespace pattern_demo {
class LamborghiniFactory : public VehicleFactory {
public:
Vehicle* create() override {
return new Lamborghini();
}
};
} // namespace pattern_demo
#endif
メイン処理では、必要な工廠をインスタンス化して利用します。これにより、特定の車種に関するロジックが工廠クラス内に閉じ込められます。
// main_factory_method.cpp
#include "BenzFactory.h"
#include "AudiFactory.h"
#include "LamborghiniFactory.h"
using namespace pattern_demo;
void execute_brand(VehicleFactory* factory) {
Vehicle* car = factory->create();
if (car) {
car->ignite();
car->shutdown();
factory->dispose(car);
}
delete factory;
}
int main() {
execute_brand(new BenzFactory());
execute_brand(new AudiFactory());
execute_brand(new LamborghiniFactory());
return 0;
}
コンパイルおよび実行結果は以下の通りです。
$ g++ *.cpp -o vehicle_factory
$ ./vehicle_factory
Benz engine starting...
Benz engine stopping...
Audi engine starting...
Audi engine stopping...
Lamborghini engine starting...
Lamborghini engine stopping...