Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am working in a lemonade stand game in Java I have some conditions to determine the "perfect recipe" I see a lot of repeated code that I am sure it can be improved with a loop but I am having a hard time figuring it out.

        double perfectPrice = 0;
        int perfectIce = 0;

        ///perfect price and ice depending on weather and temperature
        if(temperature > 58){
            switch(weather[randomWeather]){
            case "Sunny":
                perfectPrice = 0.23;
                perfectIce = 5;
                customers -= 20;
                break;
            case "Hazy":
                perfectPrice = 0.21;
                perfectIce = 5;
                customers -= 25;
                break;
            case "Cloudy":
                perfectPrice = 0.19;
                perfectIce = 4;
                customers -= 30;
                break;
            case "Overcast":
                perfectPrice = 0.17;
                perfectIce = 3;
                customers -= 35;
                break;
            case "Rainny":
                perfectPrice = 0.15;
                perfectIce = 2;
                customers -= 40;
                break;
            }
        }
        if(temperature > 69){
            switch(weather[randomWeather]){
            case "Sunny":
                perfectPrice = 0.25;
                perfectIce = 7;
                customers -= 17;
                break;
            case "Hazy":
                perfectPrice = 0.23;
                perfectIce = 6;
                customers -= 21;
                break;
            case "Cloudy":
                perfectPrice = 0.21;
                perfectIce = 5;
                customers -= 25;
                break;
            case "Overcast":
                perfectPrice = 0.19;
                perfectIce = 4;
                customers -= 30;
                break;
            case "Rainny":
                perfectPrice = 0.17;
                perfectIce = 3;
                customers -= 35;
                break;
            }
        }
        if(temperature > 80){
            switch(weather[randomWeather]){
            case "Sunny":
                perfectPrice = 0.28;
                perfectIce = 9;
                customers -= 10;
                break;
            case "Hazy":
                perfectPrice = 0.25;
                perfectIce = 8;
                customers -= 15;
                break;
            case "Cloudy":
                perfectPrice = 0.22;
                perfectIce = 7;
                customers -= 20;
                break;
            case "Overcast":
                perfectPrice = 0.20;
                perfectIce = 6;
                customers -= 25;
                break;
            case "Rainny":
                perfectPrice = 0.18;
                perfectIce = 5;
                customers -= 30;
                break;
            }
        }
        if(temperature > 90){
            switch(weather[randomWeather]){
            case "Sunny":
                perfectPrice = 0.32;
                perfectIce = 10;
                customers -= 0;
                break;
            case "Hazy":
                perfectPrice = 0.28;
                perfectIce = 9;
                customers -= 12;
                break;
            case "Cloudy":
                perfectPrice = 0.25;
                perfectIce = 8;
                customers -= 18;
                break;
            case "Overcast":
                perfectPrice = 0.23;
                perfectIce = 7;
                customers -= 23;
                break;
            case "Rainny":
                perfectPrice = 0.21;
                perfectIce = 6;
                customers -= 30;
                break;
            }
        }

I came up with this trying to figure out but I am getting negative results for customers something is wrong I can't seem to figure it out

        double perfectPrice = 0;
        int perfectIce = 0;
        double [] tempPrice = {0.23,0.25,0.28,0.32};
        int [] tempOptions = {58,69,80,90};
        int [] tempIce = {5,7,9,10};
        int [] tempCustomers = {20,15,10,0};

        ///perfect price and ice depending on weather and temperature
        //if(temperature > 58){
            switch(weather[randomWeather]){
            case "Sunny":
                for(int p=0; p<4; p++){
                    if(temperature > tempOptions[p]){
                        System.out.println("Temperature " + temperature);
                        System.out.println("Options " + tempOptions[p]);
                        perfectPrice = tempPrice[p];
                        perfectIce = tempIce[p];
                        customers = customers - tempCustomers[p];
                    }
                }
            case "Hazy":
                for(int p=0; p<4; p++){
                    if(temperature > tempOptions[p]){
                        perfectPrice = tempPrice[p] - 0.03;
                        perfectIce = tempIce[p] - 1;
                        customers = customers - (tempCustomers[p] + 5);
                    }
                }
            case "Cloudy":
                for(int p=0; p<4; p++){
                    if(temperature > tempOptions[p]){
                        perfectPrice = tempPrice[p] - 0.06;
                        perfectIce = tempIce[p] - 2;
                        customers = customers - (tempCustomers[p] + 10);
                    }
                }
            case "Overcast":
                for(int p=0; p<4; p++){
                    if(temperature > tempOptions[p]){
                        perfectPrice = tempPrice[p] - 0.09;
                        perfectIce = tempIce[p] - 3;
                        customers = customers - (tempCustomers[p] + 15);
                    }
                }
            case "Rainny":
                for(int p=0; p<4; p++){
                    if(temperature > tempOptions[p]){
                        perfectPrice = tempPrice[p] - 0.12;
                        perfectIce = tempIce[p] - 4;
                        customers = customers - (tempCustomers[p] + 20);
                    }
                }
            }
