Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface Versioning

Where I work we've just finished releasing a feature that uses a dll that heavily relies on interfaces. The dll and all client applications are written in Delphi. There is no registration needed. This dll is not a proper com server. The only constraint is that the unit containing the interface is accessible to both the dll and the client applications. This allowed us to pass complex data to and from the applications that use this dll without needing to resort to record pointers, arrays or extremely fat function signatures and without the baggage that a bpl or a fully compliant COM server would introduce.

It looked like it solved a major problem we'd had with no down sides. Unfortunately there is a downside. Any change to an interface after it's been released requires a subsequent recompile of any consumers of that interface. This is fine for projects that are all part of the same release cycle but some of our projects have different release schedules.

I've researched this a bit and it looks like it's a common practice to introduce a new interface that inherits from the previously released interface rather than modify the original interface.

type
  IOriginalInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  INewInterface = interface(IOriginalInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;

This ensures older executables compiled against the original interface continue to work.

I've noticed with the RAD Studio open tools api the interfaces are renamed whenever a new one is introduced. So rather than giving the newest interface a new name the newest interface gets the original interface's name and the original interface is renamed.

type
  IOldInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  IOriginalInterface = interface(IOldInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;

This has obviously worked well for the RAD Studio team as well as third party extension vendors. This ensures that any clients that get recompiled will be using the latest interface without needing any code changes. I assume this works because the name is irrelevant and after the code is compiled all that remains is the interface definition, which is resolved using the GUID.

Having said all that, is this a good solution to the interface versioning problem we are now facing? Are there any other issues I need to be aware of?

like image 502
Kenneth Cochran Avatar asked May 15 '14 22:05

Kenneth Cochran


1 Answers

Let me explain the logic behind what you've observed with the ToolsAPI and you can then determine how that applies to your situation. Your reasoning is very close.

For interfaces that are meant to be consumed by plugins and IDE extensions, you are correct about the manner in which the interfaces are versioned and named. The idea is that existing code will reference a particular interface name and because all the existing methods also exist on that interface. Since the older code simply cannot reference any new methods, it's safe to include them in the new interface.

However if you look closely, for interfaces that are meant to be implemented by the plugin or IDE extension, you will see that the reverse is true. The new interfaces get new names and all the older existing interfaces remain unchanged. This is because, as the implementer of the interface you must implement all the methods of an interface. Existing code, by definition, will not have implemented the new methods. When the IDE needs to call a user's implemented method, it will always make that call by querying for the version of the interface on which that method was introduced, which may not be the most recent version of the interface. For this reason one should list all ancestral interfaces as being implemented on the implementing class.

In summary, the rule here for the IDE's ToolsAPI are that for interfaces that plugins use, the newest version of the interface always gets the unversioned name. For interfaces that the plugin implements, new interfaces always get a new name.

like image 98
Allen Bauer Avatar answered Oct 03 '22 23:10

Allen Bauer