How to implement the factory method pattern in C++ correctly

后端 未结 11 1072
暗喜
暗喜 2020-11-22 14:55

There\'s this one thing in C++ which has been making me feel uncomfortable for quite a long time, because I honestly don\'t know how to do it, even though it sounds simple:<

相关标签:
11条回答
  • 2020-11-22 15:03

    I know this question has been answered 3 years ago, but this may be what your were looking for.

    Google has released a couple of weeks ago a library allowing easy and flexible dynamic object allocations. Here it is: http://google-opensource.blogspot.fr/2014/01/introducing-infact-library.html

    0 讨论(0)
  • 2020-11-22 15:07

    I don't try to answer all of my questions, as I believe it is too broad. Just a couple of notes:

    there are cases when object construction is a task complex enough to justify its extraction to another class.

    That class is in fact a Builder, rather than a Factory.

    In the general case, I don't want to force the users of the factory to be restrained to dynamic allocation.

    Then you could have your factory encapsulate it in a smart pointer. I believe this way you can have your cake and eat it too.

    This also eliminates the issues related to return-by-value.

    Conclusion: Making a factory by returning an object is indeed a solution for some cases (such as the 2-D vector previously mentioned), but still not a general replacement for constructors.

    Indeed. All design patterns have their (language specific) constraints and drawbacks. It is recommended to use them only when they help you solve your problem, not for their own sake.

    If you are after the "perfect" factory implementation, well, good luck.

    0 讨论(0)
  • 2020-11-22 15:09

    This is my c++11 style solution. parameter 'base' is for base class of all sub-classes. creators, are std::function objects to create sub-class instances, might be a binding to your sub-class' static member function 'create(some args)'. This maybe not perfect but works for me. And it is kinda 'general' solution.

    template <class base, class... params> class factory {
    public:
      factory() {}
      factory(const factory &) = delete;
      factory &operator=(const factory &) = delete;
    
      auto create(const std::string name, params... args) {
        auto key = your_hash_func(name.c_str(), name.size());
        return std::move(create(key, args...));
      }
    
      auto create(key_t key, params... args) {
        std::unique_ptr<base> obj{creators_[key](args...)};
        return obj;
      }
    
      void register_creator(const std::string name,
                            std::function<base *(params...)> &&creator) {
        auto key = your_hash_func(name.c_str(), name.size());
        creators_[key] = std::move(creator);
      }
    
    protected:
      std::unordered_map<key_t, std::function<base *(params...)>> creators_;
    };
    

    An example on usage.

    class base {
    public:
      base(int val) : val_(val) {}
    
      virtual ~base() { std::cout << "base destroyed\n"; }
    
    protected:
      int val_ = 0;
    };
    
    class foo : public base {
    public:
      foo(int val) : base(val) { std::cout << "foo " << val << " \n"; }
    
      static foo *create(int val) { return new foo(val); }
    
      virtual ~foo() { std::cout << "foo destroyed\n"; }
    };
    
    class bar : public base {
    public:
      bar(int val) : base(val) { std::cout << "bar " << val << "\n"; }
    
      static bar *create(int val) { return new bar(val); }
    
      virtual ~bar() { std::cout << "bar destroyed\n"; }
    };
    
    int main() {
      common::factory<base, int> factory;
    
      auto foo_creator = std::bind(&foo::create, std::placeholders::_1);
      auto bar_creator = std::bind(&bar::create, std::placeholders::_1);
    
      factory.register_creator("foo", foo_creator);
      factory.register_creator("bar", bar_creator);
    
      {
        auto foo_obj = std::move(factory.create("foo", 80));
        foo_obj.reset();
      }
    
      {
        auto bar_obj = std::move(factory.create("bar", 90));
        bar_obj.reset();
      }
    }
    
    0 讨论(0)
  • 2020-11-22 15:13

    Factory Pattern

    class Point
    {
    public:
      static Point Cartesian(double x, double y);
    private:
    };
    

    And if you compiler does not support Return Value Optimization, ditch it, it probably does not contain much optimization at all...

    0 讨论(0)
  • 2020-11-22 15:14

    I mostly agree with the accepted answer, but there is a C++11 option that has not been covered in existing answers:

    • Return factory method results by value, and
    • Provide a cheap move constructor.

    Example:

    struct sandwich {
      // Factory methods.
      static sandwich ham();
      static sandwich spam();
      // Move constructor.
      sandwich(sandwich &&);
      // etc.
    };
    

    Then you can construct objects on the stack:

    sandwich mine{sandwich::ham()};
    

    As subobjects of other things:

    auto lunch = std::make_pair(sandwich::spam(), apple{});
    

    Or dynamically allocated:

    auto ptr = std::make_shared<sandwich>(sandwich::ham());
    

    When might I use this?

    If, on a public constructor, it is not possible to give meaningful initialisers for all class members without some preliminary calculation, then I might convert that constructor to a static method. The static method performs the preliminary calculations, then returns a value result via a private constructor which just does a member-wise initialisation.

    I say 'might' because it depends on which approach gives the clearest code without being unnecessarily inefficient.

    0 讨论(0)
  • 2020-11-22 15:14
    extern std::pair<std::string_view, Base*(*)()> const factories[2];
    
    decltype(factories) factories{
      {"blah", []() -> Base*{return new Blah;}},
      {"foo", []() -> Base*{return new Foo;}}
    };
    
    0 讨论(0)
提交回复
热议问题