Tuesday, December 22, 2009

The Evil of Java ME preprocess

Developing Java ME application is painful, because of Java ME's own fragmentation in nature. Java Mobile developers have to deal with different devices, and each device or platform has different implementation. Writing a portable application to support different devices is really hard, and some time it is really a torture.

To solve the fragment issue, NetBeans IDE provides a solution - Preprocess, it is similar to pre-compile in C and C++. Actually preprocess in NetBeans is quite perfect, quite powerful. When I first use it, I feel it is so powerful, so easy to use.  Several years later, when I look at our code, which have #if, #else every where, I just feel the code are so ugly, seems to return to the c code era. I really feel preprocess is an evil! Following are my reasons.

Violate OO Principles
Since preprocess is so easy to use, just add something like comments, it gives developer a backdoor to break OO principles easily and brutally. It is so easy to hack the program, so there is no need to care about abstraction, no need to care about design patterns. And the problem is my colleagues like to overuse preprocess, mostly even use preprocess for constants!  I saw so many code full of magic numbers, and so many preprocess directive scattered every where to change those values.

It also violates OCP principle, OCP means "Open for extension, closed for modification". But preprocess is modifying the existing source code when you add new preprocess configuration. It is quite common that the change works for the new configuration, but will break the old ones.

I found if we use more design patterns deliberately, like strategy pattern, abstract factory pattern etc, and use constant class instead of magic number directly, we can minimize the preprocess greatly.

Hard to Refactor
Refactoring the preprocessed code is really difficult, for example when you rename a method name or variable name, you can only refactor the current active preprocessed code, because others are commented out. Right now in my current project I have 4 different devices type, so every time when I refactor, I have to test 4 times to make sure my change is working. It is really painful!

Hard to Reuse
If you want to reuse a class or a component to another project, but when you see so many preprocess scattered in the code, probably you will not use it.  Either you have to add all those preprocess into your new project which might does not make any sense, or you have to manually remove them, which is even harder to test.

Hard to understand
When you read the code has so many #if #else statement, it is ugly to read, more difficult to understand, code is not clean.

Hard to maintain
When you want to port the current application to a new device, you have to understand how the previous preprocess work; when you want to fix a bug for specific device, you have to make sure it won¡¯t break the others. Manage so many preprocess configuration is really painful. So preprocess is good for short-term, but really painful for long term.

IDE Lock-in
Because of widely use of preprocess, our projects are locked in NetBeans IDE, we can not use other IDE, like Eclipse.  For my experience, NetBeans IDE has some problems for example:
  • you can't use different source code folder for different configuration, you can easily do that in Eclipse MTJ;
  • Build automation is difficult, I don't understand the NetBeans generated build xml, I have to build inside the IDE, I never tried to run build file outside the IDE, and I don't know how create a customized ant build file, I think it is really hard;
  • Difficult for team work. NetBeans project use absolute path, and private project properties. Which means it works in your computer, but might not work in your co-workers environment, or you have to take a while to set up the environment correctly.

Conclusions
Java ME preprocess is easy for short-term, but painful for long term, it solves one issue by generating more issues in future; it delay your pain in a short term, but you will be more painful for long term.

Suggestions for anti-preprocess
  • Try to minimize the use of preprocess, we can't 100% percent remove the preprocess, but be ware to use it, only use it when no other options.
  • Have the discipline of writing clean code, readable code and maintainable code, before you use preprocess, think it carefully, if there is better solution;
  • Use OO design patterns, for example strategy pattern and abstract factory can remove lots of preprocess;
  • Try to use configuration file instead of preprocess, if we put the constant value into resource property file instead of hardcoded using preprocess, we can also make the code cleaner;
  • Centralize the preprocess, if you have to use preprocess, don't allow them scattering every where in the code, try to put those preprocess specific logic together, even better to put them into one class, it would be easier to read, understand and maintain;
  • DSL/Meta programming, if fragmentation is unavoidable, why not use code generation technology to generate source code for each specific platform for us? This needs compiler skills and Domain Specific Language knowledge, it is really an interesting topic.

No comments:

Post a Comment