12 Feb 2008

A better way to use Java Enum than switch

I think java enum are a nice addition to the language. I used to combine them with switch/case statements. But lately Fran├žois, a colleague, shown we a better way :)

You can embedded behavior in each enumeration case, reducing client code and making it more elegant :)

Let's take a simple example: Every day of the week you need to do something: take out the garbage, go to the laundry etc. ...

Using switch here's how I would have done it:
    private enum Week {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}

public static void main(String[] args) {
final Week today = getToday();
Action action;
switch (today) {
case Sunday:
action = new FamillyDiner();
break;
case Monday:
case Friday:
action = new Garbage();
break;
case Tuesday:
action = new Laundry();
break;
case Wednesday:
action = new ChillOut();
break;
case Thursday:
action = new Grocery();
break;
case Saturday:
action = new SoccerGame();
break;
default:
throw new RuntimeException("Impossible day!");
}
action.execute();
}

Conclusion:
  • You can see the number of lines in main() is quite important
  • The client code (the switch/case code) is likely to be duplicate
  • There is an unnecessary case "default" that can never happen (even null case would result in a NullPointerException in the switch)
  • But it still much better than a if/elseif/else using int

Adding behavior to the enum:
    private enum Week {
Sunday {
@Override
public void execute() {
System.out.println("Mom and Dad diner.");
}
}, Monday {
@Override
public void execute() {
System.out.println("Take the garbage out.");
}
}, Tuesday {
@Override
public void execute() {
System.out.println("Do the laundry.");
}
}, Wednesday {
@Override
public void execute() {
System.out.println("Have a beer. Just Chill out!");
}
}, Thursday {
@Override
public void execute() {
System.out.println("Go out to the local store for Grocery with shoping list.");
}
}, Friday {
@Override
public void execute() {
System.out.println("Take the garbage out.");
}
}, Saturday {
@Override
public void execute() {
System.out.println("Manchester vs Arsenal! Can't miss that!");
}
};

public abstract void execute();
}

public static void main(String[] args) {
final Week today = getToday();
today.execute();
}

Conclusion:
  • Less lines of code and still readable
  • main() code is soooo easy
  • No unnecessary case "default"
  • If you don't want this behavior and still use the enum you are free to use the switch/case way


Clearly I think this approach is far better and much more elegant unless someone show me otherwise...



Download the complete code sample.

7 Feb 2008

Comparison: java.util.logging vs org.apache.commons.logging

Here's a comparison between the java logging system and apache commons logging system.

For the lazy ones here's the conclusion:
  • Commons Logging Rocks!
  • Java Logging Sucks!

Why ? Well because:
  • Commons Logging api is better
  • Commons Logging let you choose your logging implementation
  • Commons Logging is bulletproof, production ready, heavily use everywhere
  • Everybody knows Commons Logging

Not convince yet ? Good I like people that don't take things for granted easily ;)

Let's compare the 2 frameworks api:
Java LoggingCommons Logging
Unclear log level: SEVERE > WARNING > INFO > CONFIG (wtf?) > FINE > FINER > FINEST (missing some imagination here ?!)Clear log level using different names: FATAL > ERROR > WARN > INFO > DEBUG > TRACE
No ERROR level! I find it really disturbing especialy since there is a System.err stream for so many yearsThere is an ERROR level
To log an exception:
logger.log(Level.SEVERE, e.getMessage(), e);
or may be
logger.log(Level.WARNING, e.getMessage(), e);.
Unclear what Level to use and why do I need to do e.getMessage() it could have been done in the log() method.
Missing simple methods: logger.warning(Throwable t) and logger.severe(Throwable t)
To log an exception: logger.error(e);
To configure either modify the logging.properties in your JRE/lib folder or set -Djava.util.logging.config.file=/my/folder/logging.properties
No way to put a conf in the classpath to be automaticaly pick up by the logging framework
Possibility to put an already configured conf file
Create a logger:
private static Logger logger = Logger.getLogger(MyCLass.class.getName());
Missing method: Logger.getLogger(class)
Create a logger:
private Logger logger = Logger.getLogger(getClass());


The good things about Java Logging:
  • Classloader: About the configuration part the way java logging does the configuration is less flexible but might reduce classloader problems that you encounter with commons logging (see: Commons_Logging_FUD)

  • No jars to add, No additional dependency


Working with Commons Logging api is some much better, simple and easier that I will continue to put those 2 jars in the classpath whenever possible if that's what it takes to use it.

Now try both and make your own opinion ;)