'. '


From APIDesign

Jump to: navigation, search

When someone depends on someone else, it means that there is an action, activity which one can only do with help of that other person. This does not mean one relies on the other person in all situations. I can depend on help when walking, but I may be able to sit myself without any assistance. This thought makes me think that there are no simple, general dependencies. There are various kinds of them, each kind useful for a particular type of action. At least it seems so in real life. However, as following sections show, it seems true also in the world of software development.


Compile Dependencies

Why you need dependencies at all? Just having a source code for your application is the first reason why you need some external tools to compile your code. Without proper compiler your source code is just a prose, in non way it is an algorithm. This means you have to define dependencies on the compiler and similar tools to properly handle your source files. This includes a dependency on your build system (for example ant or maven, or whatever will appear in the future). Also, and not less importantly, to express dependencies on the libraries that export symbols your source code references.

Some of these dependencies are useful only for the compilation phase, some of them will be necessary at later stages as well. Usually the libraries needed during the compilation are also necessary during execution. On the other hand the build system, compiler and for example annotation processors may not be needed later at all.

This can lead to strange situations. During compilation a library may need richer environment than during execution. Imagine that you want to create a @java.util.ServiceProvider annotation to generate META-INF/services/something files needed for java.util.ServiceLoader. This means that for compilation of java.util module (and anything that compiles against it) you need the javac compiler, libraries in javax.lang.model, and even more. However the execution still needs just java.util.

Linkage Dependencies

After the system is compiled the next stage is to prepare it for execution. This includes loading the system in and resolving all external symbols during linkage time. This usually implies that all libraries needed during compilation are also needed during the linkage. Yet, this is not necessarily true. Many systems support optional dependencies. E.g. dependencies that may be satisfied, but does not need to be. In such case there can be a library needed during compile time, yet if it is missing the system can run without it. If this is the case the linkage dependencies can be a subset of compile time ones.

Runtime Dependencies

Linkage is definitely the first action during execution. Yet, it is not the only one and there can be additional requirements on the execution environment beyond the linkage one. For example if you need a database populated with data, then you do not need to depend on the database classes, you just need the database to be around. As such the runtime dependencies can be a clear super set of linkage ones.

Here is yet another example that relates to java.util.ServiceLoader: an API may contain no implementation, but in order to function properly it may need the implementation to be present. Such implementation is then to be located with use of java.util.ServiceLoader. This clearly means that the linkage dependencies of the API need no reference to the implementation, yet the implementation is part of the necessary execution environment. Good modular system allow such dependency to be declaratively expressed.

Test Dependencies

Independent, yet important, category of dependencies are dependencies of tests. If we accept the XP approach that tests shall accompany each code, it might seem that the tests are so closely related to the code, that they shall have the same dependencies as the code. But surprisingly, this is not usually true.

If an API wants to support its own testability, and I argued in Chapter 9, Keep Testability In Mind why it should, then the most common approach is to provide a support for creation of Mock Objects. However this extra classes or utility methods are unlikely to be in the API itself, they need to be in separate distribution unit. This implies that whoever uses such API and wants to cover own code with tests, needs extra test dependency on the separate distribution unit. Test dependencies are different to regular runtime ones.

One may argue that the tests shall be packaged as just another module and shall have regular dependency on the API module and the tests of the API module. This is an option, yet one needs to keep in mind the relationship between an API itself and its test is above standard. This is usually done by placing the tests and the tested code into the same package and allowing the tests to access package private classes.

Personal tools