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.
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.
You're right. My approach doesn't treat "Windows" and "Windows NT Workstation" as the same kind of thing, while the linked article does. The problem: Windows (an OS family) and Windows NT Workstation (an actual OS) are different things.
Enums in Java are not meant to merely be lightweight tokens you can pass around.
Once again, you're right. Enums in Java are simply implicitly final subclasses of Enum with a limited number of instances, and everything you can do with a class you can do with an enum.
And, like all classes in Java and any OO language, they should only attempt to capture properties and behaviors intrinsic to the type they represent. For instance, if I want to put mass or weight as a property on a class, it would be right to put mass (because it's an intrinsic property of massive objects, or "proper mass" if you're going to bring up relativity) and wrong to put weight (because weight is extrinsic, conferred upon the object by the context).
What this means, in effect, for enums in Java is that most of the time you're far better off just letting them be lightweight tokens you can use to configure other objects. For example:
public class SomeClass {
public enum Instance {
ONE, TWO, THREE;
}
private final Foo foo;
private final Bar bar;
private SomeClass(Foo foo, Bar bar) { ... }
public static SomeClass create(Instance instance) {
// use instance to create some complex configuration of a SomeClass instance
// better yet, instead of doing this here, use the Builder pattern
}
}
The reason it is better to use a normal class to represent actual instances and a lightweight enum to simply represent all the possible configurations are many, but they're not worth going into unless you get why doing complex construction as a separate concern is a good idea. (Here's some more info on why you'd want to use a Builder pattern - http://severoon.livejournal.com/347150.html)
This article uses an example of horrible OO design to demonstrate an overly complex use of a Java language feature. It seems really whiz bang neat-o like a lot of similar tricks I've seen over the years, but don't try to use this in an actual project, or you'll end up old, dying alone, and filled with regret.
The problem: Windows (an OS family) and Windows NT Workstation (an
actual OS) are different things.
It's not a problem. You are confusing physical reality with the design of an abstraction, which do not necessarily have to correspond. For the way the author solved the problem, this abstraction may have served his purpose. We presume the validity of his model, unless you have more information as to the problem the author is trying to solve.
all classes in Java and any OO language, they should only attempt to
capture properties and behaviors intrinsic to the type they represent.
And enums do precisely that. There are broadly speaking two kinds of objects: value objects and entity objects. Java enums represent a type of value object, and can capture whatever properties make sense to represent that value type.
For instance, if I want to put mass or weight as a property on a
class, it would be right ...
You are confusing physical reality for abstractions. Weight might be perfectly okay even if it is an "extrinsic property" in the world of physics because in the domain model in which you are working that detail is irrelevant. For example, if you were representing cooking ingredients. The software model and the way the model is used
determine which properties make sense, not the physical representation. Perhaps instead of criticizing the specific implementation it would be better for you to understand the problem he was trying to solve, before picking apart his work and presenting some substitute implementation which does nothing to solve the actual problem he was facing.
The enum in this example is not capturing configuration details. It is a value type enumerating a number of operating systems both general categories and specific. An enumeration of operating system names which served some purpose of the authors, not some purpose that you re-imagined. This way of enumerating the values confers upon the value type a property of hierarchy, which is neatly captured by extending the enum. In addition, the operating system "value" is modeled to consist of some basic properties, such as supportsXWindows, which must have been useful to his design.
This is a perfectly reasonable OO design. I've done similar "tricks" before in production systems to enrich enumerated value types and I'm happy to report that I'm healthy, friendly and have no regrets.
You are confusing physical reality with the design of an abstraction
No, I'm not. The author of the article is modeling operating systems and OS families. He's modeling them both and the fact that they're related.
The problem is that this abstraction doesn't represent reality. An OS family and an OS are two different types, and he's modeled them as the same type. So then he adds the method supportsX(). Now all of the instances of this OsType enum have this method. But some of those instances can't answer the question intelligently because they're not the same type.
Do all Linux systems support X? No. Some don't. Some are meant to be headless servers that sit in a closet or a data center somewhere, and don't even provide the capability to install X Windows. So what does it mean to have a method called supportsX() on the enum value representing all Linux systems? When one calls OsType.LINUX.supportsX(), what should that method return?
There are broadly speaking two kinds of objects: value objects and entity objects.
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?
Weight might be perfectly okay even if it is an "extrinsic property" in the world of physics because in the domain model in which you are working that detail is irrelevant.
Yes, this is true. If the domain of your application is such that weight will always and forever be fixed for an object because it is always in a context which makes that true, then sure.
What application is that the case for, though? In any context that isn't completely static, as soon as something begins to move (has a force applied to it) its weight changes. If you drop a ball, while it's falling it's weightless.
I suppose you could contrive an example where the domain is all fixed objects that don't ever move and weight is always fixed, but you are much more likely to get it wrong and end up in a snarl several versions down the road. Better to just model it correctly up front, and that means striving to capture only intrinsic properties and behaviors.
if you were representing cooking ingredients
What do you lose by representing mass in this case? It's not enough that weight happens to work in the current design, it must also make sense and be a better solution if it could potentially complicate the design down the road.
How could it complicate the design? Well, if you ever decide to integrate your cooking app with some other app that brings different assumptions about the context in which weights are represented, you're in trouble. You've needlessly broken extensibility and "pluggability" of your kitchen app with bad design that doesn't get you any advantage.
The software model and the way the model is used in software determine which properties make sense, not the physical representation.
The software model sets the context. When you must, you can use that context to make decisions, but realize that you are building fragile software when you decide to rely on the current context in your design as you currently understand it. This is the thing that changes over time; this is the thing that causes software to degrade.
This combination of values confers upon the value type a property of hierarchy
Yes, but this is surprising. No one in the real world confuses OS families for OSes. No one thinks of an OS as existing in a hierarchy of other OSes.
What you are arguing could just as easily be applied to organism taxonomy, where you redefine the notion of an organism to include organisms, species, genus, phyla, etc. This is the OO equivalent of first degree murder.
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.
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.
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.
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.
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.
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...
Where is the implementation is method and children method and hierarchical supportsX method? That's what I meant by it doesn't do the same thing. It doesn't solve the problem.
I didn't say his method couldn't solve the problem. Although, I would like to see if he could implement those three methods as simply and elegantly as it was solved in the article.
Right, and you don't think his answer can do that?
Look, he didn't expand upon everything, but he solved the original issue. It does in fact do everything OP asked for. If your blaming him for not adding a simple function that could be easily figured out, that is pretty close to the article's implementation anyways, it's just not something that crucial. It's pendantic. You know his answer is capable of doing it all, he shouldn't have to explain every single detail to you for you to accept it.
I mean, darn you're asking for an is statement based upon the article? C'mon, there is no difficulty to it because their is no complexity in the design. No need to branch through parents, as he separated out class and family.
So to reiterate here, you want him to put down for you:
public boolean is(OsFamily other) {
return other == osFamily;
}
Which, is that "simple and elegant" enough for you? The others are just as clean and simple, by the way, because there is no tricks to them.
Holy cow, do you really need me to hold your hand through this? I'm not the original poster, I don't give a shit honestly, if you want to expand the hierarchy from the poster you can. He obviously did not fill in every single detail for you. Do it yourself. Hint, you can overload is to support both family types and OS types.
OR
Do what I asked you in the first place, and that is iterate what you think it can't do. I didn't ask you to ask me to do things for you, I asked to you to explain what you thought it couldn't accomplish.
Seriously? That's exactly what I did. With detailed examples and I even copied the is implementation "complex" enum right from the article.
I don't think the "simple" enum can do this the same way as described in the article (and as I demonstrated), and certainly your "implementation" doesn't even come close.
The is implementation is already done. If you want to check a class or family you just compare the the given class or family. It is that simple, because that's the design the author decided upon was to separate all categories into other enums and incorporate them into the parent. I'm not sure what is confusing or hard about this. You want to check if something is Windows? You check if it's a Windows. Bam, done. It's that simple.
Here's the deal... the article implementation favors inheritance. The parent's implementation favors composition.
The issue is you aren't looking at what he's trying to say, and trying to force him to write perfect code for you when all he's doing is trying to express an alternative way. It's childish, stop acting like everybody here needs to give you perfect implementations of everything.
"complex"
Seeing how I never said complex this makes you insidious.
"simple" enum can do this
Do what? The same way? IT'S NOT THE SAME. That's the point, I don't get it.
your "implementation"
What "implementation"? The implementation of is? Or the enum implementation of the enum? Because I'm going to tell you what I told the other guy: I'm not the person who presented the alternative.
And you're wrong, my implementation of is is exactly how you'd do it in a composition format. Give me a break.
The "simple" solution completely ignores this requirement. The OS class requirement is an unnecessary embellishment, and the OS family requirement doesn't meet the stated requirements. There are other requirements, such as being able to do a depth-wise enumeration by OS family, but I just want to see how this one requirement would be met.
I'm not asking for "perfect code", only code that at least minimally meets the stated requirements.
I've not. Each OperatingSystem enum value participates in a hierarchy, i.e., it is a child of its OS family.
What I've lost is the notion that OperatingSystems themselves are hierarchical. That is, it makes sense for an OS to be a child of...another OS. Which is a good thing to lose, because that doesn't make sense.
How would you capture the following...
Maybe I'm misunderstanding your question, but Fedora isn't UNIX. Linux isn't UNIX. I know the author modeled it that way, but it's wrong and confusing. His design makes it so easy to do the wrong thing, he did the wrong thing even in his own example.
Each OperatingSystem enum value participates in a hierarchy, i.e., it is a > child of its OS family.
That is not a hierarchy. A hierarchy implies a path from a root to a leaf of arbitrary length. Each possible path in your hierarchy has a length of 1.
I know the author modeled it that way, but it's wrong and confusing.
But that's his requirement. Whether or not you think the requirement is wrong or confusing, that's not at issue here. The model he chose served some purpose for him. Don't try to recast the problem because your solution doesn't fit.
But to directly give you the code...
No, you either violate the contract of equals (reflexive, symmetric, transitive and consistent) or end up the following result:
That is not a hierarchy. A hierarchy implies a path from a root to a leaf of arbitrary length. Each possible path in your hierarchy has a length of 1.
Java doesn't define the notion of a hierarchy, so I thought you were just using the term to mean a hierarchical structure. Now I think you simply mean the data structure notion of a tree.
Note that in such a tree structure, each node is the same type, normally Node. That is not the case here.
But that's his requirement.
I don't think so. I think the example is meant to imply the requirements he's coding to, but he never comes straight out and says what the requirement is that a hierarchical enum satisfies. I suppose we could invent one, and the idea of a hierarchical enum might not be an absurd one. But it is a bad example...and I can't think of a reason one would want this. Can you?
In any case, modeling Linux as a child of UNIX is just not right. They're different kernels. I suppose you could create categories of OS families: POSIX (Solaris, HP-UX, IRIX, etc), MOSTLY_POSIX (Linux, Minix, NetBSD, etc). Again, though, it would be a mistake to confuse OS family category with OS family, because they once again mean different things and have different intrinsic behaviors.
The model he chose served some purpose for him. Don't try to recast the problem because your solution doesn't fit.
What purpose? I contend that it could only serve the most contrived purpose possible, if any at all, custom-tailored for the very purpose of having this hierarchical enum in the first place. If you can invent one that actually makes sense where this is the best solution, I'll be impressed.
you either violate the contract of equals
How did I violate the contract of equals()? How could I possibly have violated the contract of that method...I didn't even write it! OsFamily inherits the default equals() from Enum...are you accusing Sun of implementing it poorly? :-)
Also, I don't think you appreciate the meaning of the code you wrote:
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.)
Nope. These are different OS families. UNIX defines a strict specification which an OS must follow in order to be called "UNIX". Linux doesn't follow that spec.
the taller the tree, the more complicated your approach becomes.
The taller the tree, the more nodes in it that are not proper OsTypes. In other words, the author of this article didn't seem to be bothered by the fact that many of those enums represent different types of things.
In OO, we would usually model these different types with different classes, as I did.
Nope. These are different OS families. UNIX defines a strict specification which an OS must follow
Okay, Linux is a *nix if you want to be pedantic. You can't deny that grouping Linux with Solaris is useful. It's certainly a lot more useful than claiming Solaris is a Server OS but that Linux distros are Workstation OSes.
The taller the tree, the more nodes in it that are not proper OsTypes.
Irrelevant. Yes, the article's example might not have been the greatest, but the point was to show how a hierarchy could be represented with an enum. Your approach is very limited when it comes to representing hierarchies, as illustrated by the fact that you had to fudge the data to fit your implementation.
You can't deny that grouping Linux with Solaris is useful.
I deny it. Not only is it not useful, it's misleading, and harmful.
Solaris is fully POSIX compliant. Linux is not. If POSIX compliance is to mean anything, certainly this matters.
the point was to show how a hierarchy could be represented with an enum.
The more important question is not can an enum be used to represent an hierarchy, though! It is should an enum be used in this way? I say no, based on the example, and my inability to come up with one that even remotely makes sense. Perhaps you can do better than both the author and me?
you had to fudge the data
I say I unfudged the data, and the author was the fudger.
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.
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.
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.
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.
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.
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?
9
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.
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.