Almost there. Finished a lot of the Challenge, plus the extras from the new Challenge.
- Author
- Maarten 'Vngngdn' Vangeneugden
- Date
- Dec. 16, 2016, 12:47 p.m.
- Hash
- 5c90f8e84f1a4f2ae7feada51ba2e9104ea76a42
- Parent
- 6be1f9e57b06e831fecffc73592e96a65c728220
- Modified files
- Challenge 6/Main.java
- Challenge 6/Reservation.java
- Challenge 6/ReservationController.java
- Challenge 6/ReservationView.java
- Challenge 6/SearchView.java
- Challenge 6/ontwerpkeuzes2.md
Challenge 6/Main.java ¶
7 additions and 2 deletions.
View changes Hide changes
1 |
1 |
import java.util.HashSet; |
2 |
2 |
import java.util.Random; |
3 |
3 |
|
4 |
4 |
/** |
5 |
5 |
* The program starts here. |
6 |
6 |
* @author Maarten Vangeneugden - 1438256 |
7 |
7 |
*/ |
8 |
8 |
public class Main { |
9 |
9 |
public static void main(String[] args) { |
10 |
10 |
// Starting controllers and linking them together |
11 |
11 |
ReservationController rc = new ReservationController(); |
12 |
- | RoomController roc = new RoomController(); |
13 |
12 |
rc.setRoomController(roc); |
14 |
- | addTestRooms(roc); |
+ |
13 |
WalkController wc = new WalkController(rc); |
+ |
14 |
|
+ |
15 |
addTestRooms(roc); |
15 |
16 |
MainWindow mainWindow = new MainWindow(rc, roc); |
+ |
17 |
MainWindow mainWindow = new MainWindow(rc, roc); |
16 |
18 |
} |
17 |
19 |
|
18 |
20 |
/** |
19 |
21 |
* Generates a series of Rooms to test the program. |
20 |
22 |
* This method is mostly for debugging purposes. |
21 |
23 |
* It features a set of constants in the front of the method, which you can |
22 |
24 |
* edit to your heart's content. =) |
23 |
25 |
* @param roc The Room Controller to which the Rooms will be added. |
24 |
26 |
* @pre roc mustn't be null. |
25 |
27 |
* @throws NullPointerException if roc is a null pointer. |
26 |
28 |
*/ |
27 |
29 |
public static void addTestRooms(RoomController roc) { |
28 |
30 |
// Constants; edit to suit your needs |
29 |
31 |
final int ROOM_COUNT = 10; // Amount of Rooms that will be generated |
30 |
32 |
final int MAX_BEDS = 20; // The maximum amount of Beds in 1 Room |
31 |
33 |
final String[] POSSIBLE_FACILITIES = {"Shower", "Curtains", "Airco", "Bath", "Minibar"}; |
32 |
34 |
final String[] POSSIBLE_TYPES = {"Male", "Female", "Mixed"}; |
33 |
35 |
|
34 |
36 |
Set<Room> testRooms = new HashSet<>(); |
35 |
37 |
for(int i=0; i<ROOM_COUNT; i++) { |
36 |
38 |
Random random = new Random(); |
37 |
39 |
int beds = random.nextInt(MAX_BEDS) + 1; // +1, because it's in range [0, MAX_BEDS[ |
38 |
40 |
String type = POSSIBLE_TYPES[random.nextInt(POSSIBLE_TYPES.length)]; |
39 |
41 |
Set<String> facilities = new HashSet<>(); |
40 |
42 |
for(String possibleFacility: POSSIBLE_FACILITIES) { |
41 |
43 |
if(random.nextBoolean()) { |
42 |
44 |
facilities.add(possibleFacility); |
43 |
45 |
} |
44 |
46 |
} |
45 |
47 |
testRooms.add(new Room(beds, type, facilities)); |
46 |
48 |
// For debugging purposes, a human readable layout of the Rooms is |
47 |
49 |
// printed: |
48 |
50 |
System.out.println("ROOM"); |
49 |
51 |
System.out.println("Beds: "+ beds); |
50 |
52 |
System.out.println("Type: "+ type); |
51 |
53 |
System.out.print("Facilities: "); |
52 |
54 |
for(String facility : facilities) { |
53 |
55 |
System.out.print(facility +", "); |
54 |
56 |
} |
55 |
57 |
System.out.println(); |
56 |
58 |
System.out.println("-------------------------------"); |
57 |
59 |
} |
58 |
60 |
roc.setRooms(testRooms); |
59 |
61 |
} |
60 |
62 |
} |
+ |
63 |
public static void addTestWalks(WalkController wc) { |
+ |
64 |
} |
+ |
65 |
} |
61 |
66 |
Challenge 6/Reservation.java ¶
4 additions and 8 deletions.
View changes Hide changes
1 |
1 |
import java.util.HashSet; |
2 |
2 |
import java.util.Date; |
3 |
3 |
|
4 |
4 |
/** |
5 |
5 |
* Represents a reservation in a hostel. |
6 |
6 |
* Reservation is a simple class that allows one to store reservation |
7 |
7 |
* information, request it when necessary, and so on. |
8 |
8 |
* Certain methods are provided for interaction, which use contracts to assert |
9 |
9 |
* proper functioning. |
10 |
10 |
* |
11 |
11 |
* Note: There is a form of redundancy in this class. |
12 |
12 |
* Reservation holds a Set of Beds, and an integer, indicating the amount of |
13 |
13 |
* people that are in this Reservation. |
14 |
14 |
* Normally, the amount of people in the Reservation is determined by the amount |
15 |
15 |
* of reserved Beds, because the amount of Beds implied the amount of people to |
16 |
16 |
* facilitate. |
17 |
17 |
* Because of the design of this program, I've weighed the advantages and |
18 |
18 |
* disadvantages of holding this implication, of using a seperate member |
19 |
19 |
* explicitely indicating the amount of people. |
20 |
20 |
* |
21 |
21 |
* I've chosen the latter option. It scales better in terms of weird |
22 |
22 |
* afterthoughts ("I also want to make people sleep on the floor"), but being |
23 |
23 |
* explicit is also a tad easier to manage. It also allows the other classes to |
24 |
24 |
* be far more decoupled from Reservation (otherwise, they'd have to check for |
25 |
25 |
* errors every time themselves, hindering cohesiveness). |
26 |
26 |
* |
27 |
27 |
* To overcome the possible difference, I've made it so that, every time, the |
28 |
28 |
* relation between the values is changed (the people in the Reservation |
29 |
29 |
* changes, for example), the class automatically checks if the values are |
30 |
30 |
* corresponding. If they're not, a warning is printed to system.out.err, |
31 |
31 |
* informing about the fact that there's a discrepancy. |
32 |
32 |
* |
33 |
33 |
* I think that's the best way to overcome this problem. |
34 |
34 |
* @author Maarten Vangeneugden - 1438256 |
35 |
35 |
*/ |
36 |
36 |
public class Reservation { |
37 |
37 |
|
38 |
38 |
// Members |
39 |
39 |
private String groupName; |
40 |
40 |
private Date beginDate; |
41 |
41 |
private Date endDate; |
42 |
42 |
private Set<Bed> reservedBeds; |
43 |
43 |
private int people; // The amount of people in this Reservation |
44 |
44 |
private int reservationID; |
45 |
45 |
private String roomType; // Room type requested by group |
46 |
46 |
private Set<String> roomFacilities; // Requested room facilities |
47 |
47 |
private Set<Date> breakfastDays; // Set of days numbers |
48 |
48 |
// Controllers |
49 |
49 |
private RoomController roomController; |
50 |
50 |
private ReservationController reservationController; |
51 |
51 |
|
52 |
52 |
/** |
53 |
53 |
* Create a new Reservation. |
54 |
54 |
* Be aware about the limitations of a Reservation: it's not the |
55 |
55 |
* Reservation's duty to check whether the provided Beds are properly |
56 |
56 |
* reserved; take care of this accordingly. |
57 |
57 |
* |
58 |
58 |
* Some information is implicit. For example: The size of the set of |
59 |
59 |
* reserved Beds implies the amount of people in the group; no breakfast |
60 |
60 |
* days implies no breakfast, ... |
61 |
61 |
* @param groupName The name of the group. |
62 |
62 |
* @param people The amount of people that are reserving. |
63 |
63 |
* @param beginDate The date that the Reservation begins. |
64 |
64 |
* @param endDate The date that the Reservation ends. |
65 |
65 |
* @param reservedBeds The set of Beds reserved and assigned to this |
66 |
66 |
* Reservation. |
67 |
67 |
* @param roomType The requested room type. |
68 |
68 |
* @param roomFacilities The set of all requested room facilities. |
69 |
69 |
* @param breakfastDays A set of all days that the group wants breakfast. |
70 |
70 |
* @param reservationID An ID for this reservation, to differentiate from |
71 |
71 |
* other Reservations. |
72 |
72 |
* @pre No parameter must be a null pointer. |
73 |
73 |
* @pre people must be a natural number, different from 0. |
74 |
74 |
* @pre beginDate must come before endDate. |
75 |
75 |
* @pre All dates in breakfastDays must fall between beginDate and endDate. |
76 |
76 |
* @pre No string parameter may be empty. |
77 |
77 |
* @post The amount of people in the Reservation is determined by the amount |
78 |
78 |
* of reserved Beds. |
79 |
79 |
* @throws NullPointerException if any parameter is a null pointer. |
80 |
80 |
* @throws IllegalArgumentException if any of the other preconditions is not |
81 |
81 |
* met. |
82 |
82 |
*/ |
83 |
83 |
public Reservation(String groupName, int people, Date beginDate, Date endDate, Set<Bed> reservedBeds, String roomType, Set<String> roomFacilities, Set<Date> breakfastDays) { |
84 |
84 |
// Contract validation: |
85 |
85 |
if(people < 1) { |
86 |
86 |
throw new IllegalArgumentException("The amount of people should be at least 1."); |
87 |
87 |
} |
88 |
88 |
if(!beginDate.before(endDate)) { |
89 |
89 |
throw new IllegalArgumentException("The begin date occurs after the end date."); |
90 |
90 |
} |
91 |
91 |
if(groupName.isEmpty() || roomType.isEmpty()) { |
92 |
92 |
throw new IllegalArgumentException("groupName and/or roomType were empty strings."); |
93 |
93 |
} |
94 |
94 |
for(String roomFacility: roomFacilities) { |
95 |
95 |
if(roomFacility.isEmpty()) { |
96 |
96 |
throw new IllegalArgumentException("One of the room facilities was an empty string."); |
97 |
97 |
} |
98 |
98 |
} |
99 |
99 |
for(Date breakfastDay : breakfastDays) { |
100 |
100 |
if(breakfastDay.before(beginDate) || breakfastDay.after(endDate)) { |
101 |
101 |
throw new IllegalArgumentException("One of the breakfast days occurs before/after the reservation period."); |
102 |
102 |
} |
103 |
103 |
} |
104 |
104 |
// Contract validated, execute constructor |
105 |
105 |
this.groupName = groupName; |
106 |
106 |
this.people = people; |
107 |
107 |
this.beginDate = beginDate; |
108 |
108 |
this.endDate = endDate; |
109 |
109 |
this.reservedBeds = reservedBeds; |
110 |
110 |
this.reservationID = reservationID; |
111 |
111 |
this.roomType = roomType; |
112 |
112 |
this.roomFacilities = roomFacilities; |
113 |
113 |
this.breakfastDays = breakfastDays; |
114 |
114 |
} |
115 |
115 |
|
116 |
116 |
/** |
117 |
117 |
* Construct template Reservation. |
118 |
118 |
* Use this constructor if you need to create an empty/new Reservation. |
119 |
119 |
* It omits the standard restrictions (for example, the group name will be |
120 |
120 |
* empty) to accomodate for the expected usage of this Reservation. |
121 |
121 |
*/ |
122 |
122 |
public Reservation() { |
123 |
123 |
this.groupName = "Blank name"; |
124 |
124 |
this.people = 1; |
125 |
125 |
this.beginDate = new Date(); |
126 |
126 |
this.endDate = new Date(); |
127 |
127 |
// Adding 1 day, because the end date must be after the begin date. |
128 |
128 |
this.endDate.setTime(this.beginDate.getTime() + (1000*60*60*24)); |
129 |
129 |
this.reservedBeds = new HashSet<>(); |
130 |
130 |
this.reservationID = 0; |
131 |
131 |
this.roomType = ""; |
132 |
132 |
this.roomFacilities = new HashSet<>(); |
133 |
133 |
this.breakfastDays = new HashSet<>(); |
134 |
134 |
} |
135 |
135 |
|
136 |
136 |
/** |
137 |
137 |
* Checks whether the amount of people corresponds with the reserved Beds. |
138 |
138 |
* Call this method whenever a change in the amount of Beds, or the amount |
139 |
139 |
* of people is made, or, whenever you need to assure consistency. |
140 |
140 |
* |
141 |
141 |
* It also prints a warning to system.out.err to inform about a discrepancy, |
142 |
142 |
* should one be found. |
143 |
143 |
* |
144 |
144 |
* @return True if the counts are consistent, false otherwise. |
145 |
145 |
*/ |
146 |
146 |
private boolean checkPeopleCountConsistency() { |
147 |
147 |
// The getters for people and beds mustn't be used, as this causes an |
148 |
148 |
// infinite recursion. |
149 |
149 |
int people = this.people; |
150 |
150 |
int beds = this.reservedBeds.size(); |
151 |
151 |
if(people != beds) { |
152 |
152 |
System.err.println("There's a discrepancy in the amount of people in Reservation "+ |
153 |
153 |
this.reservationID +":"); |
154 |
154 |
System.err.println("People: "+String.valueOf(people)); |
155 |
155 |
System.err.println("Reserved Beds: "+String.valueOf(beds)); |
156 |
156 |
return false; |
157 |
157 |
} |
158 |
158 |
return true; |
159 |
159 |
} |
160 |
160 |
|
161 |
161 |
/** |
162 |
162 |
* Set the group name for this Reservation. |
163 |
163 |
* @param groupName The new group name. |
164 |
164 |
* @pre groupName mustn't be empty, or a null pointer. |
165 |
165 |
* @post The group name is changed to the given name. |
166 |
166 |
* @throws NullPointerException if groupName is a null pointer. |
167 |
167 |
* @throws IllegalArgumentException if groupName is an empty String. |
168 |
168 |
*/ |
169 |
169 |
public void setGroupName(String groupName) { |
170 |
170 |
if(groupName.isEmpty()) |
171 |
171 |
throw new IllegalArgumentException("groupName is an empty String."); |
172 |
172 |
this.groupName = groupName; |
173 |
173 |
} |
174 |
174 |
|
175 |
175 |
/** |
176 |
176 |
* Retrieve the current group name. |
177 |
177 |
* @return The group name of this Reservation. |
178 |
178 |
*/ |
179 |
179 |
public String getGroupName() { |
180 |
180 |
return this.groupName; |
181 |
181 |
} |
182 |
182 |
|
183 |
183 |
/** |
184 |
184 |
* Get amount of people in this Reservation. |
185 |
185 |
* @post A warning will be printed to system.out.err if the amount of people |
186 |
186 |
* is inconsistent with the amount of reserved Beds. |
187 |
187 |
* @see Reservation.checkPeopleCountConsistency |
188 |
188 |
* @return The amount of people in this Reservation. |
189 |
189 |
*/ |
190 |
190 |
public int getPeople() { |
191 |
191 |
this.checkPeopleCountConsistency(); |
192 |
192 |
return this.people; |
193 |
193 |
} |
194 |
194 |
|
195 |
195 |
/** |
196 |
196 |
* Set the amount of people for this Reservation. |
197 |
197 |
* Note that this method will not notify you if the new amount of people is |
198 |
198 |
* equal to the previous amount. |
199 |
199 |
* |
200 |
200 |
* This method will print |
201 |
201 |
* @param people The new amount of people in this Reservation. |
202 |
202 |
* @pre people must be at least 1. |
203 |
203 |
* @post A warning will be printed to system.out.err if the amount of people |
204 |
204 |
* is inconsistent with the amount of reserved Beds. |
205 |
205 |
* @post The amount of people is changed to the given value. |
206 |
206 |
* @throws IllegalArgumentException if people is less than 1. |
207 |
207 |
*/ |
208 |
208 |
public void setPeople(int people) { |
209 |
209 |
if(people < 1) { |
210 |
210 |
throw new IllegalArgumentException("people must be at least 1."); |
211 |
211 |
} |
212 |
212 |
this.people = people; |
213 |
213 |
this.checkPeopleCountConsistency(); |
214 |
214 |
} |
215 |
215 |
|
216 |
216 |
/** |
217 |
217 |
* Returns a copy of the begin Date of this Reservation. |
218 |
218 |
* @return a copy of the begin Date of this Reservation. |
219 |
219 |
*/ |
220 |
220 |
public Date getBeginDate() { |
221 |
221 |
return (Date)this.beginDate.clone(); |
222 |
222 |
} |
223 |
223 |
|
224 |
224 |
/** |
225 |
225 |
* Set the begin date for this Reservation. |
226 |
226 |
* @param beginDate The new begin date. |
227 |
227 |
* @pre beginDate mustn't be a null pointer. |
228 |
228 |
* @pre beginDate must come strictly before the end date. |
229 |
229 |
* @post The begin date is updated accordingly. |
230 |
230 |
* @throws NullPointerException if beginDate is a null pointer. |
231 |
231 |
* @throws IllegalArgumentException if beginDate comes after the end date. |
232 |
232 |
*/ |
233 |
233 |
public void setBeginDate(Date beginDate) { |
234 |
234 |
if(beginDate.after(this.endDate)) |
235 |
235 |
throw new IllegalArgumentException("beginDate comes after the end date."); |
236 |
236 |
this.beginDate = beginDate; |
237 |
237 |
} |
238 |
238 |
|
239 |
239 |
/** |
240 |
240 |
* Returns a copy of the end Date of this Reservation. |
241 |
241 |
* @return a copy of the end Date of this Reservation. |
242 |
242 |
*/ |
243 |
243 |
public Date getEndDate() { |
244 |
244 |
return (Date)this.endDate.clone(); |
245 |
245 |
} |
246 |
246 |
|
247 |
247 |
/** |
248 |
248 |
* Set the end date for this Reservation. |
249 |
249 |
* @param endDate The new end date. |
250 |
250 |
* @pre endDate mustn't be a null pointer. |
251 |
251 |
* @pre endDate must come strictly after the begin date. |
252 |
252 |
* @post The end date is updated accordingly. |
253 |
253 |
* @throws NullPointerException if endDate is a null pointer. |
254 |
254 |
* @throws IllegalArgumentException if endDate comes after the begin date. |
255 |
255 |
*/ |
256 |
256 |
public void setEndDate(Date endDate) { |
257 |
257 |
if(endDate.before(this.beginDate)) |
258 |
258 |
throw new IllegalArgumentException("endDate comes before the begin date."); |
259 |
259 |
this.endDate = endDate; |
260 |
260 |
} |
261 |
261 |
|
262 |
262 |
public void setReservedBeds(Set<Bed> reservedBeds) { |
263 |
263 |
this.reservedBeds = reservedBeds; |
264 |
264 |
this.checkPeopleCountConsistency(); |
265 |
265 |
} |
266 |
266 |
|
267 |
267 |
public Set<Bed> getReservedBeds() { |
268 |
268 |
this.checkPeopleCountConsistency(); |
269 |
269 |
return reservedBeds; |
270 |
270 |
} |
271 |
271 |
|
272 |
272 |
// TODO: Write documentation for all of these, even though it's all mostly |
273 |
273 |
// copy/pasting. pfffff |
274 |
274 |
public void setReservationID(int reservationID) { |
275 |
275 |
this.reservationID = reservationID; |
276 |
276 |
} |
277 |
277 |
|
278 |
278 |
public int getReservationID() { |
279 |
279 |
return reservationID; |
280 |
280 |
} |
281 |
281 |
|
282 |
282 |
public void setRoomType(String roomType) { |
283 |
283 |
this.roomType = roomType; |
284 |
284 |
} |
285 |
285 |
|
286 |
286 |
public String getRoomType() { |
287 |
287 |
return roomType; |
288 |
288 |
} |
289 |
289 |
|
290 |
290 |
public void setRoomFacilities(Set<String> roomFacilities) { |
291 |
291 |
this.roomFacilities = roomFacilities; |
292 |
292 |
} |
293 |
293 |
|
294 |
294 |
public Set<String> getRoomFacilities() { |
295 |
295 |
return roomFacilities; |
296 |
296 |
} |
297 |
297 |
|
298 |
298 |
/** |
299 |
299 |
* Calculates the price of the Reservation, based on its current state. |
300 |
300 |
* Price table: |
301 |
301 |
* - 20/person/day |
302 |
302 |
* - 5 less in low season, 5 more in high season |
303 |
303 |
* - 4/breakfast ordered |
304 |
304 |
* - If room is fully booked by the group --> 10% discount |
305 |
305 |
* @param room The Room the Beds of this Reservation are in. |
306 |
306 |
* @pre The Room must contain all Beds of this Reservation. |
307 |
307 |
* @pre No parameter may be null. |
308 |
308 |
* @throws IllegalArgumentException if room doesn't contain all Beds. |
309 |
309 |
* @throws NullPointerException if any parameter is a null pointer. |
310 |
310 |
* @return The price in euros. |
311 |
311 |
*/ |
312 |
312 |
public int getPrice(Room room) { |
313 |
313 |
// Contract validation |
314 |
314 |
if(!room.getBeds().containsAll(this.reservedBeds)) { |
315 |
315 |
throw new IllegalArgumentException("room does not have all the Beds of this Reservation."); |
316 |
316 |
} |
317 |
317 |
// Contract validated |
318 |
318 |
int totalPrice = 0; |
319 |
319 |
// Jan - Apr: Mid |
320 |
320 |
// May - Aug: High |
321 |
321 |
// Sep - Dec: Low |
322 |
322 |
|
323 |
323 |
// Calculate bed prices |
324 |
324 |
int month = this.getBeginDate().getMonth(); |
325 |
325 |
int bedPrice = 20; |
326 |
326 |
if(month >=8) { // From September: |
327 |
327 |
bedPrice -= 5; |
328 |
328 |
} else if(month >=4) { // From May: |
329 |
329 |
bedPrice += 5; |
330 |
330 |
} |
331 |
331 |
// ΔDate represents the difference between the begin and end date. |
332 |
332 |
// allows to retrieve the days between them, because the difference is |
333 |
333 |
// expressed in milliseconds (nights = ΔDate ÷ 1000 ÷ 60 ÷ 60 ÷ 24) |
334 |
334 |
long deltaDate = this.getEndDate().getTime() - this.getBeginDate().getTime(); |
335 |
335 |
long nights = deltaDate/1000/60/60/24; |
336 |
336 |
totalPrice += (this.getReservedBeds().size() * nights * bedPrice); |
337 |
337 |
// Calculate price for breakfasts |
338 |
338 |
int breakfasts = this.getBreakfastDays().size(); |
339 |
339 |
totalPrice += breakfasts * this.getPeople(); |
340 |
340 |
// Check if eligible for discount |
341 |
- | Set<Bed> roomBeds = room.getBeds(); |
+ |
341 |
// A group gets 10% discount iff they reserve a full Room for |
+ |
342 |
// themselves. |
+ |
343 |
Set<Bed> roomBeds = room.getBeds(); |
342 |
344 |
if(roomBeds.containsAll(this.reservedBeds)) { |
343 |
- | double discount = (double)totalPrice * 0.1; |
+ |
345 |
double discount = (double)totalPrice * 0.1; |
344 |
346 |
totalPrice -= (int)discount; |
345 |
347 |
} |
346 |
348 |
return totalPrice; |
347 |
349 |
} |
348 |
350 |
|
349 |
351 |
|
350 |
- | |
351 |
- | |
352 |
- | |
353 |
- | |
354 |
- | |
355 |
- | public void setBreakfastDays(Set<Date> breakfastDays) { |
356 |
352 |
this.breakfastDays = breakfastDays; |
357 |
353 |
} |
358 |
354 |
public Set<Date> getBreakfastDays() { |
359 |
355 |
return this.breakfastDays; |
360 |
356 |
} |
361 |
357 |
|
362 |
358 |
} |
363 |
359 |
Challenge 6/ReservationController.java ¶
12 additions and 25 deletions.
View changes Hide changes
1 |
1 |
import java.util.HashSet; |
2 |
2 |
import java.util.Date; |
3 |
3 |
|
4 |
4 |
/** |
5 |
5 |
* Controller class for the Reservations of this program. |
6 |
6 |
* Since this program handles a youth hostel, it's imperative that it holds a |
7 |
7 |
* number of Reservations. |
8 |
8 |
* |
9 |
9 |
* ReservationController takes the responsibility of handling the addition, |
10 |
10 |
* cancelling, editing, ... of Reservations, and the related tasks inherent to |
11 |
11 |
* these responsibilities, such as reserving beds, generating IDs, ... |
12 |
12 |
* @author Maarten Vangeneugden - 1438256 |
13 |
13 |
*/ |
14 |
14 |
public class ReservationController { |
15 |
15 |
|
16 |
16 |
/* Rationale: |
17 |
17 |
* Normally I'd put this as an interface (Set), but an interface does not |
18 |
18 |
* inherit from Object, and thus, does not provide clone(). This is |
19 |
19 |
* necessary, because otherwise, there's no point in having private |
20 |
20 |
* members. |
21 |
21 |
*/ |
22 |
22 |
private HashSet<Reservation> reservations; // Holds all Reservations |
23 |
23 |
private RoomController roomController; |
24 |
24 |
|
25 |
25 |
/** |
26 |
26 |
* Creates the ReservationController. |
27 |
27 |
* |
28 |
- | * WARNING: This program avoids the use of null pointers, but because of |
29 |
- | * circular dependency issues, this class requires that the developer (which |
30 |
- | * means YOU) set the RoomController reference member manually. |
31 |
- | * Be advised that the setRoomController() method will not accept a null |
32 |
- | * pointer. |
33 |
- | * |
34 |
- | * Once again: Set the RoomController reference manually ASAP. Failure to do |
35 |
- | * so will cause this program to blow up. You've been warned. |
36 |
- | * @see ReservationController.setRoomController |
37 |
- | */ |
+ |
28 |
* @pre roomController mustn't be null. |
+ |
29 |
* @throws NullPointerException if roomController is null. |
+ |
30 |
*/ |
38 |
31 |
public ReservationController() { |
39 |
- | this.reservations = new HashSet<>(); |
+ |
32 |
if(roomController == null) |
+ |
33 |
throw new NullPointerException("roomController mustn't be a null pointer."); |
+ |
34 |
this.reservations = new HashSet<>(); |
40 |
35 |
} |
+ |
36 |
} |
41 |
37 |
|
42 |
38 |
/** |
43 |
39 |
* Returns a copy of all Reservations. |
44 |
40 |
* Emphasis on "copy"; There is no setReservations() for a reason, using |
45 |
41 |
* this to edit the pointer variable would omit the use of 'private'. |
46 |
42 |
* @return A verbatim copy of all Reservations. |
47 |
43 |
*/ |
48 |
44 |
@SuppressWarnings("unchecked") |
49 |
45 |
public Set<Reservation> getReservations() { |
50 |
46 |
return (HashSet<Reservation>)this.reservations.clone(); |
51 |
47 |
} |
52 |
48 |
|
53 |
49 |
/** |
54 |
- | * Sets the RoomController. |
55 |
- | * @param roomController The RoomController. |
56 |
- | * @pre roomController mustn't be a null pointer. |
57 |
- | * @throws NullPointerException if roomController is a null pointer. |
58 |
- | */ |
59 |
- | public void setRoomController(RoomController roomController) { |
60 |
- | if(roomController == null) { |
61 |
- | throw new NullPointerException("roomController mustn't be a null pointer."); |
62 |
- | } |
63 |
- | this.roomController = roomController; |
64 |
- | } |
65 |
- | |
66 |
- | /** |
67 |
50 |
* Generates a unique ID for a new Reservation. |
68 |
51 |
* This method is to be called when a new Reservation is to be stored. |
69 |
52 |
* As every Reservation carries an ID, this method searches for a unique |
70 |
53 |
* one, and returns it. |
71 |
54 |
* @return An integer, different from all other stored Reservation IDs. |
72 |
55 |
*/ |
73 |
56 |
public int generateReservationID() { |
74 |
57 |
/* Small optimization idea: |
75 |
58 |
* Instead of starting from 0, and incrementing until it's unique, it |
76 |
59 |
* will take the amount of stored Reservations, and test that as an ID. |
77 |
60 |
* This may still overlap (by removing an older Reservation), but may be |
78 |
61 |
* much faster. Consider implemting and testing for effectiveness if |
79 |
62 |
* generating an ID takes too long. |
80 |
63 |
*/ |
81 |
64 |
int ID = 0; |
82 |
65 |
boolean isUnique = false; |
83 |
66 |
do { |
84 |
67 |
ID++; |
85 |
68 |
isUnique = true; |
86 |
69 |
for(Reservation reservation: this.reservations) { |
87 |
70 |
if(ID == reservation.getReservationID()) { |
88 |
71 |
isUnique = false; |
89 |
72 |
} |
90 |
73 |
} |
91 |
74 |
} while(!isUnique); |
92 |
75 |
// Test: |
93 |
76 |
for(Reservation reservation: this.reservations) { |
94 |
77 |
assert reservation.getReservationID() != ID : "Duplicate ID generated!"; |
95 |
78 |
} |
96 |
79 |
return ID; |
97 |
80 |
} |
98 |
81 |
|
99 |
82 |
/** |
100 |
83 |
* Check if Reservation can be made. |
101 |
84 |
* Call this method whenever you're planning on adding a new Reservation. It |
102 |
85 |
* will check the provided requirements (Room facilities, for example), and |
103 |
86 |
* return whether this Reservation can continue. |
104 |
87 |
* |
105 |
88 |
* This method is deprecated. Use RoomController directly. |
106 |
89 |
* @see RoomController.getQualifiedRoom |
107 |
90 |
* @param reservation The Reservation to check for possible addition. |
108 |
91 |
* @pre reservation mustn't be null. |
109 |
92 |
* @throws NullPointerException if reservation is null. |
110 |
93 |
* @return True if reservation can be added, False otherwise. |
111 |
94 |
*/ |
112 |
95 |
@Deprecated |
113 |
96 |
public boolean checkReservation(Reservation reservation) { |
114 |
97 |
if(reservation == null) { |
115 |
98 |
throw new NullPointerException("reservation was a null pointer."); |
116 |
99 |
} |
117 |
100 |
if(this.roomController.getQualifiedRoom(reservation) == null) { |
118 |
101 |
return false; |
119 |
102 |
} |
120 |
103 |
return true; |
121 |
104 |
} |
122 |
105 |
|
123 |
106 |
/** |
124 |
107 |
* Adds and confirms the reservation. |
125 |
108 |
* By calling this method, the given reservation will be stored in the |
126 |
109 |
* system, and the given room will be filled. |
127 |
110 |
* You are to collect the necessary data, and assure yourself that all |
128 |
111 |
* preconditions have been met. |
129 |
112 |
* |
130 |
113 |
* The best way to accomplish this, is to only send 'sterile' Reservations |
131 |
114 |
* as the first parameter. That is: Reservations without reserved Beds, |
132 |
115 |
* without prior addition to the active Reservations, etc. |
133 |
116 |
* NOTE: You must check for yourself if the Reservation can take place in |
134 |
117 |
* this Room! If there are not enough Beds available, an exception will be |
135 |
118 |
* thrown. |
136 |
119 |
* @param reservation The Reservation to add. |
137 |
120 |
* @param room The Room in which the people will reside. |
138 |
121 |
* @pre room must have enough empty beds. |
139 |
122 |
* @pre room must accomodate the Reservation's requirements. |
140 |
123 |
* @pre No parameter must be null. |
141 |
124 |
* @pre reservation mustn't already be stored in the active Reservations. |
142 |
125 |
* @post reservation is stored in the active Reservations. |
143 |
126 |
* @post The Beds in the provided Room are reserved for the Reservation's |
144 |
127 |
* period. |
145 |
128 |
* @throws IllegalArgumentException if the given Room can't fulfill the |
146 |
129 |
* requirements of the Reservation, or occupants is less than 1. |
147 |
130 |
* @throws NullPointerException if any parameter is a null pointer. |
148 |
131 |
*/ |
149 |
132 |
public void addReservation(Reservation reservation, Room room) { |
150 |
133 |
// Contract validation. Null pointers are implicitely checked by calling |
151 |
134 |
// methods. |
152 |
135 |
if(!this.roomController.getQualifiedRooms(reservation).contains(room)) |
153 |
136 |
throw new IllegalArgumentException("The given Room cannot meet all requirements of the Reservation."); |
154 |
137 |
if(this.getReservations().contains(reservation)) |
155 |
138 |
throw new IllegalArgumentException("The given Reservation was already included in the active Reservations."); |
156 |
139 |
// Contract validated |
157 |
140 |
this.reservations.add(reservation); |
158 |
- | Bed[] beds = room.getEmptyBeds(reservation.getBeginDate(), reservation.getEndDate()).toArray(new Bed[1]); |
+ |
141 |
reservation.setReservationID(this.generateReservationID()); |
+ |
142 |
// Fill the required beds |
+ |
143 |
Bed[] beds = room.getEmptyBeds(reservation.getBeginDate(), reservation.getEndDate()).toArray(new Bed[1]); |
159 |
144 |
Set<Bed> reservedBeds = new HashSet<>(); |
160 |
145 |
for(int i=0; i<reservation.getPeople(); i++) { |
161 |
146 |
beds[i].reservePeriod(reservation.getBeginDate(), reservation.getEndDate()); |
162 |
147 |
reservedBeds.add(beds[i]); |
163 |
148 |
} |
164 |
149 |
reservation.setReservedBeds(reservedBeds); |
165 |
150 |
} |
+ |
151 |
this.reservations.add(reservation); |
+ |
152 |
} |
166 |
153 |
|
167 |
154 |
/** |
168 |
155 |
* Cancels and removes the given Reservation. |
169 |
156 |
* If you want to remove a Reservation, use this method, and provide the |
170 |
157 |
* Reservation up for removal. |
171 |
158 |
* This method will take care of related actions, such as releasing Beds. |
172 |
159 |
* @param reservation The Reservation to be removed. |
173 |
160 |
* @pre reservation mustn't be null. |
174 |
161 |
* @pre reservation must be contained in the active Reservations. |
175 |
162 |
* @post The Reservation is removed from the active Reservations. |
176 |
163 |
* @post The Beds, previously reserved for this Reservation, are now |
177 |
164 |
* released, and can be reserved for another Reservation. |
178 |
165 |
* @throws NullPointerException if reservation is a null pointer. |
179 |
166 |
* @throws IllegalArgumentException if reservation is not contained in the |
180 |
167 |
* active Reservations. |
181 |
168 |
*/ |
182 |
169 |
public void cancelReservation(Reservation reservation) { |
183 |
170 |
// Contract validation |
184 |
171 |
if(!this.getReservations().contains(reservation)) { |
185 |
172 |
throw new IllegalArgumentException("The given Reservation was not contained in the active Reservations."); |
186 |
173 |
} |
187 |
174 |
if(reservation == null) { |
188 |
175 |
throw new NullPointerException(); |
189 |
176 |
} |
190 |
177 |
// Contract validated, execute method |
191 |
178 |
this.reservations.remove(reservation); // Remove from active Reservations |
192 |
179 |
for(Bed bed: reservation.getReservedBeds()) { // Release reserved Beds |
193 |
180 |
bed.removeReservationPeriod(reservation.getBeginDate(), reservation.getEndDate()); |
194 |
181 |
} |
195 |
182 |
// Asserting post conditions are met |
196 |
183 |
assert !this.getReservations().contains(reservation) : "The reservation is still part of the active Reservations."; |
197 |
184 |
for(Bed bed: reservation.getReservedBeds()) { |
198 |
185 |
assert bed.isFree(reservation.getBeginDate(), reservation.getEndDate()) : "One or more of the Beds are still reserved."; |
199 |
186 |
} |
200 |
187 |
} |
201 |
188 |
} |
202 |
189 |
Challenge 6/ReservationView.java ¶
3 additions and 1 deletion.
View changes Hide changes
1 |
1 |
import java.util.Set; |
2 |
2 |
import java.util.HashSet; |
3 |
3 |
import java.util.Date; |
4 |
4 |
import java.util.ArrayList; |
5 |
5 |
import java.text.DateFormat; |
6 |
6 |
import java.awt.Dimension; |
7 |
7 |
/** |
8 |
8 |
* Creates a view screen that allows interaction with the reservation. |
9 |
9 |
* This window will place the reservation details in editable fields, so its |
10 |
10 |
* state can be adapted. |
11 |
11 |
* It then also adds a couple of buttons; add/update/cancel, with their |
12 |
12 |
* respective actions. These will then be passed to the ReservationController. |
13 |
13 |
* It will not allow to add/update a reservation if dates overlap, i.e. beds |
14 |
14 |
* can't be reserved for that period. |
15 |
15 |
* @author Maarten Vangeneugden - 1438256 |
16 |
16 |
*/ |
17 |
17 |
public class ReservationView { |
18 |
18 |
|
19 |
19 |
private Reservation reservation; |
20 |
20 |
private Window window; |
21 |
21 |
|
22 |
22 |
// Controllers for communication with the rest |
23 |
23 |
private ReservationController rc; |
24 |
24 |
private RoomController roc; |
25 |
25 |
|
26 |
26 |
// GUI widgets |
27 |
27 |
private JTextField nameField; |
28 |
28 |
private JSpinner amountPeopleField; |
29 |
29 |
private JTextField beginDateField; |
30 |
30 |
private JTextField endDateField; |
31 |
31 |
private JSpinner durationField; |
32 |
32 |
private JRadioButton[] typeField; |
33 |
33 |
private JCheckBox showerField; |
34 |
34 |
private JCheckBox bathField; |
35 |
35 |
private JCheckBox minibarField; |
36 |
36 |
private JCheckBox aircoField; |
37 |
37 |
private ArrayList<JCheckBox> breakfastDayFields; |
38 |
38 |
|
39 |
39 |
|
40 |
40 |
/** |
41 |
41 |
* Construct a window to edit a Reservation. |
42 |
42 |
* This window presents its caller with the necessary tools to edit the |
43 |
43 |
* given Reservation, and save it in the system. |
44 |
44 |
* |
45 |
45 |
* By sending it an empty Reservation, you can add a new Reservation to the |
46 |
46 |
* system. |
47 |
47 |
* This class is built in a way that allows it to be used for both creating |
48 |
48 |
* new Reservations, and updating/changing existing Reservations. |
49 |
49 |
* Existing Reservations can also be removed by clicking the appropriate |
50 |
50 |
* button. |
51 |
51 |
* @param reservation The Reservation that will be updated. |
52 |
52 |
* @param rc The ReservationController class. |
53 |
53 |
* @param roc The RoomController class. |
54 |
54 |
* @pre No parameter may be a null pointer. |
55 |
55 |
* @post A window is displayed with GUI widgets, which are filled in |
56 |
56 |
* according to the state of reservation. |
57 |
57 |
* @throws NullPointerException if any parameter is a null pointer. |
58 |
58 |
*/ |
59 |
59 |
public ReservationView(Reservation reservation, ReservationController rc, RoomController roc) { |
60 |
60 |
// Contract validation |
61 |
61 |
if(rc == null || roc == null || reservation == null) |
62 |
62 |
throw new NullPointerException("One or more of the given parameters is a null pointer."); |
63 |
63 |
// Contract validated |
64 |
64 |
this.rc = rc; |
65 |
65 |
this.roc = roc; |
66 |
66 |
|
67 |
67 |
this.reservation = reservation; |
68 |
68 |
this.window = new Window("Reservation screen"); |
69 |
69 |
this.addFields(); |
70 |
70 |
} |
71 |
71 |
|
72 |
72 |
public void setReservation(Reservation reservation) { |
73 |
73 |
this.reservation = reservation; |
74 |
74 |
} |
75 |
75 |
|
76 |
76 |
public Reservation getReservation() { |
77 |
77 |
return reservation; |
78 |
78 |
} |
79 |
79 |
|
80 |
80 |
/** |
81 |
81 |
* Add the necessary fields to the window. |
82 |
82 |
* This method inspects the Reservation, and copies the included information |
83 |
83 |
* to the widget's content. |
84 |
84 |
*/ |
85 |
85 |
private void addFields() { |
86 |
86 |
this.window.createLabel("Group name"); |
87 |
87 |
this.nameField = window.createTextField(this.reservation.getGroupName()); |
88 |
88 |
// nameField needs manual sizing, since an empty name makes it too |
89 |
89 |
// small. |
90 |
90 |
Dimension minimumSize = new Dimension(500, 0); |
91 |
91 |
this.nameField.setMinimumSize(minimumSize); |
92 |
92 |
this.window.createLabel("Amount of people"); |
93 |
93 |
this.amountPeopleField = this.window.createSpinner(1, 20, this.reservation.getPeople(), 1); |
94 |
94 |
System.out.println("HIER AL"); |
95 |
95 |
// Formatting date for the date field: |
96 |
96 |
this.window.createLabel("Begin date"); |
97 |
97 |
this.beginDateField = window.createTextField(this.reservation.getBeginDate().toGMTString()); |
98 |
98 |
this.window.createLabel("End date"); |
99 |
99 |
this.endDateField = window.createTextField(this.reservation.getEndDate().toGMTString()); |
100 |
100 |
String[] types = {"Male", "Female", "Mixed"}; |
101 |
101 |
this.typeField = this.window.createRadioButtons(types); |
102 |
102 |
// TODO: Add a test to see if the Reservation has indicated which type |
103 |
103 |
// it is, and then already check the corresponding radio button. |
104 |
104 |
this.showerField = this.window.createCheckbox("Shower"); |
105 |
105 |
this.bathField = this.window.createCheckbox("Bath"); |
106 |
106 |
this.minibarField = this.window.createCheckbox("Minibar"); |
107 |
107 |
this.aircoField = this.window.createCheckbox("Airco"); |
108 |
108 |
// TODO: Idem for the facilities, test if already in Reservation, and |
109 |
109 |
// check accordingly. |
110 |
110 |
System.out.println("ZELGS HIER"); |
111 |
111 |
this.breakfastDayFields = new ArrayList<>(); |
112 |
112 |
this.window.createButton("Add/Update reservation", "", "addReservation", this); |
113 |
113 |
this.window.createButton("Remove reservation", "", "removeReservation", this); |
114 |
114 |
this.window.createButton("Update breakfast possibilities", "", "generateBreakfastOptions", this); |
115 |
115 |
this.generateBreakfastOptions(); |
116 |
116 |
} |
117 |
117 |
|
118 |
118 |
/** |
119 |
119 |
* Creates all possible breakfast options for this Reservation. |
120 |
120 |
* This method will first inform itself with the BeginDateField and |
121 |
121 |
* EndDateField values. |
122 |
122 |
* If these are filled with correctly formatted dates, the method will |
123 |
123 |
* generate all times that breakfast can be served in the given timespan. |
124 |
124 |
* |
125 |
125 |
* After all possible breakfasts are generated, the method will inform with |
126 |
126 |
* the Reservation itself if there are already breakfasts put in it. If so, |
127 |
127 |
* the checkboxes of those days are checked by default. |
128 |
128 |
* |
129 |
129 |
* Call this method whenever an update of the days is requested. |
130 |
130 |
*/ |
131 |
131 |
public void generateBreakfastOptions() { |
132 |
132 |
Date beginDate = new Date(Date.parse(this.beginDateField.getText())); |
133 |
133 |
Date endDate = new Date(this.endDateField.getText()); |
134 |
134 |
if(!beginDate.before(endDate)) { |
135 |
135 |
System.err.println("Begin date did not occur before end date. No update executed."); |
136 |
136 |
return; |
137 |
137 |
} |
138 |
138 |
|
139 |
139 |
// Time in Date class is represented using milliseconds |
140 |
140 |
final long ONE_DAY = 1000*60*60*24; |
141 |
141 |
Date breakfastDay = (Date)beginDate.clone(); |
142 |
142 |
// Breakfast is served at 9 o'clock! |
143 |
143 |
breakfastDay.setSeconds(0); |
144 |
144 |
breakfastDay.setMinutes(0); |
145 |
145 |
breakfastDay.setHours(9); |
146 |
146 |
// First check: Can people have breakfast on day of arrival? |
147 |
147 |
if(breakfastDay.before(beginDate)) { |
148 |
148 |
breakfastDay.setTime(breakfastDay.getTime() + ONE_DAY); |
149 |
149 |
} |
150 |
150 |
// Removing the previous checkboxes, and creating a new list of them: |
151 |
151 |
for(int i=0; i<this.breakfastDayFields.size(); i++) { |
152 |
152 |
this.window.removeComponent(this.breakfastDayFields.get(i)); |
153 |
153 |
} |
154 |
154 |
ArrayList<JCheckBox> checkboxes = new ArrayList<>(); |
155 |
155 |
while(breakfastDay.before(endDate)) { |
156 |
156 |
String dateString = |
157 |
157 |
String.valueOf(breakfastDay.getDate()) +"/"+ |
158 |
158 |
String.valueOf(breakfastDay.getMonth()+1) +"/"+ |
159 |
159 |
String.valueOf(breakfastDay.getYear()+1900); |
160 |
160 |
JCheckBox checkbox = this.window.createCheckbox(dateString); |
161 |
161 |
if(this.reservation.getBreakfastDays().contains(breakfastDay)) {// Check if already enabled |
162 |
162 |
checkbox.setSelected(true); |
163 |
163 |
} |
164 |
164 |
checkboxes.add(checkbox); |
165 |
165 |
breakfastDay.setTime(breakfastDay.getTime() + ONE_DAY); |
166 |
166 |
} |
167 |
167 |
// Putting these checkboxes in the appropriate member: |
168 |
168 |
this.breakfastDayFields = checkboxes; |
169 |
169 |
} |
170 |
170 |
|
171 |
171 |
/** |
172 |
172 |
* Get all selected breakfast days. |
173 |
173 |
* Goes over all the current checkboxes concerning the breakfast days, and |
174 |
174 |
* looks which ones are selected. |
175 |
175 |
* The selected ones are put in a Set, which is then returned to the method |
176 |
176 |
* caller. |
177 |
177 |
* @pre generateBreakfastOptions must be run ONCE before calling this |
178 |
178 |
* method. |
179 |
179 |
* @throws NullPointerException if the precondition wasn't met. |
180 |
180 |
* @return A Set containing all dates on which breakfast should be served. |
181 |
181 |
* An empty Set indicates no selected breakfast days. all breakfast days |
182 |
182 |
* occur at 9 o'clock in the morning. |
183 |
183 |
*/ |
184 |
184 |
public Set<Date> getSelectedBreakfastDays() { |
185 |
185 |
Set<Date> breakfastDays = new HashSet<>(); |
186 |
186 |
for(JCheckBox checkbox : this.breakfastDayFields) { |
187 |
187 |
if(checkbox.isSelected()) { |
188 |
188 |
// Extract information first |
189 |
189 |
String[] parts = checkbox.getText().split("\\/"); // Regex for "/" |
190 |
190 |
int day = Integer.parseInt(parts[0]); |
191 |
191 |
int month = Integer.parseInt(parts[1]); |
192 |
192 |
int year = Integer.parseInt(parts[2]); |
193 |
193 |
breakfastDays.add(new Date(year+1900, month-1, day, 8, 0, 0)); |
194 |
- | } |
+ |
194 |
} |
195 |
195 |
} |
196 |
196 |
return breakfastDays; |
197 |
197 |
} |
198 |
198 |
|
199 |
199 |
/** |
200 |
200 |
* Update stored Reservation with displayed information. |
201 |
201 |
* This method reads all information displayed in the fields. |
202 |
202 |
* It will then update the Reservation with that data. |
203 |
203 |
* This method will not update the Reservation until all data is correctly |
204 |
204 |
* formatted. |
205 |
205 |
*/ |
206 |
206 |
public void updateReservation() { |
207 |
207 |
// Collect all data from the fields |
208 |
208 |
String name = this.nameField.getText(); |
209 |
209 |
if(name.isEmpty()) { |
210 |
210 |
this.window.messageDialog("Name mustn't be blank."); |
211 |
211 |
return; |
212 |
212 |
} |
213 |
213 |
Date beginDate = null, endDate = null; |
214 |
214 |
try { |
215 |
215 |
beginDate = new Date(this.beginDateField.getText()); |
216 |
216 |
endDate = new Date(this.endDateField.getText()); |
+ |
217 |
endDate = new Date(this.endDateField.getText()); |
217 |
218 |
} |
+ |
219 |
} |
218 |
220 |
catch(Exception e) { |
219 |
221 |
this.window.messageDialog("Not all date fields were properly formatted."); |
220 |
222 |
return; |
221 |
223 |
} |
222 |
224 |
if(!beginDate.before(endDate)) { |
223 |
225 |
this.window.messageDialog("Begin date must come before end date."); |
224 |
226 |
return; |
225 |
227 |
} |
226 |
228 |
int people = (Integer)this.amountPeopleField.getValue(); |
227 |
229 |
String type = ""; |
228 |
230 |
for(int i=0; i<this.typeField.length; i++) { |
229 |
231 |
if(this.typeField[i].isSelected()) { |
230 |
232 |
type = this.typeField[i].getText(); |
231 |
233 |
break; |
232 |
234 |
} |
233 |
235 |
} |
234 |
236 |
Set<String> facilities = new HashSet<>(); |
235 |
237 |
if(this.showerField.isSelected()) { |
236 |
238 |
facilities.add("Shower"); |
237 |
239 |
} |
238 |
240 |
if(this.bathField.isSelected()) { |
239 |
241 |
facilities.add("Bath"); |
240 |
242 |
} |
241 |
243 |
if(this.minibarField.isSelected()) { |
242 |
244 |
facilities.add("Minibar"); |
243 |
245 |
} |
244 |
246 |
if(this.aircoField.isSelected()) { |
245 |
247 |
facilities.add("Airco"); |
246 |
248 |
} |
247 |
249 |
Set<Date> breakfastDays = this.getSelectedBreakfastDays(); |
248 |
250 |
// All data collected; save in Reservation. |
249 |
251 |
this.reservation.setGroupName(name); |
250 |
252 |
this.reservation.setPeople(people); |
251 |
253 |
this.reservation.setBeginDate(beginDate); |
252 |
254 |
this.reservation.setEndDate(endDate); |
253 |
255 |
this.reservation.setRoomType(type); |
254 |
256 |
this.reservation.setRoomFacilities(facilities); |
255 |
257 |
this.reservation.setBreakfastDays(breakfastDays); |
256 |
258 |
} |
257 |
259 |
|
258 |
260 |
/** |
259 |
261 |
* Start the addition of this Reservation to the system. |
260 |
262 |
* This method will check the current fields, and update the Reservation |
261 |
263 |
* with those values. In doing so, the validity of the values is checked |
262 |
264 |
* (formatting, begin date before end date, ...). |
263 |
265 |
* If everything is okay, the method will check if these changes are |
264 |
266 |
* possible in the current system; are there enough empty beds? |
265 |
267 |
* |
266 |
268 |
* If everything is okay, the changes will be propagated to the |
267 |
269 |
* Reservation's state, and (if it's a new Reservation) will be added to the |
268 |
270 |
* system's active Reservations. |
269 |
271 |
*/ |
270 |
272 |
public void addReservation() { |
271 |
273 |
// HACK: |
272 |
274 |
// I'm temporarily replacing the real Reservation with a fake one, as to |
273 |
275 |
// test whether there is a free Room. If so, we can return the real |
274 |
276 |
// Reservation and do the same thing. |
275 |
277 |
Reservation tempReservation = this.reservation; |
276 |
278 |
this.reservation = new Reservation(); |
277 |
279 |
this.updateReservation(); |
278 |
280 |
// XXX: To make sure that, if this is an active Reservation, it's not |
279 |
281 |
// counted as being active, we must temporarily remove it from the |
280 |
282 |
// active Reservations: |
281 |
283 |
if(this.rc.getReservations().contains(tempReservation)) { |
282 |
284 |
this.rc.cancelReservation(tempReservation); |
283 |
285 |
} |
284 |
286 |
// Now checking for a free Room: |
285 |
287 |
if(this.roc.getQualifiedRooms(this.reservation).isEmpty()) { |
286 |
288 |
boolean tryAgain = this.window.confirmDialog("No rooms met the requirements! Would you like to continue and change the parameters?"); |
287 |
289 |
if(tryAgain) { |
288 |
290 |
this.reservation = tempReservation; // Sanitizing what I made dirty =3 |
289 |
291 |
// We don't need to add the Reservation back, because, if we add |
290 |
292 |
// it later, it will be correct nonetheless. |
291 |
293 |
} |
292 |
294 |
else { |
293 |
295 |
this.removeReservation(); |
294 |
296 |
} |
295 |
297 |
} |
296 |
298 |
else { |
297 |
299 |
// We've found qualified Rooms and everything went well! So let's add |
298 |
300 |
// it. |
299 |
301 |
this.reservation = tempReservation; |
300 |
302 |
this.updateReservation(); |
301 |
303 |
Room room = this.roc.getQualifiedRoom(this.reservation); |
302 |
304 |
this.rc.addReservation(this.reservation, room); |
303 |
305 |
// Now closing the window, we're done here |
304 |
306 |
// Confirm and show price: |
305 |
307 |
int price = this.reservation.getPrice(room); |
306 |
308 |
this.window.messageDialog("Reservation confirmed! Price: " +String.valueOf(price)); |
307 |
309 |
this.window.close(); |
308 |
310 |
} |
309 |
311 |
} |
310 |
312 |
|
311 |
313 |
/** |
312 |
314 |
* Remove Reservation and the GUI. |
313 |
315 |
* Calling this method will remove the Reservation, followed by closing this |
314 |
316 |
* window. |
315 |
317 |
* If the Reservation is active (i.e. saved in the ReservationController), |
316 |
318 |
* it will be removed. |
317 |
319 |
* Any changes will be cancelled. |
318 |
320 |
*/ |
319 |
321 |
public void removeReservation() { |
320 |
322 |
if(this.rc.getReservations().contains(this.reservation)) { |
321 |
323 |
this.rc.cancelReservation(this.reservation); |
322 |
324 |
} |
323 |
325 |
this.window.close(); |
324 |
326 |
} |
325 |
327 |
} |
326 |
328 |
Challenge 6/SearchView.java ¶
60 additions and 15 deletions.
View changes Hide changes
1 |
1 |
import java.util.ArrayList; |
2 |
2 |
import java.util.Date; |
3 |
3 |
|
4 |
4 |
/** |
5 |
5 |
* Class for creating a screen that allows querying actions. |
6 |
6 |
* This program offers the ability to search in the program for Reservations, |
7 |
7 |
* Room status, etc. |
8 |
8 |
* This class creates a GUI for user interaction. |
9 |
9 |
* When matches are found, it will display them. In case of a unique match, that |
10 |
10 |
* match is immediately displayed. |
11 |
11 |
* @see ReservationView |
12 |
12 |
* @author Maarten Vangeneugden - 1438256 |
13 |
13 |
*/ |
14 |
14 |
public class SearchView { |
15 |
15 |
private ReservationController rc; |
16 |
16 |
private RoomController roc; |
17 |
17 |
private Window window; |
18 |
18 |
|
19 |
19 |
private JTextField groupNameField; |
20 |
20 |
private JTextField reservationIDField; |
21 |
21 |
private JTextField beginDateField; |
22 |
- | private JTextField endDateField; |
23 |
- | private JTextField breakfastDayField; |
+ |
22 |
private JTextField breakfastDayField; |
24 |
23 |
|
25 |
24 |
public SearchView(ReservationController rc, RoomController roc) { |
26 |
25 |
this.rc = rc; |
27 |
26 |
this.roc = roc; |
28 |
27 |
|
29 |
28 |
this.window = new Window("Search screen"); |
30 |
29 |
this.addFields(); |
31 |
30 |
} |
32 |
31 |
|
33 |
32 |
private void addFields() { |
34 |
33 |
// TODO: Add fields to private members! |
35 |
34 |
this.window.createLabel("Search reservations:"); |
36 |
35 |
this.groupNameField = this.window.createTextField("Search by group name"); |
37 |
36 |
this.reservationIDField = this.window.createTextField("Search by reservation ID"); |
38 |
37 |
this.window.createButton("Search reservation", "", "queryReservations", this); |
39 |
38 |
this.window.createLabel("Search reserved beds in period (begin-end):"); |
40 |
- | this.beginDateField = this.window.createTextField(new Date().toGMTString()); |
41 |
- | this.endDateField = this.window.createTextField(new Date().toGMTString()); |
42 |
- | this.window.createButton("Get reserved beds", "", "queryBeds", this); |
+ |
39 |
this.bedDateField = this.window.createTextField(new Date().toGMTString()); |
+ |
40 |
this.window.createButton("Get reserved beds", "", "queryBeds", this); |
43 |
41 |
this.window.createLabel("Find amount of breakfasts:"); |
44 |
42 |
this.breakfastDayField = this.window.createTextField(new Date().toGMTString()); |
45 |
43 |
this.window.createButton("Get breakfasts on given day", "", "queryBreakfasts", this); |
46 |
44 |
} |
47 |
45 |
|
48 |
46 |
public void queryReservations() { |
49 |
47 |
ArrayList<Reservation> foundReservations = new ArrayList<>(); |
50 |
48 |
String nameQuery = this.groupNameField.getText(); |
51 |
49 |
String IDQuery = this.reservationIDField.getText(); |
52 |
50 |
for(Reservation activeReservation : this.rc.getReservations()) { |
53 |
51 |
if(activeReservation.getGroupName().contains(nameQuery) && |
54 |
52 |
String.valueOf(activeReservation.getReservationID()).contains(IDQuery)) { |
55 |
53 |
foundReservations.add(activeReservation); |
56 |
54 |
} |
57 |
55 |
} |
58 |
56 |
// After collecting all results, offer the user the choice about which |
59 |
57 |
// one to take: |
60 |
58 |
String[] reservationNames = new String[foundReservations.size()]; |
61 |
59 |
for(int i=0; i<foundReservations.size(); i++) { |
62 |
60 |
String text = foundReservations.get(i).getGroupName() +" ("+ |
63 |
61 |
String.valueOf(foundReservations.get(i).getReservationID()) +")"; |
64 |
62 |
reservationNames[i] = text; |
65 |
63 |
} |
66 |
64 |
int choice = this.window.choiceDialog("Multiple results were found. Specify which one you want to view:", reservationNames); |
67 |
65 |
if(choice == -1) { // No result found |
68 |
66 |
this.window.messageDialog("No reservations matched the given details."); |
69 |
67 |
} |
70 |
68 |
else { |
71 |
69 |
new ReservationView(foundReservations.get(choice), this.rc, this.roc); |
72 |
70 |
} |
73 |
71 |
} |
74 |
72 |
|
75 |
73 |
public void queryBeds() { |
+ |
74 |
* Search and display the reservation of Beds in Rooms. |
+ |
75 |
* This method takes the given begin and end dates from the GUI, and lists |
+ |
76 |
* for each Room, how many Beds are reserved, and by whom. |
+ |
77 |
*/ |
+ |
78 |
public void queryBeds() { |
76 |
79 |
// The amount of reserved Beds is determined by the Reservations. |
77 |
80 |
// If a Reservation (partially) overlaps with the given time, then this |
78 |
81 |
// Bed is reserved in the given period. |
79 |
82 |
Date beginDate = new Date(this.beginDateField.getText()); |
80 |
- | Date endDate = new Date(this.endDateField.getText()); |
81 |
- | String result = ""; |
+ |
83 |
// endDate is required as it has to be strictly later than date. |
+ |
84 |
final Date endDate = new Date(date.getTime() + 1); |
+ |
85 |
String result = ""; |
82 |
86 |
for(Reservation reservation: this.rc.getReservations()) { |
83 |
- | if(! |
+ |
87 |
for(Room room: this.roc.getRooms()) { |
+ |
88 |
// XXX: The Java Object class provides a "hashcode()" method, to |
+ |
89 |
// retrieve an object's hashcode. I'm using that to identify the |
+ |
90 |
// different rooms. |
+ |
91 |
int usedBeds = room.getBeds().size() - room.getEmptyBeds(date, endDate).size(); |
+ |
92 |
result = result + "Room "+ String.valueOf(room.hashCode()) +": "+ String.valueOf(usedBeds) +" reserved beds.\n"; |
+ |
93 |
} |
+ |
94 |
this.window.messageDialog(result); |
+ |
95 |
/*for(Reservation reservation: this.rc.getReservations()) { |
+ |
96 |
if(! |
84 |
97 |
(reservation.getBeginDate().before(beginDate) && reservation.getEndDate().before(beginDate)) || |
85 |
98 |
(reservation.getBeginDate().after(endDate) && reservation.getEndDate().after(endDate))) |
86 |
99 |
{ // This block is only reached if the valid options didn't match. |
87 |
100 |
String name = reservation.getGroupName(); |
88 |
101 |
String beds = String.valueOf(reservation.getReservedBeds().size()); |
89 |
102 |
String begin = reservation.getBeginDate().toGMTString(); |
90 |
103 |
String end = reservation.getEndDate().toGMTString(); |
91 |
104 |
result = result + |
92 |
105 |
name +" reserved "+ beds +" between "+ begin +" and "+ end +".\n"; |
93 |
106 |
} |
94 |
107 |
} |
95 |
- | if(!result.isEmpty()) { |
96 |
- | this.window.messageDialog(result); |
97 |
- | } |
98 |
- | else { |
99 |
- | this.window.messageDialog("No beds have reservation periods overlapping with the given timespan."); |
100 |
- | } |
+ |
108 |
} |
101 |
109 |
|
102 |
110 |
public void queryBreakfasts() { |
+ |
111 |
* Queries and displays breakfast reservations. |
+ |
112 |
* After the user has entered a day in the corresponding field, this method |
+ |
113 |
* can be called to search for breakfast reservations on that day. |
+ |
114 |
* Breakfasts are ordered by Reservation, accompagnied by the amount of |
+ |
115 |
* people in that Reservation. |
+ |
116 |
* // TODO finish this javadoc |
+ |
117 |
*/ |
+ |
118 |
public void queryBreakfasts() { |
103 |
119 |
|
104 |
- | } |
+ |
120 |
// Breakfast is only served at 9 o'clock, so set that time for easy |
+ |
121 |
// reference during querying: |
+ |
122 |
date.setHours(8); |
+ |
123 |
date.setMinutes(0); |
+ |
124 |
date.setSeconds(0); |
+ |
125 |
String response = ""; |
+ |
126 |
for(Reservation reservation: this.rc.getReservations()) { |
+ |
127 |
if(reservation.getBreakfastDays().contains(date)) { |
+ |
128 |
String name = reservation.getGroupName(); |
+ |
129 |
String amount = String.valueOf(reservation.getPeople()); |
+ |
130 |
response = response + |
+ |
131 |
"Reservation "+ name +" ordered breakfast for "+ amount +" people.\n"; |
+ |
132 |
} |
+ |
133 |
// else block is DEBUG CODE |
+ |
134 |
else { |
+ |
135 |
System.out.println("No match!"); |
+ |
136 |
System.out.println("Query: "+date.toGMTString()); |
+ |
137 |
System.out.println("Tests:"); |
+ |
138 |
for(Date bfd: reservation.getBreakfastDays()) { |
+ |
139 |
System.out.println(bfd.toGMTString()); |
+ |
140 |
} |
+ |
141 |
} |
+ |
142 |
} |
+ |
143 |
if(!response.isEmpty()) { |
+ |
144 |
this.window.messageDialog(response); |
+ |
145 |
} |
+ |
146 |
else { |
+ |
147 |
this.window.messageDialog("There are no breakfasts ordered for this day."); |
+ |
148 |
} |
+ |
149 |
} |
105 |
150 |
} |
106 |
151 |
Challenge 6/ontwerpkeuzes2.md ¶
50 additions and 2 deletions.
View changes Hide changes
1 |
1 |
|
2 |
2 |
## GRASP |
3 |
3 |
Om zoveel mogelijk tegemoet te komen aan de GRASP-richtlijnen, heb ik volgende |
4 |
4 |
ontwerpkeuzes toegepast voor de verschillende richtlijnen. |
5 |
5 |
|
6 |
6 |
### High cohesion & Low coupling |
7 |
7 |
- Veel classes zijn totaal van elkaar losgekoppeld. Een goed voorbeeld hiervan |
8 |
8 |
is Bed; Bed weet niet over dat er Reservaties bestaan, zelfs niet in welke |
9 |
9 |
Room het 'staat'. |
10 |
10 |
- Als er toch gekoppeld moet worden, dan blijft dit tot een minimum beperkt; Een |
11 |
11 |
Room weet wel welke Bedden het heeft, maar niet hoe deze gebruikt worden, een |
12 |
12 |
Reservation weet wel welke Bedden voor hem gereserveerd zijn, maar heeft geen |
13 |
13 |
weet van welke kamer dat deze staat. |
14 |
14 |
- Uitzondering op deze regel vormen de Controller-classes; omwille van hun taken |
15 |
15 |
zijn deze vaak verplicht om van veel verschillende classes op de hoogte te |
16 |
16 |
zijn. |
17 |
17 |
|
18 |
18 |
### Indirectie |
19 |
19 |
- In plaats van functionaliteit toe te wijzen aan class X om met class Y te |
20 |
20 |
interageren, wordt er vaak gebruik gemaakt van een class Z, die deze |
21 |
21 |
verantwoordelijkheid op zich neemt. |
22 |
22 |
- Dit wordt toegepast d.m.v. een MVC-structuur, waarin de \*Controller-classes |
23 |
23 |
als 'tussenpersoon' werken. |
24 |
24 |
- Dit komt ook tegemoet aan de eigenschappen van Information Expertise. |
25 |
25 |
|
26 |
26 |
### Creator |
27 |
27 |
- Controllers staan ook in als zgn. Creators van de models die ze beheren. Ze |
28 |
28 |
voldoen dan ook aan de eigenschappen zoals |
29 |
29 |
[hier](https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Creator) |
30 |
30 |
opgesomd wordt. |
31 |
31 |
|
32 |
32 |
|
33 |
33 |
## SOLID |
34 |
34 |
|
35 |
35 |
### Single Responsibility |
36 |
36 |
|
37 |
37 |
## Veranderingen t.o.v. Challenge 1 |
38 |
38 |
|
39 |
39 |
- Verkrijgen van een vrije kamer is verplaatst van ReservationView naar |
40 |
40 |
RoomController |
41 |
41 |
- Toevoeging van veel documentatie, vooral in de vorm van Javadoc, maar ook |
42 |
42 |
_inline commenting_ waar nodig/gewenst. |
43 |
43 |
|
44 |
44 |
## Algemene keuzes ter bevordering codekwaliteit |
+ |
45 |
De aanpassingen die ik heb moeten doorvoeren om wandelingen toe te voegen, zijn |
+ |
46 |
grotendeels beperkt gebleven tot de plekken in de code die sowieso van meerdere |
+ |
47 |
onderdelen van het programma op de hoogte zijn; |
+ |
48 |
Zo is er niks veranderd aan Reservation of Room, maar WalkController en |
+ |
49 |
ReservationController hebben elkaar nu wel als member. |
+ |
50 |
|
+ |
51 |
Aan de GUI-kant was het slechts 1 knopje toevoegen in het hoofdmenu. |
+ |
52 |
|
+ |
53 |
Ik heb het zo opgesteld dat een groepsnaam die overeenkomt met een |
+ |
54 |
hostelreservering, wordt aanzien als een reservering van dezelfde persoon. |
+ |
55 |
Als de namen niet overeen komen, dan zijn het verschillende groepen. |
+ |
56 |
|
+ |
57 |
Deze conventie laat mij toe om het programma veel beter te laten voldoen aan |
+ |
58 |
high cohesion/low coupling, zonder daarvoor veel voordelen voor hoeven in te |
+ |
59 |
leveren. |
+ |
60 |
|
+ |
61 |
## Algemene keuzes ter bevordering codekwaliteit |
45 |
62 |
|
46 |
63 |
## Strings, integers, ... i.p.v. Enums/Eigen classes |
47 |
64 |
In het programma zijn er sommige members die doen vermoeden dat ze beter als |
48 |
65 |
Enums aangeduid kunnen worden; Het type van een kamer, faciliteiten, ... |
49 |
66 |
|
50 |
67 |
In plaats van een speciale class hiervoor te maken, heb ik gekozen om deze |
51 |
68 |
gewoon te behandelen als simpele Strings in lijsten. |
52 |
69 |
|
53 |
70 |
Het voordeel aan deze werkwijze, is dat Strings in Java SE quasi universeel |
54 |
71 |
voorkomen; data kunnen geconverteerd worden naar Strings (en vice versa), veel |
55 |
72 |
gebruikte GUI-libraries (waaronder Swing, JavaFX, ...) gebruiken Strings voor |
56 |
73 |
tekst voor te stellen in de GUI-widgets, ... |
57 |
74 |
Daarnaast weet iedereen die ook maar een beetje geschoold is in Java, direct hoe |
58 |
75 |
deze members behandeld kunnen worden. Had ik een class gemaakt die bv. |
59 |
76 |
RoomFacilities heette, dan is die parate kennis over de taal zelf nutteloos. |
60 |
77 |
|
61 |
78 |
Strings zijn ook voorzien van extreem veel methods, en zijn dus zeer flexibele |
62 |
79 |
classes om te gebruiken. Het feit dat ze zo makkelijk doorheen het programma te |
63 |
80 |
gebruiken zijn is dus een groot pluspunt. |
64 |
81 |
|
65 |
82 |
Als dat nog niet genoeg is: Het gebruiken van de hulpmiddelen uit de _standard |
66 |
83 |
library_ is niet enkel waarvoor libraries überhaupt bestaan, het beperkt ook |
67 |
84 |
het benodigde aantal classes in de eigen software, waardoor het geheel |
68 |
85 |
uiteindelijk veel overzichtelijker blijft, en bijgevolg, makkelijk uit te |
69 |
86 |
breiden, te testen, te begrijpen, ... |
70 |
87 |
|
71 |
88 |
Al deze voordelen graan grotendeels verloren als ik beslis om zelf |
72 |
89 |
gespecialiseerde classes op te stellen. |
73 |
90 |
Men kan misschien stellen dat "de voordelen die het schrijven van eigen |
74 |
91 |
classes veel beter tegemoet komt aan de essentie van object-georiënteerd |
75 |
92 |
programmeren, dan zich _beperken_ tot slechts een handvol generieke classes, die |
76 |
93 |
misschien niet volledig aan de benodigdheden beantwoorden". |
77 |
94 |
|
78 |
95 |
Toch laat ik mijn afweging overslaan in het voordeel van minder classes, en meer |
79 |
96 |
uniformiteit. _Sometimes, less is more._ Meer classes betekent ook: |
80 |
97 |
- Meer onderhoud |
81 |
98 |
- Meer kans op bugs |
82 |
99 |
- Groter programma |
83 |
100 |
|
84 |
101 |
En al die problemen resulteren op lange termijn in: |
85 |
102 |
- Slechtere uitbreidbaarheid |
86 |
103 |
- Slechtere onderhoudbaarheid |
87 |
104 |
- Slechtere schaalbaarheid |
88 |
105 |
|
89 |
106 |
Zijn al die problemen de moeite van een (zogezegd) "beter object-georiënteerd |
90 |
107 |
design" wel waard? |
91 |
108 |
Veel mensen stellen juist dat OOP net voordelig is als men op zoek is naar |
92 |
109 |
- Uitbreidbaarheid |
93 |
110 |
- Onderhoudbaarheid |
94 |
111 |
- Modulariteit |
95 |
112 |
- Schaalbaarheid |
96 |
113 |
|
97 |
114 |
Als de voordelen van het paradigma verdwijnen als ik dat "beter design" volg, |
98 |
115 |
is dat design dan wel echt beter? |
99 |
116 |
|
100 |
117 |
|
101 |
118 |
**Conclusie: Uniforme classes zijn volgens mij soms beter dan een stel |
102 |
119 |
gespecialiseerde classes. Daarom dat ik mij soms behelp met Strings, intergers, |
103 |
120 |
... i.p.v. zelf een oplossing te schrijven.** |
104 |
121 |
|
105 |
122 |
|
106 |
123 |
### Null pointers |
107 |
124 |
Het valt misschien op dat ik doorheen mijn programma in veel contracten eis dat |
108 |
125 |
geen enkele parameter een zgn. *null pointer* is. |
109 |
126 |
|
110 |
127 |
Het gebruik van null pointers staat garant voor een overvloed aan moeilijk te |
111 |
128 |
vinden bugs die (door het design van objectgeörienteerd programmeren) enorm diep |
112 |
129 |
kunnen doorpropageren. |
113 |
130 |
|
114 |
131 |
Ik maak er een kerntaak van dat, als er aan mijn programma gewerkt wordt, de |
115 |
132 |
programmeur zichzelf steeds kan garanderen dat **alle** data die hij |
116 |
133 |
ontvangt, valide data is. |
117 |
- | Op deze manier valt er een hele last van de schouders van de programmeur; een |
+ |
134 |
Op deze manier valt er een hele last van de schouders van de programmeur; een |
118 |
135 |
reeks fouten worden voorkomen, simpelweg door een strikt schema aan te houden. |
119 |
136 |
|
120 |
137 |
Het controleren op null pointers wordt op 2 manieren gedaan: |
121 |
138 |
- Gebruik van *methods* aanwezig in het gegeven type. Als de gegeven variabele |
122 |
139 |
een null pointer is, zal het programma direct crashen, en een |
123 |
140 |
NullPointerException geven. |
124 |
141 |
- Expliciet testen of *var == null*. Wordt toegepast op parameters die direct |
125 |
142 |
als members opgeslagen dienen te worden. |
126 |
143 |
|
127 |
144 |
Deze (contractuele) controle laat toe dat, mocht er een null pointer gebruikt |
+ |
145 |
slechts 1 object teruggeven. In deze gevallen kan het voorvallen dat het te |
+ |
146 |
zoeken object niet gevonden wordt. Om de ontwikkelaar hiervan op de hoogte te |
+ |
147 |
stellen, wordt dan soms een null teruggestuurd. |
+ |
148 |
Dit wordt steeds expliciet vermeld. |
+ |
149 |
|
+ |
150 |
Deze (contractuele) controle laat toe dat, mocht er een null pointer gebruikt |
128 |
151 |
worden, het programma de programmeur hiervan direct op de hoogte stelt, en dit |
129 |
152 |
op het laagst mogelijke niveau (namelijk de eerste method waar deze waarde |
130 |
153 |
gebruikt wordt). |
131 |
154 |
|
132 |
155 |
### Cloning |
133 |
156 |
members 'private' maken (encapsuleren) is volledig nutteloos als men getters en |
134 |
157 |
setters op deze members toepast; In Java worden references doorgegeven (m.u.v. |
135 |
158 |
primitives), die de hele notie van encapsulatie voorbijgaan (bij sommige types). |
136 |
159 |
Een voorbeeld hiervan is het privatiseren van een Set<T>-member: men kan daar |
137 |
160 |
een 'getSet()'-method op plaatsen, en dan toch de inhoud van deze 'private' |
138 |
161 |
aanpassen. |
139 |
162 |
|
140 |
163 |
Ik heb geopteerd om, waar van toepassing, deze variabelen te 'clonen', om zo |
141 |
164 |
exacte kopieën terug te geven. |
142 |
165 |
Deze manier van werken brengt enkele voordelen teweeg: |
143 |
166 |
- Zeer defensief programmeren; De ontwikkelaar kan geen members aanpassen als |
144 |
167 |
dat niet de bedoeling was |
145 |
168 |
- Duidelijkheid code: getSet().clear() zal de member niet meer leegmaken. Om dat |
146 |
169 |
te doen, moet men gebruikmaken van de method die daarvoor bedoeld is: |
147 |
170 |
setSet(clearedSet) |
148 |
171 |
|
149 |
172 |
### Inheritance |
150 |
173 |
Overerving is een goed concept over het algemeen, maar **niet** in OOP. |
151 |
174 |
De problemen omtrent impliciet gedrag en onnodige *state* zijn al te vaak |
152 |
175 |
beschreven met OOP-inheritance. |
153 |
176 |
|
154 |
177 |
Ik heb in mijn programma geen gebruik gemaakt van inheritance, exact omwille van |
155 |
178 |
de problemen die het voortbrengt, zeker in termen van herbruikbaarheid en |
156 |
179 |
robuustheid, wat toch zware vereisten waren voor deze opdracht. |
157 |
180 |
|
158 |
181 |
Ik heb al mijn problemen makkelijk kunnen oplossen d.m.v. compositie. |
159 |
182 |
|
160 |
183 |
### Benaming variabelen |
161 |
184 |
Doorheen mijn programma maak ik heel veel gebruik van dezelfde benamingen. |
162 |
185 |
Bijvoorbeeld: Een variabele van het type Reservation zal haast altijd |
163 |
186 |
'reservation' heten, een Set van een bepaald type zal de naam van datzelfde type |
164 |
187 |
dragen, in het meervoud, ... |
165 |
188 |
|
166 |
189 |
Sommige programmeurs gebruiken liever afkortingen (bv. 'reservation' --> |
167 |
190 |
'resv'), omdat dit sneller schrijft. |
168 |
191 |
|
169 |
192 |
Naar mijn mening moet men bij deze werkwijze inleveren aan leesbaarheid, vooral |
170 |
193 |
wanneer iemand die nog nooit met de code gewerkt heeft, dit programma moet |
171 |
194 |
overnemen. |
172 |
195 |
|
173 |
196 |
Daarnaast zorgt de consistentie van woordgebruik ervoor dat een andere |
174 |
197 |
programmeur, doorheen het hele programma dezelfde context in zijn/haar gedachten |
175 |
198 |
kan gebruiken. |
176 |
199 |
|
177 |
200 |
|
178 |
201 |
|
179 |
202 |
|
180 |
203 |
------------------------------------------------------------ TODO |
181 |
204 |
## Toepassing types/classes |
182 |
205 |
Doorheen het programma heb ik getracht zoveel mogelijk gebruik te maken van |
183 |
206 |
types/classes die |
184 |
207 |
- Veelvoorkomend zijn in de Java STL (Zoals String, Set, List, ...) |
185 |
208 |
- primitief zijn (ints, ...), omdat deze operatoren hebben en de code |
186 |
209 |
leesbaarder maken |
187 |
210 |
|
188 |
211 |
Een goed voorbeeld hiervan zijn bv. de faciliteiten: |
189 |
212 |
I.p.v. een aparte "Facility"-class te maken, heb ik de verschillende |
190 |
213 |
faciliteiten voorgesteld door een simpele String. De voordelen van deze aanpak |
191 |
214 |
zijn ook direct duidelijk: |
192 |
215 |
- Betekenis is direct duidelijk; de faciliteit letterlijk in de code vernoemd |
193 |
216 |
- Makkelijke interactie met GUI, die sowieso Strings vraagt voor bv. JLabel |
194 |
217 |
- Uitbreidbaarheid wordt bekomen door simpelweg een nieuwe String te |
195 |
218 |
introduceren |
196 |
219 |
|
197 |
220 |
## View en GUI |
198 |
221 |
Werken met GUI's is vaak tijdrovend en veroorzaakt snel errors, zeker met bv. |
199 |
222 |
anonieme methods, exceptions, ... |
200 |
223 |
Alhoewel mijn programma grotendeels in een MVC-stijl is geschreven, maken de |
201 |
224 |
view-classes (RegistrationView, SearchView, ...) achterliggend gebruik van een |
202 |
225 |
zelfgemaakt framework om makkelijk vensters te maken. |
203 |
226 |
Dit kleine framework is een persoonlijk hobbyproject dat ik JSugar noem. |
204 |
227 |
Het biedt een heleboel voordelen, vergeleken met elk GUI-venster zelf opstellen: |
205 |
228 |
- Vaak gebruikte GUI-widgets (zoals een label, textfield) worden aangemaakt en |
206 |
229 |
toegevoegd door slechts 1 method op te roepen |
207 |
230 |
- JSugar maakt gebruik van reflectie om op een leesbare en uitbreidbare manier |
208 |
231 |
knoppen te activeren: |
209 |
232 |
public JButton createButton(String text, String action, String methodName, Object triggerObject) |
210 |
233 |
'methodName' is een simpele String, en 'triggerObject' is het object waar deze |
211 |
234 |
method moet worden opgeroepen. |
212 |
235 |
- Automatische uitlijning van widgets |
213 |
236 |
Voor meer informatie kunt u JSugar-readme.md raadplegen. |
214 |
237 |
|
215 |
238 |
## java.util.Date |
216 |
239 |
|
217 |
240 |
Doorheen het programma maak ik vaak gebruik van de Date-class uit de Java STL, |
218 |
241 |
om de volgende redenen: |
219 |
242 |
- Uitbreidbaarheid: Door overal eenzelfde type te gebruiken, is het gemakkelijk |
220 |
243 |
om nieuwe modules toe te voegen, zonder dat daarvoor abstractielagen etc. |
221 |
244 |
nodig zijn. |
222 |
245 |
- Uniformiteit: Eenzelfde type uit de STL laat de ontwikkelaar toe om door het |
223 |
246 |
hele programma heen hetzelfde denkpatroon aan te houden; een |
224 |
247 |
Stringvoorstelling hier, een integer daar, ... veroorzaken problemen en bugs, |
225 |
248 |
die met deze class voorkomen worden. |
226 |
249 |
|
227 |
250 |
### Ontbijtdata |
228 |
251 |
|
229 |
252 |
Het moeilijkste om te implementeren waren de data voor ontbijt. |
230 |
253 |
Er wordt voor de reservaties steeds een set bijgehouden van de data waarop |
231 |
254 |
ontbijt besteld is. |
232 |
255 |
Op zich is dit niet zo moeilijk. Het moeilijke hieraan, is de gebruiker van het |
233 |
256 |
programma op een begrijpelijke manier deze data te laten invoeren. |
234 |
257 |
Data worden ingevoerd via tekstvelden, omdat Swing geen zgn. "DatePicker" heeft. |
235 |
258 |
|
236 |
259 |
## Bedden |
+ |
260 |
### GUI |
+ |
261 |
|
+ |
262 |
Swing biedt geen widget die zich specialiseert in het weergeven van informatie |
+ |
263 |
over een datum (een zgn. _calendar widget_). |
+ |
264 |
|
+ |
265 |
In de opdracht stond dat we geen "visueel aantrekkelijke GUI" hoeven te |
+ |
266 |
ontwerpen, maar wel een die "alle functionaliteit ondersteunt". |
+ |
267 |
|
+ |
268 |
Wegens het tijdsgebrek en de tekortkoming van Swing, heb ik besloten om data |
+ |
269 |
weer te geven in een simpel JTextField. |
+ |
270 |
Het biedt de beste oplossing voor wat gevraagd wordt in deze opdracht, en wegens |
+ |
271 |
de tijdslimiet tijdens de challenges zelf vormen ze een goede |
+ |
272 |
"kortetermijnoplossing". |
+ |
273 |
|
+ |
274 |
## Bedden |
237 |
275 |
|
238 |
276 |
Ik heb voor bedden een aparte class aangemaakt, omdat deze een bepaalde state |
239 |
277 |
bijhouden, nml. wanneer ze gereserveerd zijn. |
240 |
278 |
|
241 |
279 |
|
242 |
280 |
## Reservation |
243 |
281 |
Voor de reservaties heb ik besloten om enkel die data bij te houden die inherent |
244 |
282 |
gerelateerd is aan die reservatie: |
245 |
283 |
- Enkel gereserveerde bedden i.p.v. gereserveerde kamers. Qua uitbreidbaarheid |
246 |
284 |
heeft dit tot gevolg dat men gemakkelijk kan uitbreiden naar reservaties, |
247 |
285 |
gespreid over verschillende kamers. |
248 |
286 |
|
249 |
287 |
|
250 |
288 |
## ReservationView |
251 |
- | Merk op hoe in ReservationView de data van de Reservation direct in de GUI wordt |
+ |
289 |
Merk op hoe in ReservationView de data van de Reservation direct in de GUI wordt |
252 |
290 |
geplaatst. |
253 |
291 |
Dit staat toe om zeer gemakkelijk deze class te hergebruiken voor zowel het |
254 |
292 |
**aanmaken** als het **updaten** van de reservatie. |
255 |
293 |
|
+ |
294 |
Dezelfde werkwijze wordt toegepast ook WalkView. |
+ |
295 |
|
+ |
296 |
|
+ |
297 |
## Opmerkingen |
+ |
298 |
|
+ |
299 |
- Door het gebruik van de FlowLayout in de GUI, is het mogelijk dat sommige |
+ |
300 |
gedeelten van de GUI niet direct worden weergegeven, daar ze verborgen worden |
+ |
301 |
door de hoogte van het venster. Men kan deze tevoorschijn halen door zelf het |
+ |
302 |
venster in de hoogte te verstellen. |
+ |
303 |