Fluent interfaces and inheritance in C++

前端 未结 5 2038
失恋的感觉
失恋的感觉 2020-12-16 21:11

I\'d like to build a base (abstract) class (let\'s call it type::base) with some common funcionality and a fluent interface, the problem I\'m facing is the retu

相关标签:
5条回答
  • 2020-12-16 21:28

    One solution would work like this:

    return *static_cast<my_type*>(&my_type().with_foo().with_bar());
    

    Using static_cast basically tells the compiler 'I know what I'm doing here'.

    0 讨论(0)
  • 2020-12-16 21:33

    The way I'd do it in C#, and I believe it would work in C++ too is to provide a default implementation for with_foo() and with_bar()... Forgive my c#, but:

    class base {
      virtual base with_foo()
      { throw new NotImplementedException(); }
      virtual base with_bar();
      { throw new NotImplementedException(); }
    }
    
    0 讨论(0)
  • 2020-12-16 21:43

    In C++ you should be returing pointers or references rather than values. Also, you might want to explain what you mean by "fluent interfaces".

    0 讨论(0)
  • 2020-12-16 21:46

    You should be returning references/pointers, and you should not need to keep the type information.

    class base {
      public:
         base();
         virtual ~base();
    
         base &with_foo();
         base &with_bar();
      protected:
         // whatever...
    };
    
    class my_type : public base {
      public:
        my_type();        
        // more methods...
    };
    
    base *build_my_type()
    {
       return &new my_type()->with_foo().with_bar();
    }
    

    You already have a virtual destructor. Presumably you have other virtual functions. Access everything through the base type and the virtual functions declared there.

    0 讨论(0)
  • 2020-12-16 21:49

    This problem of "losing the type" can be solved with templates - but it's rather complicated.

    Eg.

    class Pizza
    {
      string topping;
    public:
      virtual double price() const;
    };
    
    template <class T, class Base>
    class FluentPizza : public Base
    {
      T* withAnchovies() { ... some implementation ... };
    };
    
    class RectPizza : public FluentPizza<RectPizza, Pizza>
    {
      double price() const { return length*width; :) }
    };
    
    class SquarePizza : public FluentPizza<SquarePizza, RectPizza>
    {
       ... something else ...
    };
    

    You can then write

    SquarePizza* p=(new SquarePizza)->withAnchovies();
    

    The pattern is that instead of

    class T : public B
    

    you write

    class T : public Fluent<T, B>
    

    Another approach could be not to use fluent interface on the objects, but on pointers instead:

    class Pizza { ... };
    class RectPizza { ... };
    class SquarePizza { ... whatever you might imagine ... };
    
    template <class T>
    class FluentPizzaPtr
    {
      T* pizza;
    public:
      FluentPizzaPtr withAnchovies() {
        pizza->addAnchovies(); // a nonfluent method
        return *this;
      }
    };
    

    Use like this:

    FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... }
    
    FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies();
    
    0 讨论(0)
提交回复
热议问题