OOP2

Refactor of Reservation related classes, mainly GUI

Author
Maarten 'Vngngdn' Vangeneugden
Date
Dec. 15, 2016, 10:33 p.m.
Hash
70c4f4fde97168ddd55ad491ce96bd3731314269
Parent
212ab469ae6defcc415d9558e2bf77a3bb06039f
Modified files
Challenge 6/MainWindow.java
Challenge 6/Reservation.java
Challenge 6/ReservationController.java
Challenge 6/ReservationView.java
Challenge 6/Window.java
Challenge 6/ontwerpkeuzes2.md

Challenge 6/MainWindow.java

1 addition and 2 deletions.

View changes Hide changes
1
1
 * Main window for the hostel program.
2
2
 * This class creates some sort of "main menu screen". It displays a concise set
3
3
 * of buttons, that allow the user to reach all parts of the program's GUI.
4
4
 * To be called as soon as the program is started (Essentially from the Main
5
5
 * class).
6
6
 * @author Maarten Vangeneugden - 1438256
7
7
 */
8
8
public class MainWindow {
9
9
10
10
	private ReservationController reservationController;
11
11
	private RoomController roomController;
12
12
	
13
13
	public MainWindow(ReservationController reservationController, RoomController roomController) {
14
14
		this.reservationController = reservationController;
15
15
		this.roomController = roomController;
16
16
17
17
		Window window = new Window("Main menu");
18
18
		window.createButton("New reservation", "", "addReservation", this);
19
19
		window.createButton("Search screen", "", "openSearchView", this);
20
20
	}
21
21
22
22
	public void openSearchView() {
23
23
		SearchView sv = new SearchView(this.reservationController, this.roomController);
24
24
	}
25
25
	
26
26
	public void addReservation() {
27
27
		Reservation reservation = new Reservation(this.reservationController, this.roomController);
28
-
		ReservationView rv = new ReservationView(reservation, this.reservationController, this.roomController);
+
28
		ReservationView rv = new ReservationView(reservation, this.reservationController, this.roomController);
29
29
30
-
	}
31
30
32
31
}
33
32

Challenge 6/Reservation.java

20 additions and 2 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 begin;
41
41
	private Date end;
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 the group wants breakfast
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 begin The date that the Reservation begins.
64
64
	 * @param end 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 begin must come before end.
