I have created a small chess game and implemented a factory class to produce game pieces. However, I realized that my implementation is not ideal, as every game piece is responsible for registering itself with the factory. For instance, the bishop registers itself with the following code:
// white bishop registration
bool Bishop::m_registerit_white =
Factory<GamePiece>::registerit
(
'B', []()->std::unique_ptr<GamePiece>
{ return std::make_unique<Bishop>(WHITE); }
);
// black bishop registration
bool Bishop::m_registerit_black =
Factory<GamePiece>::registerit
(
'b', []()->std::unique_ptr<GamePiece>
{ return std::make_unique<Bishop>(BLACK); }
);
My teacher pointed out that these registration calls result in A LOT of code duplication for each piece (I agree). He suggested that I reduce the duplication by creating a map between characters and their corresponding classes, so that I could call the class constructor when needed, instead of having each piece register itself with the factory.
I am not entirely sure how to implement this suggestion and would appreciate some guidance.
This is my factory class:
template<typename T>
class Factory {
public:
//the pointer to the function that creates the object
using creationFunc = unique_ptr<T>(*)();
//register a pair of char+creator function into the map:
static bool registerit(const char& name, creationFunc f) {
getMap().emplace(name, f);
return true;
}
//creates the objects according to the char that represents it
//(searches this char in the map):
static unique_ptr<T> create(const char& name) {
const auto& map = getMap();
auto it = map.find(name);
if (it == map.end())
return nullptr;
return it->second();
}
private:
static auto& getMap() {
static map<char, creationFunc> map;
return map;
}
};
If all the pieces are constructed the same way you could create a templated utility function or class to perform the registration.
For example:
template <typename Piece>
struct PieceRegister
{
PieceRegister(char white, char black)
{
Factory<GamePiece>::registerit
(
white, []()->std::unique_ptr<GamePiece>
{ return std::make_unique<Piece>(WHITE); }
);
Factory<GamePiece>::registerit
(
black, []()->std::unique_ptr<GamePiece>
{ return std::make_unique<Piece>(BLACK); }
);
}
};
PieceRegister<Bishop> bishopRegister('B', 'b');