Skip to content

Instantly share code, notes, and snippets.

@mpusz
Created December 14, 2012 11:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpusz/4284785 to your computer and use it in GitHub Desktop.
Save mpusz/4284785 to your computer and use it in GitHub Desktop.
[OOD] Builder, Factory Method and Abstract Factory design patterns
//
// 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