75
75
	 * @pre All dates in breakfastDays must fall between begin and end.
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 begin, Date end, Set<Bed> reservedBeds, String roomType, Set<String> roomFacilities) {
84
-
		// Contract validation:
+
84
		// Contract validation:
85
85
		if(people < 1) {
86
86
			throw IllegalArgumentException("The amount of people should be at least 1.");
87
87
		}
88
88
		if(!begin.before(end)) {
89
89
			throw IllegalArgumentException("The begin date occurs after the end date.");
90
90
		}
91
91
		if(groupName.isEmpty() || roomType.isEmpty()) {
92
92
			throw IllegalArgumentException("groupName and/or roomType were empty strings.");
93
93
		}
94
94
		for(String roomFacility: roomFacilities) {
95
95
			if(roomFacility.isEmpty()) {
96
96
				throw 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(begin) || breakfastDay.after(end)) {
101
101
				throw 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 = begin;
108
108
		this.endDate = end;
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
	
+
116
	/**
+
117
	 * Construct template Reservation.
+
118
	 * Use this constructor if you need to create an empty/new Reservation. 
+
119
	 * It omits the standard restrictions (for example, the group name will be
+
120
	 * empty) to accomodate for the expected usage of this Reservation.
+
121
	 */
+
122
	public Reservation() {
+
123
		this.groupName = "";
+
124
		this.people = 1;
+
125
		this.beginDate = new Date();
+
126
		this.endDate = new Date();
+
127
		this.reservedBeds = new HashSet<>();
+
128
		this.reservationID = 0;
+
129
		this.roomType = "";
+
130
		this.roomFacilities = new HashSet<>();
+
131
		this.breakfastDays = new HashSet<>();
+
132
	}
115
133
116
134
	/**
117
135
	 * Checks whether the amount of people corresponds with the reserved Beds.
118
136
	 * Call this method whenever a change in the amount of Beds, or the amount
119
137
	 * of people is made, or, whenever you need to assure consistency.
120
138
	 *
121
139
	 * It also prints a warning to system.out.err to inform about a discrepancy,
122
140
	 * should one be found.
123
141
	 *
124
142
	 * @return True if the counts are consistent, false otherwise.
125
143
	 */
126
144
	private boolean checkPeopleCountConsistency() {
127
145
		int people = this.getPeople();
128
146
		int beds = this.getReservedBeds().size();
129
147
		if(people != beds) {
130
148
			system.out.err("There's a discrepancy in the amount of people in Reservation"+
131
149
					this.reservationID +":");
132
150
			system.out.err("People: "+String.valueOf(people));
133
151
			system.out.err("Reserved Beds: "+String.valueOf(beds));
134
152
			return false;
135
153
		}
136
154
		return true;
137
155
	}
138
156
139
157
	/**
140
158
	 * Set the group name for this Reservation.
141
159
	 * @param groupName The new group name.
142
160
	 * @pre groupName mustn't be empty, or a null pointer.
143
161
	 * @post The group name is changed to the given name.
144
162
	 * @throws NullPointerException if groupName is a null pointer.
145
163
	 * @throws IllegalArgumentException if groupName is an empty String.
146
164
	 */
147
165
	public void setGroupName(String groupName) {
148
166
		if(groupName.isEmpty())
149
167
			throw IllegalArgumentException("groupName is an empty String.");
150
168
		this.groupName = groupName;
151
169
	}
152
170
153
171
	/**
154
172
	 * Retrieve the current group name.
155
173
	 * @return The group name of this Reservation.
156
174
	 */
157
175
	public String getGroupName() {
158
176
		return this.groupName;
159
177
	}
160
178
161
179
	/**
162
180
	 * Get amount of people in this Reservation.
163
181
	 * @post A warning will be printed to system.out.err if the amount of people
164
182
	 * is inconsistent with the amount of reserved Beds.
165
183
	 * @see Reservation.checkPeopleCountConsistency
166
184
	 * @return The amount of people in this Reservation.
167
185
	 */
168
186
	public int getPeople() {
169
187
		this.checkPeopleCountConsistency();
170
188
		return this.people;
171
189
	}
172
190
173
191
	/**
174
192
	 * Set the amount of people for this Reservation.
175
193
	 * Note that this method will not notify you if the new amount of people is
176
194
	 * equal to the previous amount.
177
195
	 * 
178
196
	 * This method will print
179
197
	 * @param people The new amount of people in this Reservation.
180
198
	 * @pre people must be at least 1.
181
199
	 * @post A warning will be printed to system.out.err if the amount of people
182
200
	 * is inconsistent with the amount of reserved Beds.
183
201
	 * @post The amount of people is changed to the given value.
184
202
	 * @throws IllegalArgumentException if people is less than 1.
185
203
	 */
186
204
	public void setPeople(int people) {
187
205
		if(people < 1) {
188
206
			throw IllegalArgumentException("people must be at least 1.");
189
207
		}
190
208
		this.people = people;
191
209
		this.checkPeopleCountConsistency();
192
210
	}
193
211
	/**
194
212
	 * Set the begin date for this Reservation.
195
213
	 * @param begin The new begin date.
196
214
	 * @pre begin mustn't be a null pointer.
197
215
	 * @pre begin must come strictly before the end date.
198
216
	 * @post The begin date is updated accordingly.
199
217
	 * @throws NullPointerException if begin is a null pointer.
200
218
	 * @throws IllegalArgumentException if begin comes after the end date.
201
219
	 */
202
220
	public void setBegin(Date begin) {
203
221
		if(!begin.before(this.getEnd()))
204
222
			throw IllegalArgumentException("begin comes after the end date.")
205
223
		this.begin = begin;
206
224
	}
207
225
208
226
	
209
227
210
228
	/**
211
229
	 * Set the end date for this Reservation.
212
230
	 * @param end The new end date.
213
231
	 * @pre end mustn't be a null pointer.
214
232
	 * @pre end must come strictly after the begin date.
215
233
	 * @post The end date is updated accordingly.
216
234
	 * @throws NullPointerException if end is a null pointer.
217
235
	 * @throws IllegalArgumentException if end comes after the end date.
218
236
	 */
219
237
	public Date getDate() {
220
238
		return date;
221
239
	}
222
240
223
241
	public void setReservedBeds(Set<Bed> reservedBeds) {
224
242
		this.reservedBeds = reservedBeds;
225
243
		this.checkPeopleCountConsistency();
226
244
	}
227
245
228
246
	public Set<Bed> getReservedBeds() {
229
247
		return reservedBeds;
230
248
		this.checkPeopleCountConsistency();
231
249
	}
232
250
233
251
	// TODO: Write documentation for all of these, even though it's all mostly
234
252
	// copy/pasting. pfffff
235
253
	public void setReservationID(int reservationID) {
236
254
		this.reservationID = reservationID;
237
255
	}
238
256
239
257
	public int getReservationID() {
240
258
		return reservationID;
241
259
	}
242
260
243
261
	public void setRoomType(String roomType) {
244
262
		this.roomType = roomType;
245
263
	}
246
264
247
265
	public String getRoomType() {
248
266
		return roomType;
249
267
	}
250
268
251
269
	public void setRoomFacilities(Set<String> roomFacilities) {
252
270
		this.roomFacilities = roomFacilities;
253
271
	}
254
272
255
273
	public Set<String> getRoomFacilities() {
256
274
		return roomFacilities;
257
275
	}
258
276
259
277
	/**
260
278
	 * Calculates the price of the Reservation, based on its current state.
261
279
	 * Price table:
262
280
	 * - 20/person/day
263
281
	 *   - 5 less in low season, 5 more in high season
264
282
	 * - 4/breakfast ordered
265
283
	 * - If room is fully booked by the group --> 10% discount
266
284
	 * @return The price in euros.
267
285
	 */
268
286
	public int getPrice() {
269
287
		int totalPrice = 0;
270
288
		// Jan - Apr: Mid
271
289
		// May - Aug: High
272
290
		// Sep - Dec: Low
273
291
		
274
292
		// Calculate bed prices
275
293
		int month = this.getDate().getMonth();
276
294
		int bedPrice = 20;
277
295
		if(month >=8) { // From September:
278
296
			bedPrice -= 5;
279
297
		} else if(month >=4) { // From May:
280
298
			bedPrice += 5;
281
299
		}
282
300
		totalPrice += (this.getReservedBeds().size() * this.getNights() * bedPrice);
283
301
		// Calculate price for breakfasts
284
302
		int breakfasts = this.getBreakfastDays().length;
285
303
		totalPrice += breakfasts * this.getReservedBeds().size();
286
304
		// Check if eligible for discount
287
305
		for(Room room: roomController.getRooms()) {
288
306
			Set<Bed> roomBeds = room.getBeds();
289
307
			if(roomBeds.containsAll(this.reservedBeds)) {
290
308
				double discount = (double)totalPrice * 0.1;
291
309
				totalPrice -= (int)discount;
292
310
			}
293
311
		}
294
312
		return totalPrice;
295
313
	}
296
314
297
315
298
316
299
317
300
318
301
319
302
320
303
321
	public void setBreakfastDays(int[] breakfastDays) {
304
322
		this.breakfastDays = breakfastDays;
305
323
	}
306
324
	public int[] getBreakfastDays() {
307
325
		return this.breakfastDays;
308
326
	}
309
327
310
328
}
311
329

Challenge 6/ReservationController.java

9 additions and 12 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
28
	 * WARNING: This program avoids the use of null pointers, but because of
29
29
	 * circular dependency issues, this class requires that the developer (which
30
30
	 * means YOU) set the RoomController reference member manually.
31
31
	 * Be advised that the setRoomController() method will not accept a null
32
32
	 * pointer.
33
33
	 *
34
34
	 * Once again: Set the RoomController reference manually ASAP. Failure to do
35
35
	 * so will cause this program to blow up. You've been warned.
36
36
	 * @see ReservationController.setRoomController
37
37
	 */
38
38
	public ReservationController() {
39
39
		this.reservations = new HashSet<>();
40
40
	}
41
41
42
42
	/**
43
43
	 * Returns a copy of all Reservations.
44
44
	 * Emphasis on "copy"; There is no setReservations() for a reason, using
45
45
	 * this to edit the pointer variable would omit the use of 'private'.
46
46
	 * @return A verbatim copy of all Reservations.
47
47
	 */
48
48
	@SuppressWarnings("unchecked")
49
49
	public Set<Reservation> getReservations() {
50
50
		return (HashSet<Reservation>)this.reservations.clone();
51
51
	}
52
52
53
53
	/**
54
54
	 * Generates a unique ID for a new Reservation.
55
55
	 * This method is to be called when a new Reservation is to be stored.
56
56
	 * As every Reservation carries an ID, this method searches for a unique
57
57
	 * one, and returns it.
58
58
	 * @return An integer, different from all other stored Reservation IDs.
59
59
	 */
60
60
	public int generateReservationID() {
61
61
		/* Small optimization idea:
62
62
		 * Instead of starting from 0, and incrementing until it's unique, it
63
63
		 * will take the amount of stored Reservations, and test that as an ID.
64
64
		 * This may still overlap (by removing an older Reservation), but may be
65
65
		 * much faster. Consider implemting and testing for effectiveness if
66
66
		 * generating an ID takes too long.
67
67
		 */
68
68
		int ID = 0;
69
69
		boolean isUnique = false;
70
70
		do {
71
71
			ID++;
72
72
			isUnique = true;
73
73
			for(Reservation reservation: this.reservations) {
74
74
				if(ID == reservation.getReservationID()) {
75
75
					isUnique = false;
76
76
				}
77
77
			}
78
78
		} while(!isUnique);
79
79
		// Test:
80
80
		for(Reservation reservation: this.reservations) {
81
81
			assert reservation.getReservationID() != ID : "Duplicate ID generated!";
82
82
		}
83
83
		return ID;
84
84
	}
85
85
	
86
86
	/** 
87
87
	 * Check if Reservation can be made.
88
88
	 * Call this method whenever you're planning on adding a new Reservation. It
89
89
	 * will check the provided requirements (Room facilities, for example), and
90
90
	 * return whether this Reservation can continue.
91
91
	 * @param reservation The Reservation to check for possible addition.
92
92
	 * @pre reservation mustn't be null.
93
93
	 * @throws NullPointerException if reservation is null.
94
94
	 * @return True if reservation can be added, False otherwise.
95
95
	 */
96
96
	public boolean checkReservation(Reservation reservation) {
97
97
		if(reservation == null) {
98
98
			throw NullPointerException("reservation was a null pointer.");
99
99
		}
100
100
		if(this.getRoomController().getQualifiedRooms(reservation).size() == 0) {
101
101
			return null;
102
102
	}
103
103
104
104
	/**
105
105
	 * Adds and confirms the reservation.
106
106
	 * By calling this method, the given reservation will be stored in the
107
107
	 * system, and the given room will be filled.
108
108
	 * You are to collect the necessary data, and assure yourself that all
109
109
	 * preconditions have been met.
110
110
	 * 
111
111
	 * The best way to accomplish this, is to only send 'sterile' Reservations
112
112
	 * as the first parameter. That is: Reservations without reserved Beds,
113
113
	 * without prior addition to the active Reservations, etc.
114
114
	 * NOTE: You must check for yourself if the Reservation can take place in
115
115
	 * this Room! If there are not enough Beds available, an exception will be
116
116
	 * thrown.
117
117
	 * @param reservation The Reservation to add.
118
118
	 * @param room The Room in which the people will reside.
119
119
	 * @pre room must have enough empty beds.
120
120
	 * @pre room must accomodate the Reservation's requirements.
121
121
	 * @pre No parameter must be null.
122
122
	 * @pre reservation mustn't already be stored in the active Reservations.
123
123
	 * @post reservation is stored in the active Reservations.
124
124
	 * @post The Beds in the provided Room are reserved for the Reservation's
125
125
	 * period.
126
126
	 * @throws IllegalArgumentException if the given Room can't fulfill the
127
127
	 * requirements of the Reservation, or occupants is less than 1.
128
128
	 * @throws NullPointerException if any parameter is a null pointer.
129
129
	 */
130
130
	public void addReservation(Reservation reservation, Room room) {
131
131
		// Contract validation. Null pointers are implicitely checked by calling
132
132
		// methods.
133
133
		if(occupants < 1)
134
-
			throw IllegalArgumentException("An invalid amount of occupants was given.");
135
-
		if(occupants > room.getEmptyBeds(reservation.getBegin(), reservation.getEnd()).size())
136
-
			throw IllegalArgumentException("The given Room has not enough empty Beds for the Reservation period.");
+
134
			throw IllegalArgumentException("The given Room has not enough empty Beds for the Reservation period.");
137
135
		if(!this.getRoomController().getQualifiedRooms(reservation).contains(room))
138
136
			throw IllegalArgumentException("The given Room cannot meet all requirements of the Reservation.");
139
137
		if(this.getReservations().contains(reservation))
140
138
			throw IllegalArgumentException("The given Reservation was already included in the active Reservations.");
141
139
		// Contract validated
142
140
		this.reservations.add(reservation);
143
141
		Set<Bed> beds
144
-
		for(int i=0; i<occupants; i++) {
145
-
146
-
147
-
		
148
-
		
149
-
		
150
-
		// TODO: Add reservation to the set, and add the reserved days to the
151
-
		// beds in the given room.
152
-
	}
+
142
		Set<Bed> reservedBeds = new HashSet<>();
+
143
		for(int i=0; i<reservation.getPeople(); i++) {
+
144
			beds[i].reservePeriod(reservation.getBegin(), reservation.getEnd());
+
145
			reservedBeds.add(beds[i]);
+
146
		}
+
147
		reservation.setReservedBeds(reservedBeds);
+
148
	}
153
149
	/**
+
150
	/**
154
151
	 * Cancels and removes the given Reservation.
155
152
	 * If you want to remove a Reservation, use this method, and provide the
156
153
	 * Reservation up for removal.
157
154
	 * This method will take care of related actions, such as releasing Beds.
158
155
	 * @param reservation The Reservation to be removed.
159
156
	 * @pre reservation mustn't be null.
160
157
	 * @pre reservation must be contained in the active Reservations.
161
158
	 * @post The Reservation is removed from the active Reservations.
162
159
	 * @post The Beds, previously reserved for this Reservation, are now
163
160
	 * released, and can be reserved for another Reservation.
164
161
	 * @throws NullPointerException if reservation is a null pointer.
165
162
	 * @throws IllegalArgumentException if reservation is not contained in the
166
163
	 * active Reservations.
167
164
	 */
168
165
	public void cancelReservation(Reservation reservation) {
169
166
		// Contract validation
170
167
		if(!this.getReservations().contains(reservation)) {
171
168
			throw IllegalArgumentException("The given Reservation was not contained in the active Reservations.");
172
169
		}
173
170
		if(reservation == null) {
174
171
			throw NullPointerException();
175
172
		}
176
173
		// Contract validated, execute method
177
174
		this.reservations.remove(reservation); // Remove from active Reservations
178
175
		for(Bed bed: reservation.getReservedBeds()) { // Release reserved Beds
179
176
			bed.removeReservationPeriod(reservation.getBegin(), reservation.getEnd());
180
177
		}
181
178
		// Asserting post conditions are met
182
179
		assert !this.getReservation().contains(reservation) : "The reservation is still part of the active Reservations.";
183
180
		for(Bed bed: reservation.getReservedBeds()) {
184
181
			assert bed.isFree(reservation.getBegin(), reservation.getEnd()) : "One or more of the Beds are still reserved.";
185
182
		}
186
183
	}
187
184
188
185
}
189
186

Challenge 6/ReservationView.java

72 additions and 35 deletions.

View changes Hide changes
1
1
import java.util.Set;
2
2
import java.util.HashSet;
3
3
import java.util.Date;
4
4
/**
5
5
 * Creates a view screen that allows interaction with the reservation.
6
6
 * This window will place the reservation details in editable fields, so its
7
7
 * state can be adapted.
8
8
 * It then also adds a couple of buttons; add/update/cancel, with their
9
9
 * respective actions. These will then be passed to the ReservationController.
10
10
 * It will not allow to add/update a reservation if dates overlap, i.e. beds
11
11
 * can't be reserved for that period.
12
12
 * @author Maarten Vangeneugden - 1438256
13
13
 */
14
14
public class ReservationView {
15
15
16
16
	private Reservation reservation;
17
17
	private Window window;
18
18
19
19
	private ReservationController rc;
+
20
	private ReservationController rc;
20
21
	private RoomController roc;
21
22
22
23
	// GUI widgets:
23
-
	private JTextField nameField;
+
24
	private JTextField nameField;
24
25
	private JSpinner amountPeopleField;
25
26
	private JTextField dateField;
26
27
	private JSpinner durationField;
27
28
	private JRadioButton[] typeField;
28
29
	private JCheckBox showerField;
29
30
	private JCheckBox bathField;
30
31
	private JCheckBox minibarField;
31
32
	private JCheckBox aircoField;
32
33
33
34
34
35
	public ReservationView(Reservation reservation, ReservationController rc, RoomController roc) {
+
36
	 * Construct a window to edit a Reservation.
+
37
	 * This window presents its caller with the necessary tools to edit the
+
38
	 * given Reservation, and save it in the system.
+
39
	 *
+
40
	 * By sending it an empty Reservation, you can add a new Reservation to the
+
41
	 * system.
+
42
	 * This class is built in a way that allows it to be used for both creating
+
43
	 * new Reservations, and updating/changing existing Reservations.
+
44
	 * Existing Reservations can also be removed by clicking the appropriate
+
45
	 * button.
+
46
	 * @param reservation The Reservation that will be updated.
+
47
	 * @param rc The ReservationController class.
+
48
	 * @param roc The RoomController class.
+
49
	 * @pre No parameter may be a null pointer.
+
50
	 * @post A window is displayed with GUI widgets, which are filled in
+
51
	 * according to the state of reservation.
+
52
	 * @throws NullPointerException if any parameter is a null pointer.
+
53
	 */
+
54
	public ReservationView(Reservation reservation, ReservationController rc, RoomController roc) {
35
55
		this.rc = rc;
+
56
		if(rc == null || roc == null || reservation == null)
+
57
			throw NullPointerException("One or more of the given parameters is a null pointer.");
+
58
		// Contract validated
+
59
		this.rc = rc;
36
60
		this.roc = roc;
37
61
38
62
		this.reservation = reservation;
39
63
		this.window = new Window("Reservation screen");
40
64
		this.addFields();
41
65
	}
42
66
43
67
	public void setReservation(Reservation reservation) {
44
68
		this.reservation = reservation;
45
69
	}
46
70
47
71
	public Reservation getReservation() {
48
72
		return reservation;
49
73
	}
50
74
51
75
	private void addFields() {
+
76
	 * Add the necessary fields to the window.
+
77
	 * This method inspects the Reservation, and copies the included information
+
78
	 * to the widget's content.
+
79
	 */
+
80
	private void addFields() {
52
81
		this.window.createLabel("Group name");
53
82
		this.nameField = window.createTextField(this.reservation.getGroupName());
54
83
		this.window.createLabel("Amount of people");
55
84
		this.amountPeopleField = window.createSpinner(1, 20);
56
-
		// Formatting date for the date field:
+
85
		// Formatting date for the date field:
57
86
		this.window.createLabel("date");
58
-
		Date date = this.reservation.getDate();
59
-
		String day = String.valueOf(date.getDate());
60
-
		String month = String.valueOf(date.getMonth()+1);
61
-
		String year = String.valueOf(date.getYear()+1900);
62
-
		this.dateField = window.createTextField(day +" "+ month +" "+ year);
63
-
		this.window.createLabel("Duration");
64
-
		this.durationField = window.createSpinner(1, 355);
65
-
		String[] types = new String[3];
66
-
		types[0] = "Male";
67
-
		types[1] = "Female";
68
-
		types[2] = "Mixed";
69
-
		this.typeField = this.window.createRadioButtons(types);
+
87
		this.beginDateField = window.createTextField(this.reservation.getBegin().toString());
+
88
		this.window.createLabel("End date");
+
89
		this.endDateField = window.createTextField(this.reservation.getEnd().toString());
+
90
		String[] types = {"Male", "Female", "Mixed"};
+
91
		this.typeField = this.window.createRadioButtons(types);
70
92
		this.showerField = this.window.createCheckbox("Shower");
+
93
		// it is, and then already check the corresponding radio button.
+
94
		this.showerField = this.window.createCheckbox("Shower");
71
95
		this.bathField = this.window.createCheckbox("Bath");
72
96
		this.minibarField = this.window.createCheckbox("Minibar");
73
97
		this.aircoField = this.window.createCheckbox("Airco");
74
98
75
-
76
-
		this.window.createButton("Add/Update reservation", "", "addReservation", this);
+
99
		// check accordingly.
+
100
		this.window.createButton("Add/Update reservation", "", "addReservation", this);
77
101
		this.window.createButton("Cancel reservation", "", "cancelReservation", this);
78
-
	}
+
102
	}
79
103
80
104
	
81
-
82
-
	public void addReservation() {
+
105
	 * Start the addition of this Reservation to the system.
+
106
	 * This method will check the current fields, and update the Reservation
+
107
	 * with those values. In doing so, the validity of the values is checked
+
108
	 * (formatting, begin date before end date, ...).
+
109
	 * If everything is okay, the method will check if these changes are
+
110
	 * possible in the current system; are there enough empty beds?
+
111
	 *
+
112
	 * If everything is okay, the changes will be propagated to the
+
113
	 * Reservation's state, and (if it's a new Reservation) will be added to the
+
114
	 * system's active Reservations.
+
115
	 */
+
116
	public void addReservation() {
83
117
		// Collect all data from the fields
84
118
		String name = this.nameField.getText();
85
119
		String date = this.dateField.getText();
86
-
		// Extracting date data:
87
-
		String[] dateParts = date.split(" ");
88
-
		int day = Integer.parseInt(dateParts[0]);
89
-
		int month = Integer.parseInt(dateParts[1]);
90
-
		int year = Integer.parseInt(dateParts[2]);
91
-
		Date actualDate = new Date(day, month-1, year-1900);
92
-
93
-
		int amount = (Integer)this.amountPeopleField.getValue();
94
-
		int duration = (Integer)this.durationField.getValue();
95
-
		String type = "";
+
120
		try {
+
121
			beginDate = DataFormat.parse(this.beginDateField.getText());
+
122
			endDate = DataFormat.parse(this.endDateField.getText());
+
123
		}
+
124
		catch(ParseException e) {
+
125
			this.window.messageDialog("Not all date fields were properly formatted.");
+
126
			return;
+
127
		}
+
128
		int people = (Integer)this.amountPeopleField.getValue();
+
129
		String type = "";
96
130
		for(int i=0; i<this.typeField.length; i++) {
97
131
			if(this.typeField[i].isSelected()) {
98
132
				type = this.typeField[i].getText();
99
133
				break;
100
134
			}
101
135
		}
102
136
		Set<String> facilities = new HashSet<>();
103
137
		if(this.showerField.isSelected()) {
104
138
			facilities.add("Shower");
105
139
		}
106
140
		if(this.bathField.isSelected()) {
107
141
			facilities.add("Bath");
108
142
		}
109
143
		if(this.minibarField.isSelected()) {
110
144
			facilities.add("Minibar");
111
145
		}
112
146
		if(this.aircoField.isSelected()) {
113
147
			facilities.add("Airco");
114
148
		}
115
149
116
150
		Set<Room> possibleRooms = this.roc.getQualifiedRooms(actualDate, duration, type, facilities);
117
-
		if(possibleRooms.size() == 0) {
+
151
+
152
		//Set<Room> possibleRooms = this.roc.getQualifiedRooms(actualDate, duration, type, facilities);
+
153
		// TODO: Refactor upper method to work with the Reservation.
+
154
		// TODO: Implement all checks of valid data above this line!
+
155
		
+
156
		// Data validated; Try finding appropriate beds
+
157
		if(possibleRooms.size() == 0) {
118
158
			boolean tryAgain = this.window.confirmDialog("No rooms met the requirements! Would you like to continue and change the parameters?");
119
159
			if(!tryAgain) {
120
160
				// TODO close window
121
161
			}
122
162
		}
123
163
		else {
124
164
			// Determine end date:
125
-
			long beginDate = actualDate.getTime();
126
-
			long endDate = beginDate + (duration * 24 * 60 * 60 * 1000);
127
-
			Date actualEndDate = new Date(endDate);
128
-
			Room pickedRoom = null;
129
165
			for(Room room: possibleRooms) {
130
166
				if(room.getEmptyBeds(actualDate, actualEndDate).size() < room.getBeds().size()) {
131
167
					// First, fill the rooms that are partially filled.
132
168
					pickedRoom = room;
133
169
					break;
134
170
				}
135
171
			}
136
172
			if(pickedRoom == null) { // If still no room, pick an empty room
137
173
				for(Room room: possibleRooms) {
138
174
					if(room.getEmptyBeds(actualDate, actualEndDate).size() >= amount) {
139
175
						pickedRoom = room;
140
176
						break;
141
177
					}
142
178
				}
143
179
			}
144
180
			assert pickedRoom != null;
145
181
			// TODO: Set reservation fields here!
146
182
			this.rc.addReservation(reservation, pickedRoom);
147
183
148
184
			// Confirm and show price:
149
185
			int price = this.reservation.getPrice();
150
186
			this.window.confirmDialog("Reservation confirmed! Price: " +String.valueOf(price));
151
187
		}
152
188
		
153
189
	}
154
190
	public void cancelReservation() {
155
-
		// TODO: Close the window. That's all.
+
191
	public void removeReservation() {
+
192
		// TODO: Close the window. That's all.
156
193
	}
157
194
}
158
195

Challenge 6/Window.java

26 additions and 0 deletions.

View changes Hide changes
1
1
 * Window.java - Module to create a new window with JSugar.
2
2
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
3
3
 * 
4
4
 * This program is free software: you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
6
6
 * the Free Software Foundation, either version 3 of the License, or
7
7
 * (at your option) any later version.
8
8
 * 
9
9
 * This program is distributed in the hope that it will be useful,
10
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
12
 * GNU General Public License for more details.
13
13
 * 
14
14
 * You should have received a copy of the GNU General Public License
15
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
16
 */
17
17
18
18
/*
19
19
 * TODO list:
20
20
 * - JSlider (It's the same as the JSpinner, only longer. So an extra.)
21
21
 * - JTable (And a JScrollBar to accompany it) (extra, because of JList)
22
22
 * - JFileChooser (?)
23
23
 * DONE list:
24
24
 * - JLabel
25
25
 * - JText
26
26
 * - JButton
27
27
 * - JDialogBoxes (you know, everything dialog related)
28
28
 * - JCheckbox
29
29
 * - JRadioButton (properly grouping them has been taken care of as well)
30
30
 * - JSpinner
31
31
 * - JComboBox
32
32
 * - JList
33
33
 */
34
34
35
35
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
36
36
import java.util.NoSuchElementException;
37
37
import java.lang.reflect.Method;
38
38
import java.io.File;
39
39
/**
+
40
+
41
/**
40
42
 * Window class for the program.
41
43
 *
42
44
 * Window contains the necessary data and methods to present the user with what
43
45
 * he's familiar with as being a "window". To make it functional, the developer
44
46
 * can make use of a series of methods to add components to said window, remove
45
47
 * components, and so on.
46
48
 * Currently, Window also contains methods to show dialogs. This will be cleaned
47
49
 * in the near future.
48
50
 * @author Maarten Vangeneugden
49
51
 */
50
52
public class Window { // Must be public, in order to generate Javadoc.
51
53
	private JPanel panel; // The panel that contains all the components.
52
54
	private JFrame frame; // The "window" being presented to the user.
53
55
54
56
	/**
55
57
	 * Constructor of Window.
56
58
	 * By creating a new Window instance, this constructor will automatically
57
59
	 * start the initialization of the GUI. After doing so, the caller can
58
60
	 * start adding components to the window as pleased.
59
61
	 * @param title The title to be shown in the window's title bar.
60
62
	 */
61
63
	public Window(String title) {
62
64
		// Setting the UI style to the platform's UI style. Fuck Swing's,
63
65
		// really.
64
66
		try {
65
67
		UIManager.setLookAndFeel(
66
68
				UIManager.getSystemLookAndFeelClassName());
67
69
		} catch(Exception e) {
68
70
			e.printStackTrace();
69
71
		}
70
72
71
73
		if(title == null || title.equals("")) { // If the title was omitted:
72
74
			title = "JSugar";
73
75
		}
74
76
		this.panel = new JPanel();
75
77
		// TODO: The current title is "Hello world!" but that will become caller
76
78
		// defined soon.
77
79
		JFrame frame = new JFrame(title);
78
80
		// Makes it so that if the user clicks the X in the titlebar, the window
79
81
		// closes:
80
82
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // XXX: Closes
81
83
		//ALL open windows!
82
84
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
83
85
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
84
86
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
85
87
		frame.pack(); 	
86
88
		frame.setVisible(true); // Makes the window visible to the user.
87
89
		this.frame = frame;
88
90
	}
89
91
90
92
	/**
91
93
	 * Resizes the window to fit all components.
92
94
	 * By calling this method, the window will evaluate the currently visible
93
95
	 * components, and resize itself so that all components become properly
94
96
	 * visible.
95
97
	 */
96
98
	private void updateWindow() {
97
99
		this.frame.pack();
98
100
	}
99
101
100
102
	/**
101
103
	 * A series of tests for method and class handling.
102
104
	 * When a caller presents certain methods with data concerning reflection,
103
105
	 * the Java classes you need to use for that are quite opaque, and don't
104
106
	 * offer much safety in any way.
105
107
	 * The solution therefore, is run some validation checks, but these take up
106
108
	 * a decent amount of space in terms of LoC.
107
109
	 * This method takes care of all that. Call this function whenever data
108
110
	 * needs to be validated.
109
111
	 * @param methodName The name of the method, as it is declared in object.
110
112
	 * @param object The class instance in where this method will be called.
111
113
	 * @return The method that could be derived from the supplied data, or null
112
114
	 * if that wasn't possible.
113
115
	 * @throws NullPointerException if either methodName or object are null
114
116
	 * pointers.
115
117
	 * @throws IllegalArgumentException if methodName is empty, or the method
116
118
	 * does not appear to be declared in the given object, or object is not a
117
119
	 * class.
118
120
	 */
119
121
	// All unchecked typecasts are safe, and the use of raw types is taken care
120
122
	// of.
121
123
	@SuppressWarnings({"unchecked","rawtypes"})
122
124
	private Method handleReflectionData(String methodName, Object object) {
123
125
		// Null pointer checking:
124
126
		if (methodName == null || object == null) {
125
127
			throw new NullPointerException("One or more of the given parameters are null pointers.");
126
128
		}
127
129
128
130
		// XXX: Some might say the next line should be in an else{} block. But
129
131
		// Scoping rules require that I'd then have to wrap the rest of the
130
132
		// method in the same else to use it.
131
133
		Class methodClass = object.getClass(); 
132
134
		if (methodName.equals("")) {
133
135
			throw new IllegalArgumentException("The given methodName was empty.");
134
136
		}
135
137
		Method method;
136
138
		try { // First: Look if there's a method without parameters.
137
139
			method = methodClass.getMethod(methodName, null);
138
140
		}
139
141
		catch (NoSuchMethodException exception) {
140
142
			try {
141
143
				// It's possible that the method requires an event parameter, so
142
144
				// check for that as well:
143
145
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
144
146
				method = methodClass.getMethod(methodName, parameters);
145
147
			}
146
148
			catch (NoSuchMethodException e) {
147
149
				throw new IllegalArgumentException("The given method does not appear in the given class. Be aware that the given method mustn't have any parameters, or only 1 parameter, which has to be of type java.awt.event.ActionEvent.");
148
150
			}
149
151
		}
150
152
		// At this stage, the given data has been validated, and we've been able
151
153
		// to retrieve the method itself.
152
154
		return method;
153
155
	}
154
156
155
157
	/**
156
158
	 * Creates a button in the GUI for interaction.
157
159
	 * This function offers a convenient way to create a button, that can be
158
160
	 * directly interacted with by the user. After creation, the button itself
159
161
	 * is returned to the caller, if he wishes to do something else with it.
160
162
	 * @param text The text that will be displayed in the button.
161
163
	 * @param action The action that will be returned to the action listener.
162
164
	 * @param methodName The name of the method that will be called when an
163
165
	 * action is triggered.
164
166
	 * @param triggerObject The object instance that contains the given method.
165
167
	 * This may only be a null pointer if triggerMethod is not an instance
166
168
	 * method.
167
169
	 * performed. This method may accept an ActionEvent parameter as its only
168
170
	 * parameter, or no parameters at all.
169
171
	 * @throws NullPointerException if triggerMethod is a null pointer, or
170
172
	 * the empty String was given.
171
173
	 * @throws IllegalArgumentException if triggerMethod has more than 1
172
174
	 * parameter, or the 1 required parameter is not of type ActionEvent.
173
175
	 * @return The button that was created.
174
176
	 * @see java.awt.event.ActionEvent
175
177
	 * @see java.lang.reflect.Method#invoke
176
178
	 */
177
179
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
178
180
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
179
181
180
182
		// For starters, we first assert that the given parameters are valid:
181
183
		if (text == null) {
182
184
			text = "";
183
185
		}
184
186
		if (action == null) {
185
187
			action = "";
186
188
		}
187
189
		
188
190
		// When the method gets here, everything's been validated correctly.
189
191
		JButton button = new JButton(text);
190
192
		button.setActionCommand(action);
191
193
		button.addActionListener(
192
194
				new java.awt.event.ActionListener() {
193
195
					public void actionPerformed(java.awt.event.ActionEvent event) {
194
196
						try {
195
197
							triggerMethod.setAccessible(true);
196
198
							if (triggerMethod.getParameterTypes().length == 0) {
197
199
								// FIXME: Next line throws a warning?
198
200
								triggerMethod.invoke(triggerObject, null);
199
201
							}
200
202
							else {
201
203
								triggerMethod.invoke(triggerObject, new Object[]{event});
202
204
							}
203
205
						}
204
206
						catch (Exception useless) {
205
207
							/*
206
208
							 * XXX: Some info on why I don't just throw said
207
209
							 * Exception to the caller:
208
210
							 * Java has this awful language constraint, which
209
211
							 * forces every damn exception that isn't a subclass
210
212
							 * of RuntimeException, to be declared in the method
211
213
							 * declaration. This tends to infect all underlying
212
214
							 * methods as well, and all that for reasons I can't
213
215
							 * comprehend. In order to keep JSugar a simple and
214
216
							 * clean library, I'll rather just handle it here,
215
217
							 * and throw a RuntimeException with appropriate
216
218
							 * details.
217
219
							 */
218
220
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
219
221
						}
220
222
					}
221
223
				});
222
224
		this.addComponent(button);
223
225
		return button;
224
226
	}
225
227
226
228
	/**
227
229
	 * Ask the user for input through a dialog box.
228
230
	 * This method presents the user with an input field, that can accept
229
231
	 * textual input. The method will return the given input after the user's
230
232
	 * clicked a button to send.
231
233
	 * @param text The text/question to be asked to the user.
232
234
	 * @return A String, equal to what the user entered.
233
235
	 * @throws NullPointerException if text is a null pointer.
234
236
	 */
235
237
	public String inputDialog(String text) {
236
238
		if (text == null) {
237
239
			throw new NullPointerException("The given text/question was a null pointer.");
238
240
		}
239
241
		return JOptionPane.showInputDialog(text);
240
242
	}
241
243
242
244
	/**
243
245
	 * Give the user a dialog box.
244
246
	 * This method can be used to provide a simple dialog to the user.
245
247
	 * This will show the user the given question, after which a boolean value
246
248
	 * is returned, holding the choice.
247
249
	 * @param text The text/question to be asked to the user.
248
250
	 * @return True if the user confirms, False if he denies.
249
251
	 * @throws NullPointerException if text is a null pointer.
250
252
	 */
251
253
	public boolean confirmDialog(String text) {
252
254
		if (text == null) {
253
255
			throw new NullPointerException("The given text/question was a null pointer.");
254
256
		}
255
257
		final int ACCEPTED = 0;
256
258
		//final int DENIED = 1; // Not used in the current context.
257
259
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
258
260
		if (result == ACCEPTED) {
259
261
			return true;
260
262
		}
261
263
		else {
262
264
			return false;
263
265
		}
264
266
	}
265
267
266
268
	/**
267
269
	 * Give the user a choice dialog box.
268
270
	 * This method gives the user a simple dialog with predefined choices.
269
271
	 * These choices are to be provided by the caller in a simple array.
270
272
	 *
271
273
	 * Tip: This method works extremely well with arbitrary created choices.
272
274
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
273
275
	 * then that value is immediately returned.
274
276
	 * @param text The text/question to be asked to the user.
275
277
	 * @param choices An array of Strings, containing the choices the user can
276
278
	 * pick.
277
279
	 * @return The index value of the picked choice, or -1 if no choices were
278
280
	 * given.
279
281
	 * @throws NullPointerException if text is a null pointer.
280
282
	 */
281
283
	public int choiceDialog(String text, String[] choices) {
282
284
		if (text == null) {
283
285
			throw new NullPointerException("The given text/question was a null pointer.");
284
286
		}
285
287
		// First: handling the trivial cases:
286
288
		if (choices.length == 0) {
287
289
			return -1;
288
290
		}
289
291
		else if (choices.length == 1) {
290
292
			return 0;
291
293
		}
292
294
		int answer = JOptionPane.CLOSED_OPTION;
293
295
		// The dialog needs to be shown again until the user has made a possible
294
296
		// choice, i.e. Chickening out using the close button is not possible
295
297
		// (Because that returns CLOSED_OPTION).
296
298
		while (answer == JOptionPane.CLOSED_OPTION) {
297
299
				JOptionPane.showOptionDialog(
298
300
					null, // The parent component. May become the panel?
299
301
					text, // The text/question to describe the goal
300
302
					"Dialog", // The text in the title bar
301
303
					JOptionPane.DEFAULT_OPTION, // The kind of available options
302
304
					JOptionPane.QUESTION_MESSAGE, // The type of message
303
305
					null, // The icon to show
304
306
					choices, // The possible choices
305
307
					choices[0] // The standard choice
306
308
					);
307
309
		}
308
310
		return answer;
309
311
	}
310
312
		
311
313
312
314
	/**
313
315
	 * Creates a label in the GUI for interaction.
314
316
	 * This function offers a convenient way to create a label, that can be
315
317
	 * directly interacted with by the user. After creation, the label itself
316
318
	 * is returned to the caller, if he wishes to do something else with it.
317
319
	 * @param text The text that will be displayed in the label.
318
320
	 * @return The label that was created.
319
321
	 */
320
322
	public JLabel createLabel(String text) {
321
323
		JLabel label = new JLabel(text);
322
324
		this.addComponent(label);
323
325
		return label;
324
326
	}
325
327
326
328
	/**
327
329
	 * Adds a checkbox to the window.
328
330
	 * By providing a String, you can use this method to easily
329
331
	 * create a checkbox, and add it to the window. 
330
332
	 * @param text The text to put next to the checkbox.
331
333
	 * @return The checkbox that was created and added to the GUI.
332
334
	 */
333
335
	public JCheckBox createCheckbox(String text) {
334
336
		JCheckBox checkbox = new JCheckBox(text);
335
337
		this.addComponent(checkbox);
336
338
		return checkbox;
337
339
	}
338
340
339
341
	/**
340
342
	 * Adds radio buttons to the window.
341
343
	 * Given a list of Strings, this method will create the same amount of radio
342
344
	 * buttons.
343
345
	 *
344
346
	 * The radio buttons will silently be grouped in a ButtonGroup object,
345
347
	 * making them automatically disable each other, so only 1 radio button can
346
348
	 * be enabled. This ButtonGroup is immutable.
347
349
	 *
348
350
	 * If you need a mutable ButtonGroup, create your own, and use the 
349
351
	 * {@link #addComponent} method to add the radio buttons manually.
350
352
	 * @param text An array of Strings. The length of the array will determine
351
353
	 * the amount of radio buttons that will be created.
352
354
	 * @return An array of radio buttons, in the same order as text.
353
355
	 */
354
356
	public JRadioButton[] createRadioButtons(String text[]) {
355
357
		JRadioButton[] radioButtons = new JRadioButton[text.length];
356
358
		ButtonGroup buttonGroup = new ButtonGroup();
357
359
		for (int i=0; i<radioButtons.length; i++) {
358
360
			radioButtons[i] = new JRadioButton(text[i]);
359
361
			//radioButtons[i].setText(text[i]);
360
362
			buttonGroup.add(radioButtons[i]);
361
363
			this.addComponent(radioButtons[i]);
362
364
		}
363
365
364
366
		assert radioButtons.length == buttonGroup.getButtonCount() : "The amount of radio buttons ("+ radioButtons.length +") differs from the amount of buttons in buttonGroup ("+ buttonGroup.getButtonCount() +").";
365
367
		return radioButtons;
366
368
	}
367
369
368
370
	public JTextField createTextField(String text) {
369
371
		JTextField textField = new JTextField(text);
370
372
		this.addComponent(textField);
371
373
		return textField;
372
374
	}
373
375
374
376
	/**
375
377
	 * Adds a number spinner component to the GUI.
376
378
	 * This method allows the caller to create a so-called "spinner component"
377
379
	 * to the window. This spinner is an input box, in which only integers can
378
380
	 * be put.
379
381
	 *
380
382
	 * The caller can set a range, a start value, and a step size.
381
383
	 *
382
384
	 * The spinner created with this method may modify itself based on the
383
385
	 * parameters.
384
386
	 * For example: If the minimum and maximum value are equal, the spinner will
385
387
	 * be disabled.
386
388
	 *
387
389
	 * @param minimum The minimum value that can be selected.
388
390
	 * @param maximum The maximum value that can be selected.
389
391
	 * @param start The value that will initially be shown in the component.
390
392
	 * @param stepSize The step size when the user increases/decreases the
391
393
	 * value.
392
394
	 * @throws IllegalArgumentException if minimum is larger than maximum, 
393
395
	 * start is not in the range of the selectable values, or stepsize is not a
394
396
	 * natural number.
395
397
	 * @return The JSpinner that was added to the window.
396
398
	 */
397
399
	public JSpinner createSpinner(int minimum, int maximum, int start, int stepSize) {
398
400
		// As usual, we begin with checking the contract:
399
401
		if(minimum > maximum) {
400
402
			throw new IllegalArgumentException("The minimum value ("+ minimum +") was larger than the maximum value ("+ maximum +")");
401
403
		}
402
404
		// The "start ∉ [minimum, maximum]" is done by the SpinnerNumberModel
403
405
		// constructor, which will be constructed later.
404
406
		if(stepSize <= 0) { // stepSize ∉ ℕ¹ (In Belgium: ℕ₀)
405
407
			throw new IllegalArgumentException("The stepSize ("+ stepSize +") is not a natural number (excluding 0).");
406
408
		}
407
409
		// If the contract is valid, we can begin:
408
410
		/*
409
411
		 * I'd like to interject here, because this is a nice example of why
410
412
		 * JSugar was a good idea:
411
413
		 * If you want a spinner, you'll typically want an integer spinner. But
412
414
		 * in Swing, when you create a JSpinner, it creates a JSpinner, with a
413
415
		 * predefined 'SpinnerNumberModel' attached to it.
414
416
		 * It's this model you then have to extract from the created spinner, on
415
417
		 * which you need to apply the configuration.
416
418
		 * What you actually have to do, is create a SpinnerNumberModel
417
419
		 * yourself, put your settings on that, and then, create a JSpinner to
418
420
		 * which you give that SpinnerNumberModel.
419
421
		 * In essence: The entire Java framework is shit.
420
422
		 */
421
423
		SpinnerNumberModel spinnerSettings = new SpinnerNumberModel(
422
424
				start,
423
425
				minimum,
424
426
				maximum,
425
427
				stepSize
426
428
				);
427
429
		JSpinner spinner = new JSpinner(spinnerSettings);
428
430
		if(minimum == maximum) { // Trivial value is set already, --> disable.
429
431
			spinner.setEnabled(false);
430
432
		}
431
433
		this.addComponent(spinner);
432
434
		return spinner;
433
435
	}
434
436
435
437
	/**
436
438
	 * Adds a number spinner component to the GUI.
437
439
	 * This method allows the caller to create a so-called "spinner component"
438
440
	 * to the window. This spinner is an input box, in which only integers can
439
441
	 * be put.
440
442
	 * 
441
443
	 * Tip: This method is a convenience method, and works extremely well with
442
444
	 * arbitrary data.
443
445
	 * For example: The start value is automatically set to the minimal possible
444
446
	 * value, and the step size defaults to 1.
445
447
	 * If the minimum and maximum are equal, the component will be disabled, and
446
448
	 * thus, be locked on the only (trivially) possible value.
447
449
	 * If minimum is larger than maximum, the method will notify you of this,
448
450
	 * but also swap the values. So you can rest assured that the spinner will
449
451
	 * handle errors, but also, inform you about it.
450
452
	 * @param minimum The minimum value that can be selected.
451
453
	 * @param maximum The maximum value that can be selected.
452
454
	 * @return The JSpinner component that was added to the window.
453
455
	 */
454
456
	public JSpinner createSpinner(int minimum, int maximum) {
455
457
		// The disabling of equal values is done in the full createSpinner(), so
456
458
		// this is merely switching values if they need to be swapped.
457
459
		if(minimum > maximum) {
458
460
			System.err.println("minimum ("+ minimum +") was larger than maximum ("+ maximum +").");
459
461
			// FIXME: Consider whether it's appropriate to print a stacktrace
460
462
			// here, which may be convenient for debugging.
461
463
			
462
464
			// XXX: I know you don't need the help variable when swapping
463
465
			// integers, because you can also do basic arithmetics. Change it if
464
466
			// it causes too much eye burn for you.
465
467
			int swapValue = minimum;
466
468
			minimum = maximum;
467
469
			maximum = swapValue;
468
470
		}
469
471
470
472
		// Yeah, these 2 variables make you cringe huh, performance addicts?
471
473
		// Drown me in the tears of your useless performance-related opinions.
472
474
		int startValue = minimum;
473
475
		int stepSize = 1;
474
476
		return this.createSpinner(minimum, maximum, startValue, stepSize);
475
477
	}
476
478
477
479
	/**
478
480
	 * Adds a combobox to the GUI.
479
481
	 * Allows the caller to create a combobox by providing the values that
480
482
	 * should be put in it.
481
483
	 *
482
484
	 * This method can only be used for String values. If that is not what you
483
485
	 * need, consider creating your own combobox and adding it manually. Or, if
484
486
	 * you need a combobox for integers, consider {@link #createSpinner}.
485
487
	 *
486
488
	 * WARNING: {@link JComboBox#getSelectedItem} returns an object, not a
487
489
	 * String. You need to manually typecast this. This is a constraint of the
488
490
	 * Swing framework.
489
491
	 * @param items An array of Strings that will be put in the combobox.
490
492
	 * @throws NullPointerException if one of the values in items is a null
491
493
	 * pointer.
492
494
	 * @throws IllegalArgumentException if items is empty.
493
495
	 * @return The JCombobox that was added to the window.
494
496
	 */
495
497
	public JComboBox<String> addComboBox(String[] items) {
496
498
		// Contract validation:
497
499
		if(items.length == 0) {
498
500
			throw new IllegalArgumentException("The given array of items was empty.");
499
501
		}
500
502
		for(String item : items) {
501
503
			if(item == null) {
502
504
				throw new NullPointerException("One of the given Strings is a null pointer.");
503
505
			}
504
506
		}
505
507
		// Contract validated, create the component:
506
508
		JComboBox<String> comboBox = new JComboBox<String>(items);
507
509
		comboBox.setSelectedIndex(0);
508
510
		if(comboBox.getItemCount() == 1) { // Trivial selection
509
511
			comboBox.setEnabled(false);
510
512
		}
511
513
		this.addComponent(comboBox);
512
514
		return comboBox;
513
515
	}
514
516
515
517
	/**
516
518
	 * Creates a list of the given data, and adds it to the GUI.
517
519
	 * This will create a JList component, containing the given data. 
518
520
	 * To jar up your memory: A list in this context, is a component in which
519
521
	 * data of the same type is printed out. The user of said list, can then
520
522
	 * select a subset of these items.
521
523
	 *
522
524
	 * @see JList for a collection of possible operations.
523
525
	 * @param items The String items that will be put in the list.
524
526
	 * @throws NullPointerException if one of the values in items is a null
525
527
	 * pointer.
526
528
	 * @throws IllegalArgumentException if items is empty.
527
529
	 * @return A JList component, that was a added to the GUI.
528
530
	 */
529
531
	public JList createList(String[] items) {
530
532
		// Contract validation:
531
533
		if(items.length == 0) {
532
534
			throw new IllegalArgumentException("The given array of items was empty.");
533
535
		}
534
536
		for(String item : items) {
535
537
			if(item == null) {
536
538
				throw new NullPointerException("One of the given Strings is a null pointer.");
537
539
			}
538
540
		}
539
541
		// Contract validated, create the component:
540
542
		JList list = new JList(items);
541
543
		this.addComponent(list);
542
544
		return list;
543
545
	}
544
546
545
547
	/**
546
548
	 * Creates a table of the given data, and adds it to the GUI.
547
549
	 * This method allows you to create a table with sorting functionality in
548
550
	 * the GUI.
549
551
	 * This method relies on implications, deducted from the given data. That
550
552
	 * is, the length of the rows and columns is calculated by the longest
551
553
	 * length it can find in the nested array.
552
554
	 * To change the data, take a look at the JTable documentation.
553
555
	 *
554
556
	 * @see JTable for a collection of possible operations.
555
557
	 * @param items The String items that will be put in the list.
556
558
	 * @throws NullPointerException if one of the values in items is a null
557
559
	 * pointer.
558
560
	 * @throws IllegalArgumentException if items is empty, or the amount of
559
561
	 * column names does not correspond with the given amount of items.
560
562
	 * @return A JTable component, that was a added to the GUI.
561
563
	 */
562
564
	public JTable createTable(String[] columns, String[][] items) {
563
565
		// Contract validation:
564
566
		if(items.length == 0) {
565
567
			throw new IllegalArgumentException("The given array of items was empty.");
566
568
		}
567
569
		if(columns.length != items[0].length) {
568
570
			throw new IllegalArgumentException("The amount of columns does not correspond to the given amount of items.");
569
571
		}
570
572
		for(int i=0; i<items.length; i++) {
571
573
			if(items[i] == null) {
572
574
				throw new NullPointerException("One of the given Strings is a null pointer.");
573
575
			}
574
576
		}
575
577
		// Contract validated, create the component:
576
578
		// Deducting max length:
577
579
		int columnCount = columns.length;
578
580
		int rowCount = items.length;
579
581
		
580
582
		JTable table = new JTable(items, columns);
581
583
		this.addComponent(table);
582
584
		return table;
583
585
	}
584
586
585
587
	/**
586
588
	 * Adds the given component to the GUI.
587
589
	 * This method allows its caller to give a pre-made component, so that it
588
590
	 * can be added to the GUI. Even though its main use is for the Window class
589
591
	 * itself, the user of JSugar can also use it to create components himself,
590
592
	 * and then add them. As such, this method doesn't provide parameters for
591
593
	 * reflection/action triggering purposes.
592
594
	 * @param component The component to be added to the window.
593
595
	 * @throws NullPointerException if the given component is a null pointer.
594
596
	 */
595
597
	public void addComponent(JComponent component) {
596
598
		int originalSize = this.panel.getComponentCount();
597
599
		this.panel.add(component); // Throws the exception if null.
598
600
		this.updateWindow();
599
601
600
602
		assert originalSize == this.panel.getComponentCount()-1 : "A component was supposed to be added to the window, but the total amount of components was unchanged after the addition.";
601
603
	}
602
604
603
605
	/**
604
606
	 * Removes the given component from the GUI.
605
607
	 * This method allows its caller to remove a component from the GUI.
606
608
	 * @param component The component to be removed.
607
609
	 * @throws NoSuchElementException if the given component does not exist in
608
610
	 * the GUI.
609
611
	 * @throws NullPointerException if the given component is a null pointer.
610
612
	 */
611
613
	public void removeComponent(JComponent component) {
612
614
		int originalSize = this.panel.getComponentCount();
613
615
		this.panel.remove(component);
614
616
		int newSize = this.panel.getComponentCount();
615
617
		if (originalSize != newSize+1) {
616
618
			throw new NoSuchElementException("The given component does not exist in the GUI.");
617
619
		}
618
620
		this.updateWindow();
619
621
	}
620
622
	/**
621
623
	 * Prompts the user with a file chooser dialog.
622
624
	 * By calling this method, the user will be presented with a file chooser
623
625
	 * dialog, out of which a single file can be selected. If the selected file
624
626
	 * exists, a File object is returned, a null pointer if the user cancelled.
625
627
	 * @return A File object representing the file the user selected, or null
626
628
	 * otherwise.
627
629
	 */
628
630
	public File openFileChooserDialog() {
629
631
		JFileChooser fileDialog = new JFileChooser();
630
632
		fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
631
633
632
634
		int userResponse = fileDialog.showOpenDialog(this.panel);
633
635
		if(userResponse == JFileChooser.APPROVE_OPTION) {
634
636
			return fileDialog.getSelectedFile();
635
637
		}
636
638
		else {
637
639
			return null;
638
640
		}
639
641
	}
640
642
}
+
643
	/** Prompts the user with a message dialog.
+
644
	 * This method creates a dialog that is purely informative. As such, it
+
645
	 * only presents the user with the option to confirm it.
+
646
	 * @param text The text to be presented in the dialog box.
+
647
	 * @pre text mustn't be null or an empty string.
+
648
	 * @throws NullPointerException if text is a null pointer.
+
649
	 * @throws IllegalArgumentException if text is an empty string.
+
650
	 */
+
651
	public void messageDialog(String text) {
+
652
		if(text.isEmpty())
+
653
			throw IllegalArgumentException("text mustn't be an empty string.");
+
654
		JOptionPane.showMessageDialog(this.frame, text);
+
655
	}
+
656
+
657
	/**
+
658
	 * Closes this window.
+
659
	 * This method tells this Window's JFrame that a closing event has been
+
660
	 * fired.
+
661
	 * Use this method when you need to programatically close this window.
+
662
	 */
+
663
	public void close() {
+
664
		this.frame.dispatchEvent(new WindowEvent(this.frame, WindowEvent.WINDOW_CLOSING));
+
665
	}
+
666
}
641
667

Challenge 6/ontwerpkeuzes2.md

3 additions and 0 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
+
15
  zijn deze vaak verplicht om van veel verschillende classes op de hoogte te
+
16
  zijn.
+
17
15
18
### Indirectie
16
19
- In plaats van functionaliteit toe te wijzen aan class X om met class Y te
17
20
  interageren, wordt er vaak gebruik gemaakt van een class Z, die deze
18
21
  verantwoordelijkheid op zich neemt.
19
22
- Dit wordt toegepast d.m.v. een MVC-structuur, waarin de \*Controller-classes
20
23
  als 'tussenpersoon' werken.
21
24
- Dit komt ook tegemoet aan de eigenschappen van Information Expertise.
22
25
23
26
### Creator
24
27
- Controllers staan ook in als zgn. Creators van de models die ze beheren. Ze
25
28
  voldoen dan ook aan de eigenschappen zoals
26
29
  [hier](https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Creator)
27
30
  opgesomd wordt.
28
31
29
32
30
33
## SOLID
31
34
32
35
### Single Responsibility
33
36
34
37
## Algemene keuzes ter bevordering codekwaliteit
35
38
36
39
### Null pointers
37
40
Het valt misschien op dat ik doorheen mijn programma in veel contracten eis dat
38
41
geen enkele parameter een zgn. *null pointer* is.
39
42
40
43
Het gebruik van null pointers staat garant voor een overvloed aan moeilijk te
41
44
vinden bugs die (door het design van objectgeörienteerd programmeren) enorm diep
42
45
kunnen doorpropageren.
43
46
44
47
Ik maak er een kerntaak van dat, als er aan mijn programma gewerkt wordt, de
45
48
programmeur zichzelf steeds kan garanderen dat **alle** data die hij
46
49
ontvangt, valide data is.
47
50
Op deze manier valt er een hele last van de schouders van de programmeur; een
48
51
reeks fouten worden voorkomen, simpelweg door een strikt schema aan te houden.
49
52
50
53
Het controleren op null pointers wordt op 2 manieren gedaan:
51
54
- Gebruik van *methods* aanwezig in het gegeven type. Als de gegeven variabele
52
55
  een null pointer is, zal het programma direct crashen, en een
53
56
  NullPointerException geven.
54
57
- Expliciet testen of *var == null*. Wordt toegepast op parameters die direct
55
58
  als members opgeslagen dienen te worden.
56
59
57
60
Deze (contractuele) controle laat toe dat, mocht er een null pointer gebruikt
58
61
worden, het programma de programmeur hiervan direct op de hoogte stelt, en dit
59
62
op het laagst mogelijke niveau (namelijk de eerste method waar deze waarde
60
63
gebruikt wordt).
61
64
62
65
### Cloning
63
66
members 'private' maken (encapsuleren) is volledig nutteloos als men getters en
64
67
setters op deze members toepast; In Java worden references doorgegeven (m.u.v.
65
68
primitives), die de hele notie van encapsulatie voorbijgaan (bij sommige types).
66
69
Een voorbeeld hiervan is het privatiseren van een Set<T>-member: men kan daar
67
70
een 'getSet()'-method op plaatsen, en dan toch de inhoud van deze 'private'
68
71
aanpassen.
69
72
70
73
Ik heb geopteerd om, waar van toepassing, deze variabelen te 'clonen', om zo
71
74
exacte kopieën terug te geven.
72
75
Deze manier van werken brengt enkele voordelen teweeg:
73
76
- Zeer defensief programmeren; De ontwikkelaar kan geen members aanpassen als
74
77
  dat niet de bedoeling was
75
78
- Duidelijkheid code: getSet().clear() zal de member niet meer leegmaken. Om dat
76
79
  te doen, moet men gebruikmaken van de method die daarvoor bedoeld is:
77
80
  setSet(clearedSet)
78
81
79
82
### Inheritance
80
83
Overerving is een goed concept over het algemeen, maar **niet** in OOP.
81
84
De problemen omtrent impliciet gedrag en onnodige *state* zijn al te vaak
82
85
beschreven met OOP-inheritance.
83
86
84
87
Ik heb in mijn programma geen gebruik gemaakt van inheritance, exact omwille van
85
88
de problemen die het voortbrengt, zeker in termen van herbruikbaarheid en
86
89
robuustheid, wat toch zware vereisten waren voor deze opdracht.
87
90
88
91
Ik heb al mijn problemen makkelijk kunnen oplossen d.m.v. compositie.
89
92
90
93
### Benaming variabelen
91
94
Doorheen mijn programma maak ik heel veel gebruik van dezelfde benamingen.
92
95
Bijvoorbeeld: Een variabele van het type Reservation zal haast altijd
93
96
'reservation' heten, een Set van een bepaald type zal de naam van datzelfde type
94
97
dragen, in het meervoud, ...
95
98
96
99
Sommige programmeurs gebruiken liever afkortingen (bv. 'reservation' -->
97
100
'resv'), omdat dit sneller schrijft.
98
101
99
102
Naar mijn mening moet men bij deze werkwijze inleveren aan leesbaarheid, vooral
100
103
wanneer iemand die nog nooit met de code gewerkt heeft, dit programma moet
101
104
overnemen.
102
105
103
106
Daarnaast zorgt de consistentie van woordgebruik ervoor dat een andere
104
107
programmeur, doorheen het hele programma dezelfde context in zijn/haar gedachten
105
108
kan gebruiken.
106
109
107
110
108
111
109
112
110
113
------------------------------------------------------------ TODO
111
114
## Toepassing types/classes
112
115
Doorheen het programma heb ik getracht zoveel mogelijk gebruik te maken van
113
116
types/classes die
114
117
- Veelvoorkomend zijn in de Java STL (Zoals String, Set, List, ...)
115
118
- primitief zijn (ints, ...), omdat deze operatoren hebben en de code
116
119
  leesbaarder maken
117
120
118
121
Een goed voorbeeld hiervan zijn bv. de faciliteiten:
119
122
I.p.v. een aparte "Facility"-class te maken, heb ik de verschillende
120
123
faciliteiten voorgesteld door een simpele String. De voordelen van deze aanpak
121
124
zijn ook direct duidelijk:
122
125
- Betekenis is direct duidelijk; de faciliteit letterlijk in de code vernoemd
123
126
- Makkelijke interactie met GUI, die sowieso Strings vraagt voor bv. JLabel
124
127
- Uitbreidbaarheid wordt bekomen door simpelweg een nieuwe String te
125
128
  introduceren
126
129
127
130
## View en GUI
128
131
Werken met GUI's is vaak tijdrovend en veroorzaakt snel errors, zeker met bv.
129
132
anonieme methods, exceptions, ...
130
133
Alhoewel mijn programma grotendeels in een MVC-stijl is geschreven, maken de
131
134
view-classes (RegistrationView, SearchView, ...) achterliggend gebruik van een
132
135
zelfgemaakt framework om makkelijk vensters te maken.
133
136
Dit kleine framework is een persoonlijk hobbyproject dat ik JSugar noem.
134
137
Het biedt een heleboel voordelen, vergeleken met elk GUI-venster zelf opstellen:
135
138
- Vaak gebruikte GUI-widgets (zoals een label, textfield) worden aangemaakt en
136
139
  toegevoegd door slechts 1 method op te roepen
137
140
- JSugar maakt gebruik van reflectie om op een leesbare en uitbreidbare manier
138
141
  knoppen te activeren:
139
142
  public JButton createButton(String text, String action, String methodName, Object triggerObject)
140
143
  'methodName' is een simpele String, en 'triggerObject' is het object waar deze
141
144
  method moet worden opgeroepen.
142
145
- Automatische uitlijning van widgets
143
146
Voor meer informatie kunt u JSugar-readme.md raadplegen.
144
147
145
148
## java.util.Date
146
149
147
150
Doorheen het programma maak ik vaak gebruik van de Date-class uit de Java STL,
148
151
om de volgende redenen:
149
152
- Uitbreidbaarheid: Door overal eenzelfde type te gebruiken, is het gemakkelijk
150
153
  om nieuwe modules toe te voegen, zonder dat daarvoor abstractielagen etc.
151
154
  nodig zijn.
152
155
- Uniformiteit: Eenzelfde type uit de STL laat de ontwikkelaar toe om door het
153
156
  hele programma heen hetzelfde denkpatroon aan te houden; een
154
157
  Stringvoorstelling hier, een integer daar, ... veroorzaken problemen en bugs,
155
158
  die met deze class voorkomen worden.
156
159
157
160
## Bedden
158
161
159
162
Ik heb voor bedden een aparte class aangemaakt, omdat deze een bepaalde state
160
163
bijhouden, nml. wanneer ze gereserveerd zijn.
161
164
162
165
163
166
## Reservation
164
167
Voor de reservaties heb ik besloten om enkel die data bij te houden die inherent
165
168
gerelateerd is aan die reservatie:
166
169
- Enkel gereserveerde bedden i.p.v. gereserveerde kamers. Qua uitbreidbaarheid
167
170
  heeft dit tot gevolg dat men gemakkelijk kan uitbreiden naar reservaties,
168
171
  gespreid over verschillende kamers.
169
172
170
173
171
174
## ReservationView
172
175
Merk op hoe in ReservationView de data van de Reservation direct in de GUI wordt
173
176
geplaatst.
174
177
Dit staat toe om zeer gemakkelijk deze class te hergebruiken voor zowel het
175
178
**aanmaken** als het **updaten** van de reservatie.
176
179