c++design-patternsfactory-pattern

reduce code duplication - factory design pattern


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;
    }
};

Solution

  • 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');