13

I have an abstract class Example and concrete subclasses to go along with it. I used a discriminator to pull data out of the database, like so:

<resultMap id="ExampleResultMap" class="Example">
    <discriminator column="stateCode" javaType="java.lang.String">
        <subMap value="AL" resultMap="AlabamaStateResultMap"/>
        <subMap value="AR" resultMap="ArkansasStateResultMap"/>
        [...]
    </discriminator>
</resultMap>

<resultMap extends="ExampleResultMap" 
           id="AlabamaStateResultMap"
           class="AlabamaState"/>
<resultMap extends="ExampleResultMap" 
           id="ArkansasStateResultMap"
           class="ArkansasState"/>
[...]

Thus I have an AlabamaState object (a subclass of the abstract Example object) with no attributes of any kind on him. This is contrived, but the gist is that I don't have any attribute that uniquely identifies the object's type--and there's no reason I would if not for this case.

(Note: The classes aren't empty, they're behavioral, so refactoring them out of existence isn't an option.)

How do I save it back to the database?

Ideally there would be a Discriminator for ParameterMaps, but there doesn't seem to be one.

As far as I can tell, there are a number of undesirable solutions, among them:

  • Give up and add a "getType()" method on all my subclasses that returns a static string. In this case, AL. (Note that I tried pretty hard to avoid needing this throughout all my code, so having this = OOD-defeat).
  • Make a "DB" object that's exactly like my big, complex object but happens to also have an extra string saying "Oh, btw, my TYPE is AL."
  • Extract all 20 attributes I want to persist into a HashMap before inserting the object.
  • Some other craziness like using the toString() or something to help me out.

Likely I'll go with the first option, but it seems rather ridiculous, doesn't it? If iBatis can create it, shouldn't it be able to persist it? What I really need is a discriminator for insert.

Am I out of luck, or am I just overlooking something obvious?

inanutshellus
  • 9,683
  • 9
  • 53
  • 71

1 Answers1

2

If you have no attributes belonging to your subclasses, you should consider removing these subclasses and add an enum to your former base-class, since the only purpose your subclasses serve is to differentiate the type of your objects (if I understood you correctly). Using an enum for this is easier to extend and more elegant in client code (since you can switch on the enum instead of using blocks of instanceof expressions).

If are having special implementations of certain operations on your subclasses, you could move them to the enum as well, and have your base class delegate to the implementation on the enum.

EDIT

Here is an example:

public interface GreetingStrategy {
    abstract String sayHello();
}

enum UserType implements GreetingStrategy {
    ADMIN {
        @Override
        public String sayHello() {
            return "hello from admin";
        }
    },

    GUEST {
        @Override
        public String sayHello() {
            return "hello from guest";
        }
    };

}

class User {

    private final GreetingStrategy greetingStrategy;

    public User(GreetingStrategy greetingStrategy) {
        this.greetingStrategy = greetingStrategy;
    }

    public String sayHello() {
        return greetingStrategy.sayHello();
    }

}
mbelow
  • 1,093
  • 6
  • 11
  • It's not possible to actually extend enums, so I assume you're suggesting I make a giant if-else block inside custom methods on my enum? Not a fan! Further, having an actual class is extremely beneficial in code readability when you know exactly which option you're dealing with (such as with state machines). – inanutshellus Dec 03 '12 at 16:22
  • I think I did not make myself clear enough. With "extending", I did not mean inheritance. Introducing such an enum will *not* require you to write giant if-else blocks. (Well, at least not if you do not already have giant if-else blocks with instanceof-tests in your client code to figure out the subtype.). My point was: 1. If you are having subclasses that declare no fields and methods, you should consider replacing them with an enum. If you have subclasses with no fields but with methods, you can as well replace them with an enum, and declare the methods on the enum itself. – mbelow Dec 03 '12 at 18:30
  • I have added an example to my original answer. – mbelow Dec 03 '12 at 23:44
  • I agree that in some cases you're better off with a simple enum than with a bloated design of subclasses. This question is specifically trying to solve the case where an enum is insufficient. In my case, my "empty" (from a db perspective) classes are full of behavior. The class itself *is* the state for a parent (has-a) object and each contains a half-dozen event-based rules. Nonetheless, at the end, the parent object only needs to persist the state (which subclass your State ended up being). – inanutshellus Dec 05 '12 at 21:44
  • If you do not have any mutable state on your subclasses, than enums are a perfect match for your scenario. Enums are mutable and singleton by design. If you just use your subclasses to denote a state, I would definitely go for enums. I cannot think of a where subclasses are inferior, except that you can't *declare* operations on a single enum constant individually (yet you can have different implementations of a fixed set of operations). But I don't want to sound dogmatic - maybe I'm just missing something important in your special scenario. – mbelow Dec 05 '12 at 22:54
  • Because enums can't be extended, once your business logic becomes cumbersome and bloated, you have no way to cleanly refactor (classes let you hide the details of your implementation). After a few behaviors per type your single enum containing all your business logic turns into a convoluted mess. – inanutshellus Dec 06 '12 at 03:42
  • I see. What about this: Each enum constant (which represent a state of your state machine) has an `execute(domainObject, service)` method. DomainObject is your former BaseClass, service is a class that hosts your business logic. With this, each state can execute the appropriate method on your business service. This also separates your persistence-related domain-object from the actual implementation of your business logic. – mbelow Dec 06 '12 at 08:18
  • What I ended up with last year was to have a "getType()" method (backed by a simple behaviorless enum) on my nice clean interface / abstract base class. A small compromise in a good design to accommodate iBATIS. – inanutshellus Dec 06 '12 at 13:23
  • If you don't want to override methods in the enums directly, you can encapsulate your `AlabamaState`, `ArkensasState`, etc in the enum members. – WilQu Aug 28 '13 at 14:59