Monday, September 14, 2015

Java enum usages

A major reason to use statically typed languages is the type safety; the ability to catch certain errors at compile/development time.  One of the great Java features which promotes that is enums.  These are a great replacement for things such as static final ints/strings/etc and it supports functionality well beyond straight replacement of those simple types.

You normally think of enums in context like the following;


public class EnumExample1 
{
  enum Color {RED,  YELLOW, GREEN};
 
  public static void main(String[] args) 
  {
    Color c = Color.RED;
    EnumExample1 example1 = new EnumExample1();
    example1.process(c);
  }
 
  void process(Color color)
  {
    switch(color)
    {
      case RED:  
        System.out.println("Stop");
        break;
      case YELLOW: 
        System.out.print("Caution - slow down");
        break;
      case GREEN:   
        System.out.println("Go");
        break;
    }
  }
}

That is well and good but you can do more; you can extend the enum with interfaces or even add methods to individual enum values.

/**
 * Note: Part of an example program for my kids to help them learn programming..
  
 * Interface for gaining access to the value or values assigned to a 
 * playing card.
 *
 */
public interface CardValIntfc 
{
 public abstract int[] getNumericVals();
}


/**
 * Simple representation of a playing cards "value".  
 *
 */
public enum Value implements CardValIntfc 
{
 Ace(1,11),
 Two(2),
 Three(3),
 Four(4),
 Five(5),
 Six(6),
 Seven(7),
 Eight(8),
 Nine(9),
 Ten(10),
 Jack(10),
 Queen(10),
 King(10);
 
 Value(int tmpVal)
 {
  this.val = new int[]{ tmpVal};
 }
 
 Value(int tmpVal1, int tmpVal2)
 {
  this.val = new int[]{ tmpVal1, tmpVal2};
 }
 
 @Override
 public int[] getNumericVals()
 {
  return val;
 }
 int val []; 
}

Enums are immutable but you can either put in static values as shown above or with care you can load data from properties files or even a database.

I find increased usefulness when I work with multiple application code bases and have shared code needing to process enums but the set of values differs (slightly) between the code bases. Below is description of the progressive changes from a simple enum to something more flexible.

A real example I have is where I represent various databases as distinct enum values and have a bunch of non-database specific code which simple needs an enum value to determine what database to use.  Initially just creating a single enum in the shared code worked within a single application.  A trade-off occurs when you move to multiple applications though.  The enum contains values for all  possible databases whether the application needs them all or not; as in a manufacturing app may not need to know about HR. Also, if you need to add a new database, you have to worry about mixing utility jars which may break if not consistent with the enum configuration they were originally compiled against. 

So initially, an enum representing databases might be made up of values like these:

enum Database 
{
  Finance,
  Service,
  HR,
  Manufacturing
}

To get around this; instead of using an explicit enum in the various application specific and shared API's you instead use an interface and combine that with enums defined in each application.


/**
 * A very basic interface; nearly just a "marker interface"
 *  but could provide more functionality if needed.
 */
public interface DatabaseIntfc  
{
 String getEnumName();
}

So the above might reside in a jar like, db-common-utils.jar. If you have, for example, three department type web applications then you would include that jar in each WAR file. If the source for all three web applications were in separate source trees then you likely would define a Database enum in each source tree in the same package but only include the particular items for the databases you need to reference.


enum Database implements DatabaseIntfc
{
  // Only 2 DB systems in use in this application.
  Service,  
  Manufacturing;

  @Override
  public String getEnumName() 
  {
    return this.name();
  } 
   ....
   // utility stuff
 }

Then in each applications code, you can call the shared utility code but pass the enum constant you are interested in. This might look something like:

        OurDBUtil.hasGrant(Database.HR, "all_users", "admin");

With some creativity, it is pretty easy to come up with ways to put these ideas to work in beneficial ways.

I have been focusing mainly on the benefits of using enums but there is also a sort of downside - not really with enums but care must be taken with their usage.  An example which has bitten me is:  With a big ERP system, the end-users said "here are the ONLY values (5) we will ever use for field XYZ."  What about the other 4 values which are valid for the field, I asked?  "We don't ever need to use those"  was the reply.  And in this case, it appeared that an enum was a great way to go - read field text values and directly convert into enums  or specify directly where needed and pass them around without worry of some type of textual typo occurring. This is great until someone puts unsupported values into the database which then results in things going south quickly when the code can't convert the text to a known enum value.  A simple text field may have been more resilient to errors in this case.  Another option would have been to create enums for all *possible* values and then do some semi-intelligent but certainly more graceful error handling if the unexpected values were found during runtime.

Certainly, the enum usages here could be replaced by some simple ideas such as some regular classes and maybe Spring bean (or CDI) type singletons.  What is better?  It depends.. on too many things to make a blanket statement.  Personally, I like to use enums when it makes sense but there are times when it turns out better using an alternative.  So plan on looking at application needs and other criteria to determine what is the best way for the current and possible future circumstances.


Hope you found this interesting and maybe helpful.

God Bless!
Scott




No comments:

Post a Comment