OOP2

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