bastiat
bastiat

Reputation: 2071

Mandatory class recompilation when dependency changes

Context:

I'm java programmer and reading Uncle Bob Agile Software Development. Regarding ISP Interface Segregation Principle there is given an argument which I understand as:

Lets have:

interface Service {
  function doA();
}
class ServiceImpl implements Service {...}
class ServiceClient {
  // ServiceImpl is injected; eg either through constructor or setter
  private Service service;
  function useOnlyDoA() {
    service.doA();
  }
}

And now, if interface Service changes, e.g. method doB() is added, then all dependent classes e.g. ServiceClient have to be recompiled, even if they don't use added method! (really??)

  1. Is it true for java?
  2. Is it true for c++?
  3. For which other languages it it true, and for which isn't?

I lived in conviction that regarding java if ServiceClient is in package e.g. client.jar, interface Service in service.jar and ServiceImpl in impl.jar, then client.jar doesn't have to be recompiled and rebuild if it doesn't use new methods from Service interface and it can be used together with new versions of service.jar and impl.jar. It seems to me that things go this way in java. Is it other in eg c++ or some other languages?

Possibly in c++ much more is broken, eg https://stackoverflow.com/a/4033900/1423685

To make it clear:

I'm not asking about recompiling class implementing changed interface (this is obvious, it class has to implement new added method). But I am asking if I have to recompile class ServiceClient which is using this interface even when class isn't using new added methods. Possibly in c++ BC changes and client really has to be recompiled, but it seems to me that not in java.

Edit:

I implemented a test in java. 4 jars:

The main in App run successfully after change of interface.jar and implementation.jar (I added some unused methods and removed one old method).

So the challenge is how to change interface (of course without changing doA() method declaration) and implementation to stop it running successfully? Is it possible? If so, how?

Upvotes: 8

Views: 1159

Answers (2)

Christophe
Christophe

Reputation: 73376

Yes, if you change the interface, you need to recompile the classes that implement it in Java and in C++. This has several reasons, among others:

  • the Java compiler needs to check if the classes implementing the interface are still compliant (a class that implemetns the interface must implement all its methods).
  • the C++ compiler might need to change the technical layout of the classes (e.g. vtable if doB() is virtual), and verify if another member function with the same signature but in another base class is not hidden by the newly created one.

The whole thing of the ISP is to avoid such situations. So if doB() has in reality nothing to do with this interface, you'd better go for a segregated interface with just doB(), and change/recompile only the classes that need to implement it.

Edit: The same principle applies to classes that use the interface. Some of the arguments may of course be implementation dependent:

  • For C++, the code generated for the using class will need to rely on the right interface definition, so that it can make the right assumptions about the vtable layout and generate the right code. This is why recompilation is also required for the using classes.
  • For Java, the call to interface methods could indeed rely on explicit names and run-time resolution. I'm not a java specialist, so here a corrigendum: Some changes to the interface are in fact allowed by the Java Specifications without requiring recompilation. But some changes require a recompilation.

Upvotes: 8

Lie Ryan
Lie Ryan

Reputation: 64845

For which other languages it it true, and for which isn't?

This is really implementation-specific characteristic rather than language characteristic.

Recompilation is usually needed for language implementations that compiles member access on interfaces to memory addresses or array indexes of a function table. This is how most static language compilers are implemented. Most Java and C implementations would fall under this category.

Recompilation wouldn't be necessary for languages implementation that compiles member access to dictionary lookup (so that it uses the function name for the lookup, rather than a substitute index). This is how most dynamic languages compilers are implemented. For example, most Python implementations don't need to recreate .pyc files if classes in another file changes its interface.

So the main divide is between static and dynamic language. There are exceptions of course. It's possible in some static language to ensure that any new additions are appended to the function table, so existing code that code that uses the old indexes will still work. For library authors in most static languages, it is an important consideration to only change interfaces in ways that preserves binary compatibility if the language implementation allows for it.

Upvotes: 0

Related Questions