Despite common belief, splitting up an application into a number of bundles is not automatically introducing modularity. The almost magic benefits of modularity are caused by the particular decomposition. Many decompositions can actually increase complexity, only the right decompositions reduce complexity. This was perfectly demonstrated by the father of modularity David Parnas. In his seminal paper On the Criteria To Be Used in Decomposing Systems into Modules (PDF), written in 1972. Parnas takes a simple problem, a text indexer, and shows two different decompositions. He then added the requirement that the source text files could be very large, no longer fitting in memory. The first decomposition required changing each module, the second decomposition only had one affected module.
Parnas' paper clearly shows why creating the correct decomposition is so hard, it is about predicting the future. With this disclaimer, there are some general rules that apply. These rules are in their essence the same as we use daily in writing our object oriented code. The most important being information hiding. Information hiding works because if you do not know something, you cannot make false assumptions about it. Assumptions that could break your code when violated in runtime.
With modularity, we have the fantastic tool that the rules inside the module are different from the rules outside the module. As a module author we can control what we expose from the inside and what is hidden from the outside. We can make sure that outsiders cannot make assumptions about our internal workings because they do not have access to them. By minimizing our dependencies on other modules we can reduce our own assumptions of the outside world. The less we assume, the more resilient our module will be to changes in the outside world.
In OSGi we even quite far with this model. In the initial model, you could basically only export API, not implementation classes. Each bundle could only talk to other bundles through services. A bundle is not even allowed to make assumptions about its and other modules' life-cycle. Though for many this sounds extreme, it was put in place to minimize assumptions that are so easily broken.
But there is more to modularity than just robustness and resilience. The proper decomposition can also simplify by requiring much less code. In 1984 I was the designer for an object oriented window system used in a page makeup terminal, this was my largest object oriented design so far. This design process was accompanied with an uneasy feeling that I was not designing, I was only postponing. Every abstraction seems to just postpone the really hard problems. However, one day I realized that I was done without ever having felt that I solved those really hard problems, they seem to have disappeared. In small this effect is visible with recursive algorithms. With the right decomposition, you need very little code, with the wrong decomposition you wrestle with the special cases of start and ending. The right decomposition somehow removes the need for much code.
I do not have a good understanding where the complexity goes when you have a proper modular structure but I have seen it too many times to know it happens. The problem is that once you found a proper decomposition, it feels so natural that you can't understand why you did not see this immediately.
Modularity provides benefits when it is used in the proper way, splitting an application into parts is not guaranteed to give you these benefits. If the cohesion is low and the coupling high between modules it is likely to give you more pain than gain. But there are more ways you can kill the benefits. If you break through abstractions, you likely will be broken when the implementation behind those abstractions change. Unfortunately, there are patterns in Java, mainly class loading related, that are fundamentally not modular and can easily kill the benefits of modularity.
However, if you refactor that application and you hit that right composition of modules, I can ensure you that you will immediately know why it is worth it.
Peter Kriens
Friday, August 28, 2009
Wednesday, August 19, 2009
A Simple Module System
To progress the work in JSR 294, BJ Hargrave, Richard Hall and undersigned have submitted a proposal for a simple module system. The purpose of this module system is to simple enough that can be supported both by Jigsaw and OSGi. In reality, this will likely be sufficient for most people. More advanced features, for example some of the special requirements that Jigsaw has with regard to the JDK modularization, can then be supported in a module system specific way.
This proposal is based on the module accessibility keyword, as is the current proposal, and there are no additional visibility rules. Module boundaries are defined by the artifact in which the modules are delivered. Modules can require other modules that must then become completely visible to the requiree.
The approach of finding a common denominator looks very promising. I am looking forward to continue working with the JSR 294 EG to iron out all the details.
Peter Kriens
This proposal is based on the module accessibility keyword, as is the current proposal, and there are no additional visibility rules. Module boundaries are defined by the artifact in which the modules are delivered. Modules can require other modules that must then become completely visible to the requiree.
The approach of finding a common denominator looks very promising. I am looking forward to continue working with the JSR 294 EG to iron out all the details.
Peter Kriens