🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.oodesign
1 messages
1 total messages Started by Clayton Arends Fri, 23 Feb 2007 18:18
Class design using interfaces
#79
Author: Clayton Arends
Date: Fri, 23 Feb 2007 18:18
188 lines
6310 bytes
I'm a little shy asking this question.  This is a slightly long post but it 
should be easy to follow.  I'm trying to change the design of an area of 
code to use __interface classes.  Eventually I may go COM but for now these 
interfaces are purely internal to get the benefits of implementation 
isolation and reference counting.  I've been using interfaces for a while so 
the concept isn't new to me but I've always stayed away from the following 
case.  I am curious how other developers handle this.

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 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads