Dealing with an opaque objects hierarchy
"even monoliths can have a family" 2012 by Arnaud Sintès Keywords: architecture, c++, interface, multiple-inheritance, template
specialization As an introduction note, please refer to my previous document
about the opaque objects and the specialized template interfaces concept. |
We’ve seen before the interest to add multiple interfaces access to your object to keep the full control over the user’s visibility of its content.
It was called full
opaque objects (http://www.silentbreed.com/opaque/).
This is great, but you may be afraid to generalize this approach
of object implementation by considering a classic object hierarchy with a lot
of parent/child dependencies which
will have to deal with crossed access over
all these objects interfaces.
Let’s consider a simple class hierarchy were two objects (‘A’ and ‘B’) have to be accessed in read only mode at some point and in read/write mode somewhere else.
Writing the ‘B’ class with its interfaces is easy:
// ---------- // interface access
types class Read; class ReadWrite; // ---------- // 'B' templatized
interfaces declaration template< class
T = void > class
IB {}; // Read interface
access to 'B' template<> __interface IB< Read > { const void
GetSomething() const; }; // Read/Write
interface access to 'B' template<> __interface IB< ReadWrite > { const void
GetSomething() const; void SetSomething(); }; // ---------- // 'B' object
definition class B sealed :
public IB< Read > ,
public IB< ReadWrite > { private: //
IB< Read, ReadWrite > const void
GetSomething() const sealed
{} void SetSomething() sealed
{} }; |
The problem we have to consider is how to get the read only access interface of ‘B’ through the read only access interface of ‘A’, and how to get the read/write access interface of ‘B’ through the read/write access interface of ‘A’.
To be more clear, we hope to get a single method “GetB()” in both read only and read/write interfaces of ‘A’ to get a const IB< Read > interface in the first case and a IB< ReadWrite > interface in the second one.
// ---------- // interface access
types class Read; class ReadWrite; // ---------- // 'A' templatized
interfaces declaration template< class
T = void > class
IA {}; // Read interface
access to 'A' template<> __interface IA< Read > { // through the IA< Read > interface, we want GetB()
to return a const IB< Read > interface const IB< Read > & GetB() const; }; // Read/Write
interface access to 'A' template<> __interface IA< ReadWrite > { // through the IA< ReadWrite > interface, we want
GetB() to return a IB< ReadWrite > interface IB<
ReadWrite > & GetB() const; void SetSomething(); }; |
The answer is... it’s fucking trivial, thank you C++! J
You will just have to define a single sealed “GetB()” method in your inherited class which will return the base ‘B’ class reference, then the compiler smartly collapse both definitions into the implementation thanks to the inheritance magic.
// ---------- // 'A' object
definition class A sealed :
public IA< Read > ,
public IA< ReadWrite > { private: // the two interfaces definition will collapse to the
same implementation B
& GetB() const sealed
{ return m_b; } void SetSomething() sealed
{} private: mutable B m_b; }; |
Let’s demo this:
void Demo() { // get a Read interface of a new 'A' object IA<
Read > & A_R = *new A(); // GetB() will return the const Read version of 'B' const IB< Read > & B_R = A_R.GetB(); B_R.GetSomething(); // get the Read/Write interface of a new 'A' object IA<
ReadWrite > & A_RW = *new A(); // GetB() will return the Read/Write version of 'B' IB<
ReadWrite > & B_RW = A_RW.GetB(); B_RW.GetSomething(); B_RW.SetSomething(); } |
That’s all folks, hope this help!