I am working with SFML (Simple Fast Media Library) on a hobbyist game design project, and ran into quite an annoying conundrum working with sf::Sprite, sf::Drawable, and cloning. Here's the issue:
As a major fan of composite and factory design pattern, many of the objects in my program contain members which are pointers to abstract bases that may be a single primitive, or a composite of sf::Drawable. To solve the issue of copy-construction/factory creation with abstract objects, I have been using type covariance in the form of a 'clone' method inherited from a widely used 'Clonable' base class. As a specific example, I created the abstract class 'Graphic' which represents a clonable drawable primitive:
class Clonable{
public:
virtual Clonable* clone() const = 0;
};
class Graphic: public sf::Drawable, public Clonable{
public:
virtual Graphic* clone() const = 0;
// Derivatives define as: {return new MyClass(*this);}
};
With this, I have designed multiple composites of Graphic which are also Clonable. The problem is, I want these composite members to able to be an sf::Sprite as well, which derives from sf::Drawable. This creates a diamond as both sf::Sprite and Graphic inherit from sf::Drawable. Normally, virtual inheritance would come to the rescue, but sf::Sprite is owned by SFML, not me.
I considered editing their source and recompiling the library, but this feels like butchery. Whenever SMFL decides to update their library, I will need to hack and recompile their source each time.
In summary: how do you solve the diamond problem when one (or both) of the first derived classes are proprietary/inaccessible?
Another possible avenue: how else might you promise c++ that an object will fulfill multiple requirements (draw and clone) without multiple inheritance?
Thanks,
Jordan
Whenever I run into inheritance issue, I consider replacing it by composition, which is another valid way to add behavior to existing classes.
class Graphic : public Clonable {
public:
const sf::Drawable& getDrawable() const { return *drawable; }
private:
sf::Drawable* drawble;
};
A Graphic is then a Clonable, but it also exposes a drawable interface through its getDrawable() method.