Calum Mullen
Calum Mullen

Reputation: 11

Condensing down multiple IF statements

I'm fairly new to Java and I'm trying to make a program that allows me to enter a month, then enter a day. I want it to end if the day within the month is valid (I.e. 28 in February is valid, 29 is invalid). I've made it work, but I've used 12 IF statements for each month, and it looks too messy and like too much code. Could anyone suggests a better method of condensing this down?

import java.util.Scanner;

public class Calender {

public static void main(String[] args) {

int month=0;
int day=0;

Scanner in = new Scanner (System.in);

do {        
System.out.print("Enter a month [1..12]: ");
month=in.nextInt();
//continue;
} while (month > 12);
    //System.out.println ("This is not a valid month.");

do {
System.out.print("Enter a day [1..31]: ");
day=in.nextInt();
} while (day > 31);


if (day > 31 && month == 1) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 31 && month == 3) {
    System.out.print("This is not a valid day of the month");
}
if (day > 31 && month == 5) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 31 && month == 7) {
    System.out.print("This is not a valid day of the month");
}
if (day > 31 && month == 8) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 31 && month == 10) {
    System.out.print("This is not a valid day of the month");
}
if (day > 31 && month == 12) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 30 && month == 4) {
    System.out.print("This is not a valid day of the month");
}
if (day > 30 && month == 6) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 30 && month == 9) {
    System.out.print("This is not a valid day of the month");
}
if (day > 30 && month == 11) {
    System.out.print("This is not a valid day of the month");
} 
if (day > 28 && month == 2) {
    System.out.print("This is not a valid day of the month");
}
}
}

Upvotes: 1

Views: 409

Answers (4)

user177800
user177800

Reputation:

Idiomatic Object Oriented Approach:

Ignoring that this is a naive strawman problem domain already handled by classes in the JDK as well as JodaTime and other existing solutions, this is intended to be an example of how to eliminate dense and complex if/else if/else and switch conditions in general.

This implements a variation of the Strategy Pattern which is specifically designed for replacing if/elseif/else statements that have a highcyclomatic complexity. It works just as well for narrow domains like this.

This solution leverages the ability for Enums to implement Interfaces and provide the Predicate logic right inside the actual Month enum instance. Using the Predicate interface instead of a custom isValidDay() method also allows for composition or chaining of the Predicate with others.

This eliminates all the noisy duplicated if/else if/else statements as well allowing you to adhere to the DRY Principle as well.

Q34909522.java

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.common.base.Predicate;

public class Q34909522
{
    private static enum Month implements Predicate<Integer>
    {
        JANUARY(31),
        FEBRUARY(28),
        MARCH(31),
        APRIL(28),
        MAY(31),
        JUNE(30),
        JULY(31),
        AUGUST(31),
        SEPTEMBER(30),
        OCTOBER(31),
        NOVEMBER(30),
        DECEMBER(31);

        public final int daysInMonth;

        private Month(final int daysInMonth)
        {
            this.daysInMonth = daysInMonth;
        }

        @Override
        public boolean apply(@Nullable final Integer input)
        {
            return input != null && input >= 1 && input <= this.daysInMonth;
        }
    }

    public static void main(@Nonnull final String[] args)
    {
        /** Scanner excluded for brevity */
        for (final Month m : Month.values())
        {
            for (int i = 0; i <= 33; i++)
            {
                if (!m.apply(i))
                {
                    System.out.format("%d is not a valid day of the month for %s (%d)", i, m, m.daysInMonth );
                    System.out.println();
                }
            }
        }
    }
}

Outputs:

0 is not a valid day of the month for JANUARY (31)
32 is not a valid day of the month for JANUARY (31)
33 is not a valid day of the month for JANUARY (31)
0 is not a valid day of the month for FEBRUARY (28)
29 is not a valid day of the month for FEBRUARY (28)
30 is not a valid day of the month for FEBRUARY (28)
31 is not a valid day of the month for FEBRUARY (28)
32 is not a valid day of the month for FEBRUARY (28)
33 is not a valid day of the month for FEBRUARY (28)
truncated for brevity ...

Leap Year:

Enhancing this to determine if February is a leap year is an exercise for the reader since this is a trivial well documented problem and outside the scope of the requirements as stated in the question, that specifically instructs that 28 in February is valid, 29 is invalid.

Upvotes: 4

Jim W
Jim W

Reputation: 512

The Java core libraries already do date parsing.

Feed the text input directly through a SimpleDateFormat with lenient set to false. It will throw an exception for all invalid values.

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy MM dd");
sdf.setLenient(false);
try
{
    sdf.parse("2016 " + args[0] + " " + args[1]);
}
....

In addition to not reinventing the wheel, this also forces you to explicitly choose the year, which has a major impact on the legality of leap year dates.

Upvotes: 0

John Slegers
John Slegers

Reputation: 47081

First, create an array with the number of days for every month.

int[ ] daysInMonth = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

Then, compare the value of day with the corresponding position in the array :

if (day > daysInMonth[month]) {
    System.out.print("This is not a valid day of the month");
}

The only extra check left, is to check whether February is a leap year :

boolean isLeapYear = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))

Upvotes: 0

tddmonkey
tddmonkey

Reputation: 21184

There's quite a few things you could do here, but off the top of my head here are a couple...

1) You only have 3 different cases- 28, 30 and 31 days so you could collapse these down into 3 if statements like so:

if (day > 30 && (month == 4 || month == 6 || month == 9 || month == 11) {
    ...
} 

2) Store the values in an array and do a lookup:

int[] daysInMonth = new int[] { 31, 28, 31, etc... };
if (day > daysInMonth[month]) {
    ....
}

3) Use the built in time/date functions to handle this for you.

Upvotes: 0

Related Questions