OOP2

A lot of general refactoring on Challenge 6. Also added a new design choices document, which will be constructed as I go.

Author
Vngngdn
Date
Nov. 24, 2016, 8:45 p.m.
Hash
e71d5ec3ad9dcfde04b78d5772529e5d0f0ff601
Parent
e58905458bfca974a57ba52df51b22014fa68ec8
Modified files
Challenge 6/Bed.java
Challenge 6/Reservation.java
Challenge 6/ReservationController.java
Challenge 6/Room.java
Challenge 6/RoomController.java
Challenge 6/ontwerpkeuzes2.md

Challenge 6/Bed.java

8 additions and 0 deletions.

View changes Hide changes
1
1
import java.util.Map;
2
2
import java.util.HashMap;
3
3
4
4
/**
5
5
 * Class representing a bed in the hostel.
6
6
 * Even though beds in a hostel could simply be an integer in a Room, these Beds
7
7
 * need to remember on what dates they are reserved.
8
8
 * @author Maarten Vangeneugden - 1438256
9
9
 */
10
10
public class Bed {
11
11
12
12
	/* Rationale:
13
13
	 * I know that a mapping is not necessarily the best solution for storing
14
14
	 * periods, because it doesn't offer built-in protection yadda yadda.
15
15
	 * However, that's Java's fault, as it doesn't provide a Pair<L,R> thing in
16
16
	 * the STL.
17
17
	 * The best solution is actually a collection of tuples: (Begin, End), but
18
18
	 * that doesn't exist in Java, and thus requires a custom implementation.
19
19
	 * All things considered, a Map is the least bad choice.
20
20
	 */
21
21
	private Map<Date, Date> reservedPeriods;
22
22
23
23
	public Bed() {
24
24
		this.reservedPeriods = new HashMap<>();
25
25
	}
26
26
27
27
	/**
28
28
	 * Reserves this Bed for the given period.
29
29
	 * This method will mark the given period for this Bed as "reserved", which
30
30
	 * can then be cancelled or queried later.
31
31
	 * @param begin The time where the reservation begins.
32
32
	 * @param end The time where the reservation ends.
33
33
	 * @pre No parameter may be a null pointer.
34
34
	 * @pre The given period mustn't overlap with a period already marked as
35
35
	 * reserved.
36
36
	 * @pre begin must come before end.
37
37
	 * @pre begin and end mustn't be equal.
38
38
	 * @post The given period will be marked as "Reserved", and will have to be
39
39
	 * manually cancelled/removed in order to reserve again.
40
40
	 * @throws NullPointerException if any parameter is a null pointer.
41
41
	 * @throws IllegalArgumentException if begin is equal to, or comes after
42
42
	 * end.
43
43
	 * @throws DateTimeException if the given period overlaps with an already
44
44
	 * reserved period.
45
45
	 */
46
46
	public void reservePeriod(Date begin, Date end) {
47
47
		if(!begin.before(end)) {
48
48
			throw IllegalArgumentException("The begin date occurs after the end date.");
49
49
		}
50
50
		if(!this.isFree(begin, end)) {
51
51
			throw DateTimeException("This period overlaps with a reserved period.");
52
52
		}
53
53
		// Contract validated, execute method
54
54
		this.reservedPeriods.put(begin, end);
55
55
	}
56
56
57
57
	/**
58
58
	 * Remove a previous registration from this Bed.
59
59
	 * This method will remove/cancel the given reservation period from the
60
60
	 * Bed, opening it up for reservation again.
61
61
	 * @param begin The time where the reservation begins.
62
62
	 * @param end The time where the reservation ends.
63
63
	 * @pre No parameter may be a null pointer.
64
64
	 * @pre The given period must already be marked as "Reserved" in this Bed.
65
65
	 * @pre begin must come before end.
66
66
	 * @pre begin and end mustn't be equal.
67
67
	 * @post The given period will lose its "Reserved" mark, allowing the period
68
68
	 * to be reserved again.
69
69
	 * @throws NullPointerException if any parameter is a null pointer.
70
70
	 * @throws IllegalArgumentException if begin is equal to, or comes after
71
71
	 * end; or, if the given period is not reserved.
72
72
	 */
73
73
	public void removeReservationPeriod(Date begin, Date end) {
74
74
		if(!begin.before(end)) {
75
75
			throw IllegalArgumentException("The begin date occurs after the end date.");
76
76
		}
77
77
		if(!this.isFree(begin, end)) {
78
78
			throw DateTimeException("This period overlaps with a reserved period.");
79
79
		}
80
80
		// Contract partially validated; further validation occurs while looking
81
81
		// for the reservation.
82
82
		// XXX: Check if Java correctly handles equality: 2 different Date
83
83
		// objects with the same date representation should be equal!
84
84
		boolean reservationFound = this.reservedPeriods.remove(begin, end);
85
85
		if(!reservationFound) {
86
86
			throw IllegalArgumentException("The given period was not marked as reserved.");
87
87
		}
88
88
	}
89
89
		
90
90
	/**
91
91
	 * Checks whether this Bed can be reserved in the given period.
92
92
	 * Use this method whenever you need to inform yourself about any
93
93
	 * conflicting reservation period.
94
94
	 * @param begin The time where the reservation begins.
95
95
	 * @param end The time where the reservation ends.
96
96
	 * @pre No parameter must be a null pointer.
97
97
	 * @pre begin must come before end.
98
98
	 * @pre begin and end mustn't be equal.
99
99
	 * @throws NullPointerException if any parameter is a null pointer.
100
100
	 * @throws IllegalArgumentException if begin is equal to, or comes after
101
101
	 * end.
102
102
	 * @return True if the given period does not overlap with any reservation, false otherwise.
103
103
	 */
104
104
	public boolean isFree(Date begin, Date end) {
105
105
		if(!begin.before(end)) {
106
106
			throw IllegalArgumentException("The begin date occurs after the end date.");
107
107
		}
108
108
		if(!this.isFree(begin, end)) {
109
109
			throw DateTimeException("This period overlaps with a reserved period.");
110
110
		}
111
111
		// Contract validated, execute method
112
112
		for(Map.Entry<Date, Date> reservedPeriod: this.reservedPeriods.EntrySet()) {
113
113
			Date reservedBegin = reservedPeriod.key();
114
114
			Date reservedEnd = reservedPeriod.value();
115
115
			/* Forbidden possibilities:
116
116
			 * (A,B = reserved; X,Y = requested)
117
117
			 * X-A-Y-B / A-X-B-Y  -  Begins or ends in a reserved period
118
118
			 * X-A-B-Y  -  Complete overlapping of reserved period
119
119
			 * Allowed possibilities:
120
120
			 * A-B-X-Y / X-Y-A-B  -  No overlapping
121
121
			 */
122
122
			if((begin.after(reservedBegin) && begin.before(reservedEnd)) ||
123
123
			   (end.after(reservedBegin) && end.before(reservedEnd))) {
124
124
				// Triggered if any forbidden structure is detected
125
125
				return false;
126
126
			}
127
127
		}
128
128
		return true; // No overlapping found
129
129
	}
130
130
}
+
131
	/**
+
132
	 * Checks whether this Bed has open reservation periods.
+
133
	 * @return True if this Bed has reserved periods, false otherwise.
+
134
	 */
