• Post Reply Bookmark Topic Watch Topic
  • New Topic

Dynamic Enum?  RSS feed

 
Kevin Hamrick
Ranch Hand
Posts: 37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've been programing for a long time but I had never used enum and so I thought I would teach myself this feature. I wrote a simple enum and a test class. The enum represents us coins and my test class uses the enum to calculate the change with the least number of coins. For example, I pass in 95 cents, I get back 3 quarters, 2 dimes. It all works just fine, easy to implement and I see why I should use an enum. Then I thought, what if the person is out of dimes. How could I tell the enum to skip dimes in it's switch statements? I can't figure this one out, how would I make DIME(10) false? Any ideas?

Thanks
 
Tushar Goel
Ranch Hand
Posts: 934
4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Can you show us some code? I may able to help you then.
 
Tim Cooke
Marshal
Posts: 4040
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That would be a different problem altogether. I don't think Enum is going to be enough for you to solve that.

Enum is used to define a classification set of things, in your case valid coin denominations. Your solution assumes an endless supply of those coins is available.

However, what you are suggesting introduces the notion of having a bounded collection of coins to choose from. You would still need the Enum for classification, but you'd also need some other construct to define the quantities of each type of coin you have to choose from. Which introduces the possibility that an answer cannot be reached.

What do you think?
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There is a nice section about enums in the Java® Tutorials.
 
Kevin Hamrick
Ranch Hand
Posts: 37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here is what I did....
simple enum


and a class to use it...



It produces this output which is fine...

2 Half Dollar
0 Quarters
0 Dimes
1 Nickles
0 Pennies

But I wanted a way to exclude a coin, like say Half Dollar. I suspect I would need to do it in the calculateChange method but I am not sure how to implement. Any ideas?
 
Paweł Baczyński
Bartender
Posts: 2077
44
Firefox Browser IntelliJ IDE Java Linux Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I advise against storing an amount inside Change enum. If you ever needed a PENNY instance in two different places you would run into problems.
I do not understand why did you put switch in your getCoins() method. All branches do exactly the same thing.
Do not use instanceof in toString() method. Use a private String variable for each instance.
 
Bear Bibeault
Author and ninkuma
Marshal
Posts: 66304
152
IntelliJ IDE Java jQuery Mac Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why would an accessor for the amount property be called getCoins() versus getAmount(), and why does it have a complicated switch statement that does the exact same thing for each case?

[Edit: beat by a few sconds]
 
Dave Tolls
Ranch Foreman
Posts: 3056
37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kevin Hamrick wrote:Here is what I did....
simple enum



I'll just comment on this.
Both your getCoins and toString methods shouldn't have those switch statements.
For getCoins you simply need to return this.amount, as you are already in the particular enum instance.
For toString I would add a name attribute to the enum to hold the display name, so you can just return this.name.
 
Bear Bibeault
Author and ninkuma
Marshal
Posts: 66304
152
IntelliJ IDE Java jQuery Mac Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Paweł Baczyński wrote:I advise against storing an amount inside Change enum

There is no problem with storing the value (a better word than amount, in my opinion) of each item. What's your beef with that?

My question is, if you are going to store the values in this way, why not also the names?
 
Paweł Baczyński
Bartender
Posts: 2077
44
Firefox Browser IntelliJ IDE Java Linux Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bear Bibeault wrote:There is no problem with storing the value (a better word than amount, in my opinion) of each item. What's your beef with that?


Ah, sorry, I misread it and thought the field is mutable. My fault.
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Maybe, but you aren't using the power of object‑orientation. You are passing 5 to the NICKEL constructor, and have a field for value which stores that 5, and there is a much easier way to return the 5 without having to use a switch block. Similarly for the name of the coin. Before you try overriding the toString method remember there is a default implementation in all enums. Try it and see what happens. Go through the Java® Language Specification, even though it may be difficult to read, because it gives you a lot of examples about enums.
 
Kevin Hamrick
Ranch Hand
Posts: 37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you. I see better now and I have removed the switch statements but to get a more user friendly name shouldn't I over-write the toString something like this?



Thoughts?
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Now work out the fields and the constructors. So that all you have to do in the toString method is write
return coinName;
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@OP: this an improper use of enum, IMO. Can't go into detail right now as typing on my phone is a pain. Will explain later when I have a proper keyboard.
 
Kevin Hamrick
Ranch Hand
Posts: 37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell - I get what you are saying and I thought about that implementation but then I thought "toString would always be the same for each coin" I guess you could argue that the value would be as well. I will wait and see what Junilu has to add before doing anything else. Thanks to everyone.
 
Stefan Evans
Bartender
Posts: 1837
10
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
For my 2 cents (or tuppence if you prefer)
The enum should be defining all of the possible coins, their values and anything else about them e.g. readable name.
"Change" per se is a completely different object which contains a list of coins that make up an amount.

The MakeChange class manipulates the Change object, adding coins to it according to its business rules.

You might define the rule of "no dimes" in the MakeChange object
Currently you have implemented it as a greedy algorithm, which may not work for some denominations of coins.
 
Kevin Hamrick
Ranch Hand
Posts: 37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stefan,

That makes cents(ha) and you get back to the original question. So my make change class would contain, IDK, a list of...values(?) such as 50, 25, etc that represents the coins the user wants to be considered and then in the loop check if the value in the enum is contained in the possible values before doing your calculations or is there a better way of doing that, something else that I am missing about enum?

could you also go further into detail about greedy algorithm? I didn't catch what you are inferring.
 
Stefan Evans
Bartender
Posts: 1837
10
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You could actually use the enum values to represent which coins to consider.

That is the whole point of enums. You use them in place of arbitrary values like 50, 25 etc to limit the inputs possible.

Lets say you wanted to limit answers based on the coins in your wallet.

You could set up a map



In terms of greedy algorithm: Right now you just take the biggest coin available. That works with the coins you have defined because of the denominations.
The problem would come if we change the coins and add some constraints.

Consider coinage which contains a 20c coin rather than the 25c.
And say also you are allowed to only use the 50c and 20c coins.
Now make change for 60 cents...

Your algorithm would start with a 50 cent coin (60 > 50), but then be unable to get the remaining 10, and might return an "this cannot be done" result.
But it IS possible using three 20c pieces.

Admittedly it is a contrived example.
The standard coinage is designed so that it can work well with a greedy algorithm for making change.
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I should qualify my initial comment: What you did is not all out of line with the intent of enums but there are some things that are.

My thoughts are along the same lines as Stefan's. You have to go back to the reason enums were introduced to the language. Before enums, Java programmers just used public static final values, perhaps all declared in an interface or perhaps a class with a private constructor so you couldn't instantiate it. Then Joshua Bloch introduced the Typesafe Enum Pattern and that quickly gained popularity. It was still not without problems though.

All these approaches were made obsolete with the introduction of enums. The Java documentation says: "you should use enum types any time you need to represent a fixed set of constants."

As Stefan noted, the name Change should be changed . Ignoring that, I get your intent. Assuming we change the name to something like Coin, your most basic enum definition might be something like:

As you can see, the code in the Coin enum is common to all of the instances you declare (PENNY, NICKEL, etc.)

Your initial implementation of toString() was not very good though and you already know why, I guess. Breaking apart the switch statement and moving each case into each individual constant is somewhat of an improvement but still "smelly" to me.

Enum types already have a toString() that works just fine: it returns the value that the public name() method returns, which is the name of the enum value as declared in the source code.

If you follow the standard naming convention for enum values (all caps, words separated by underscores) and you don't happen to like that the value returned by name() will be all caps, you could define an instance method, maybe call it properName() where you would take the value returned by name() and remove underscores and capitalize the first letter of each word.

Here's a quick-and-dirty implementation:

Again, this method applies to all instances of Coin alike.

So you have to ask yourself, that other method that makes change from a given amount, is it appropriately assigned to the Coin enum? Would this be code that applies to each instance of Coin, irrespective of what and how many instances of the enum there may be? My answer would be "No".

What's the alternative then? You could define a public static method in the enum. That might be appropriate. But I think you are already on a better track by assigning the responsibility to another class, MakeChange. That name could probably be better since it's a verb phrase and class names are more preferably noun phrases. Maybe ChangeMaker is a better name.
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I seldom dig into the JLS because it's just too dense for my taste most of the time but I was pleasantly surprised that they have a Coin example in the section about enums that's very much like the one I just proposed: https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And here's the JUnit test I used for the Coin enum:

(Edit: test renamed from "removes_spaces_" to "removes_underscores")
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here are a couple of methods that may be appropriate in the Coin enum:

Every Enum type also has an implicit static values() method which returns an array with all the constant values for that Enum, in source order. In the case of the Coin constants that I declared:

You might define a public static method in Coin that makes change for a given amount by using the two methods and the array you get back from values(). The method might return a Map<Coin, Integer> that represents the best way to make change for the given amount if you had an unlimited supply of coins. From there, you can have any number of variations. These I can accept as valid methods in the Coin enum.
 
Bear Bibeault
Author and ninkuma
Marshal
Posts: 66304
152
IntelliJ IDE Java jQuery Mac Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote: maybe call it properName() where you would take the value returned by name() and remove underscores and capitalize the first letter of each word.


I'm not a big fan of this approach. It ties the display name and the enum too strongly together. I almost always prefer Campbells's approach of initializing it in the constructor and storing as an instance variable. That way, they enum name and the display name are independent (and can even be a key for internationalization).
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bear Bibeault wrote:
Junilu Lacar wrote: maybe call it properName() where you would take the value returned by name() and remove underscores and capitalize the first letter of each word.


I'm not a big fan of this approach. It ties the display name and the enum too strongly together. I almost always prefer Campbells's approach of initializing it in the constructor and storing as an instance variable. That way, they enum name and the display name are independent (and can even be a key for internationalization).

Maybe with a different kind of Enum constant. With Coin and these values, I don't see how you would want any name other than what you've declared in the source.

Edit: And looking more closely at Campbell's example with the British coinage, those proper names would be more consistent in capitalization and spelling if the general properName() method I proposed was used rather than explicitly specifying the value as a constructor parameter. The slight variations seem to be quite arbitrary and that's fine if you really want to name the constant TWOPENCE and have a display name of "Tuppence" or have "Two pounds" instead of "Two Pounds." However, I would really have to ask "Why?! Why not just be consistent? Why make the reader work harder?"

I did refactor a bit so that the value would be assigned when the constant is instantiated rather than calculating it every time the method is called. I guess I wrote pretty good tests since they still pass without any modification.

 
Bear Bibeault
Author and ninkuma
Marshal
Posts: 66304
152
IntelliJ IDE Java jQuery Mac Mac OS X
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:With Coin and these values, I don't see how you would want any name other than what you've declared in the source.

Perhaps it might work out in this case, but I don't think it's a good general pattern to use when dealing with enums.
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Bear Bibeault wrote:
Junilu Lacar wrote:With Coin and these values, I don't see how you would want any name other than what you've declared in the source.

Perhaps it might work out in this case, but I don't think it's a good general pattern to use when dealing with enums.

I can buy that there may be cases where you'd want the display name to be different from the name that you declared in source code but since the name() and toString() methods are inherited from the common base Enum class and the values() method is automatically added to the Enum by the Java compiler, isn't it more probable that the language designers were thinking this would be a good general pattern rather than an exception?

Edit: Actually, to the point about tightly coupling the Enum name with the display string, I just remembered that the approach I took was to do the mapping between the Enum value and the desired display string outside of the Enum itself. The mapping/translation is done closer to the presentation layer itself and you just use the Enum value as the key, as well as for L10N/I18N if you're doing any of that stuff.
 
Campbell Ritchie
Marshal
Posts: 56529
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I wrote that example in a couple of minutes over beer, leaving out all sorts of things. I just stuck the slang “tuppence” in and didn't worry about consistent capitalisation because I knew there was no significant chance of OP actually having a 2p or 2¢ coin. Nor £2 or $2.
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:I wrote that example in a couple of minutes over beer, leaving out all sorts of things. I just stuck the slang “tuppence” in and didn't worry about consistent capitalisation because I knew there was no significant chance of OP actually having a 2p or 2¢ coin. Nor £2 or $2.

My point is that but for the little beer-induced inconsistencies , you wouldn't need to pass the second constructor argument if you used the method I showed. I agree with Bear that if there's a significant difference in the value you want to display vs the name you want to use for the constant in the program, it's not a good thing to couple the presentation concerns too tightly with the Enum.
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@OP Putting aside the specific design choices discussed about the display name, the more general point I was trying to make is that instance methods of an enum should pertain to all instances just as instance methods of a normal class do to all instances of that class. You don't define an instance method that's meant for just one specific instance and you don't write an instance method that knows about particular instances of the class. The latter was the case where you had a switch-case statement to figure out the display name for each constant that you had defined and that's what made it "smelly".

Also, the strategy of making each constant override a method to provide specific behavior can be a valid approach and may be the best choice for certain behaviors. An Enum for arithmetic operations comes to mind, where ADD.exec() would do something differently from MULT.exec(). This, BTW, might be a good example where your display value is very different from your constant name, in which case I would probably go with Campbell's approach.

However, if there is a method in Enum that is already provided that can help you achieve what you want, you should probably use that instead. For example, Enum constants also have a built-in ordinal() method that returns their source order as a zero-based index value. This complements the static values() method I mentioned earlier. You need to be careful with adding any custom logic that may contradict this logical ordering of the Enum values. I'm almost certain that this ordinal value is what is used by the Enum.compareTo() method.
 
Paweł Baczyński
Bartender
Posts: 2077
44
Firefox Browser IntelliJ IDE Java Linux Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu, you are right
Javadoc for Enum says:
public final int compareTo(E o)

Compares this enum with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. Enum constants are only comparable to other enum constants of the same enum type. The natural order implemented by this method is the order in which the constants are declared.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Kevin Hamrick wrote:But I wanted a way to exclude a coin, like say Half Dollar. I suspect I would need to do it in the calculateChange method but I am not sure how to implement. Any ideas?

I reckon you're fairly close, and you're certainly right to keep your Change enum separate from your MakeChange class; except that, like the others, I'd call it Coin, since that's what it describes.

One thing that doesn't seem to have been mentioned is that enums have lovely things called EnumSets, which you could easily populate based on what kinds are available in your "machine" (or till). Alternatively (and possibly better), you could use an EnumMap<Coin, AtomicInteger> to maintain the "state" of your till.

HIH

Winston
 
Junilu Lacar
Sheriff
Posts: 11477
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:One thing that doesn't seem to have been mentioned is that enums have lovely things called EnumSets, which you could easily populate based on what kinds are available in your "machine" (or till). Alternatively (and possibly better), you could use an EnumMap<Coin, AtomicInteger> to maintain the "state" of your till.

Great point. Thanks for that, Winston!
 
Tushar Goel
Ranch Hand
Posts: 934
4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Good to see you back Winston after long time.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tushar Goel wrote:Good to see you back Winston after long time.

Thanks Tushar. I've just completed a move from Belgium to the UK, and am currently on an extended holiday in Canada, so my opportunities for Ranching have been limited. After the first week of June, you should see me on a lot more.

Winston
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!