Let's say I have a base interface. For simplicity let's call this interface
INode. The interfaces that inherit from it are IFolder, IFile, and maybe
others. Here is what the classes might look like:
__interface INode : public IInterface
{
public:
virtual String GetName()=0;
};
typedef DelphiInterface<INode> _di_INode;
__interface IFolder : public INode
{
public:
virtual _di_INode AddChild(_di_INode Child)=0;
virtual _di_INode GetChild(unsigned Index)=0;
virtual unsigned GetChildCount()=0;
};
typedef DelphiInterface<IFolder> _di_IFolder;
__interface IFile : public INode
{
public:
virtual unsigned __int64 GetFileSize()=0;
};
typedef DelphiInterface<IFile> _di_IFile;
This looks okay (to me at least) and programmers using it can understand the
hierarchy. However, my conceptual problem is in understanding how to code
the implementation. If I code the classes with the exact same structure I
would get this:
class TNode : public TInterfacedObject, public INode
{
private:
String fName;
public:
// Implementation of IUnknown
// (QueryInterface, AddRef, and Release)
// Implementation of INode
virtual String GetName();
};
class TFolder : public TNode, public IFolder
{
private:
// supporting data
public:
// Implementation of IFolder
virtual _di_INode AddChild(_di_INode Child);
virtual _di_INode GetChild(unsigned Index);
virtual unsigned GetChildCount();
};
class TFile : public TNode, public IFile
{
private:
// supporting data
public:
// Implementation of IFile
virtual unsigned __int64 GetFileSize();
};
It's obvious there are problems with this structure since TFolder and TFile
will have two INode inherited classes. Constructing a TFolder at this point
would yield an abstract class that doesn't have an implementation of
IUnknown's methods nor INode's. I can add the methods to the descendant
that simply call their inherited equivalents but I don't think the vtables
match up correctly in that situation. IOW, I *don't* think this is the
correct approach.
Another way I can create the structure is thusly:
class TNode : public TInterfacedObject, public INode
{
private:
String fName;
public:
// Implementation of IUnknown
// (QueryInterface, AddRef, and Release)
// Implementation of INode
virtual String GetName();
};
class TFolder : public TInterfacedObject, public IFolder
{
private:
String fName;
// other supporting data
public:
// Implementation of IUnknown
// (QueryInterface, AddRef, and Release)
// Implementation of INode
virtual String GetName();
// Implementation of IFolder
virtual _di_INode AddChild(_di_INode Child);
virtual _di_INode GetChild(unsigned Index);
virtual unsigned GetChildCount();
};
class TFile : public TInterfacedObject, public IFile
{
private:
String fName;
// other supporting data
public:
// Implementation of IUnknown
// (QueryInterface, AddRef, and Release)
// Implementation of INode
virtual String GetName();
// Implementation of IFile
virtual unsigned __int64 GetFileSize();
};
This works correctly all around but it requires that I duplicate code in
each class. This can lead to errors and changes in one area of code not
propogating to other areas. IOW, not very good code reuse or OOD.
So my question is how do other developers code something like this? Should
the interfaces be changed to some other structure? Are interface
hierarchies only for COM versioning (like recordset15 and recordset20 of
ADO)?
As I reached this point in my post another idea came to me. I could
separate the implementation classes from the data classes. Something like
this:
class TNode
{
public:
String Name;
};
class TFolder : public TNode { ... };
class TFile : public TNode { ... };
class ImplFolder : public TInterfacedObject, public IFolder
{
private:
TFolder fFolder;
public:
virtual String GetName() { return fFolder.Name; }
// ...
};
class ImplFile : public TInterfacedObject, public IFile
{
private:
TFile fFile;
public:
virtual String GetName() { return fFile.Name; }
// ...
};
This requires only a little extra code and doesn't violate the code reuse
issues. Could this be the favored approach that other people employ? Or am
I making this too complicated?
Thanks for reading through this and for any words of wisdom.
- Clayton