Simple factory - checking data correctness - java

I have Month class which contains some data eg. number, number of days etc. I would like to create this class instance simply. I decided to create simple factory which contains months data and returns ready object. My question is where to check data correctness eg. January has 31 days and a couple others... Should Month class checks that data is correct or Factory should be responsible for it? (in this case we can create Month object with invalid data)...

Please don't reinvent calendar classes! Someone has already done it for you. Take a look at Joda-Time or the Java 8 package java.time. This are good starting points to learn how classes like Month should be designed.
The concepts of month and day of month shouldn't be mixed within a single class. Because how many days a month has depends on the year and the calendar system. The validation for invalid number of days should be done when you create a date from day of month, month and year. So it is part of the construction process of a date object.
This looks strange:
Month january = Month.newMonth(31);
The factory method which creates a Month object from an int should check if the parameter is in the range from 1 (January) to 12 (December) and return an enum representing the concrete month.
This is what I would expect:
Month january = Month.newMonth(1);

Your Month class should check this itself. It's always best practice to have the logic that is dependent only on the class in the class itself. Otherwise if someone else uses your application and instantiates a Month object without using the factory it will work, but it won't be what you as the designer intended.
You can handle an error however you like it, but I would do it in the constructor and then throw an error if someone tries instantiating it with invalid parameters.

You should validate the parameters before passing them in a constructor.
However a static factory method might not be the best place to validate input, if the input is dynamic and NOT done by you.
If you just want to make sure the compiler warns you if you use invalid parameters, you could throw a checked exception like this:
public static final Month newMonth(int numberOfDays) {
if(numberOfDays > 31 || numberOfDays < 28) {
throw new IllegalArgumentException("invalid numberOfDays");
}
return new Month(numberOfDays);
}
Either way, in my opinion, you should not go the try...catch way or make the constructor validate anything as it makes your code a lot less readable and also could break a lot of other code if you change your validation method.

Related

LocalDateTime class in Java and Period

