Storage system A
(Java) is the default and has a Python API API_A
. Storage system B
(Java/C++) was recently introduced, but did not have a Python API. People "somehow" made B
work with API_A
, but it is extremely slow, especially as the scale increases. Since, this was the only choice for a Python API, a lot of code now depends on API_A
to interact with B
.
The team from Storage system B
now has a Python API, API_B
. API_B
is clearly more efficient. However, there are a lot of variations between API_A
and API_B
. For example, the list directory methods return objects with completely different fields. API_B
does not allow recursive deletes for directories, but API_A
does. And so on...
What is the best way to merge the two APIs without breaking compatibility? In particular, since API_A
has been in the use, what is the best way to integrate API_B
into it transparently?
I don't think creating decorators will work as method signatures vary between the APIs. There are some methods in API_A
not present in API_B
. (Raising NotImplementedException
is the only option here).
The only solution I can think of is the naive one, where I have to do this checking in every function in API_A
:
if self.isStorageA:
return method_of_A(args)
else:
return method_of_B(args, args)
I am looking for a better and more maintainable way to do this. Any help is greatly appreciated!
In particular, it must provide exactly the same methods with exactly the same signatures. If API B does not implement some functionality (like recursive enumeration), the wrapper must re-implement this functionality in terms of API B. For example, you might write a function that enumerates the current directory, then every subdirectory, and so on recursively, to replace the native functionality of API A. If it is not possible to do this (that is, if there is some functionality which API A provides and which cannot be done with API B at all), then API B is not a suitable replacement for API A and you should not go forward with your plan.
You should also write a series of unit tests for your wrapper which ensure that it behaves the same as API A does in a variety of conditions specifically including edge cases. If A already has unit tests, then just use those.
Finally, once you're certain the wrapper is indistinguishable from A, you can replace A with the wrapper. It's simple enough to just replace the modules and packages, but if this does not work for you for whatever reason, you can replace A in sys.modules
to ensure imports of A get the wrapper instead.