I am working on a slide-based application in C++. Each slide has a slide-items collection which can include items like caption, button, rectangle, etc.
Only some of these items support fill, while others don't.
What is the best way to implement the fill for the slide items in this case? Here are two ways that I thought of:
Create an interface Fillable
and implement this interface for slide items
which support fill, keeping all the properties related to fill in the interface. When iterating over the list of slide items, dynamic_cast them
into Fillable
, and if successful, do the operation related to fill.
Make a fill
class. Make a fill
pointer a part of slide item class, assign the
fill
object to the fill
pointer for those objects which support fill, and for rest of them keep it null. Give a function GetFill
, which will return the fill
for the items if it exists otherwise returns NULL
.
What's the best approach for this? I'm interested in performance and maintainability.
I would do a combination of the two. Make your Fillable
interface and have it be the return type for your GetFill
method. This is better than the dynamic cast approach. Using dynamic cast to query for the interface requires that the actual slide item object implement the interface if it is to support it. With an accessor method like GetFill
however, you have the option of providing a reference/pointer to some other object that implements the interface. You can also just return this
if the interface is in fact implemented by this object. This flexibility can help avoid class bloat and promote the creation of re-usable component objects that can be shared by multiple classes.
Edit:
This approach also works nicely with the null object pattern. Instead of returning a null pointer for the objects that don't support Fillable
, you can return a simple no-op object that implements the interface. Then you don't have to worry about always checking for null pointers in the client code.
The answer is it depends.
I don't see the point in having to clutter your base interface with fill/get_fillable_instance/...
if not every object is supposed to handle fill. You can however get away with just
struct slide_object
{
virtual void fill() {} // default is to do nothing
};
but it depends on whether you think fill
should appear in the slide object abstract class. It rarely should however, unless being non fillable is exceptional.
Dynamic casting can be correct in the case you need to provide two distinct classes of objects (and no more than two), some of them being fillable, and the other having nothing to do with fillability. In this case, it makes sense to have two sub-hierarchies and use dynamic casting where you need.
I have used this approach successfully in some cases and it is simple and maintainable, provided the dispatch logic is not scattered (ie. there is only one or two places where you dynamic cast).
If you are expected to have more fill-like behavior, then dynamic_cast
is a wrong choice since it will lead to
if (auto* p = dynamic_cast<fillable*>(x))
...
else if (auto* p = dynamic_cast<quxable*>(x))
...
which is bad. If you are going to need this, then implement a Visitor pattern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With