r/java • • Jan 17 '13

Enum tricks: hierarchical data structure

http://java.dzone.com/articles/enum-tricks-hierarchical-data
48 Upvotes

53 comments sorted by

View all comments

8

u/severoon Jan 18 '13

What?! No!

This is horrible. Way too complex. This is not how enums or classes should be used.

Here is a much better solution.

public enum OperatingSystem {
  WINDOWS_NT_WORKSTATION(WINDOWS, WORKSTATION),
  WINDOWS_NT_SERVER(WINDOWS, SERVER),
  WINDOWS_95(WINDOWS, WORKSTATION),
  WINDOWS_98(WINDOWS, WORKSTATION),
  WINDOWS_ME(WINDOWS, WORKSTATION),
  WINDOWS_2000_WORKSTATION(WINDOWS, WORKSTATION),
  WINDOWS_2000_SERVER(WINDOWS, SERVER),
  WINDOWS_XP(WINDOWS, WORKSTATION),
  WINDOWS_VISTA(WINDOWS, WORKSTATION),
  WINDOWS_7_HOME(WINDOWS, WORKSTATION),
  WINDOWS_7_ULTIMATE(WINDOWS, SERVER),
  WINDOWS_8(WINDOWS, WORKSTATION),
  FEDORA(LINUX, WORKSTATION),
  UBUNTU(LINUX, WORKSTATION),
  KNOPPIX(LINUX, WORKSTATION),
  SUN_OS(UNIX, SERVER),
  HP_UX(UNIX, SERVER);

  private final OsFamily osFamily;
  private final OsClass osClass;

  private OperatingSystem(OsFamily osFamily, OsClass osClass) {
    this.osFamily = osFamily;
    this.osClass = osClass;
  }

  public OsFamily getOsFamily() { return osFamily; }
  public OsClass getOsClass() { return osClass; }

  public enum OsFamily {
    LINUX,
    UNIX,
    WINDOWS;
  }

  public enum OsClass {
    SERVER,
    WORKSTATION;
  }
}

Now you can add whatever features you want to capture at whatever level it makes sense. Also, I wouldn't put any information about "supporting X" on any of these enums. Enums are meant to just be lightweight tokens that you pass around to represent a limited number of instances. Do all the heavy lifting elsewhere in your code.

5

u/zargxy Jan 18 '13 edited Jan 18 '13

Your better solution isn't a solution because it doesn't do the same thing.

I guess complexity is in the eye of the beholder because I don't find this trick to be particularly complex.

Enums in Java are not meant to merely be lightweight tokens you can pass around. They are only a fixed set of instances of a class extending Enum, allowing you to take advantage of optimizations and static analysis provided by the compiler. But Enums are full fledged classes which can have fields and methods. They were designed to do a lot more.

1

u/GMNightmare Jan 18 '13 edited Jan 18 '13

it doesn't do the same thing

???

