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 return type of all those methods
class base {
public:
base();
virtual ~base();
base& with_foo();
base& with_bar();
protected:
// whatever...
};
Now I could make subtypes, e.g.:
class my_type : public base {
public:
myType();
// more methods...
};
The problem comes when using those subtypes like this:
my_type build_my_type()
{
return my_type().with_foo().with_bar();
}
This won't compile because we're returning base instead of my_type.
I know that I could just:
my_type build_my_type()
{
my_type ret;
ret.with_foo().with_bar();
return ret;
}
But I was thinking how can I implement it, and I've not found any valid ideas, some suggestion?
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(); }
}