Reputation: 11
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
Reputation:
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
andswitch
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.
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();
}
}
}
}
}
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 ...
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
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
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
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