share|improve this question

closed as off-topic by Jamal May 16 at 19:56

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions containing broken code or asking for advice about code not yet written are off-topic, as the code is not ready for review. After the question has been edited to contain working code, we will consider reopening it." – Jamal
If this question can be reworded to fit the rules in the help center, please edit the question.

2 Answers 2

A bug

First of all, you get perfectPrice = 0, perfectIce = 0, and undefined customers for temperatures below 50 °F. I guess that's not what you want, so avoid such strange situations by cleaner coding. One possibility is

// Leaving local variables undefined is fine in this case
// ... as Java enforces that they get set before use.
double perfectPrice;
int perfectIce;

// Note that for temperatures above 90 your code is assigning
// every variable four times.
// That's why I had to reverse the condition order.
if (temperature > 90) {
    ...
} else if (temperature > 80) {
    ...
} else if (temperature > 69) {
    ...
} else if (temperature > 58) {
    ...
} else {
    throw new IllegalArgumentException("temperature=" + temperature);
}

Or drop the first condition (when you can sell lemonade at 1000 °F, you can sell it at −459.67 °F as well). Usually, such conditions are actually separators for the ranges and there's one fewer of them.

Separation of concerns

    if(temperature > 58){
        switch(weather[randomWeather]){

This makes your method much less usable. What random? The method should accept as its input a temperature and a weather. Leave the randomness outside.

What is it all about?

Ask yourself, what are you actually doing? There's temperature, weather and various variables. This looks like a 3D table to me (one dimension is what variable gets set). That's not nice, and get even uglier when transformed in a lengthy code.

So let's create (using Lombok)

@RequiredArgsConstructor @Getter class Recipe {
    private final double perfectPrice;
    private final int perfectIce;
    private final int customersDelta;
}

Working with Strings representing one of n possibilities is wrong, so let's create

enum Weather weather {SUNNY, HAZY, CLOUDY, OVERCAST, RAINY}

and now you could rewrite your if-cascade to something like

if (temperature > 58) {
    switch(weather[randomWeather]){
        case SUNNY: return new Recipe(0.23, 5, -20);
        ...
    ...
...

It's shorter and more structured, but still ugly. What about a table?

A Table

With aggregating the recipe into a Recipe, we turned it to a 2D table. Unfortunately, the entries are recipes, i.e., composite things and don't fit well into a CSV table (which would be my clear favorite otherwise).

But still, it is a table. I'd structure it like this

Threshold; Weather; PerfectPrice; PerfectIce; CustomersDelta
58;        SUNNY;   0.23;         5;          -20
58;        HAZY;    0.21;         5;          -25
...

Now you can read it from an external file or create a simple method so that you can write

new RecipeTable()
.put(58, Weather.SUNNY, 0.23, 5, -20)
.put(58, Weather.HAZY,  0.21, 5, -25)
...

Then you'd need some code to query the table. It's not exactly trivial, but I (or someone else) can help you in another CR if you try.

To equal or not to equal

You're using conditions like temperature > 58, where temperature >= ... feels much more natural. If the input is a double, this hardly matters and > can be simply replaced by >=. If it's an int, then you can replace > 58 by >= 59.

A formula

Assuming that for sunny weather and 58 °F the perfectPrice = 0.23 and for sunny weather and 69 °F the perfectPrice = 0.25;, I'd bet that the perfect price for 64 °F is much closer to 0.24 than 0.23. That's why a formula should be used, something as simple as linear interpolation should do.

For beginners

This all can get a bit complicated and I'd recommend top concentrate on the simple things for now.

  • Create the enum Weather and the class Recipe (you don't need to use Lombok, just write the boring stuff manually).
  • Using if and switch instead of the table is not nice, but acceptable, when done right. Here, this means to really return when a condition is met (otherwise debugging is a pain).
  • Always think about what happens when no condition is met.
  • Always think about the method's inputs. You're giving no method declaration, as it was an irrelevant thingy. But it's actually the mosty important part.

Bug in the loop code

You're getting negative customers, because of what I wrote in the very first paragraph:

// Note that for temperatures above 90 your code is assigning
// every variable four times.

You're also executing customers = customers - tempCustomers[p]; multiple times. Just don't do it and write a method returning a result (containing customersDelta) instead of modifying variables. It's cleaner and less error prone as you can see just now.

Update for the loop code

With the condition

if (temperature >= 59 && temperature <= 69){

you've fixed the problem, but it's not nice as the bounds get repeated. Using "else-if" should be enough.

customers = customers - 20;

This is strictly worse than the original

customers -= 20;

If you don't have the time to create class Recipe (which you really should), then don't do it. But note that without it, you can't write a simple method computing and returning a value (as you can't return more than one thing).

If you don't have the time to create enum Weather, then at the very least define the string constants like

public static final String SUNNY = "Sunny";

This way, you program won't stop working when you fix the typo ("Rainny" -> "Rainy").

share|improve this answer
    
I am new to Java I don't know how tables work :/ the temperature is random from 59 to 99 –  joanb May 16 at 19:02
    
@joanb OK, by table I mean e.g. something like 2D array. Bot forget it for now and I'm adding a beginners advice paragraph now. –  maaartinus May 16 at 19:22
    
@joanb The fact that the temperature is random is irrelevant for a method using the temperature for computing something else. It should care no more than a formula for a circle area cares about the circle being drawn on a blackboard. +++ Knowing the temperature range does not mean that you can ignore values outside of this range. You can refuse them be throwing an IllegalArgumentException, but simply ignoring them calls for problems later. –  maaartinus May 16 at 19:41
    
Thank you very much for the input. I appreciate it there is a lot of good information here. I tried to fix it as much as possible. It looks like its working I don't see bugs yet. Unfortunately a lot of the terms use here I don't understand the assignment is due on Monday and I can't just seem to have enough time to achieve all your suggestions :/ the table approach looks perfect, but I have no idea how to read to a file I don't have the time to get it right before the assignment is due so I have to find simpler approach –  joanb May 16 at 20:10
    
@joanb You're welcome. In general, you should not post updated code in the question (though adding it is by far not as bad as editing the original). See my update. –  maaartinus May 16 at 20:31

I don't thinks it's a loop you need.

Using a formula

The values per weather type and temperature range seem to follow a pattern that perhaps you can capture with a formula. However, on closer look, I see some irregularities that break the pattern, so probably this won't work well. See the next point.

Using a table-driven approach

When you have multiple variables with many possible combinations, and irregular values, a table-driven approach typically works better. The code will be both easier to read and maintain.

In this example, you have data (price, ice, customers) that depend on weather conditions and temperature range. It's easy to see what's happening if you rewrite the value mappings in a table:

weather | temperature | value
-----------------------------
sunny   | > 58        | 0.23
sunny   | > 69        | 0.25
...
hazy    | > 58        | 0.21
hazy    | > 69        | 0.23
...

Among the possible ways to accomplish this in Java, you could:

  • Create a Map<Key, Double>, where Key is a simple class to contain the fields necessary for the mapping, in this case weather type, and temperature range

  • Create a Map<String, Double[]>, where String is the weather type, and Double[] contains the values for each temperature range

There could be other ways too.

Other concerns

Looking at this code:

    if(temperature > 58){
        // ... set perfectPrice, perfectIce, customers
    }
    if(temperature > 69){
        // ... set perfectPrice, perfectIce, customers
    }
    if(temperature > 80){
        // ... set perfectPrice, perfectIce, customers
    }

What do you think will happen when temperature is 81? The code will set perfectPrice, perfectIce, customers again, again, and again. The conditions should have been chained with else if, or you should have used another way to exit those blocks after setting the values.

Avoid magic strings

"Sunny", "Hazy", "Cloudy" and the others are repeated multiple times. If you make a typo in one of them, the compiler can't help you spot the problem. You should at least convert these to static String constants, or possibly even an enum (if you don't need to add new types dynamically).

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.