+
135
	public boolean hasReservations() {
+
136
		return !this.reservedPeriods.isEmpty();
+
137
	}
+
138
}
131
139

Challenge 6/Reservation.java

34 additions and 28 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
 * @author Maarten Vangeneugden - 1438256
11
11
 */
12
12
public class Reservation {
13
13
14
14
	// Members
15
15
	private String groupName;
16
16
	private Date beginDate;
17
-
	private Date endDate;
18
-
	private Set<Bed> reservedBeds;
+
17
	private Date end;
+
18
	private Set<Bed> reservedBeds;
19
19
	private int reservationID;
20
20
	private String roomType; // Room type requested by group
21
21
	private Set<String> roomFacilities; // Requested room facilities
22
22
	private Set<Date> breakfastDays; // Set of days the group wants breakfast
23
23
	// Controllers
24
24
	private RoomController roomController;
25
25
	private ReservationController reservationController;
26
26
27
27
	/**
28
28
	 * Create a new Reservation.
29
29
	 * Be aware about the limitations of a Reservation: it's not the
30
30
	 * Reservation's duty to check whether the provided Beds are properly
31
31
	 * reserved; take care of this accordingly.
32
32
	 * 
33
33
	 * Some information is implicit. For example: The size of the set of
34
34
	 * reserved Beds implies the amount of people in the group; no breakfast
35
35
	 * days implies no breakfast, ...
36
36
	 * @param groupName The name of the group.
37
37
	 * @param begin The date that the Reservation begins.
38
38
	 * @param end The date that the Reservation ends.
39
39
	 * @param reservedBeds The set of Beds reserved and assigned to this
40
40
	 * Reservation.
41
41
	 * @param roomType The requested room type.
42
42
	 * @param roomFacilities The set of all requested room facilities.
43
43
	 * @param breakfastDays A set of all days that the group wants breakfast.
44
44
	 * @param reservationID An ID for this reservation, to differentiate from
45
45
	 * other Reservations.
46
46
	 * @pre No parameter must be a null pointer.
47
47
	 * @pre begin must come before end.
48
48
	 * @pre All dates in breakfastDays must fall between begin and end.
49
49
	 * @pre No string parameter may be empty.
50
50
	 * @post The amount of people in the Reservation is determined by the amount
51
51
	 * of reserved Beds.
52
52
	 * @throws NullPointerException if any parameter is a null pointer.
53
53
	 * @throws IllegalArgumentException if any of the other preconditions is not
54
54
	 * met.
55
55
	 */
56
56
	public Reservation(String groupName, Date begin, Date end, Set<Bed> reservedBeds, String roomType, Set<String> roomFacilities) {
57
57
		// Contract validation:
58
58
		if(!begin.before(end)) {
59
59
			throw IllegalArgumentException("The begin date occurs after the end date.");
60
60
		}
61
61
		if(groupName.isEmpty() || roomType.isEmpty()) {
62
62
			throw IllegalArgumentException("groupName and/or roomType were empty strings.");
63
63
		}
64
64
		for(String roomFacility: roomFacilities) {
65
65
			if(roomFacility.isEmpty()) {
66
66
				throw IllegalArgumentException("One of the room facilities was an empty string.");
67
67
			}
68
68
		}
69
69
		for(Date breakfastDay : breakfastDays) {
70
70
			if(breakfastDay.before(begin) || breakfastDay.after(end)) {
71
71
				throw IllegalArgumentException("One of the breakfast days occurs before/after the reservation period.");
72
72
			}
73
73
		}
74
74
		// Contract validated, execute constructor
75
75
		this.groupName = groupName;
76
76
		this.beginDate = begin;
77
77
		this.endDate = end;
78
78
		this.reservedBeds = reservedBeds;
79
79
		this.reservationID = reservationID;
80
80
		this.roomType = roomType;
81
81
		this.roomFacilities = roomFacilities;
82
82
		this.breakfastDays = breakfastDays
83
83
	}
84
84
85
85
	/**
86
86
	 * Creates an empty Reservation.
87
-
	 * This constructor is espcially useful for adding a new reservation.
88
-
	 */
+
87
	 * @param groupName The new group name.
+
88
	 * @pre groupName mustn't be empty, or a null pointer.
+
89
	 * @post The group name is changed to the given name.
+
90
	 * @throws NullPointerException if groupName is a null pointer.
+
91
	 * @throws IllegalArgumentException if groupName is an empty String.
+
92
	 */
89
93
	public Reservation(ReservationController reservationController, RoomController roomController) {
90
-
		this(
91
-
				"",
92
-
				new Date(),
93
-
				new HashSet<>(),
94
-
				"",
95
-
				new HashSet<>(),
96
-
				1,
97
-
				new int[0],
98
-
				reservationController,
99
-
				roomController
100
-
				);
101
-
	}
102
-
103
-
	public void setGroupName(String groupName) {
104
94
		this.groupName = groupName;
+
95
			throw IllegalArgumentException("groupName is an empty String.");
+
96
		this.groupName = groupName;
105
97
	}
106
98
107
99
	public String getGroupName() {
108
100
		return groupName;
109
101
	}
110
102
111
103
	public void setDate(Date date) {
112
-
		this.date = date;
113
-
	}
+
104
	 * Set the begin date for this Reservation.
+
105
	 * @param begin The new begin date.
+
106
	 * @pre begin mustn't be a null pointer.
+
107
	 * @pre begin must come strictly before the end date.
+
108
	 * @post The begin date is updated accordingly.
+
109
	 * @throws NullPointerException if begin is a null pointer.
+
110
	 * @throws IllegalArgumentException if begin comes after the end date.
+
111
	 */
+
112
	public void setBegin(Date begin) {
+
113
		if(!begin.before(this.getEnd()))
+
114
			throw IllegalArgumentException("begin comes after the end date.")
+
115
		this.begin = begin;
+
116
	}
114
117
115
118
	public Date getDate() {
+
119
+
120
	/**
+
121
	 * Set the end date for this Reservation.
+
122
	 * @param end The new end date.
+
123
	 * @pre end mustn't be a null pointer.
+
124
	 * @pre end must come strictly after the begin date.
+
125
	 * @post The end date is updated accordingly.
+
126
	 * @throws NullPointerException if end is a null pointer.
+
127
	 * @throws IllegalArgumentException if end comes after the end date.
+
128
	 */
+
129
	public Date getDate() {
116
130
		return date;
117
131
	}
118
132
119
133
	public void setReservedBeds(Set<Bed> reservedBeds) {
120
134
		this.reservedBeds = reservedBeds;
121
135
	}
122
136
123
137
	public Set<Bed> getReservedBeds() {
124
138
		return reservedBeds;
125
139
	}
126
140
127
141
	public void setReservationID(int reservationID) {
128
142
		this.reservationID = reservationID;
129
143
	}
130
144
131
145
	public int getReservationID() {
132
146
		return reservationID;
133
147
	}
134
148
135
149
	public void setRoomType(String roomType) {
136
150
		this.roomType = roomType;
137
151
	}
138
152
139
153
	public String getRoomType() {
140
154
		return roomType;
141
155
	}
142
156
143
157
	public void setRoomFacilities(Set<String> roomFacilities) {
144
158
		this.roomFacilities = roomFacilities;
145
159
	}
146
160
147
161
	public Set<String> getRoomFacilities() {
148
162
		return roomFacilities;
149
163
	}
150
164
151
165
	public void setNights(int nights) {
152
-
		this.nights = nights;
153
-
	}
154
-
155
-
	public int getNights() {
156
-
		return nights;
157
-
	}
158
-
159
-
	/**
160
166
	 * Calculates the price of the Reservation, based on its current state.
161
167
	 * Price table:
162
168
	 * - 20/person/day
163
169
	 *   - 5 less in low season, 5 more in high season
164
170
	 * - 4/breakfast ordered
165
171
	 * - If room is fully booked by the group --> 10% discount
166
172
	 * @return The price in euros.
167
173
	 */
168
174
	public int getPrice() {
169
175
		int totalPrice = 0;
170
176
		// Jan - Apr: Mid
171
177
		// May - Aug: High
172
178
		// Sep - Dec: Low
173
179
		
174
180
		// Calculate bed prices
175
181
		int month = this.getDate().getMonth();
176
182
		int bedPrice = 20;
177
183
		if(month >=8) { // From September:
178
184
			bedPrice -= 5;
179
185
		} else if(month >=4) { // From May:
180
186
			bedPrice += 5;
181
187
		}
182
188
		totalPrice += (this.getReservedBeds().size() * this.getNights() * bedPrice);
183
189
		// Calculate price for breakfasts
184
190
		int breakfasts = this.getBreakfastDays().length;
185
191
		totalPrice += breakfasts * this.getReservedBeds().size();
186
192
		// Check if eligible for discount
187
193
		for(Room room: roomController.getRooms()) {
188
194
			Set<Bed> roomBeds = room.getBeds();
189
195
			if(roomBeds.containsAll(this.reservedBeds)) {
190
196
				double discount = (double)totalPrice * 0.1;
191
197
				totalPrice -= (int)discount;
192
198
			}
193
199
		}
194
200
		return totalPrice;
195
201
	}
196
202
197
203
198
204
199
205
200
206
201
207
202
208
203
209
	public void setBreakfastDays(int[] breakfastDays) {
204
210
		this.breakfastDays = breakfastDays;
205
211
	}
206
212
	public int[] getBreakfastDays() {
207
213
		return this.breakfastDays;
208
214
	}
209
215
210
216
}
211
217

Challenge 6/ReservationController.java

105 additions and 15 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
	private Set<Reservation> reservations;
17
-
+
17
	 * Normally I'd put this as an interface (Set), but an interface does not
+
18
	 * inherit from Object, and thus, does not provide clone(). This is
+
19
	 * necessary, because otherwise, there's no point in having private
+
20
	 * members.
+
21
	 */
+
22
	private HashSet<Reservation> reservations; // Holds all Reservations
+
23
18
24
	public ReservationController() {
+
25
	 * Creates the ReservationController.
+
26
	 */
+
27
	public ReservationController() {
19
28
		this.reservations = new HashSet<>();
20
29
	}
21
30
22
31
	public void setReservations(Set<Reservation> reservations) {
23
-
		this.reservations = reservations;
24
-
	}
25
-
26
-
	public Set<Reservation> getReservations() {
+
32
	 * Returns a copy of all Reservations.
+
33
	 * Emphasis on "copy"; There is no setReservations() for a reason, using
+
34
	 * this to edit the pointer variable would omit the use of 'private'.
+
35
	 * @return A verbatim copy of all Reservations.
+
36
	 */
+
37
	@SuppressWarnings("unchecked")
+
38
	public Set<Reservation> getReservations() {
27
39
		return reservations;
28
-
	}
+
40
	}
29
41
30
42
	public int generateReservationID() {
+
43
	 * Generates a unique ID for a new Reservation.
+
44
	 * This method is to be called when a new Reservation is to be stored.
+
45
	 * As every Reservation carries an ID, this method searches for a unique
+
46
	 * one, and returns it.
+
47
	 * @return An integer, different from all other stored Reservation IDs.
+
48
	 */
+
49
	public int generateReservationID() {
31
50
		int ID = 0;
+
51
		 * Instead of starting from 0, and incrementing until it's unique, it
+
52
		 * will take the amount of stored Reservations, and test that as an ID.
+
53
		 * This may still overlap (by removing an older Reservation), but may be
+
54
		 * much faster. Consider implemting and testing for effectiveness if
+
55
		 * generating an ID takes too long.
+
56
		 */
+
57
		int ID = 0;
32
58
		while(true) {
33
-
			for(Reservation reservation: this.reservations) {
+
59
		do {
+
60
			ID++;
+
61
			isUnique = true;
+
62
			for(Reservation reservation: this.reservations) {
34
63
				if(reservation.getReservationID() == ID) {
35
-
					ID++;
36
-
					continue;
37
-
				}
+
64
					isUnique = false;
+
65
				}
38
66
			}
39
67
			break;
40
-
		}
41
-
		// Test:
+
68
		// Test:
42
69
		for(Reservation reservation: this.reservations) {
43
70
			assert reservation.getReservationID() != ID : "Duplicate ID generated!";
44
71
		}
45
72
		return ID;
46
73
	}
47
74
+
75
	/** 
+
76
	 * Check if Reservation can be made.
+
77
	 * Call this method whenever you're planning on adding a new Reservation. It
+
78
	 * will check the provided requirements (Room facilities, for example), and
+
79
	 * return whether this Reservation can continue.
+
80
	 * @param reservation The Reservation to check for possible addition.
+
81
	 * @pre reservation mustn't be null.
+
82
	 * @throws NullPointerException if reservation is null.
+
83
	 * @return True if reservation can be added, False otherwise.
+
84
	 */
+
85
	public boolean checkReservation(Reservation reservation) {
+
86
		if(reservation == null) {
+
87
			throw NullPointerException("reservation was a null pointer.");
+
88
		}
+
89
		if(this.getRoomController().getQualifiedRooms(reservation).size() == 0) {
+
90
			return null;
+
91
	}
+
92
48
93
	/**
49
94
	 * Adds and confirms the reservation.
50
95
	 * By calling this method, the given reservation will be stored in the
51
96
	 * system, and the given room will be filled.
52
97
	 * @pre room must have enough empty beds
53
-
	 * @pre no parameter may be null
54
-
	 */
+
98
	 * this Room! If there are not enough Beds available, an exception will be
+
99
	 * thrown.
+
100
	 * @param reservation The Reservation to add.
+
101
	 * @param room The Room in which the people will reside.
+
102
	 * @pre room must have enough empty beds.
+
103
	 * @pre room must accomodate the Reservation's requirements.
+
104
	 * @pre No parameter must be null.
+
105
	 * @post reservation is stored in the active Reservations.
+
106
	 * @post The Beds in the provided Room are reserved for the Reservation's
+
107
	 * period.
+
108
	 * @throws IllegalArgumentException if the given Room can't fulfill the
+
109
	 * requirements of the Reservation.
+
110
	 * @throws NullPointerException if any parameter is a null pointer.
+
111
	 */
55
112
	public void addReservation(Reservation reservation, Room room) {
56
113
		// TODO: Add reservation to the set, and add the reserved days to the
+
114
		
+
115
		// TODO: Add reservation to the set, and add the reserved days to the
57
116
		// beds in the given room.
58
117
	}
59
118
	public void cancelReservation(Reservation reservation) {
+
119
	 * Cancels and removes the given Reservation.
+
120
	 * If you want to remove a Reservation, use this method, and provide the
+
121
	 * Reservation up for removal.
+
122
	 * This method will take care of related actions, such as releasing Beds.
+
123
	 * @param reservation The Reservation to be removed.
+
124
	 * @pre reservation mustn't be null.
+
125
	 * @pre reservation must be contained in the active Reservations.
+
126
	 * @post The Reservation is removed from the active Reservations.
+
127
	 * @post The Beds, previously reserved for this Reservation, are now
+
128
	 * released, and can be reserved for another Reservation.
+
129
	 * @throws NullPointerException if reservation is a null pointer.
+
130
	 * @throws IllegalArgumentException if reservation is not contained in the
+
131
	 * active Reservations.
+
132
	 */
+
133
	public void cancelReservation(Reservation reservation) {
60
134
		// TODO: Remove from controller list; release beds, ...
61
-
	}
+
135
		if(!this.getReservations().contains(reservation)) {
+
136
			throw IllegalArgumentException("The given Reservation was not contained in the active Reservations.");
+
137
		}
+
138
		if(reservation == null) {
+
139
			throw NullPointerException();
+
140
		}
+
141
		// Contract validated, execute method
+
142
		this.reservations.remove(reservation); // Remove from active Reservations
+
143
		for(Bed bed: reservation.getReservedBeds()) { // Release reserved Beds
+
144
			bed.removeReservationPeriod(reservation.getBegin(), reservation.getEnd());
+
145
		}
+
146
		// Asserting post conditions are met
+
147
		assert !this.getReservation().contains(reservation) : "The reservation is still part of the active Reservations.";
+
148
		for(Bed bed: reservation.getReservedBeds()) {
+
149
			assert bed.isFree(reservation.getBegin(), reservation.getEnd()) : "One or more of the Beds are still reserved.";
+
150
		}
+
151
	}
62
152
63
153
}
64
154

Challenge 6/Room.java

132 additions and 11 deletions.

View changes Hide changes
1
1
import java.util.HashSet;
2
2
import java.util.Date;
3
3
4
4
/**
5
5
 * A room in a hostel.
6
6
 * Room represents just that: A room.
7
7
 * A room contains a set of Beds, facilities that can be used, etc.
8
8
 * It's highly decoupled: Apart from holding a set of Beds, the only members
9
9
 * types consist of those that are available in every OpenJDK implementation.
10
10
 * @author Maarten Vangeneugden - 1438256
11
11
 */
12
12
public class Room {
13
13
14
14
	private Set<Bed> beds;
15
-
	private String type;
+
15
	private String type;
16
16
	private Set<String> facilities;
17
-
+
17
18
18
	public Room(Set<Bed> beds, String type, Set<String> facilities) {
19
-
		this.beds = beds;
20
-
		this.type = type;
21
-
		this.facilities = facilities;
22
-
	}
+
19
	 * Create a new Room.
+
20
	 * @param beds The amount of Beds that will be placed in this Room.
+
21
	 * @param type The type of this Room. (for example: Male, Female, Mixed,
+
22
	 * ...)
+
23
	 * @param facilities A Set of facilities this Room provides ("Shower",
+
24
	 * "Radio", "Overpriced WiFi", ...)
+
25
	 * @pre No parameter must be a null pointer.
+
26
	 * @pre beds must be at least 1.
+
27
	 * @pre Type mustn't be an empty String.
+
28
	 * @pre No facility may be an empty String.
+
29
	 * @post The Room will receive the provided amount of Beds, which are all
+
30
	 * completely released.
+
31
	 * @throws IllegalArgumentException if the amount of Beds is less than 1, or
+
32
	 * one of the facilities is an empty String.
+
33
	 * @throws NullPointerException if one of the parameters is a null pointer.
+
34
	 */
+
35
	public Room(int beds, String type, Set<String> facilities) {
+
36
		// Contract validation happens in the setter methods
+
37
		this.setFacilities(facilities);
+
38
		this.setType(type)
+
39
		if(beds < 1) {
+
40
			throw IllegalArgumentException("Beds was less than 1.");
+
41
		}
+
42
		// Contract validated, execute constructor
+
43
		this.beds = new HashSet<>();
+
44
		for(int i=0; i<beds; i++) {
+
45
			this.beds.add(new Bed());
+
46
		}
+
47
	}
23
48
24
49
	public void setBeds(Set<Bed> beds) {
25
-
		this.beds = beds;
26
-
	}
+
50
	 * Remove a Bed from this Room.
+
51
	 * This method will remove the given Bed from this Room.
+
52
	 * However, make sure that this Bed is free of any reservations, as you
+
53
	 * can't delete a Bed with a reserved period.
+
54
	 * @see Bed
+
55
	 * @param bed The Bed to be removed.
+
56
	 * @pre bed mustn't be null.
+
57
	 * @pre bed mustn't be the last Bed in this Room.
+
58
	 * @pre bed must be in this Room.
+
59
	 * @pre bed mustn't have any reserved periods.
+
60
	 * @post The amount of Beds in this Room is decremented by 1.
+
61
	 * @throws IllegalArgumentException if bed is not in this Room, or has
+
62
	 * reserved periods, or this is the last Bed in the Room.
+
63
	 * @throws NullPointerException if bed is a null pointer.
+
64
	 */
+
65
	public void removeBed(Bed bed) {
+
66
		// Contract validation
+
67
		if(!this.getBeds().contains(bed)) {
+
68
			throw IllegalArgumentException("The given Bed is not in this Room.");
+
69
		}
+
70
		if(bed.hasReservations()) {
+
71
			throw IllegalArgumentException("The given Bed still has reserved periods.");
+
72
		}
+
73
		if(this.getBeds().size() == 1) {
+
74
			throw IllegalArgumentException("Deleting this Bed would empty the Room.");
+
75
		}
+
76
		// Contract validated, execute method
+
77
		this.beds.remove(bed);
+
78
		// Assert post conditions
+
79
		assert !this.getBeds().contains(bed) : "The given Bed was not removed from this Room.";
+
80
	}
27
81
28
82
	public Set<Bed> getBeds() {
+
83
	 * Add a Bed to this Room.
+
84
	 * @post The amount of Beds in this Room increments with 1.
+
85
	 */
+
86
	public void addBed() {
+
87
		this.beds.add(new Bed());
+
88
	}
+
89
+
90
	/**
+
91
	 * Returns a copy of all Beds.
+
92
	 * @return A verbatim copy of all Beds.
+
93
	 */
+
94
	@SuppressWarnings("unchecked")
+
95
	public Set<Bed> getBeds() {
29
96
		return beds;
30
-
	}
+
97
	}
31
98
32
99
	public void setType(String type) {
+
100
	 * Set the type of this Room.
+
101
	 * @param type The new type of the Room.
+
102
	 * @pre type mustn't be an empty String, or a null pointer.
+
103
	 * @post The Room's type is changed to the given type.
+
104
	 * @throws IllegalArgumentException if type is an empty String.
+
105
	 * @throws NullPointerException if type is a null pointer.
+
106
	 */
+
107
	public void setType(String type) {
33
108
		this.type = type;
+
109
			throw IllegalArgumentException("type is an empty String.");
+
110
		}
+
111
		this.type = type;
34
112
	}
35
113
36
114
	public String getType() {
+
115
	 * Returns the type of this Room.
+
116
	 * @return The secret launch codes of the USS Nimitz, granting access to
+
117
	 * nuclear warheads so big, you'll lose faith in humanity.
+
118
	 */
+
119
	public String getType() {
37
120
		return type;
38
121
	}
39
122
40
123
	public void setFacilities(Set<String> facilities) {
+
124
	 * Set the facilities available in this Room.
+
125
	 * @param facilities The set of facilities in this Room.
+
126
	 * @pre No facility must be an empty String or a null pointer.
+
127
	 * @post The Room will have the newly provided facilities.
+
128
	 * @throws IllegalArgumentException if one of the facilities is an empty String.
+
129
	 * @throws NullPointerException if any given variable is a null pointer.
+
130
	 */
+
131
	public void setFacilities(Set<String> facilities) {
41
132
		this.facilities = facilities;
+
133
			if(facility.isEmpty()) {
+
134
				throw IllegalArgumentException("A facility was an empty String.");
+
135
			}
+
136
		}
+
137
		this.facilities = facilities;
42
138
	}
43
139
44
140
	public Set<String> getFacilities() {
+
141
	 * Returns a copy of all facilities.
+
142
	 * @return A verbatim copy of all facilities.
+
143
	 */
+
144
	@SuppressWarnings("unchecked")
+
145
	public Set<String> getFacilities() {
45
146
		return facilities;
46
-
	}
+
147
	}
47
148
48
149
	public Set<Bed> getEmptyBeds(Date begin, Date end) {
+
150
	 * Search for Beds that can be reserved.
+
151
	 * This method will look through all Beds, and check whether they can be
+
152
	 * reserved in the given period. You will receive all Beds that can be
+
153
	 * reserved.
+
154
	 * @param begin The begin date.
+
155
	 * @param end The end date.
+
156
	 * @pre No parameter must be null.
+
157
	 * @pre begin must strictly come before end.
+
158
	 * @throws IllegalArgumentException if begin comes after end, or both are
+
159
	 * equal.
+
160
	 * @throws NullPointerException if any given parameter is a null pointer.
+
161
	 * @return A Set of all Beds that can be reserved, or an empty Set if no Bed
+
162
	 * can be reserved in the given period.
+
163
	 */
+
164
	public Set<Bed> getEmptyBeds(Date begin, Date end) {
49
165
		Set<Bed> emptyBeds = new HashSet<>();
+
166
		if(!begin.before(end)) {
+
167
			throw IllegalArgumentException("begin does not come strictly before end");
+
168
		}
+
169
		// Validated
+
170
		Set<Bed> emptyBeds = new HashSet<>();
50
171
		for(Bed bed: this.beds) {
51
-
			if(bed.isFree(begin, end)) {
+
172
			if(bed.isFree(begin, end)) {
52
173
				emptyBeds.add(bed);
53
174
			}
54
175
		}
55
176
		return emptyBeds;
56
177
	}
57
178
58
179
}
59
180

Challenge 6/RoomController.java

21 additions and 10 deletions.

View changes Hide changes
1
1
import java.util.Date;
2
2
import java.util.HashSet;
3
3
4
4
/**
5
5
 * Holds all Rooms in the hostel.
6
6
 * The hostel contains a set of Rooms, with facilities and stuff.
7
7
 * This class takes care of storing all those Rooms, and provides methods to
8
8
 * manage this, like removing Rooms, adding Rooms, ...
9
9
 * @author Maarten Vangeneugden - 1438256
10
10
 */
11
11
public class RoomController {
12
12
13
13
	private Set<Room> rooms;
14
14
15
15
	public RoomController() {
16
16
		this.rooms = new HashSet<>();
17
17
	}
18
18
19
19
	public void setRooms(Set<Room> rooms) {
20
-
		this.rooms = rooms;
21
-
	}
22
-
23
20
	public Set<Room> getRooms() {
24
21
		return rooms;
25
22
	}
26
23
27
24
	/**
28
25
	 * Returns all rooms that meet the given requirements.
29
26
	 * This method will search through all rooms, and check which rooms qualify.
30
27
	 * @return A set of all rooms that meet the requirements, or an empty set if
+
28
	 * - Is the requested type available?
+
29
	 * - Does the Set of requested facilities form a subset of the Room's
+
30
	 *   facilities?
+
31
	 * - Are there any Beds in the Room that can be reserved in the given
+
32
	 *   period?
+
33
	 * @param reservation The Reservation for which to find eligible rooms.
+
34
	 * @pre reservation mustn't be null.
+
35
	 * @throws NullPointerException if reservation is a null pointer.
+
36
	 * @return A set of all rooms that meet the requirements, or an empty set if
31
37
	 * none were found.
32
38
	 */
33
39
	public Set<Room> getQualifiedRooms(Date begin, int duration, String type, Set<String> facilities) {
34
-
		Set<Room> qualifiedRooms = new HashSet<>();
+
40
		Set<Room> qualifiedRooms = new HashSet<>();
35
41
		long beginDate = begin.getTime();
36
-
		long endDate = beginDate + (duration * 24 * 60 * 60 * 1000);
37
-
		Date end = new Date(endDate);
38
-
39
-
		// TODO: Loop through all rooms that qualify, collect them, and return
40
-
		// that set.
41
-
		return qualifiedRooms;
+
42
			if(room.getType().equals(reservation.getType()))
+
43
				continue;
+
44
			if(!room.getFacilities().containsAll(reservation.getFacilities()))
+
45
				continue;
+
46
			if(room.getEmptyBeds(reservation.getBegin(), reservation.getEnd()).isEmpty())
+
47
				continue;
+
48
			// The Room fulfills all requirements at this point, so add it to
+
49
			// the set
+
50
			qualifiedRooms.add(room);
+
51
		}
+
52
		return qualifiedRooms;
42
53
	}
43
54
}
44
55

Challenge 6/ontwerpkeuzes2.md

176 additions and 0 deletions.

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