Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • interface.jar - contains interface public interface Service
  • implementation.jar - contains public class ServiceImpl implements Service
  • two above jars change
  • two below jars didn't change even when above changed
  • interfaceclient.jar - dependant on interface.jar, contains class ClientOfService taking Service as a constructor parameter, uses this service in it's doA() method
  • application.jar - dependant on all above jars. Class App in main method creates ServiceImpl instance and passes it as an arg to a constructor of ClientOfService, then cals a method on ClientOfService which makes a call to a method doA() from Service: public static void main(String[] args) { Service service = new ServiceImpl(); ClientOfService clientOfService = new ClientOfService(service); System.out.println("App.main() :: calling clientOfService.doWorkCallingDoAFromService"); clientOfService.doWorkCallingDoAFromService(); System.out.println("App.main() :: end of main"); }

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?

like image 385
bastiat Avatar asked Dec 01 '18 22:12

bastiat


1 Answers

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.
like image 151
Christophe Avatar answered Nov 12 '22 20:11

Christophe