Created
December 14, 2012 11:33
-
-
Save mpusz/4284785 to your computer and use it in GitHub Desktop.
[OOD] Builder, Factory Method and Abstract Factory design patterns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// author: Mateusz Pusz | |
// | |
#include <iostream> | |
#include <map> | |
#include <memory> | |
#include <deque> | |
#include <algorithm> | |
#include <functional> | |
#include <stdexcept> | |
// ------------ UTILITIES --------------- | |
template<typename T, typename ...Args> | |
inline std::unique_ptr<T> make_unique(Args&&... args) | |
{ | |
return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); | |
} | |
class CNonCopyable { | |
public: | |
CNonCopyable() = default; | |
CNonCopyable(const CNonCopyable &) = delete; | |
CNonCopyable &operator=(const CNonCopyable &) = delete; | |
}; | |
// ------------ FRAMEWORK CODE --------------- | |
class CProduct : CNonCopyable { | |
public: | |
class CAbstractFactory : CNonCopyable { | |
public: | |
virtual ~CAbstractFactory() {} | |
virtual void CreateEngine() = 0; | |
virtual void CreateTransmission() = 0; | |
void AddAirconditioner() { std::cout << "Adding conditioner\n"; } | |
void AddLeather() { std::cout << "Adding leather\n"; } | |
void AddStereo() { std::cout << "Adding stereo\n"; } | |
}; | |
virtual ~CProduct() {} | |
virtual CAbstractFactory &Factory() = 0; | |
}; | |
class CInventory : CNonCopyable { | |
std::vector<std::unique_ptr<CProduct> > _inventory; | |
CInventory() = default; | |
public: | |
static CInventory &Instance() | |
{ | |
static CInventory instance; | |
return instance; | |
} | |
void Add(std::unique_ptr<CProduct> product) { _inventory.emplace_back(std::move(product)); } | |
}; | |
class CBuilder : CNonCopyable { | |
using FFactory = std::function<std::unique_ptr<CProduct>()>; | |
std::map<std::string, FFactory> _factories; | |
CBuilder() = default; | |
public: | |
static CBuilder &Instance() | |
{ | |
static CBuilder instance; | |
return instance; | |
} | |
void Register(const std::string &product, FFactory &&factory) | |
{ | |
_factories[product] = std::move(factory); | |
} | |
std::unique_ptr<CProduct> Create(std::deque<std::string> &tokens) const | |
{ | |
std::clog << "------- STARTING PRODUCT CONSTRUCTION ---------\n"; | |
if(tokens.empty()) | |
throw std::runtime_error("No tokens provided"); | |
// create a product | |
auto product = _factories.at(tokens.front())(); | |
tokens.pop_front(); | |
// fill product with mandatory parts | |
auto &factory = product->Factory(); | |
factory.CreateEngine(); | |
factory.CreateTransmission(); | |
// fill product with options | |
while(tokens.size()) { | |
// get next token | |
auto token = std::move(tokens.front()); | |
tokens.pop_front(); | |
// finish building the product | |
if(token == "End") { | |
std::clog << "------- PRODUCT READY ---------\n"; | |
return product; | |
} | |
// add option | |
if(token == "AirConditioner") | |
factory.AddAirconditioner(); | |
else if(token == "Leather") | |
factory.AddLeather(); | |
else if(token == "Stereo") | |
factory.AddStereo(); | |
else | |
throw std::runtime_error("Invalid token: " + token); | |
} | |
throw std::runtime_error("Product creation interrupted"); | |
} | |
}; | |
// ------------ CLASS HIERARCHY --------------- | |
class CProductWheelLoader : public CProduct { | |
class CFactory : public CAbstractFactory { | |
public: | |
void CreateEngine() override { std::cout << "Adding Wheel Loader engine\n"; } | |
void CreateTransmission() override { std::cout << "Adding Wheel Loader transmission\n"; } | |
}; | |
public: | |
static CFactory _factory; | |
virtual CFactory &Factory() { return _factory; } | |
}; | |
CProductWheelLoader::CFactory CProductWheelLoader::_factory; | |
class CProductBulldozer : public CProduct { | |
class CFactory : public CAbstractFactory { | |
public: | |
void CreateEngine() override { std::cout << "Adding Bulldozer engine\n"; } | |
void CreateTransmission() override { std::cout << "Adding Bulldozer transmission\n"; } | |
}; | |
public: | |
static CFactory _factory; | |
virtual CFactory &Factory() { return _factory; } | |
}; | |
CProductBulldozer::CFactory CProductBulldozer::_factory; | |
class CProductBackhoe : public CProduct { | |
class CFactory : public CAbstractFactory { | |
public: | |
void CreateEngine() override { std::cout << "Adding Backhoe engine\n"; } | |
void CreateTransmission() override { std::cout << "Adding Backhoe transmission\n"; } | |
}; | |
public: | |
static CFactory _factory; | |
virtual CFactory &Factory() { return _factory; } | |
}; | |
CProductBackhoe::CFactory CProductBackhoe::_factory; | |
int main() | |
{ | |
try { | |
// initialize builder | |
CBuilder &builder = CBuilder::Instance(); | |
builder.Register("WheelLoader", []{ return make_unique<CProductWheelLoader>(); }); | |
builder.Register("Bulldozer", []{ return make_unique<CProductBulldozer>(); }); | |
builder.Register("Backhoe", []{ return make_unique<CProductBackhoe>(); }); | |
// test tokens stream | |
std::deque<std::string> tokens = { | |
"WheelLoader", "End", | |
"Bulldozer", "AirConditioner", "Leather", "Stereo", "End", | |
"Backhoe", "AirConditioner", "End" | |
}; | |
// test | |
CInventory &inventory = CInventory::Instance(); | |
while(tokens.size()) | |
inventory.Add(builder.Create(tokens)); | |
} | |
catch(const std::exception &ex) { | |
std::cerr << "ERROR: " << ex.what() << "\n"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment