About interfaces

First of all, the problem concerns food soft-builders, although the project is also not going smoothly.

Let's start with a recent story. COM technology (and others, but the essence is the same) made it possible for component developers to easily separate interfaces from implementation. For application developers, this meant, for example, that when updating components, old interfaces would continue to work. In theory, of course. But in practice, it looked much better than the “hell of dynamic libraries”, which always have the “current” version.

What is the mechanism? He is very simple.

Announces Programmer Interface Component

[uuid(a03d1421-b1ec-11d0-8c3a-00c04fc31d2f)]
interface ICoolPrinting
{
  void Print(const string fileName);
}

Then the programmer implements this interface. The compiled program is installed on the user's device, where in some registry the executable module is mapped to the indicated UUID. For example, in Program Files \ CoolPrinting1_0 \ cool.exe.

In a third-party application using this interface, the interface is requested from the same “registry” by UUID and the required function is called.

ICoolPrinting printing = GetInterface(...);
printing.Print("c:\myfile.pdf");

What happens if a component developer changes functionality?

In a simple version, the interface expands. This means that the developer is voluntarily obligated (feel this wonderful combination of words):
  • Create a new ICoolPrinting2 interface with a new UUID, possibly inheriting it from the previous one;
  • implement it without breaking the old implementation.

[uuid(d26d1421-g1ec-12d0-8c3a-12c84ef31d2f)]
interface ICoolPrinting2 : ICoolPrinting
{
  bool SetupPrinter();
}

Now even installed instead of the old one, the new version continues to work as before. An application programmer using a component does not need to redo anything. That is, nothing at all. Cool, yeah?

In a more complex version, the developer releases a new version that does not support the old interface. But at the same time, he again voluntarily must provide for the possibility of simultaneously installing a new version next to the old one, somewhere in Program Files \ CoolPrinting2_0.

And in this case, the application programmer also does not need to redo anything.

However, the above occurs only in a world where programmers at least roughly imagine what they produce. And their clerks know the business they are managing. This also happens.

More often the opposite happens.

In the most humane version, the programmer does not create a new interface, but simply corrects the old one, changing, at the same time, its UUID. It is necessary to change the UUID, otherwise it will be impossible to distinguish the new interface from the old one. The new version is installed instead of the old.

A program that uses the component immediately stops working. Because the link still UUID leads to the void. But getting around this problem is quite simple, since the interface can:
  • request by name, not by UUID;
  • use dynamic function calls (at the execution stage).

OleVariant printing = GetInterface("ICoolPrinting");
printing.Print("c:\myfile.pdf");

Such a “tricky” technique will work until the component programmer removes the old methods and interfaces. Then, when you try to use the functionality in your application program, an error occurs.

The situation is aggravated by the fact that it is often impossible to install two versions of a component on the same device. Because if the developer was not able to ensure compatibility at the interface level, then he will not have enough brains to ensure the conflict-free operation of different versions of the component.

The puck goes to the application programmer. Well-wishers from the forum whisper: “Pattern adapter”. And even a "strategy." Why be surprised? The main purpose of the so-called design patterns is to create crutches and props.

And here is a simple and clear two-line application program - getting a link to a component and calling a method, turns into a mess of hundreds of lines of "template" code and several classes, according to the number of versions of the component. Of course, I will not bring them here.

Examples? You are welcome. The PdfCreator component, developing from version 0.x to the current 2.x, was developed in this way. First, editing the old interfaces with replacing the UUID, and then creating new ones that are incompatible with the old ones, but already deleted. Two different versions cannot be installed on the same computer.

You may argue that PdfCreator is a free open source utility. But what does that change? Is it possible that the negligence of developers can be compensated by the proverb “They don’t look in the mouth for a gifted horse”? There are other examples of completely commercial components that do not provide compatibility between versions.

Morality. The rule “not invented here” has good reason. At a minimum, having source codes on hand will be useful.

Also popular now: