Win a 3 month subscription to Marco Behler Videos this week in the Spring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Design ideas appreciated  RSS feed

 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

I want to write a "Coffee" class containing "Enum of enums" if that is possible.

That is, on the client side, the user will input at the main menu, either value 1 or 2 to indicate coffee type.

1 is Cappucino, 2 is Regular.

Is there a way to instantiate a "Cappucino" object (of the Coffee class type) which inturn instantiates 3 other containing ingredient objects, such as "Cream", "Sugar" and "Coffeepowder" enum types?

So also, if I instantiate "Regular" object (also of the type Coffee), it has to instantiate its own unique ingredient objects via enum.

How to write such a "Coffee" class? Any ideas?
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Programming isn't like theoretical physics where you want to let your imagination run wild. If you give your imagination free rein when you're designing, it's more likely that you'll end up with over-complicated, over-engineered designs.

One good way to avoid that is by writing lots of test code to try out your ideas. The more complicated designs will tend to be more difficult to test and you'll soon grow tired of them. This leaves you with the simpler, more straightforward designs that already have tests. How convenient would that be, right?

A simpler design might be for a CupOfCoffee to have a list of Ingredients. You might also have a Recipe that lists Ingredients. There might be a Strength enum for None, Light, Regular, Strong, and VeryStrong that can apply in different ways to Ingredient items. ... Well, now I'm letting my imagination run wild on me.

See how that goes?

So, just write some tests first to see what you can reasonably code out, then slowly build it (both your tests and production code) out with more features/capabilities.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Another thing that might help in this case is to think about how you'd translate the software entities and their behaviors into actions that can be observed in the real world. I'd imagine you'd want to install this software on some kind programmable coffee-making machine for it to be actually useful. The objects should represent abstractions of the actual physical device's capabilities. So, when somebody, a customer or a barista perhaps, presses a button on the machine that's labeled "Cappuccino", another button labeled "Large" and another labeled "Strong", then something happens in the machine to assemble a bunch of software objects that can then be used to control the type of coffee, the amount of coffee ground, the amount of water and milk and whatever else needs to go into making a good cup of Cappuccino.

Again, you have to try to keep things simple.

The Decorator design pattern is often explained by way of a coffee making example but I personally don't like that example much and it doesn't resonate well with me. Your mileage may vary (YMMV).
 
Campbell Ritchie
Marshal
Posts: 54909
155
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . . There might be a Strength enum for None, Light, Regular, Strong, and VeryStrong that can apply in different ways to Ingredient items. ... Well, now I'm letting my imagination run wild on me. . . .
There are only three possible elements for such an enum:-The other elements you suggested do not exist.
 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Dear Junilu and Campbell Ritchie,

Thank you very much for your replies!

Right now, I have an enum class only for the ingredient part. It is coded as follows:



In reality, I am forced to test everytime on the client code, whether it is "Cappucino" or "Regular" that a customer is purchasing, such as:



Now, this above check is repeated in several places on the client code and makes the code somewhat ugly and messy, as too much processing logic is exposed all over.

That is why I asked the question, if there is a way to code in the following way, that will be superb, no?

Coffee mycoffee = new Coffee(coffeetype); // And bravo! Based on the integer value of 'coffeetype' variable, mycoffee is instantiated with 'Cappucino' type with its own list of unique ingredients or 'Regular' with its unique ingredients.

So that is why my question!

I thank you both for the replies!
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Please UseCodeTags (←click that, it's a link) when you post code.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, I already suggested that you try creating a Recipe class.

Then you can write something like this:
 
Campbell Ritchie
Marshal
Posts: 54909
155
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:Please UseCodeTags (←click that, it's a link) when you post code.
I have added code tags for him, so he can see how much better they look.
 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Wow! That took me some time to understand it fully!

So first we would instantiate a coffeeRecipe class as in:



Then, the user input string would be used to fetch the value object (of CoffeeRecipe type) from menu map.

Then, we would instantiate a Coffee object passing in the fetched CoffeeRecipe object.

Then, the CoffeeRecipe object would obtain a reference of its containing Coffee object and add its internal list of ingredients to it!

I guess you have given the Beantype choice to indicate a finer grain of control, to be able to choose which coffee powder subtype, within my general coffeepowder Item type.

The advantage of this solution is that you transparently add the recipes due for each coffeetype elsewhere in the code, as opposed to explicitly stating each ingredient every time!

That is a really good piece of (tough) code, thank you very much for the novel solution!

PS: I am just re-iterating the steps for more clarity to myself and to convey my understanding to you.
 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you for adding the code tags to my previous reply, Campbell Ritchie!

BTW: I am 'her', not him.

Many thanks.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ravi Desigan wrote:
So first we would instantiate a coffeeRecipe class as in:

Almost but not quite what I had in mind. I actually already have this thing coded up. The CoffeeRecipe just starts with a name. Then you'd add ingredients and quantities to it:

Of course, you'd do that in some kind of loop and probably read/parse the values from a file or get it from a database. I just created an array of String and parsed it out in a loop to build up a test menu.

Edit: that should actually be


 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ravi Desigan wrote:Then, the CoffeeRecipe object would obtain a reference of its containing Coffee object and add its internal list of ingredients to it!

No, that's not quite accurate. The Coffee does not contain the recipe. The Coffee.make() method is just passed a CoffeeRecipe object which will provide the list of ingredients and quantities needed to make the cup of coffee.

First, you'd pass a CoffeeRecipe object to a Coffee object's make() method.

I was still trying to decide if that name was good enough. I've actually refactored to this now:

which I think reads a little better. However, this circuitous route, called a double dispatch, can be a code smell because it makes the code a little complex and difficult/confusing to follow.

I'm now re-considering whether or not the double dispatch is necessary at all. By that I mean it may be worth eliminating the extra hop through Coffee.makeWith() to call recipe.addIngredientsTo(this);

Seems like I've just complicated the design unnecessarily in which case the double dispatch is indeed a code smell. If I just do this:

the code still makes sense and I've eliminated the double dispatch and the smell of unnecessary complexity.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's another thing about design: the first one you come up with is often not the best one. You have to keep fiddling with the design and playing around with it. I use unit tests to play around with my designs. If you look around for other posts I have in the Design forum and the OO and Agile forums, you'll see that I'm a big proponent of Test-Driven Development, which to me is the best way to find a good design to move forward with. It's also a great way to reveal the flaws that will inevitably be in your first few iterations of a design.
 
Campbell Ritchie
Marshal
Posts: 54909
155
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ravi Desigan wrote:Thank you for adding the code tags to my previous reply, Campbell Ritchie!
That's a pleasure
BTW: I am 'her', not him. . . .
That's a big mistake of mine. Sorry.
 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu,

Thanks! I am actually happy to have learnt about this design pattern called 'Double Dispatch'!

Yes, I found the design quite complex, but all the same, the way the CoffeeRecipe encapsulates its ingredients and the coffee is 'made' by adding ingredients in the coffee recipe to the coffee object transparently simulate the real life scenario better.

I am currently looking for ways to incorporate this design in my code.

Thank you!
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
No problem, I'm glad you got some ideas out of the suggestions. Just remember, double dispatch can be a code smell if all it does is add more hops through object methods and nothing else.

I have toyed around with the design some more and now I have this:

So now, the CoffeeRecipe.makeACup(CupSize) method becomes a Factory method and looks something like this:

You might notice that I changed the Coffee constructor, adding a CupSize parameter to it. The BeanType is now encapsulated in a CoffeeRecipe because it made more sense to me after coming up with more recipes to put on the menu like "Robusta Negra," "Arabica Negra," and "House Blend, black." 

I'm trying to find who best to make responsible for adjusting the quantity of each ingredient based on the cup size chosen by the customer. I'm thinking of making the Quantity class do that. It makes sense to me that a Quantity object knows how to adjust itself based on a CupSize. Normally, I'd be careful about creating too many collaborations but this one seems natural and makes sense.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I wrote:I'm thinking of making the Quantity class do that. It makes sense to me that a Quantity object knows how to adjust itself based on a CupSize. Normally, I'd be careful about creating too many collaborations but this one seems natural and makes sense.

Doing that actually worked out nicely for the CoffeeRecipe.addIngredientsTo() method:


(more) - the Quantity.proportionFor() method looks like this:

I have made my Quantity class immutable so calling proportionFor() gives back a different Quantity instance if it's a different cup size, otherwise, I just return the same Quantity. Be careful about doing something similar to this (returning a this reference when there's no difference) with a mutable class because it can lead to surprises and problems.

The caveat again is that this is still a design in progress. I may change this again later. I'm just trying to show you the different options that can be explored while testing out the API for these classes.
 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This last design choice with Quantity might work out if I make sure that the menu items are always created with the smallest cup size as the baseline quantity.  That way, I can keep the amounts as integers and proportions will just get progressively larger with each larger cup size.  That means each CupSize enum value will have a proportion multiplier that's based on the smallest size having a proportion of 1.  That makes the math for proportioning to different sizes a simple integer multiplication or division.

I hope that makes sense.
 
Ravi Desigan
Ranch Hand
Posts: 35
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Junilu,

Your enthusiasm is contagious! I am thrilled to read newer and newer, novel ideas from you and am inclined to try them out on my app!

I will take some time to read your newer replies and get back to you later today!

 
Junilu Lacar
Sheriff
Posts: 10929
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, thank you. I'm glad you're seeing it that way.

Design is a very creative process so if you like doing it, then knowing how to look critically at designs and finding ways to improve them becomes both challenging and fun. One thing you might want to read up on is refactoring, and in particular, Martin Fowler's Refactoring - Improving the Design of Existing Code, the classic book on refactoring. The book also lists out twenty or so "code smells" that you should learn how to recognize; code smells tell you that something in your design needs to be changed.  Another book I like is Joshua Kerievsky's Refactoring to Patterns book.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!