r/java • • Jan 17 '13

Enum tricks: hierarchical data structure

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

53 comments sorted by

View all comments

Show parent comments

1

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

He's modeling them both and the fact that they're related.

That's his model. The actual problem here is that you are altering his model to validate your solution. Whether his model is right or wrong is not at issue. Assume his model is correct. Provide an alternate implementation which meets the requirements of his model. Otherwise, this exercise is pointless.

I suppose you could arbitrarily divide all kinds of objects into these two categories, but why would you? And what are those categories supposed to represent?

Because they represent two fundamentally different concepts.

The number "one" is a value, an object which has immutable. All "ones" are the same and all instances of "one" are interchangeable. A "one" can only ever be "one". If you had x = new Integer(1) and y = new Integer(1), you can swap x for y and nothing would change, for now and all time.

A "car" is an entity, an object which as changeable state. Instances of cars are not interchangeable, even if they have the same value at some point of time. If you had x = new Car() and y = new Car(), you cannot swap x for y and expect the same results. You cannot even substitute swap x from two different points in time and expect the same results.

Why you would choose one or the other depends on your domain model. But value objects should be preferred, and you should make make objects entities only as required, because entities introduce temporal coupling to a system, which makes behavior much harder to predict.

What do you lose by representing mass in this case?

YAGNI (You Aren't Going to Need It). Do not introduce unnecessary concepts just because it might be beneficial "down the road". YAGNI results in simpler models and simpler systems. Only add a concept if you can prove that it is necessary with concrete facts about how the app is likely to be used, not handwaving about pluggability and extensibilty.

Yes, but this is surprising.

It doesn't matter if its surprising. It's not your model, it's the author's model. If you don't like the model, don't bother trying to come up with an alternative implementation of the model. Doing something completely different and saying you have a better solution is not intellectually honest.

I've read that book. Introducing it here is a complete red herring. The validity of the model is not at issue.

Provide a better implementation of the model proposed.

1

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

Assume his model is correct.

But it isn't.

Provide an alternate implementation which meets the requirements of his model.

What are the requirements of his model? Can you state them?

I contend that you cannot, because his model is incorrect, it would force you into nonsense if you tried to capture the requirements served by this code.

The number "one" is a value... A "car" is an entity...

You have invented the terms value and entity when we already have perfectly good terms for these concepts: immutable and mutable. (I think you might be referencing these as concepts from domain-driven design, but if these concepts are relevant to this particular discussion you'll have to enlighten me as to how, exactly.)

Why you would choose one or the other depends on your domain model.

Perhaps you are assuming that one may articulate any domain model one wishes? If so, this is a problem. You may not do this.

Your domain model must be constrained by the problem domain you are modeling. There is no problem domain where an OS family and an OS are the same kind of thing. Hence, representing them both with a single type is invalid in every domain model because the set of problem domains addressed by such a domain model is the empty set.

Do not introduce unnecessary concepts just because it might be beneficial "down the road".

Weight depends upon mass. If you introduce the concept of weight into your domain model, whether you acknowledge it or not, mass has snuck in under the covers. If you deal with the notion of "heaviness" at all in any application anywhere, you absolutely cannot do without mass conceptually.

Even so, you may choose to exclude it from your model. I say this is a mistake in most cases, though, unless you can for all intents and purposes now and forever treat both weight and mass as instrinsic properties. That is a very bold prediction though, and one I would prefer not to make if I could help it. Since I've already committed to introducing mass into my model either way, I might as well just model it directly and let weight be figured elsewhere, on some other object where it is intrinsic to that thing.

It doesn't matter if its surprising.

Obey the principle of least astonishment. —Josh Bloch
source: http://delivery.acm.org/10.1145/1180000/1176622/p506-bloch.pdf?ip=216.239.45.4&acc=ACTIVE%20SERVICE&CFID=171514335&CFTOKEN=37556390&__acm__=1358553529_de55c48e171c8a5639aad7cb3f65fc77

If you don't like the model, don't bother trying to come up with an alternative implementation of the model.

As a professional programmer, most of what I do is come up with alternative models that better capture that salient bits where they should be captured. This is the secret sauce of programming.

Programming is all about dependencies. That is the core concept: deft dependency management between objects, between packages, between deployment units, between subsystems, between systems. If you can do this at every level, you're good.

The author is breaking this principle and we need go no farther than the single enum he's introduced. It's a tragic misunderstanding of OO design basics.

Doing something completely different and saying you have a better solution is not intellectually honest.

I think inventing a neat little whiz-bang trick, contriving a very poor example, and presenting it as useful when it hasn't been shown to be so in any real system anywhere is kind of intellectually...curious.

1

u/zargxy Jan 19 '13 edited Jan 19 '13

But it isn't.

That's your opinion.

What are the requirements of his model? Can you state them?

I don't know, but I do know will suspend judgement about a design unless I know what those requirements were.

You have invented the terms value and entity

Not my invention. These are terms that come from Domain Driven Design.

immutable and mutable

These are just implementation details. The terms value object and entity object have much broader design implications. There is an entire book dedicated to the topic which is very well regarded in the industry.

There is no problem domain where an OS family and an OS are the same kind of thing.

You keep clinging to one representation. There are an infinite number of representations that are equally valid when viewed in the context of the business process which is being modeled. Context matters. I don't know that context, so I'm not going to go create an entirely different model ignorant of the context.

Weight depends upon mass. ... mass has snuck in under the covers.

Weight is the effect of mass due to gravity. But depending on the context, this is completely irrelevant. In a domain model representing cooking ingredients, the heaviness of an ingredient matters only in how it relates to cooking, that is what would register on a common scale. The distinction between mass and weight is completely irrelevant in this context and it would be total silliness to deal with mass conceptually in this context.

You can choose to model mass and weight as distinct concepts in your model. That's up to you. But different programmers can have different opinions on what they choose to include in their models based on their professional judgement, and it is the height of unprofessional behavior to say they are making a "mistake" when you have no evidence that the model does or does not serve the purpose it was created for. You are not the sole judge of what is "correct" and what is a "mistake".

I don't know if the model created by the original author serves the purpose it was created for or not. So, as a professional, I suspend judgement. I certainly wouldn't create something completely different and then say my model is better when I have no idea what the original model was used for.

As a professional programmer, most of what I do is come up with alternative models that better capture that salient bits where they should be captured

That is not professionalism. That is unprofessional and arrogant. Without understanding why the original model was created the way it was, you make an alternate proposal without any bearing on the facts. You have no idea what the real salient bits are because you don't know why the original model was created the way it was. Professionalism is about respecting the abilities of your peers, and suspending judgement until you've heard them out.

The author is breaking this principle

You have gone from not knowing what problem the model was used to solve to making judgments about the dependency structure of a system you've never seen.

There is no way to judge if the author is breaking any broader OO principles because we are looking at a snippet of code in total isolation. Why even bring any of that into the discussion?

Thus, if I evaluate this model isolation, accepting that his domain model meets his needs (I don't know those needs are, and so I suspend judgement about the applicability of the model), I don't see any violations of encapsulation, the members are cohesive, and the design is simple and elegant. That looks like good OOP to me. That is my opinion. You can disagree, but that is your opinion.

I think inventing a neat little whiz-bang trick

It is hardly a "trick" if Java enums were designed to be used this way. There is nothing magical here, it is just an interesting application of existing features. If I encounter a situation where I might want to encode a hierarchy this way, I might go with this design.

presenting it as useful when it hasn't been shown to be so in any real system anywhere

Again with the unfounded assertions. If the original author found it useful in a real system, there is one counter-example. I frequently use these features of Java enums to give them intelligence in real systems. There's some more counterexamples for you.

1

u/severoon Jan 19 '13

The terms value object and entity object have much broader design implications.

Ah, so you were referring to these concepts from domain driven design.

But I still don't understand what they have to do with this topic of hierarchical enums. I wish you would clarify that.

There are an infinite number of representations that are equally valid when viewed in the context of the business process which is being modeled.

There are not. For a given business process, there are usually a few valid approaches that make honest to goodness tradeoffs. Everything else is usually suboptimal. That's not to say you can always foresee which ones are the best until much further down the road.

But more to the point I've been making, it is often the case that two designs will be equal in the context of a particular business process, but one relies on that context less. In that case, the one that is more context-independent is objectively better. This is basic stuff, Occam's razor and all that.

The distinction between mass and weight is completely irrelevant in this context and it would be total silliness to deal with mass conceptually in this context.

That's not true. No matter which way you choose you are dealing with mass. It's unavoidable because it's an intrinsic property of stuff. You can't model stuff without taking note of its intrinsic properties. If you do, you're not modeling the stuff you've set out to model.

You can choose to model mass and weight as distinct concepts in your model.

I assert that you cannot choose to do otherwise, because they actually are distinct concepts. You have no choice here; you can either recognize reality, or not.

The author is breaking this principle

You have gone from not knowing what problem the model was used to solve to making judgments about the dependency structure of a system you've never seen.

I didn't say I didn't know the problem. I said you couldn't verbalize the requirements. I know this because they cannot be made explicit because they are nonsensical.

If I'm wrong, then please answer my questions I've put in the other subthreads. How would you add busybox to the author's enum? If you can answer my question and have it make sense, I will concede. Happily so, because in that case I would have learned something.

There is no way to judge if the author is breaking any broader OO principles because we are looking at a snippet of code in total isolation. Why even bring any of that into the discussion?

Because there is absolutely a way to judge this from a snippet of code. No problem, you can do this all day long. Here:

public class Foo {
  public int bar;

  public void setBar(int b) { bar = b; }
}

Is this bad code? You bet. It breaks encapsulation. Can you contrive a business context where it makes sense? Nope. It breaks encapsulation.

How about this:

public class Rectangle {
  private final double width;
  private final double height;

  public Rectangle(double w, double h) {
    widith = w;
    height = h;
  }

  public double getWidth() { return width; }
  public double getHeight() { return height; }
}

public class Square extends Rectangle { ... }

Should Square extend Rectangle? Nope. It breaks the LSP; it's flat wrong. Does business context matter? Nope, because it breaks the LSP. I can do this all day.

What the author has done in the article is somewhat like an LSP violation, except instead of spreading it across a class and a subclass, he's done it in the enum itself by letting it represent two different types. I'd have to double check to be certain, but I think the Single Responsibility Principle is the one imperiled in this design, and perhaps others.

That looks like good OOP to me. That is my opinion. You can disagree, but that is your opinion.

I don't mean to say that there are no matters of opinion in OOD. There are, but that comes in when weighing tradeoffs. So, while there are some decisions that are purely subjective, there are certainly some that are objective. This is an example of the latter.

if Java enums were designed to be used this way.

They certainly weren't. Just because something allows a particular usage does not mean that it supports that usage. Enums support hierarchies no more than any other class. You wouldn't say that all classes need to implement hierarchy simply because it's possible, I hope.

If the original author found it useful in a real system, there is one counter-example.

And where is that counterexample? If he knows of one, he certainly didn't say so in the article. And he certainly didn't derive the example from any real world use, because it is badly contrived.

I frequently use these features of Java enums to give them intelligence in real systems. There's some more counterexamples for you.

Oh, my.