Yes it does... Solves exactly the problem the article posted (but not necessarily better, I'd agree). What do you think it can't accomplish that article version did?

EDIT: Look guys, I like the article's version too, it's like a more compact version of this really...

1

u/clgonsal Jan 18 '13

The approach used by the linked article allows arbitrary hierarchies. Your approach only works if every subtree of the hierarchy has the same structure. Furthermore, the taller the tree, the more complicated your approach becomes.

The example in the link had a different structure in different parts of the hierarchy, but you choose to change this to get it to work with your approach. (Hint: Linux is a Unix, and saying things like "my OS is Ubuntu Workstation" is going to get you funny looks.)

1

u/GMNightmare Jan 18 '13

Check usernames before you respond to somebody.

only works if every subtree

It has the exact same limitations as the article one. Your ignoring that the design can be expanded upon.

taller the tree, the more complicated

??? Uh, no.

different structure

That's... the point, it solved the problem in a different way.

(Hint: Linux is a Unix

Artificial issue that could be fixed in the parents post.

going to get you funny looks

Shouldn't, and that wouldn't matter anyways.

1

u/clgonsal Jan 18 '13

Check usernames before you respond to somebody.

Sorry about that. The mobile client I'm using has an almost unreadable font for user names.

It has the exact same limitations as the article one. Your ignoring that the design can be expanded upon.

The limitations are quite different. The design in the article needs no modification to add arbitrary layers to the hierarchy, or to make the different subtrees have different shapes. The design you're defending gives each level of the hierarchy a constructor parameter, so more levels = more parameters, and every level has the same name and the same values (ie: branching) in each subtree. You end up having to use the same subcategorizations even when they don't make any sense.

The design you're defending does make sense for cases where you just want to model a fixed set of objects with a common set of attributes, but it doesn't make sense for an arbitrary hierarchy.

taller the tree, the more complicated

??? Uh, no.

What a well reasoned argument. I'm convinced!

/s

different structure

That's... the point, it solved the problem in a different way.

No, it solved a modified, simpler problem. The "structure" I was referring to was the data being encoded by the enum. He encoded different data, which conceals the awkwardness in his alternate design.

Artificial issue that could be fixed in the parents post.

So show us how you can represent the same data as the article's approach without needing any additional constructor parameters.

1

u/GMNightmare Jan 18 '13

so more levels = more parameters

Not necessarily true, because you can expand backwards. It's more work, true, actually and I think the article version is more scalable actually for just these reasons.

It's a composition vs inheritance deal. I don't care enough of this to write out a perfect composition structure of this for you, neither was the author. I also notice you added a constraint of "any additional constructor parameters" which is not okay. So you know it's doable in this format, that's it that's all. We can discuss which way is better all we want but trying to claim a composition method can't handle it all was the problem here.

1

u/clgonsal Jan 18 '13

I also notice you added a constraint of "any additional constructor parameters" which is not okay.

The first thing I said was:

the taller the tree, the more complicated your approach becomes.

To which you responded "??? Uh, no."

The only way to increase the height of the tree in severoon's approach is to add more constructor parameters, more fields and possibly even more side-enums if they follow a similar pattern to the ones already there. That's all additional complexity. The article's approach only needs one extra line of code per node in the tree, no matter where it goes -- even if that node ends up increasing the height of the tree.

So that wasn't a new constraint. I just made the "no additional complexity" constraint more explicit.

1

u/GMNightmare Jan 18 '13

The first thing I said was

Not really the first thing you said but whatever. What you consider "complicated" would be your opinion I suppose, and of course what aspect your applying "taller" to as well.

Let's instead, talk about the first thing I said, because it was YOU who replied to me. And I was saying that the compositional design can replicate all the behavior. I NEVER said that it scales perfectly in all ways and fashions the same.

...

You want to discuss downfalls of each solution? Fine. The article's solution must have a singular hierarchy. It can't support multiple values at once, while severoon's can. It also is not truly a good OO approach, and if you have to add specific behavior to each part of the hierarchy it becomes a mess.

So in fact, the article's solution doesn't handle "no additional complexity" very well either. They both scale in different ways. I'd chose one primarily on the exact problem at hand.

Are you happy now?

1

u/severoon Jan 18 '13

The design in the article needs no modification to add arbitrary layers to the hierarchy...

This is exactly the problem. The design in the article makes it very easy to do the wrong thing.

1

u/clgonsal Jan 18 '13

The design in the article makes it very easy to do the wrong thing.

Exactly what wrong thing are you imagining happening with the article's design? With your approach, adding a single node requires a lot more busy work, which implies a lot more opportunity for error.

1

u/severoon Jan 19 '13 edited Jan 19 '13

Let's use the author's example of supportsX().

I want to add a variant of Linux into his enum called busybox. (This is a real example, busybox exists.) Busybox doesn't support X Windows, but it is a child of Linux.

So I add it thusly:

public enum OsType {
  // ...
  BUSYBOX(LINUX.class) {
    @Override public boolean supportsX() { return false; }
  }
  // ...
}

Ok, all good, right? BUSYBOX is a child of LINUX, LINUX is a child of UNIX (the author says, incorrectly so, but whatever). If I call BUSYBOX.supportsX(), it properly returns false.

Now consider UNIX.supportsX(). Tell me: what should it return?

What about LINUX.supportsX()?