How to properly implement derived classes with their own functions in a Factory Pattern?

115 Views Asked by At

I face a problem with derived classes having their own functions when using Factory Pattern. Please share how you design OOP in this case.

Assume I have a general class B, and many derived classes, named D1, D2, D3, etc. Each of these classes has its own function, named F1, F2, F3, etc. respectively. Using Factory Pattern, we will have a program like this:

class FactoryPattern {
    static B* create(string type) {
        if (type == "D1")
            return new D1();
        if (type == "D2")
            return new D2();
        ...
    }
};

However, B must have its virtual functions for all of the F, and for each of the derived classes, I will print (or throw an error or anything else) to prevent the user from using the functions.

With the below implementation, the program will scale tremendously, as when we need to implement a new function, for example, function F3 for class D3, we need to update the program for both D1 and D2.

class B {
protected:
    virtual void F1() = 0;
    virtual void F2() = 0;
};

class D1: public B {
public:
    void F1() {
        cout << "F1 from D1\n";
    }

    void F2() {
        cout << "This function is not implemented\n";
    }
};

class D2: public B {
public:
    void F1() {
        cout << "This function is not implemented\n";
    }

    void F2() {
        cout << "F2 from D2\n";
    }
};

I thought about using a general function for class B, and just overriding if needed in the derived class, however, when combining with Factory Pattern, the returned pointer calls directly to B::F1 and B::F2, which are not implemented.

class B {
protected:
    void F1() {
        cout << "This function is not implemented\n";
    }
    void F2() {
        cout << "This function is not implemented\n";
    }
};

class D1: public B {
public:
    void F1() {
        cout << "F1 from D1\n";
    }
};

class D2: public B {
public:
    void F2() {
        cout << "F2 from D2\n";
    }
};

Edit: The function F can take different types of parameters, and the naming is also specific, so I can not use a general function like foo.

Edit2: For a more detailed problem, let Inference be the general class (B), and I have 2 derived classes, named Classifier, and Detector. For initialisation, all of them take model_path, however, Detector can take the list of output information (bounding_box, label, etc.) in addition, then the initialisation of them will take different types of parameters. There are many other special functions, this is just an example.

class Inference {
public:
    virtual void Initilisation(string model_path) = 0;
    virtual void Initilisation(string model_path, vector<string> output_information) = 0;

};

class Detector: public Inference {
public:
    void Initilisation(string model_path) {
        cout << "Not implemented\n";
    }

    void Initilisation(string model_path, vector<string> output_information) {
        // do some initialisation
        // for example, output_information can be {"label", "conf", ...}
    }
};

class Classifier: public Inference {
public:
    void Initilisation(string model_path) {
        // do some initialisation
    }

    void Initilisation(string model_path, vector<string> output_information) {
        cout << "Not implemented\n";
    }
};
3

There are 3 best solutions below

5
nick On

Declaring a new function for each derived class kind of defeats to point of polymorphism. The usual way to go is to declare one virtual function in the base class, then to override it in the derivatives with their own functionality.

Example:

#include <iostream>
#include <memory>

class B
{
public:
    virtual void foo() = 0;
};

class D1: public B
{
public:
    void foo() override
    {
        std::cout << "I'm a D1\n";
    }
};

class D2: public B
{
public:
    void foo() override
    {
        std::cout << "I'm a D2\n";
    }
};


int main()
{
  std::unique_ptr<B> pD1 = std::make_unique<D1>(D1());
  pD1->foo();

  std::unique_ptr<B> pD2 = std::make_unique<D2>(D2());
  pD2->foo();

  // pD1 and pD2 are of the same type, could be stored in the same vector etc.
}
2
Pepijn Kramer On

You would need to make an abstract baseclass first and make sure the factory returns unique pointers to that. unique_ptr because naked new/delete is not recommended in current C++. SO you will transfer ownership of the pointer to the caller of the factory.

#include <iostream>
#include <memory>
#include <string>
#include <string_view>

// When using a factor always start with an
// abstract baseclass (interface)

class my_interface_t
{
public:
    virtual void do_something() = 0;
};

// then concrete classes, note the override keyword

class class_a_t :
    public my_interface_t
{
public:
    void do_something() override
    {
        std::cout << "class_a_t::do_something()\n";
    }
};

class class_b_t :
    public my_interface_t
{
public:
    void do_something() override
    {
        std::cout << "class_b_t::do_something()\n";
    }
};


class factory_t
{
public:
    std::unique_ptr<my_interface_t> create(std::string_view name)
    {
        // do NOT use new/delete in current C++

        if (name == "A") return std::make_unique<class_a_t>();
        if (name == "B") return std::make_unique<class_b_t>();

        throw std::invalid_argument("invalid class name");
    }
};


int main()
{
    factory_t factory;
    auto a = factory.create("A");
    auto b = factory.create("B");

    a->do_something();
    b->do_something();

    return 0;
}
0
Deepak Atariya On

As far as I studied about factory design pattern and it's uses. I have mentioned in this article. Please go through it. I hope it solves your doubt.

Check it out : https://deepakatariya.com/factory-design-pattern-can-help/