random technical thoughts from the Nominet technical team

Java enum methods, serialisation fun

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by dan on Apr 1st, 2008

Having been brought up on C# and .Net, Java enumerations are a breath of fresh air – you can do all sorts of fun stuff like give them methods and properties like any other object. Well, almost.

I’ve been writing some code to interact with a payments system. The rules as to whether specific types of payments can be taken are rather procedural, and so don’t instantly lend them to OO’s usual easy encapsulation. Typically I’d end up writing something like this:

public enum ProductType { SHOES, SOCKS, SHIRTS; }
 
public static boolean isBuyable(ProductType product, Account payeeAccount)
{
    // nasty procedural rules and switch statements to 
    // determine whether things are buyable for the given account...
    switch (product)
    {
        case SHOES:  /* ... */ break;
        case SOCKS:  /* ... */ break;
        case SHIRTS: /* ... */ break;
        default:
            // panic! someone added an enum instance 
            // without updating the business process!
            throw new PanicException("run!");
    }
}

This time I thought I’d try putting the rules about product types on the product type itself. Easy enough in Java: stick an abstract method on the enum instances and let them do their own work:

public enum ProductType
{
    SOCKS
    {
        @Override
        public boolean isBuyable(Account payeeAccount)
        {
            // rules about sock availability only
        }
    },
 
    // other product types go here, each looking after its own rules
    // and only its own rules:
 
    SHOES  { /* ... */ },
    SHIRTS { /* ... */ };
 
    // abstract base method for all enum instances in the group
    public abstract boolean isBuyable(Account payeeAccount);
}
 
// the nasty method previously required could then be reimplemented easily:
public static boolean isBuyable(ProductType product, Account payeeAccount)
{
    return product.isBuyable(payeeAccount);
}

The advantages of this approach include:

  • Business rules are ‘close to’ the objects they concern. In this case I could have written a method like isBuyable(ProductType) on the account object, but the payments logic is naturally separate from the rest of the system, and this approach keeps it that way.
  • Business rules for the availability of specific products are cleanly separated – each product only has to manage its own rules.
  • There are no switch statements (the equivalent for enums of the dreaded instanceof for objects) and the associated PanicExceptions.
  • It’s just not possible to add enumerated instances that don’t respect the business rules – that abstract prevents their compilation…

Unfortunately, having instance methods on enums breaks their serialisability. Or at least it does with our combination of Hessian and Java for remoting – I’ve not tracked down the precise cause. My first thought was that enum deserialisation was using reflective instantiation under the hood, so I removed the abstract keyword from the root declaration of isBuyable, but no such luck.

A quick refactor put each enumerated instance’s business logic into a discrete interface implementation, but this slightly bloats an otherwise neat solution to the problem. Reality trumps design once again…

2 Responses

  1. Enumeration internationalization Says:

    Good article but i that the method isBuyable is part of the business logic and should be separated of the static objects (constants,enumerations).

    BTW find here another article on how to internationalize enumerations using interfaces.

  2. Tom Sch. Says:

    Got the solution to our problem:

    http://www-01.ibm.com/support/docview.wss?uid=swg1IZ59603

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.

Recent Posts

Highest Rated

Categories

Archives

Meta: