1

In the target of learning the OOP in C++, I want to create a ToolFactory with the factory design pattern.

But I don't know how I must develop this. But I had try to develop it, but it doesn't work.

You find below all the code I have made for create the ToolFactory and can to create a toolbox with a vector.

I hope to be clear in my question.

The class :

Handyman.hpp :
#ifndef BRICOLEUR_HPP
#define BRICOLEUR_HPP

#include <ScrewDriver.hpp>
#include <Saw.hpp>
#include <Hammer.hpp>
#include <string>

class Handyman
{
    public:
        void screw(ScrewDriver t){t.screw();}
        void cut(Saw s){s.cut();}
        void crack(Hammer m){m.crack();}
};

#endif
Hammer.hpp :
#ifndef MARTEAU_HPP
#define MARTEAU_HPP

#include <iostream>
#include <Tool.hpp>
#include <ToolBreak.hpp>

class Hammer : public Tool, ToolBreak
{
    std::string nom;

    public:
        Hammer(std::string const & nom){this->nom = nom;}
        std::string get_nom(){return this->nom;}
        void crack(){std::cout << "Hammer crack" << std::endl;}
        ~Hammer();
};

#endif
Saw.hpp :
....
#include <ToolCutting.hpp>

class Saw : public Tool, ToolCutting
{
    ....
    public:
        ....
        void cut(){std::cout << "Saw cut" << std::endl;}
        ....
};

#endif
Screwdriver.hpp :
....
#include <ToolScrewing.hpp>

class ScrewDriver : public Tool, ToolScrewing
{
    ....
    public:
        ....
        void screw(){std::cout << "ScrewDriver screw" << std::endl;}
        ....
};

#endif

The interface

ToolBreak.hpp
#ifndef TOOLBREAK_HPP
#define TOOLBREAK_HPP

#include <string>

class ToolBreak
{
    public:
        virtual std::string get_nom() = 0;
        virtual void crack() = 0;
};

#endif

The same model for the interface :

  • ToolCutting.hpp
  • ToolScrewing.hpp

The factory

ToolFactory.hpp
#ifndef TOOLFACTORY_HPP
#define TOOLFACTORY_HPP

#include <memory>
#include <Tool.hpp>
#include <ScrewDriver.hpp>
#include <Hammer.hpp>
#include <Saw.hpp>

class ToolFactory
{
    public:
        static Tool create (std::string name, std::string arg);
};

#endif
ToolFactory.cpp
#include <ToolFactory.hpp>

Tool ToolFactory::create(std::string name, std::string args)
{
    if(name.compare("Screwdriver") == 0)
    {
        return ScrewDriver(args);
    }
    else if(name.compare("Hammer") == 0)
    {
        return Hammer(args);
    }
    else if(name.compare("Saw") == 0)
    {
        return Saw(args);
    }
    else
    {
        std::cout << "Error ! Tool not found" << std::endl;
    }
}

main.cpp

#include <vector>
#include <Handyman.hpp>
#include <ToolFactory.hpp>

using namespace std;

int main()
{
    Handyman handy;
    ScrewDriver screwdriver("Blue screwdriver");
    Saw saw("Green saw");
    Hammer hammer("Yellow hammer");

    handy.screw(screwdriver);
    handy.cut(saw);
    handy.crack(hammer);

    Tool screwdriverV2 = ToolFactory::create("Screwdriver", "Screwdriver v2");

    screwdriverV2.display();
    return 0;
}

OUTPUT

clang++ -Wall -std=c++14 -c -o obj/main.o src/main.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/Handyman.o src/Handyman.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/Saw.o src/Saw.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/ScrewDriver.o src/ScrewDriver.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/Hammer.o src/Hammer.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/Tool.o src/Tool.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/ToolScrewing.o src/ToolScrewing.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/ToolBreak.o src/ToolBreak.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/ToolCutting.o src/ToolCutting.cpp -I include
clang++ -Wall -std=c++14 -c -o obj/ToolFactory.o src/ToolFactory.cpp -I include
src/ToolFactory.cpp:21:1: warning: control may reach end of non-void function [-Wreturn-type]
}
^
1 warning generated.
clang++ -Wall -std=c++14 -o bin/main obj/main.o obj/Handyman.o obj/Saw.o obj/ScrewDriver.o obj/Hammer.o obj/Tool.o obj/ToolScrewing.o obj/ToolBreak.o obj/ToolCutting.o obj/ToolFactory.o
./bin/main
ScrewDriver screw
Saw cut
Hammer crack
It's a tool
CookieHacker
  • 103
  • 2
  • 11
  • 1
    You're running into object slicing: https://stackoverflow.com/questions/274626/what-is-object-slicing – Chad Feb 22 '19 at 15:18
  • 6
    ... and why not fix `src/ToolFactory.cpp:21:1: warning: control may reach end of non-void function` before posting the question? – Ted Lyngmo Feb 22 '19 at 15:34
  • 2
    Also consider using something else than strings for the lookup. `enum class ToolType : int { Screwdriver, Hammer, Saw };` is better since you'd get a compilation error if you try to use `ToolType::Screwdiver` in your code. – Ted Lyngmo Feb 22 '19 at 16:07
  • You might want to get used to using the constructors initialiser list (not to be confused with `std::initializer_list`): `Hammer(...) : nom(nom) { }`; you avoid first initialising the object and then doing the assignment separately, but get direct initialisation via `std::string`'s constructor (and there are some types that only *can* be initialised in the i.-list, such as references or types without default constructor). – Aconcagua Feb 22 '19 at 17:02
  • @TedLyngmo I have see this error, but I have provided to change all the if condition in the function by a switch condition with the "enum class" but thanks you for your answer ;) – CookieHacker Feb 22 '19 at 18:56
  • @Aconcagua okay I go put it in all tools, thanks. – CookieHacker Feb 22 '19 at 18:56

1 Answers1

3

To keep object type you need to pass it by pointer, not value, otherwise you will have problem with object slicing. As you pass object ownership when you create, you should use smart pointer:

class ToolFactory
{
    public:
        static std::unique_ptr<Tool> create (std::string name, std::string arg);
};

std::unique_ptr<Tool> ToolFactory::create(std::string name, std::string args)
{
    if(name.compare("Screwdriver") == 0)
    {
        return std::make_unique<ScrewDriver>(args);
    }
    // note as your if has return statement you do not really need else
    ...
}

Next step could be to eliminate cascade if with std::unordered_map:

class ToolFactory
{
        using Creator  = std::function<std::unique_ptr<Tool>(std::string)>
        using Creators = std::unordered_map<std::string,Creator>;

        Creators m_creators;
    public:
        ToolFactory();
        std::unique_ptr<Tool> create (std::string name, std::string arg);
};

ToolFactory::ToolFactory()
{
     m_creators[ "Screwdriver" ] = []( std::string arg ) { return std::make_unique<ScrewDriver>(arg); }
     ...
}

std::unique_ptr<Tool> ToolFactory::create (std::string name, std::string arg)
{
    auto f = m_creators.find( name );
    if( f == m_creators.end() ) 
       throw std::runtime_error( "unknown type:" + name );
    return f->second( arg );
}

This way it would be not only more generic but you can easily extend it to add new types dynamically.

CookieHacker
  • 103
  • 2
  • 11
Slava
  • 43,454
  • 1
  • 47
  • 90