Reputation: 19677
Apologies for this quite abstract title.
More clearly:
Controler
and Interface
(hardware sense, unrelated to design pattern)Controler
created to be associated with an Interface
objectControler
subclass only work with a subset of Interface
subclasses (ControlerA + InterfaceA
or ControlerB + InterfaceB
but not ControlerA + InterfaceB
)Interface
subclass has its own methods not inherited (this is why only one kind of Controler
can use it)Controler
base class need to call some method of the base class Interface
I try to pass an Interface
objet to the Controler
constructor, hence in my class definition the Interface
attribute represents the abstract base class. But if my Controler
subclass A need to call a specific method of the Interface
A, an compilation error is raised as the Interface
base class doesn't own this method.
The only workaround I found was to call dynamic_cast
, but it obviously seems wrong.
Here are my Interface
classes:
class Interface {
public:
Interface() {};
virtual void method() = 0;
};
class InterfaceA : public Interface {
public:
InterfaceA() : Interface() {};
void method() override { cout << "A overriding" << endl; }
void onlyA() { cout << "A only" << endl; }
};
class InterfaceB : public Interface {
public:
InterfaceB() : Interface() {};
void method() override { cout << "B overriding" << endl; }
void onlyB() { cout << "B only" << endl; }
};
Here are my Controler
classes:
class Controler {
public:
Controler(Interface* i) : m_interface(i) {};
virtual void uniqueMethod() = 0;
void commonMethod() { m_interface->method(); }
Interface* m_interface;
};
class ControlerA : public Controler {
public:
ControlerA(InterfaceA* i) : Controler(i) {};
void uniqueMethod() override {dynamic_cast<InterfaceA *>(m_interface)->onlyA();}
};
class ControlerB : public Controler {
public:
ControlerB(InterfaceB* i) : Controler(i) {};
void uniqueMethod() override {dynamic_cast<InterfaceB *>(m_interface)->onlyB();}
};
And here is how I plan to use them:
auto ia = new InterfaceA();
auto ca = ControlerA(ia);
ca.commonMethod(); // Method defined in the base class
ca.uniqueMethod(); // Method defined in InterfaceA only
You can try it on Repl.it.
Is there any design pattern to solve this issue?
Upvotes: 1
Views: 67
Reputation: 18081
There is a problem indeed. There exists an invariant between the dynamic type of m_interface
and the dynamic type of the object that implement Controler
. But this invariant cannot be maintained by the Controler
class. So the m_interface
member is not a the right place.
The consequence is that you need to check at runtime that this member has the right type by using the dynamic_cast
each time you call uniqueMethod
. If the invariant is broken, the code will have UB because it would dereference a null pointer.
So this is not really a design pattern issue, but more fundamentally an object oriented programming recommendation: classes must ensure invariants.
class Controler {
public:
virtual void uniqueMethod() = 0;
virtual void commonMethod() = 0;
};
class ControlerA : public Controler {
public:
ControlerA(InterfaceA* i):m_interface{i} {
assert(dynamic_cast<InterfaceA*>(i)!=nullptr);
};
void uniqueMethod() override { m_interface->onlyA();}
void commonMethod() override { m_interface->method(); }
private: InterfaceA* m_interface;
};
class ControlerB : public Controler {
public:
ControlerB(InterfaceB* i):m_interface{i} {
assert(dynamic_cast<InterfaceB*>(i)!=nullptr);
};
void uniqueMethod() override { m_interface->onlyB();}
void commonMethod() override { m_interface->method(); }
private: InterfaceB* m_interface;
};
So now, it looks that we have a regular pattern, so this is where we can think about a more generic design:
template<class Inter,void(Inter::* OnlyFunc)()>
class ControlerImpl : public Controler {
public:
ControlerImpl(Inter* i):m_interface{i} {
assert(dynamic_cast<Inter*>(i)!=nullptr);
};
void uniqueMethod() override { (m_interface->*OnlyFunc)();}
void commonMethod() override { m_interface->method(); }
private: Inter* m_interface;
};
using ControlerA = ControlerImpl<InterfaceA,&InterfaceA::onlyA>;
using ControlerB = ControlerImpl<InterfaceB,&InterfaceB::onlyB>;
Upvotes: 2