patternjavaMinor
Algorithm that tells the user what current "status" they fall under based on their current time in comparison to an array of meeting data times
Viewed 0 times
arrayuseralgorithmundermeetingtheytimetellsthatcurrent
Problem
This is an algorithm that takes in an
The case scenarios are:
After the algorithm determines which case scenario the user is in. It then assigns another object's "Availibility" times.
For instance, if a user's current time is found to be during an already existent meeting for that particular room. Then the "next available start time" for that room becomes the meeting's end time. And the 'next available end time" (i.e. when that room becomes busy again) is assigned to the beginning next meeting after the meeting that the current user's time is in the middle of. Handling of course edge cases like if two meetings immediately follow one another etc etc.
Here is the relevant code:
```
/**
* Method used by the MeetingDataTest test suite to check different time scenarios.
*/
public void testSetDefaultNextAvailable(ArrayList testMeetingData, DateTime caseToTest) {
this.meetingData = testMeetingData;
availableNow = false;
DateTime now = caseToTest;
//First check to see if there even are any meetings.
if (meetingData.size()!=0) {
//Check to see if there are meetings, if the user's current time/simulated time is before all meetings.
if (checkIfMorning(now)) {
System.out.println(nextAvailableStart.toString("H:mm a"));
if (nextAvailableEnd != null) {
System.out.println(nextAvailableEnd.toString("H:mm a"));
}
}
//If the user's current time is not found to
ArrayList of objects that store information on meeting/event schedules. This algorithm's purpose is to check and see what the user's status is based on their current time.The case scenarios are:
- The user's current time is before all meetings
- The user's current time is during a meeting (also handles meetings that start right when another meeting ends, also if the user is during the final meeting edge case).
- The user's current time is between two meetings
- The user's current time is after all meetings
- There is no meeting data.
After the algorithm determines which case scenario the user is in. It then assigns another object's "Availibility" times.
For instance, if a user's current time is found to be during an already existent meeting for that particular room. Then the "next available start time" for that room becomes the meeting's end time. And the 'next available end time" (i.e. when that room becomes busy again) is assigned to the beginning next meeting after the meeting that the current user's time is in the middle of. Handling of course edge cases like if two meetings immediately follow one another etc etc.
Here is the relevant code:
```
/**
* Method used by the MeetingDataTest test suite to check different time scenarios.
*/
public void testSetDefaultNextAvailable(ArrayList testMeetingData, DateTime caseToTest) {
this.meetingData = testMeetingData;
availableNow = false;
DateTime now = caseToTest;
//First check to see if there even are any meetings.
if (meetingData.size()!=0) {
//Check to see if there are meetings, if the user's current time/simulated time is before all meetings.
if (checkIfMorning(now)) {
System.out.println(nextAvailableStart.toString("H:mm a"));
if (nextAvailableEnd != null) {
System.out.println(nextAvailableEnd.toString("H:mm a"));
}
}
//If the user's current time is not found to
Solution
What you have done right:
What can be improved in
What can be improved in
What can be improved in
What can be improved in
What can be improved in
Conclusion:
I think rewriting this as a method that returns a value (the current state of the calculation class), and using a
As an added bonus the calculation will be stateless and automatically thread safe (provided you fix the meeting data encapsulation).
Edit in response to your questions:
I've also taken the time to code up how using guava's
This doesn't cap to 22 pm yet, but I'm just trying to show how using a good
- Seperate methods to check each case. Makes the main method easier to read.
- Make the
dateTimeto test against a parameter of the method. (easier to test)
What can be improved in
testSetDefaultNextAvailable:- This method, instead of returning a result, stores it internally in a field, that then needs to be queried. So, each calculation needs a new instance, or be sure that the same instance ins't reused on other threads.
- The
nowlocal variable is superfluous.
- Deeply nested if structure. And strangely the
if elsechains' bodies are all the same...
- Your code would really benefit from using a
Rangeclass of some sort. (I would recommend guava's range class).
- \$00\$ is \$0\$ but octal.
- Use of
System.out.println(): return a result and let the client print it if needed. A calculation should just calculate.
- The boolean methods have side effects. Method names do not suggest it.
- 22 pm is hard coded as end of working day? Make this an input, or configuration.
- Hard coded time zone to be the default time zone. Make this an input, or configuration.
meetingDataArrayListis used as internal state. Yet client code can still mess with the contents of this list, while the calculation is running.
- Method name can be improved.
What can be improved in
checkIfAfterAllMeetings:- This method assumes
meetingDatais properly sorted. Yet, this is not checked, nor is the list sorted by this class.
- What if
nowis 23 pm?nextAvailableEndwill be beforenextAvailableStart.
- Method should be private (this goes for all but the main method)
What can be improved in
checkIfMorning:- A loop with a break to a label: not done. (occurs in other methods too)
- Logic is faulty, checks if there is at least one meeting after current time, not that all meetings are.
What can be improved in
checkIfDuringMeeting:- Enforce sorting of meeting data, this will make looking up 'the next meeting' simpler
What can be improved in
checkIfBetweenMeetings:- Imagine a
Rangeabstraction that allowsrange.contains(dateTime).
Conclusion:
I think rewriting this as a method that returns a value (the current state of the calculation class), and using a
Range abstraction (again referencing guava's Range and RangeSet) will make the logic a lot simpler. Which, in turn will make the naming easier.As an added bonus the calculation will be stateless and automatically thread safe (provided you fix the meeting data encapsulation).
Edit in response to your questions:
- It doesn't really matter that this class is never touched by the user. By client I mean any code that calls the method. If that code is a test, then that test is the client.
- If the hard coded 22 pm is part of the test client's scenario, then this class doesn't need to define that again. I would still recommend this value to be an input or configuration.
- \$00\$ vs. \$0\$ is a very fine hair to split. It both amounts to an int with value \$0\$. Yet the \$00\$ literal is octal. (try \$09\$, it won't compile!) You were obviously trying to mimic the formatted time, and not aware of the difference. I remark it since you were likely not aware of this.
ArrayListencapsulation: your method does this;this.meetingData = testMeetingData;i.e. theArrayListis the same instance as passed to the method. If the client code modifies theArrayListafter this call (possibly even during the calculation) then the state of this class also changes, and post-conditions of the calculations may be corrupted.
I've also taken the time to code up how using guava's
Range can simplify the logic a lot:Range firstAvailableRange(List testMeetingData, DateTime now) {
RangeSet freeTimes = toMeetingTimes(testMeetingData).complement();
freeTimes.remove(Range.atMost(now));
Iterator> iterator = freeTimes.asRanges().iterator();
if (!iterator.hasNext()) {
return null;
}
return iterator.next();
}
private TreeRangeSet toMeetingTimes(List testMeetingData) {
TreeRangeSet meetingTimes = TreeRangeSet.create();
for (MeetingData data : testMeetingData) {
meetingTimes.add(data.getTimeRange());
}
return meetingTimes;
}This doesn't cap to 22 pm yet, but I'm just trying to show how using a good
Range abstraction can do most of the heavy lifting for you.Code Snippets
Range<DateTime> firstAvailableRange(List<MeetingData> testMeetingData, DateTime now) {
RangeSet<DateTime> freeTimes = toMeetingTimes(testMeetingData).complement();
freeTimes.remove(Range.atMost(now));
Iterator<Range<DateTime>> iterator = freeTimes.asRanges().iterator();
if (!iterator.hasNext()) {
return null;
}
return iterator.next();
}
private TreeRangeSet<DateTime> toMeetingTimes(List<MeetingData> testMeetingData) {
TreeRangeSet<DateTime> meetingTimes = TreeRangeSet.create();
for (MeetingData data : testMeetingData) {
meetingTimes.add(data.getTimeRange());
}
return meetingTimes;
}Context
StackExchange Code Review Q#99053, answer score: 6
Revisions (0)
No revisions yet.