Enum goodness: avoiding if-else and faking inheritance

When communicating with different payment methods that have been integrated in different time and by different people, we ended up with something like this in an Engine class that handled the action to take depending on the status of a payment.

Payments statuses set-up as static final Strings:

that where used within a switch-case block as:

Our first refactoring involved creating an enum to handle the statuses in a more proper way, so we moved to:

so the switch-case block would be:

In a second step, we moved the [... do ... stuff ...] to another class, that would call this engine to ask for the action required for the status of the payment we receive (this engine class was having too much responsibilities). The action to take is given by the enum itself.

so the switch-case block is moved to the class calling the Engine, which code is replaced by a simpler:

    Status status = Status.byId(params.get(STATUS))
    // yes, we reject by default
    status ? status.action : Action.CANCEL 

what is all about the Engine managing the integration. What to do in our database is kept outside the Engine itself (what gives us the chance to unify all the integrations into a one-service-calling-several-engines solution).

After that we needed to change the entities we get the statuses from. This was an integration need from another issue. So we needed to make some changes to our enum (some attributes removed for simplicity):

where we should add these new ones to handle statuses of the new entity integrated.

    OPENED('opened'),
    CLOSED('closed'),
    EXPIRED('expired')

As they both refer to different entities, the first approach was to duplicate the enums, as EntitiyOneStatus and EntityTwoStatus, duplicating the code inside the enum, and without having a union between them, as they are still statuses of the same integration.

Based on the "hierarchical enums" described in this enum tricks post, we ended up with something like this:

which not only does not need to duplicate the common stuff, but also have these pretty accessors for each entity, maintaining a certain level of relationship (statuses of the integration):

    Status.PAYMENT.APPROVED

or

    Status.ORDER.CLOSED