Have such a simple Java class:
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2017, 11, 26, 15, 38);
Period period = Period.ofYears(1).ofMonths(2).ofDays(3);
dateTime = dateTime.minus(period);
System.out.println(dateTime);
}
}
Which results in 2017-11-23T15:38
Can someone explain me, why a date is subtracted while year and months - doesn't?
Because each three method
(ofYears(1) & ofMonths(2) & ofDays(3))
returns an instance of Period class
Each time you call another method value in Period period gets overriden.
If say ofYears(1) return instance of Period class named a then ofMonths(2) also return a new instance and override he initial one. So, the last one remains and stored to the period variable.
That's why only the ofDays(3) is showing effect.
If you want to do the same you should try calling below method
`public static Period of(int years,
int months,
int days)
It is simple.
Each of the methods, ofYears, ofMonths and ofDays return a new instance of period.
So effectively your period is equal to Period.ofDays(3)
This is not a builder pattern, where you keep modifying the same instance.
In order to achieve what you need, this code will do the work:
LocalDateTime dateTime = LocalDateTime.of(2017, 11, 26, 15, 38);
Period period = Period.of(1, 2, 3);
dateTime = dateTime.minus(period);
System.out.println(dateTime);
prints out
2016-09-23T15:38
Because it works like Period.ofDays(3) overridden every time since they are static method.
Use Period of(int years,int months,int days)
dateTime = dateTime.minus(Period.of(1, 2, 3));
Or you can use withDays, withMonths, withYears like for chaining
Give a Man a Fish, and You Feed Him for a Day. Teach a Man To Fish,
and You Feed Him for a Lifetime.
(It certainly goes for a woman too.)
I am trying to teach you to fish. I am explaining how to catch such an error without having to ask on Stack Overflow first.
Set up your IDE to warn you when you use an object (an instance) for calling a static method. That is whenever you do something like
yourObject.someStaticMethod();
In my Eclipse your code gives this warning:
The static method ofDays(int) from the type Period should be accessed
in a static way
This tells us that ofDays() is a static method, and therefore the result of the call is the same as calling Period.ofDays(3). Eclipse even offers to fix the problem for me:
After I click “Change access to static using Period (declaring type)”, that code line becomes:
Period.ofYears(1).ofMonths(2);
Period period = Period.ofDays(3);
Now I think you can see why you got the result you got.
So:
Make sure your IDE is configured to issue such warnings. It doesn’t have to be Eclipse, other IDEs can do the same.
Read those warnings when you get them and make sure you understand them. If you've skipped a warning once, if you get an unexpected result, go back and read the warning again. If you still need to ask on Stack Overflow, you're welcome of course, and consider including the text of that warning you didn't understand.
Others have nicely explained how to fix your code to give you your desired result. If Period.of(1, 2, 3) is a bit confusing because you can't tell what's years, months weeks and days, use Period.ofYears(1).plusMonths(2).plusDays(3), and everything is clear. It also resembles what you tried in the question.
Link: How to debug small programs with many tips similar to the one I am giving here (and not because yours was a poor question, it’s a good one, I upvoted it).
Remember that all classes in the java.time package are immutable (see Java Docs). Instances of Period cannot be changed after creation, you will have to create a new instance and reassign it.
This is the reason why only the last Period.ofDays(3) has an effect on the result (as previous answers suggest). And it is also the reason why withDays returns a copy of the respective Period.

Java MOOC 92.3 ( I want to understand how to solve this question, not just be provided an answer)

This exercise is from Java MOOC 92.3
Simply, I have two dates.
And a method which gives me the difference of the two.
(for instance: 3/10/2011 and 3/9/2012)
My method states that as long as the month and the day are greater than the later, then simply subtract the one for which the method is called.
Here's the problem:
If a date given as a parameter has a greater year, then when I subtract the two I come out with a negative number (ex: 2011 - 2012).
public int differenceInYears(MyDate comparedDate){
int result = 0;
if(this.month >= comparedDate.month && this.day >= comparedDate.day){
result = this.year - comparedDate.year;
}else{
result = this.year - comparedDate.year;
result--;
}
return result;
The code beyond the else is for the case in which the year is not what it may look to be (Example: If I was born in May 2000 and I technically should be 20 since it is 2020, but since it is not May yet, I am therefore 19).
My conditions are still met within the first if statement, even though the result is not correct. I have tried finding a case where I can appease both cases where it does not matter what date goes first, but I am struggling with the logic. Would appreciate input.
First, if I understand correctly that this is an exercise, it’s a fine exercise. For production work one would never invent their own date class but would and should use LocalDate from the standard library for a date. And ChronoUnit.YEARS.between() for finding your age.
If you have not yet had your birthday this year, you want to subtract one year from the result. This is correct. So how do we determine whether this year’s birthday is in the future? Take a look at the following possibilities. I have not run your code, so have filled out the last column from what I think will happen from reading the code.
Today Birthday Subtract 1? Does your code subtract 1?
--------------------------------------------------------
Apr 3 Feb 1 No No
Apr 3 Feb 6 No Yes
Apr 3 Apr 1 No No
Apr 3 Apr 6 Yes Yes
Apr 3 May 1 Yes Yes
Apr 3 May 6 Yes Yes
Another way to put the question: if the months are different, do we need to compare the day of month too?
I like your attitude. In accordance with your title I am not giving away the correctly working code. I too am convinced that you will not only learn more from writing it yourself, it will also give you greater pleasure. If you’re still stuck, please leave a comment and I’ll take one more look.
EDIT: For the problem of getting a negative result if the dates are in the opposite order: The simplest solution is to check the result after you’ve calculated. If it is negative, redo the entire calculation with the dates reversed. That is, with this. and comparedDate. in each other’s places. One elegent option is that in this case you tell the other MyDate object to do the calculation instead. Call the differenceInYears method of the other object passing this as argument.

Check date range in java without time AND using jodatime

I have a date supplied by the user and of course today's date.
I'm attempting to verify that the difference between the 2 days is at least 2 weeks. I've done this using standard libraries - but I'm attempting to do this using jodaTime and I'm having some difficulty.
// BAD CODE - doesn't work
// NOTE: getUserSuppliedDate() returns an instance of java.sql.Date
// Also assume that validation prior to this call has been run that
// validates that the userSuppliedDate comes AFTER today's date - not sure if
// that is relevant in the context I'm attempting to use these particular jodaTime APIs.
DateTime jodaStartDate = new DateTime(getUserSuppliedDate());
if (Days.daysBetween(jodaStartDate, DateTime.now()).isLessThan(Days.days(14))) {
System.out.println("Bad user. You have chosen...poorly.");
}
else {
System.out.println("Well done user. You have supplied wisely.");
}
// GOOD CODE ---- ? Help =)
Your code gives you the wrong result because the dates supplied to Days.daysBetween() are in the wrong order. Since you specified that the user supplied date comes after the current date, your approach will result in a negative number of days.
It will work correctly if you switch the order, putting the earliest date first.
Compare the following two:
DateTime jodaStartDate = new DateTime().withYear(2018)
.withMonthOfYear(7)
.withDayOfMonth(5); // 15 days from now
System.out.println(Days.daysBetween(jodaStartDate, DateTime.now())); // "P-15D"
System.out.println(Days.daysBetween(DateTime.now(), jodaStartDate)); // "P15D"
In the first case, -15 days will evaluate to less than 14 days.
Using weeks instead of days, you'd run into the same problem:
System.out.println(Weeks.weeksBetween(jodaStartDate, DateTime.now())); // "P-2W"
System.out.println(Weeks.weeksBetween(DateTime.now(), jodaStartDate)); // "P2W"

Java: addToDay function

I'm trying to setup my addToDay function. I'm currently stuck on how to proceed with this or even write it correctly. The function itself will take a variable that ranges from -100 to 100. So you would basically add that variable to the current and if it was below the 0 then subtract a month or if it was above the months max day then add a month. Which i have that function setup so all i would have to do is call addToMonth with the correct amount. My problem lies within the amount of days each month has. For example, October has 31 days while November has 30. I have a function that will return the number of days in the current set month so i can call that to get how many max days should be in the current month. I'm thinking maybe a while loop would work but i just wanted to get anyone's thoughts on the best way to set it up.
I have 3 private ints: month, day, year. These are what need to be changed. I have both addTo functions for month and year setup already.
Here are some other functions i have created that can be used in this:
1. addToMonth(int delta) - changes the current month depending on the given parameter
2. getDaysInMonth() - will return the days in a month depending on the month itself
3. validateDay() - Will return true or false if the days fall outside the wanted requirements.
I don't want to use the calendar utility
I also don't want to use any other utilities. Just the base code with Junit for testing
Joda's plusDays() function and Java 8 LocalDate already has the logic that you are trying to achieve
Alright so i ended up just copying my original addToMonth function and modifying it abit to fit with days. So far it works but i do think it'll fail in the cases of different amounth of days not lining up.

another strange behaviour with GregorianCalendar

Take a look at the piece of code bellow:
Calendar today1 = Calendar.getInstance();
today1.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today1.getTime());
Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());
I'm quite confused... Assuming I am running it today as July 14th, 2010, the output is:
Fri Jul 16 14:23:23 PDT 2010
Wed Jul 14 00:00:00 PDT 2010
The most annoying thing is that if I add today2.getTimeInMillis() (or any other get() method) it will produce consistent result. For the code bellow:
Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.getTimeInMillis();
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());
The result is:
Fri Jul 16 00:00:00 PDT 2010
The answer is actually documented in the JavaDoc for java.util.Calendar
Quoted here:
set(f, value) changes calendar field f to value. In addition, it sets an internal member variable to indicate that calendar field f has been changed. Although field f is changed
immediately, the calendar's
milliseconds is not recomputed until
the next call to get(), getTime(), or
getTimeInMillis() is made.
So that explains the behavior you are seeing, but I concur with another responder to your question that you should consider JodaTime if you're going to do a lot of Date coding.
You should in fact be using Calendar#getInstance() to get an instance and not new GregorianCalendar(). Replace that line by
Calendar today2 = Calendar.getInstance();
today2.set(2010, Calendar.JULY, 14);
and it will go well.
Sorry, no detailed explanation for the behaviour, expect that Calendar along with java.util.Date are one of the major epic failures in the current Java SE API. If you're doing intensive date/time operations, then I'd recommend to have a look at JodaTime. The upcoming new Java 7 will ship with an improved date/time API based on JodaTime (JSR-310).
(Sorry for the edit, I wanted this to be a little more readable, but couldn't get it right when I originally wrote the answer...now it's essay length, but there you go...)
Just to add to what's already been said, the issue arises from the returned Calendar instances being prepared differently. I personally feel like this is a design flaw, but there may be good reason for it.
When you call Calendar.getInstance(), it creates a new GregorianCalendar using the default constructor. This constructor calls setCurrentTimeMillis(time) with the current system time, and then calls the protected method complete().
However, when you create a new GregorianCalendar using the constructor that you did, complete() is never called; instead, among other things, only set(field, value) is called for the various bits of information that is provided. This is all well and good, but it has some confusing consequences.
When complete() is called in the first case, the member variables dustmachine alluded to are checked to determine what information should be recalculated. This results in a branch that forces calculation all of the fields (DAY, WEEK_OF_MONTH, etc.). Note that Calendar is indeed lazy; it just happens that using this method of instantiation forces an explicit recalculation (or in this case initial calculation) on the spot.
So, what impact does this have? Given that no upfront field computation was performed in the case of the second object creation, the two objects have vastly different states. The first has all of its field information populated, while the second only has the information which you provided. When you call the various get*() methods, it shouldn't matter, because any changes should provoke the lazy recalculation step when you retrieve the information. However, the order in which this recalculation occurs exposes the differences between the two varying initial states.
In your particular case, this is due to the following relevant code in computeTime(), which is necessarily invoked to compute the correct time when you request it with getTime():
boolean weekMonthSet = isSet[WEEK_OF_MONTH] || isSet[DAY_OF_WEEK_IN_MONTH];
...
boolean useDate = isSet[DATE];
if (useDate && (lastDateFieldSet == DAY_OF_WEEK
|| lastDateFieldSet == WEEK_OF_MONTH
|| lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
}
In the first case, all fields are set due to that initial calculation. This allows weekMonthSet to be true, which, along with the DAY_OF_WEEK that you provided in your call to set(field, value) being set, causes useDate to be false.
However, in the second case, as no fields have been calculated, the only fields set are the ones you provided in the constructor and in the subsequent set(field, value) call. Thus, useDate will remain true, because isSet[DATE] is true per your constructor, but weekMonthSet is false as the other fields in the object have not been computed anywhere, nor set by you.
When useDate is true, as implied, it uses your date information to generate the value for the time. When useDate is false, it's able to use your DAY_OF_WEEK information to compute the time you expect, resulting in the difference you see.
Finally, this raises the question of why calling getTimeInMillis() before calling getTime() will fix the unexpected behaviour. As it turns out, the fields will be recalculated as a result of your set(field, value) call in both objects. This just happens to occur after the time is calculated, for whatever (probably genuine) reason. Therefore, forcing the time to be calculated once on the second Calendar will essentially align the states of the two objects. After that, I believe the calls to get*() should all work consistently for both objects.
Ideally, the constructor you used in the second case should perform this initial calculation step in the name of consistency (although maybe for reasons of performance this wouldn't be preferred), but it doesn't, and this is what you get.
So, in short, as the others mentioned, JodaTime is your friend, and clearly these classes are less so. :)

Categories

Resources