How to avoid 'Call super' code smell with multiple bases having same method name?
According to Wikipedia, Call super is a design anti-pattern in which a particular class stipulates that in a derived subclass, the user is required to override a method and call back the overridden function itself at a particular point. For instance: class Network_handler; struct A { void setup_network(Network_handler & net) { const int fd = get_file_descriptor(); net.add_callback(fd); } }; struct B : A { void setup_network(Network_handler & net) { // Setup more callbacks const std::vector descriptors = get_descriptors(); for (const int fd : descriptors) { net.add_callback(fd); } // Derived class calls the method with the same name in the base class A::setup_network(net); } }; void do_work() { Network_handler net; B b; b.setup_network(net); } The same Wikipedia article suggests a better solution such that the base class should provide a virtual method called as part of the interface function in which the derived class can do whatever specific work in needs to do and the base class will call this function: struct A { void setup_network(Network_handler & net) { // Base class calls virtual method in which derived will do its own network setup setup_network_derived(net); const int fd = get_file_descriptor(); net.add_callback(fd); } protected: virtual void setup_network_derived(Network_handler &) = 0; }; struct B : A { void setup_network_derived(Network_handler & net) override { // Do necessary work. No need to call A::setup_network(net); } }; Now B has to provide implementation for the setup_network_derived and it does not need to call A::setup_network(net); setup_network_derived isn't the most elegant name, but its just given for the sake of the example. The apparent problem is that this solution is not scalable. If there was struct C derived from struct B and if the struct C also had setup_network method, then struct B would have to provide setup_network_derived_derived(Network_handler &); method and so on: struct B : A { void setup_network_derived(Network_handler &) override; protected: virtual void setup_network_derived_derived(Network_handler &) = 0; }; struct C : B { void setup_network_derived_derived(Network_handler &) override; } A solution which violates the Call super anti-pattern would be something like this: void A::setup_network(Network_handler & net) { net.add_callback(fd()); } void B::setup_network(Network_handler & net) { A::setup_network(net); net.add_callback(some_other_fd()); } void C::setup_network(Network_handler & net) { B::setup_network(net); net.add_callback(yet_another_fd()); } Question: Is there an elegant and scalable way to avoid Call super if there are multiple classes in the inheritance tree which have the same method?

According to Wikipedia, Call super is a design anti-pattern in which
a particular class stipulates that in a derived subclass, the user is required to override a method and call back the overridden function itself at a particular point.
For instance:
class Network_handler;
struct A
{
void setup_network(Network_handler & net)
{
const int fd = get_file_descriptor();
net.add_callback(fd);
}
};
struct B : A
{
void setup_network(Network_handler & net)
{
// Setup more callbacks
const std::vector descriptors = get_descriptors();
for (const int fd : descriptors)
{
net.add_callback(fd);
}
// Derived class calls the method with the same name in the base class
A::setup_network(net);
}
};
void do_work()
{
Network_handler net;
B b;
b.setup_network(net);
}
The same Wikipedia article suggests a better solution such that the base class should provide a virtual method called as part of the interface function in which the derived class can do whatever specific work in needs to do and the base class will call this function:
struct A
{
void setup_network(Network_handler & net)
{
// Base class calls virtual method in which derived will do its own network setup
setup_network_derived(net);
const int fd = get_file_descriptor();
net.add_callback(fd);
}
protected:
virtual void setup_network_derived(Network_handler &) = 0;
};
struct B : A
{
void setup_network_derived(Network_handler & net) override
{
// Do necessary work. No need to call A::setup_network(net);
}
};
Now B
has to provide implementation for the setup_network_derived
and it does not need to call A::setup_network(net)
;
setup_network_derived
isn't the most elegant name, but its just given for the sake of the example.
The apparent problem is that this solution is not scalable. If there was struct C
derived from struct B
and if the struct C
also had setup_network
method, then struct B
would have to provide setup_network_derived_derived(Network_handler &);
method and so on:
struct B : A
{
void setup_network_derived(Network_handler &) override;
protected:
virtual void setup_network_derived_derived(Network_handler &) = 0;
};
struct C : B
{
void setup_network_derived_derived(Network_handler &) override;
}
A solution which violates the Call super anti-pattern would be something like this:
void A::setup_network(Network_handler & net)
{
net.add_callback(fd());
}
void B::setup_network(Network_handler & net)
{
A::setup_network(net);
net.add_callback(some_other_fd());
}
void C::setup_network(Network_handler & net)
{
B::setup_network(net);
net.add_callback(yet_another_fd());
}
Question:
Is there an elegant and scalable way to avoid Call super if there are multiple classes in the inheritance tree which have the same method?