Fluent interfaces and inheritance in C++

安稳与你 提交于 2019-11-29 04:27:55

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();

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.

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'.

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

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(); }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!