I'm pretty much done now.
- Author
- Maarten 'Vngngdn' Vangeneugden
- Date
- Dec. 16, 2016, 1:55 p.m.
- Hash
- cd8fef3ba559bf2ebfdb75e4d6dcedfbb6805718
- Parent
- 5c90f8e84f1a4f2ae7feada51ba2e9104ea76a42
- Modified files
- Challenge 6/Main.java
- Challenge 6/MainWindow.java
- Challenge 6/SearchView.java
- Challenge 6/ontwerpkeuzes2.md
Challenge 6/Main.java ¶
7 additions and 1 deletion.
View changes Hide changes
1 |
1 |
import java.util.HashSet; |
+ |
2 |
import java.util.HashSet; |
2 |
3 |
import java.util.Random; |
3 |
4 |
|
4 |
5 |
/** |
5 |
6 |
* The program starts here. |
6 |
7 |
* @author Maarten Vangeneugden - 1438256 |
7 |
8 |
*/ |
8 |
9 |
public class Main { |
9 |
10 |
public static void main(String[] args) { |
10 |
11 |
// Starting controllers and linking them together |
11 |
12 |
RoomController roc = new RoomController(); |
12 |
13 |
ReservationController rc = new ReservationController(roc); |
13 |
14 |
WalkController wc = new WalkController(rc); |
14 |
15 |
|
15 |
16 |
addTestRooms(roc); |
16 |
17 |
addTestWalks(wc); |
17 |
18 |
MainWindow mainWindow = new MainWindow(rc, roc); |
18 |
- | } |
+ |
19 |
} |
19 |
20 |
|
20 |
21 |
/** |
21 |
22 |
* Generates a series of Rooms to test the program. |
22 |
23 |
* This method is mostly for debugging purposes. |
23 |
24 |
* It features a set of constants in the front of the method, which you can |
24 |
25 |
* edit to your heart's content. =) |
25 |
26 |
* @param roc The Room Controller to which the Rooms will be added. |
26 |
27 |
* @pre roc mustn't be null. |
27 |
28 |
* @throws NullPointerException if roc is a null pointer. |
28 |
29 |
*/ |
29 |
30 |
public static void addTestRooms(RoomController roc) { |
30 |
31 |
// Constants; edit to suit your needs |
31 |
32 |
final int ROOM_COUNT = 10; // Amount of Rooms that will be generated |
32 |
33 |
final int MAX_BEDS = 20; // The maximum amount of Beds in 1 Room |
33 |
34 |
final String[] POSSIBLE_FACILITIES = {"Shower", "Curtains", "Airco", "Bath", "Minibar"}; |
34 |
35 |
final String[] POSSIBLE_TYPES = {"Male", "Female", "Mixed"}; |
35 |
36 |
|
36 |
37 |
Set<Room> testRooms = new HashSet<>(); |
37 |
38 |
for(int i=0; i<ROOM_COUNT; i++) { |
38 |
39 |
Random random = new Random(); |
39 |
40 |
int beds = random.nextInt(MAX_BEDS) + 1; // +1, because it's in range [0, MAX_BEDS[ |
40 |
41 |
String type = POSSIBLE_TYPES[random.nextInt(POSSIBLE_TYPES.length)]; |
41 |
42 |
Set<String> facilities = new HashSet<>(); |
42 |
43 |
for(String possibleFacility: POSSIBLE_FACILITIES) { |
43 |
44 |
if(random.nextBoolean()) { |
44 |
45 |
facilities.add(possibleFacility); |
45 |
46 |
} |
46 |
47 |
} |
47 |
48 |
testRooms.add(new Room(beds, type, facilities)); |
48 |
49 |
// For debugging purposes, a human readable layout of the Rooms is |
49 |
50 |
// printed: |
50 |
51 |
System.out.println("ROOM"); |
51 |
52 |
System.out.println("Beds: "+ beds); |
52 |
53 |
System.out.println("Type: "+ type); |
53 |
54 |
System.out.print("Facilities: "); |
54 |
55 |
for(String facility : facilities) { |
55 |
56 |
System.out.print(facility +", "); |
56 |
57 |
} |
57 |
58 |
System.out.println(); |
58 |
59 |
System.out.println("-------------------------------"); |
59 |
60 |
} |
60 |
61 |
roc.setRooms(testRooms); |
61 |
62 |
} |
62 |
63 |
|
63 |
64 |
public static void addTestWalks(WalkController wc) { |
64 |
65 |
} |
+ |
66 |
places.add("Lyon"); |
+ |
67 |
places.add("Marseille"); |
+ |
68 |
places.add("Paris"); |
+ |
69 |
wc.addWalk(new Walk("Tour de France", 5, places)); |
+ |
70 |
} |
65 |
71 |
} |
66 |
72 |
Challenge 6/MainWindow.java ¶
20 additions and 1 deletion.
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 |
14 |
public MainWindow(ReservationController reservationController, RoomController roomController) { |
14 |
- | this.reservationController = reservationController; |
+ |
15 |
this.reservationController = reservationController; |
15 |
16 |
this.roomController = roomController; |
16 |
17 |
|
+ |
18 |
|
17 |
19 |
Window window = new Window("Main menu"); |
18 |
20 |
window.createButton("New reservation", "", "addReservation", this); |
19 |
21 |
window.createButton("Search screen", "", "openSearchView", this); |
+ |
22 |
window.createButton("Search screen", "", "openSearchView", this); |
20 |
23 |
} |
+ |
24 |
} |
21 |
25 |
|
22 |
26 |
public void openSearchView() { |
23 |
27 |
SearchView sv = new SearchView(this.reservationController, this.roomController); |
24 |
28 |
} |
25 |
29 |
|
26 |
30 |
public void addReservation() { |
27 |
31 |
Reservation reservation = new Reservation(); |
28 |
32 |
ReservationView rv = new ReservationView(reservation, this.reservationController, this.roomController); |
29 |
33 |
} |
30 |
34 |
|
31 |
35 |
} |
+ |
36 |
WalkSearchView wsv = new WalkSearchView(this.reservationController, this.walkController); |
+ |
37 |
} |
+ |
38 |
|
+ |
39 |
/** |
+ |
40 |
* Create a Walk Reservation creation screen. |
+ |
41 |
* @pre walkController must have at least one Walk stored in it. |
+ |
42 |
* @throws IndexArrayOutOfBoundsException is walkController has no Walks in |
+ |
43 |
* it. |
+ |
44 |
*/ |
+ |
45 |
public void addWalkReservation() { |
+ |
46 |
WalkReservation walkReservation = new WalkReservation(); |
+ |
47 |
walkReservation.setWalk(this.walkController.getWalks().toArray(new Walk[1])[0]); |
+ |
48 |
WalkView wv = new WalkView(walkReservation, this.reservationController, this.walkController); |
+ |
49 |
} |
+ |
50 |
} |
32 |
51 |
Challenge 6/SearchView.java ¶
4 additions and 10 deletions.
View changes Hide changes
1 |
1 |
import java.util.ArrayList; |
2 |
2 |
import java.util.Date; |
3 |
3 |
|
4 |
4 |
/** |
5 |
5 |
* Class for creating a screen that allows querying actions. |
6 |
6 |
* This program offers the ability to search in the program for Reservations, |
7 |
7 |
* Room status, etc. |
8 |
8 |
* This class creates a GUI for user interaction. |
9 |
9 |
* When matches are found, it will display them. In case of a unique match, that |
10 |
10 |
* match is immediately displayed. |
11 |
11 |
* @see ReservationView |
12 |
12 |
* @author Maarten Vangeneugden - 1438256 |
+ |
13 |
* @author Maarten Vangeneugden - 1438256 |
13 |
14 |
*/ |
14 |
15 |
public class SearchView { |
15 |
16 |
private ReservationController rc; |
16 |
17 |
private RoomController roc; |
17 |
18 |
private Window window; |
18 |
19 |
|
19 |
20 |
private JTextField groupNameField; |
20 |
21 |
private JTextField reservationIDField; |
21 |
22 |
private JTextField bedDateField; |
22 |
23 |
private JTextField breakfastDayField; |
23 |
24 |
|
24 |
25 |
public SearchView(ReservationController rc, RoomController roc) { |
25 |
26 |
this.rc = rc; |
26 |
27 |
this.roc = roc; |
27 |
28 |
|
28 |
29 |
this.window = new Window("Search screen"); |
29 |
30 |
this.addFields(); |
30 |
31 |
} |
31 |
32 |
|
32 |
33 |
private void addFields() { |
33 |
34 |
// TODO: Add fields to private members! |
34 |
- | this.window.createLabel("Search reservations:"); |
+ |
35 |
this.window.createLabel("Search reservations:"); |
35 |
36 |
this.groupNameField = this.window.createTextField("Search by group name"); |
36 |
37 |
this.reservationIDField = this.window.createTextField("Search by reservation ID"); |
37 |
38 |
this.window.createButton("Search reservation", "", "queryReservations", this); |
38 |
39 |
this.window.createLabel("Search reserved beds on given day:"); |
+ |
40 |
this.window.createLabel("Search reserved beds on given day:"); |
39 |
41 |
this.bedDateField = this.window.createTextField(new Date().toGMTString()); |
40 |
42 |
this.window.createButton("Get reserved beds", "", "queryBeds", this); |
41 |
43 |
this.window.createLabel("Find amount of breakfasts:"); |
+ |
44 |
this.window.createLabel("Find amount of breakfasts:"); |
42 |
45 |
this.breakfastDayField = this.window.createTextField(new Date().toGMTString()); |
43 |
46 |
this.window.createButton("Get breakfasts on given day", "", "queryBreakfasts", this); |
44 |
47 |
} |
45 |
48 |
|
46 |
49 |
public void queryReservations() { |
47 |
50 |
ArrayList<Reservation> foundReservations = new ArrayList<>(); |
48 |
51 |
String nameQuery = this.groupNameField.getText(); |
49 |
52 |
String IDQuery = this.reservationIDField.getText(); |
50 |
53 |
for(Reservation activeReservation : this.rc.getReservations()) { |
51 |
54 |
if(activeReservation.getGroupName().contains(nameQuery) && |
52 |
55 |
String.valueOf(activeReservation.getReservationID()).contains(IDQuery)) { |
53 |
56 |
foundReservations.add(activeReservation); |
54 |
57 |
} |
55 |
58 |
} |
56 |
59 |
// After collecting all results, offer the user the choice about which |
57 |
60 |
// one to take: |
58 |
61 |
String[] reservationNames = new String[foundReservations.size()]; |
59 |
62 |
for(int i=0; i<foundReservations.size(); i++) { |
60 |
63 |
String text = foundReservations.get(i).getGroupName() +" ("+ |
61 |
64 |
String.valueOf(foundReservations.get(i).getReservationID()) +")"; |
62 |
65 |
reservationNames[i] = text; |
63 |
66 |
} |
64 |
67 |
int choice = this.window.choiceDialog("Multiple results were found. Specify which one you want to view:", reservationNames); |
65 |
68 |
if(choice == -1) { // No result found |
66 |
69 |
this.window.messageDialog("No reservations matched the given details."); |
67 |
70 |
} |
68 |
71 |
else { |
69 |
72 |
new ReservationView(foundReservations.get(choice), this.rc, this.roc); |
70 |
73 |
} |
71 |
74 |
} |
72 |
75 |
|
73 |
76 |
/** |
74 |
77 |
* Search and display the reservation of Beds in Rooms. |
75 |
78 |
* This method takes the given begin and end dates from the GUI, and lists |
76 |
79 |
* for each Room, how many Beds are reserved, and by whom. |
77 |
80 |
*/ |
78 |
81 |
public void queryBeds() { |
79 |
82 |
// The amount of reserved Beds is determined by the Reservations. |
80 |
83 |
// If a Reservation (partially) overlaps with the given time, then this |
81 |
84 |
// Bed is reserved in the given period. |
82 |
85 |
final Date date = new Date(this.bedDateField.getText()); |
83 |
86 |
// endDate is required as it has to be strictly later than date. |
84 |
87 |
final Date endDate = new Date(date.getTime() + 1); |
85 |
88 |
String result = ""; |
86 |
89 |
|
87 |
90 |
for(Room room: this.roc.getRooms()) { |
88 |
91 |
// XXX: The Java Object class provides a "hashcode()" method, to |
89 |
92 |
// retrieve an object's hashcode. I'm using that to identify the |
90 |
93 |
// different rooms. |
91 |
94 |
int usedBeds = room.getBeds().size() - room.getEmptyBeds(date, endDate).size(); |
92 |
95 |
result = result + "Room "+ String.valueOf(room.hashCode()) +": "+ String.valueOf(usedBeds) +" reserved beds.\n"; |
93 |
96 |
} |
94 |
97 |
this.window.messageDialog(result); |
95 |
98 |
/*for(Reservation reservation: this.rc.getReservations()) { |
96 |
99 |
if(! |
97 |
100 |
(reservation.getBeginDate().before(beginDate) && reservation.getEndDate().before(beginDate)) || |
98 |
101 |
(reservation.getBeginDate().after(endDate) && reservation.getEndDate().after(endDate))) |
99 |
102 |
{ // This block is only reached if the valid options didn't match. |
100 |
103 |
String name = reservation.getGroupName(); |
101 |
104 |
String beds = String.valueOf(reservation.getReservedBeds().size()); |
102 |
105 |
String begin = reservation.getBeginDate().toGMTString(); |
103 |
106 |
String end = reservation.getEndDate().toGMTString(); |
104 |
107 |
result = result + |
105 |
108 |
name +" reserved "+ beds +" between "+ begin +" and "+ end +".\n"; |
106 |
109 |
} |
107 |
110 |
}*/ |
108 |
111 |
} |
109 |
112 |
|
110 |
113 |
/** |
111 |
114 |
* Queries and displays breakfast reservations. |
112 |
115 |
* After the user has entered a day in the corresponding field, this method |
113 |
116 |
* can be called to search for breakfast reservations on that day. |
114 |
117 |
* Breakfasts are ordered by Reservation, accompagnied by the amount of |
115 |
118 |
* people in that Reservation. |
116 |
119 |
* // TODO finish this javadoc |
117 |
120 |
*/ |
118 |
121 |
public void queryBreakfasts() { |
119 |
122 |
Date date = new Date(this.breakfastDayField.getText()); |
120 |
123 |
// Breakfast is only served at 9 o'clock, so set that time for easy |
121 |
124 |
// reference during querying: |
122 |
125 |
date.setHours(8); |
123 |
126 |
date.setMinutes(0); |
124 |
127 |
date.setSeconds(0); |
125 |
128 |
String response = ""; |
126 |
129 |
for(Reservation reservation: this.rc.getReservations()) { |
127 |
130 |
if(reservation.getBreakfastDays().contains(date)) { |
128 |
131 |
String name = reservation.getGroupName(); |
129 |
132 |
String amount = String.valueOf(reservation.getPeople()); |
130 |
133 |
response = response + |
131 |
134 |
"Reservation "+ name +" ordered breakfast for "+ amount +" people.\n"; |
132 |
135 |
} |
133 |
136 |
// else block is DEBUG CODE |
134 |
- | else { |
135 |
- | System.out.println("No match!"); |
136 |
- | System.out.println("Query: "+date.toGMTString()); |
137 |
- | System.out.println("Tests:"); |
138 |
- | for(Date bfd: reservation.getBreakfastDays()) { |
139 |
- | System.out.println(bfd.toGMTString()); |
140 |
- | } |
141 |
- | } |
142 |
- | } |
143 |
137 |
if(!response.isEmpty()) { |
144 |
138 |
this.window.messageDialog(response); |
145 |
139 |
} |
146 |
140 |
else { |
147 |
141 |
this.window.messageDialog("There are no breakfasts ordered for this day."); |
148 |
142 |
} |
149 |
143 |
} |
150 |
144 |
} |
151 |
145 |
Challenge 6/ontwerpkeuzes2.md ¶
47 additions and 10 deletions.
View changes Hide changes
1 |
1 |
|
2 |
2 |
## GRASP |
3 |
3 |
Om zoveel mogelijk tegemoet te komen aan de GRASP-richtlijnen, heb ik volgende |
4 |
4 |
ontwerpkeuzes toegepast voor de verschillende richtlijnen. |
5 |
5 |
|
6 |
6 |
### High cohesion & Low coupling |
7 |
7 |
- Veel classes zijn totaal van elkaar losgekoppeld. Een goed voorbeeld hiervan |
8 |
8 |
is Bed; Bed weet niet over dat er Reservaties bestaan, zelfs niet in welke |
9 |
9 |
Room het 'staat'. |
10 |
10 |
- Als er toch gekoppeld moet worden, dan blijft dit tot een minimum beperkt; Een |
11 |
11 |
Room weet wel welke Bedden het heeft, maar niet hoe deze gebruikt worden, een |
12 |
12 |
Reservation weet wel welke Bedden voor hem gereserveerd zijn, maar heeft geen |
13 |
13 |
weet van welke kamer dat deze staat. |
14 |
14 |
- Uitzondering op deze regel vormen de Controller-classes; omwille van hun taken |
15 |
15 |
zijn deze vaak verplicht om van veel verschillende classes op de hoogte te |
16 |
16 |
zijn. |
17 |
17 |
|
18 |
18 |
### Indirectie |
19 |
19 |
- In plaats van functionaliteit toe te wijzen aan class X om met class Y te |
20 |
20 |
interageren, wordt er vaak gebruik gemaakt van een class Z, die deze |
21 |
21 |
verantwoordelijkheid op zich neemt. |
22 |
22 |
- Dit wordt toegepast d.m.v. een MVC-structuur, waarin de \*Controller-classes |
23 |
23 |
als 'tussenpersoon' werken. |
24 |
24 |
- Dit komt ook tegemoet aan de eigenschappen van Information Expertise. |
25 |
25 |
|
26 |
26 |
### Creator |
27 |
27 |
- Controllers staan ook in als zgn. Creators van de models die ze beheren. Ze |
28 |
28 |
voldoen dan ook aan de eigenschappen zoals |
29 |
29 |
[hier](https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Creator) |
30 |
30 |
opgesomd wordt. |
31 |
31 |
|
32 |
32 |
|
33 |
33 |
## SOLID |
34 |
- | |
35 |
- | ### Single Responsibility |
36 |
- | |
37 |
- | ## Veranderingen t.o.v. Challenge 1 |
38 |
- | |
39 |
- | - Verkrijgen van een vrije kamer is verplaatst van ReservationView naar |
+ |
34 |
- Verkrijgen van een vrije kamer is verplaatst van ReservationView naar |
40 |
35 |
RoomController |
41 |
- | - Toevoeging van veel documentatie, vooral in de vorm van Javadoc, maar ook |
+ |
36 |
- Toevoeging van veel documentatie, vooral in de vorm van Javadoc, maar ook |
42 |
37 |
_inline commenting_ waar nodig/gewenst. |
43 |
38 |
|
+ |
39 |
voorbeeld incluis). |
+ |
40 |
- Nieuw zoekvenster, speciaal gericht op het behandelen van wandelingen. |
+ |
41 |
- Zoekvensters zijn nu haast volledig operationeel, en geven bruikbare |
+ |
42 |
antwoorden en data terug. |
+ |
43 |
- Eerder gemaakte reservaties/wandelreservaties kunnen later aangepast en/of |
+ |
44 |
verwijderd worden. |
+ |
45 |
- Prijsberekening op orde gesteld. |
+ |
46 |
- Toevoeging van een automatische _room generator_ in Main, om snel en met |
+ |
47 |
willekeurige data het programma te testen. |
+ |
48 |
- Toepassing van contractueel programmeren in vele methods; vaak wordt de method |
+ |
49 |
nog niet uitgevoerd als het contract niet 100% wordt nageleefd. |
+ |
50 |
|
44 |
51 |
### Vereiste aanpassingen |
45 |
52 |
De aanpassingen die ik heb moeten doorvoeren om wandelingen toe te voegen, zijn |
46 |
53 |
grotendeels beperkt gebleven tot de plekken in de code die sowieso van meerdere |
47 |
54 |
onderdelen van het programma op de hoogte zijn; |
48 |
55 |
Zo is er niks veranderd aan Reservation of Room, maar WalkController en |
49 |
56 |
ReservationController hebben elkaar nu wel als member. |
50 |
57 |
|
51 |
58 |
Aan de GUI-kant was het slechts 1 knopje toevoegen in het hoofdmenu. |
52 |
59 |
|
53 |
60 |
Ik heb het zo opgesteld dat een groepsnaam die overeenkomt met een |
54 |
61 |
hostelreservering, wordt aanzien als een reservering van dezelfde persoon. |
55 |
62 |
Als de namen niet overeen komen, dan zijn het verschillende groepen. |
56 |
63 |
|
57 |
64 |
Deze conventie laat mij toe om het programma veel beter te laten voldoen aan |
58 |
65 |
high cohesion/low coupling, zonder daarvoor veel voordelen voor hoeven in te |
59 |
66 |
leveren. |
60 |
67 |
|
61 |
68 |
## Algemene keuzes ter bevordering codekwaliteit |
62 |
- | |
+ |
69 |
zoekvenster te plaatsen, maar ik vond dat dat nogal onduidelijk ging worden. Ik |
+ |
70 |
heb dus voor wandelmanagement een apart zoekvenster gemaakt, te bereiken vanuit |
+ |
71 |
het hoofdmenu. |
+ |
72 |
|
+ |
73 |
Uiteindelijk ben ik blij van de beperkte aanpassingen in het eerste gedeelte van |
+ |
74 |
de challenge. Ik heb slechts hier en daar een extra member moeten toevoegen, en |
+ |
75 |
het geheel is nog steeds degelijk gesplitst. |
+ |
76 |
|
+ |
77 |
# Algemene keuzes ter bevordering codekwaliteit |
+ |
78 |
|
63 |
79 |
## Strings, integers, ... i.p.v. Enums/Eigen classes |
64 |
80 |
In het programma zijn er sommige members die doen vermoeden dat ze beter als |
65 |
81 |
Enums aangeduid kunnen worden; Het type van een kamer, faciliteiten, ... |
66 |
82 |
|
67 |
83 |
In plaats van een speciale class hiervoor te maken, heb ik gekozen om deze |
68 |
84 |
gewoon te behandelen als simpele Strings in lijsten. |
69 |
85 |
|
70 |
86 |
Het voordeel aan deze werkwijze, is dat Strings in Java SE quasi universeel |
71 |
87 |
voorkomen; data kunnen geconverteerd worden naar Strings (en vice versa), veel |
72 |
88 |
gebruikte GUI-libraries (waaronder Swing, JavaFX, ...) gebruiken Strings voor |
73 |
89 |
tekst voor te stellen in de GUI-widgets, ... |
74 |
90 |
Daarnaast weet iedereen die ook maar een beetje geschoold is in Java, direct hoe |
75 |
91 |
deze members behandeld kunnen worden. Had ik een class gemaakt die bv. |
76 |
92 |
RoomFacilities heette, dan is die parate kennis over de taal zelf nutteloos. |
77 |
93 |
|
78 |
94 |
Strings zijn ook voorzien van extreem veel methods, en zijn dus zeer flexibele |
79 |
95 |
classes om te gebruiken. Het feit dat ze zo makkelijk doorheen het programma te |
80 |
96 |
gebruiken zijn is dus een groot pluspunt. |
81 |
97 |
|
82 |
98 |
Als dat nog niet genoeg is: Het gebruiken van de hulpmiddelen uit de _standard |
83 |
99 |
library_ is niet enkel waarvoor libraries überhaupt bestaan, het beperkt ook |
84 |
100 |
het benodigde aantal classes in de eigen software, waardoor het geheel |
85 |
101 |
uiteindelijk veel overzichtelijker blijft, en bijgevolg, makkelijk uit te |
86 |
102 |
breiden, te testen, te begrijpen, ... |
87 |
103 |
|
88 |
104 |
Al deze voordelen graan grotendeels verloren als ik beslis om zelf |
89 |
105 |
gespecialiseerde classes op te stellen. |
90 |
106 |
Men kan misschien stellen dat "de voordelen die het schrijven van eigen |
91 |
107 |
classes veel beter tegemoet komt aan de essentie van object-georiënteerd |
92 |
108 |
programmeren, dan zich _beperken_ tot slechts een handvol generieke classes, die |
93 |
109 |
misschien niet volledig aan de benodigdheden beantwoorden". |
94 |
110 |
|
95 |
111 |
Toch laat ik mijn afweging overslaan in het voordeel van minder classes, en meer |
96 |
112 |
uniformiteit. _Sometimes, less is more._ Meer classes betekent ook: |
97 |
113 |
- Meer onderhoud |
98 |
114 |
- Meer kans op bugs |
99 |
115 |
- Groter programma |
100 |
116 |
|
101 |
117 |
En al die problemen resulteren op lange termijn in: |
102 |
118 |
- Slechtere uitbreidbaarheid |
103 |
119 |
- Slechtere onderhoudbaarheid |
104 |
120 |
- Slechtere schaalbaarheid |
105 |
121 |
|
106 |
122 |
Zijn al die problemen de moeite van een (zogezegd) "beter object-georiënteerd |
107 |
123 |
design" wel waard? |
108 |
124 |
Veel mensen stellen juist dat OOP net voordelig is als men op zoek is naar |
109 |
125 |
- Uitbreidbaarheid |
110 |
126 |
- Onderhoudbaarheid |
111 |
127 |
- Modulariteit |
112 |
128 |
- Schaalbaarheid |
113 |
129 |
|
114 |
130 |
Als de voordelen van het paradigma verdwijnen als ik dat "beter design" volg, |
115 |
131 |
is dat design dan wel echt beter? |
116 |
132 |
|
117 |
133 |
|
118 |
134 |
**Conclusie: Uniforme classes zijn volgens mij soms beter dan een stel |
119 |
135 |
gespecialiseerde classes. Daarom dat ik mij soms behelp met Strings, intergers, |
120 |
136 |
... i.p.v. zelf een oplossing te schrijven.** |
121 |
137 |
|
122 |
138 |
|
123 |
139 |
### Null pointers |
124 |
140 |
Het valt misschien op dat ik doorheen mijn programma in veel contracten eis dat |
125 |
141 |
geen enkele parameter een zgn. *null pointer* is. |
126 |
142 |
|
127 |
143 |
Het gebruik van null pointers staat garant voor een overvloed aan moeilijk te |
128 |
144 |
vinden bugs die (door het design van objectgeörienteerd programmeren) enorm diep |
129 |
- | kunnen doorpropageren. |
+ |
145 |
kunnen doorpropageren. |
130 |
146 |
|
131 |
147 |
Ik maak er een kerntaak van dat, als er aan mijn programma gewerkt wordt, de |
132 |
148 |
programmeur zichzelf steeds kan garanderen dat **alle** data die hij |
133 |
149 |
ontvangt, valide data is, tenzij dat expliciet anders wordt aangegeven. |
134 |
150 |
Op deze manier valt er een hele last van de schouders van de programmeur; een |
135 |
151 |
reeks fouten worden voorkomen, simpelweg door een strikt schema aan te houden. |
136 |
152 |
|
137 |
153 |
Het controleren op null pointers wordt op 2 manieren gedaan: |
138 |
154 |
- Gebruik van *methods* aanwezig in het gegeven type. Als de gegeven variabele |
139 |
155 |
een null pointer is, zal het programma direct crashen, en een |
140 |
156 |
NullPointerException geven. |
141 |
157 |
- Expliciet testen of *var == null*. Wordt toegepast op parameters die direct |
142 |
158 |
als members opgeslagen dienen te worden. |
143 |
159 |
|
144 |
160 |
Een uitzondering op het vermijden van null pointers vormen sommige methods die |
145 |
161 |
slechts 1 object teruggeven. In deze gevallen kan het voorvallen dat het te |
146 |
162 |
zoeken object niet gevonden wordt. Om de ontwikkelaar hiervan op de hoogte te |
147 |
163 |
stellen, wordt dan soms een null teruggestuurd. |
148 |
164 |
Dit wordt steeds expliciet vermeld. |
149 |
165 |
|
150 |
166 |
Deze (contractuele) controle laat toe dat, mocht er een null pointer gebruikt |
151 |
167 |
worden, het programma de programmeur hiervan direct op de hoogte stelt, en dit |
152 |
168 |
op het laagst mogelijke niveau (namelijk de eerste method waar deze waarde |
153 |
169 |
gebruikt wordt). |
154 |
170 |
|
155 |
171 |
### Cloning |
156 |
172 |
members 'private' maken (encapsuleren) is volledig nutteloos als men getters en |
157 |
173 |
setters op deze members toepast; In Java worden references doorgegeven (m.u.v. |
158 |
174 |
primitives), die de hele notie van encapsulatie voorbijgaan (bij sommige types). |
159 |
175 |
Een voorbeeld hiervan is het privatiseren van een Set<T>-member: men kan daar |
160 |
176 |
een 'getSet()'-method op plaatsen, en dan toch de inhoud van deze 'private' |
161 |
177 |
aanpassen. |
162 |
178 |
|
163 |
179 |
Ik heb geopteerd om, waar van toepassing, deze variabelen te 'clonen', om zo |
164 |
180 |
exacte kopieën terug te geven. |
165 |
181 |
Deze manier van werken brengt enkele voordelen teweeg: |
166 |
182 |
- Zeer defensief programmeren; De ontwikkelaar kan geen members aanpassen als |
167 |
183 |
dat niet de bedoeling was |
168 |
184 |
- Duidelijkheid code: getSet().clear() zal de member niet meer leegmaken. Om dat |
169 |
185 |
te doen, moet men gebruikmaken van de method die daarvoor bedoeld is: |
170 |
186 |
setSet(clearedSet) |
171 |
187 |
|
172 |
188 |
### Inheritance |
173 |
189 |
Overerving is een goed concept over het algemeen, maar **niet** in OOP. |
174 |
190 |
De problemen omtrent impliciet gedrag en onnodige *state* zijn al te vaak |
175 |
191 |
beschreven met OOP-inheritance. |
176 |
192 |
|
177 |
193 |
Ik heb in mijn programma geen gebruik gemaakt van inheritance, exact omwille van |
178 |
194 |
de problemen die het voortbrengt, zeker in termen van herbruikbaarheid en |
179 |
195 |
robuustheid, wat toch zware vereisten waren voor deze opdracht. |
180 |
196 |
|
181 |
197 |
Ik heb al mijn problemen makkelijk kunnen oplossen d.m.v. compositie. |
182 |
198 |
|
183 |
199 |
### Benaming variabelen |
184 |
200 |
Doorheen mijn programma maak ik heel veel gebruik van dezelfde benamingen. |
185 |
201 |
Bijvoorbeeld: Een variabele van het type Reservation zal haast altijd |
186 |
202 |
'reservation' heten, een Set van een bepaald type zal de naam van datzelfde type |
187 |
203 |
dragen, in het meervoud, ... |
188 |
204 |
|
189 |
205 |
Sommige programmeurs gebruiken liever afkortingen (bv. 'reservation' --> |
190 |
206 |
'resv'), omdat dit sneller schrijft. |
191 |
207 |
|
192 |
208 |
Naar mijn mening moet men bij deze werkwijze inleveren aan leesbaarheid, vooral |
193 |
209 |
wanneer iemand die nog nooit met de code gewerkt heeft, dit programma moet |
194 |
210 |
overnemen. |
195 |
211 |
|
196 |
212 |
Daarnaast zorgt de consistentie van woordgebruik ervoor dat een andere |
197 |
213 |
programmeur, doorheen het hele programma dezelfde context in zijn/haar gedachten |
198 |
214 |
kan gebruiken. |
199 |
215 |
|
200 |
216 |
|
+ |
217 |
code in zijn geheel moeilijker leesbaar wordt. |
+ |
218 |
Een voorbeeld van hoe dit uitmondt is Objective-C. Haast **alles** wordt voluit |
+ |
219 |
genoteerd, waardoor van de eigenlijke essentie van het programma haast niks |
+ |
220 |
overblijft. |
+ |
221 |
|
201 |
222 |
|
+ |
223 |
afgekort. Ik vond dat het verlies aan expressiviteit meer dan goed gemaakt werd |
+ |
224 |
door het winnen aan leesbaarheid. |
+ |
225 |
|
202 |
226 |
|
203 |
227 |
------------------------------------------------------------ TODO |
204 |
- | ## Toepassing types/classes |
205 |
228 |
Doorheen het programma heb ik getracht zoveel mogelijk gebruik te maken van |
206 |
229 |
types/classes die |
207 |
230 |
- Veelvoorkomend zijn in de Java STL (Zoals String, Set, List, ...) |
208 |
231 |
- primitief zijn (ints, ...), omdat deze operatoren hebben en de code |
209 |
232 |
leesbaarder maken |
210 |
233 |
|
211 |
234 |
Een goed voorbeeld hiervan zijn bv. de faciliteiten: |
212 |
235 |
I.p.v. een aparte "Facility"-class te maken, heb ik de verschillende |
213 |
236 |
faciliteiten voorgesteld door een simpele String. De voordelen van deze aanpak |
214 |
237 |
zijn ook direct duidelijk: |
215 |
238 |
- Betekenis is direct duidelijk; de faciliteit letterlijk in de code vernoemd |
216 |
239 |
- Makkelijke interactie met GUI, die sowieso Strings vraagt voor bv. JLabel |
217 |
240 |
- Uitbreidbaarheid wordt bekomen door simpelweg een nieuwe String te |
218 |
241 |
introduceren |
219 |
242 |
|
220 |
243 |
## View en GUI |
221 |
244 |
Werken met GUI's is vaak tijdrovend en veroorzaakt snel errors, zeker met bv. |
222 |
245 |
anonieme methods, exceptions, ... |
223 |
246 |
Alhoewel mijn programma grotendeels in een MVC-stijl is geschreven, maken de |
224 |
247 |
view-classes (RegistrationView, SearchView, ...) achterliggend gebruik van een |
225 |
248 |
zelfgemaakt framework om makkelijk vensters te maken. |
226 |
249 |
Dit kleine framework is een persoonlijk hobbyproject dat ik JSugar noem. |
227 |
250 |
Het biedt een heleboel voordelen, vergeleken met elk GUI-venster zelf opstellen: |
228 |
251 |
- Vaak gebruikte GUI-widgets (zoals een label, textfield) worden aangemaakt en |
229 |
252 |
toegevoegd door slechts 1 method op te roepen |
230 |
253 |
- JSugar maakt gebruik van reflectie om op een leesbare en uitbreidbare manier |
231 |
254 |
knoppen te activeren: |
232 |
255 |
public JButton createButton(String text, String action, String methodName, Object triggerObject) |
233 |
256 |
'methodName' is een simpele String, en 'triggerObject' is het object waar deze |
234 |
257 |
method moet worden opgeroepen. |
235 |
258 |
- Automatische uitlijning van widgets |
236 |
- | Voor meer informatie kunt u JSugar-readme.md raadplegen. |
+ |
259 |
Voor meer informatie kunt u JSugar-readme.md raadplegen. |
237 |
260 |
|
238 |
261 |
## java.util.Date |
239 |
262 |
|
240 |
263 |
Doorheen het programma maak ik vaak gebruik van de Date-class uit de Java STL, |
241 |
264 |
om de volgende redenen: |
242 |
265 |
- Uitbreidbaarheid: Door overal eenzelfde type te gebruiken, is het gemakkelijk |
243 |
266 |
om nieuwe modules toe te voegen, zonder dat daarvoor abstractielagen etc. |
244 |
267 |
nodig zijn. |
245 |
268 |
- Uniformiteit: Eenzelfde type uit de STL laat de ontwikkelaar toe om door het |
246 |
269 |
hele programma heen hetzelfde denkpatroon aan te houden; een |
247 |
270 |
Stringvoorstelling hier, een integer daar, ... veroorzaken problemen en bugs, |
248 |
271 |
die met deze class voorkomen worden. |
249 |
272 |
|
250 |
273 |
### Ontbijtdata |
+ |
274 |
Geef data in in hetzelfde formaat, en de achtergrond doet de rest. |
+ |
275 |
|
+ |
276 |
### Deprecation |
+ |
277 |
Ik besef dat ik in java.util.Date veel gebruik maak van _deprecated methods_. |
+ |
278 |
Nu, deze methods bieden mij wel de kans om snel en gemakkelijk met data om te |
+ |
279 |
kunnen gaan. |
+ |
280 |
Zeker het tijdsgebrek dat komt kijken bij de challenges zorgen ervoor dat ik |
+ |
281 |
mijn niet de luxe kan veroorloven om een speciaal voor mij gemaakte dataclass te |
+ |
282 |
maken. |
+ |
283 |
|
+ |
284 |
### Ontbijtdata |
251 |
285 |
|
252 |
286 |
Het moeilijkste om te implementeren waren de data voor ontbijt. |
253 |
287 |
Er wordt voor de reservaties steeds een set bijgehouden van de data waarop |
254 |
288 |
ontbijt besteld is. |
255 |
289 |
Op zich is dit niet zo moeilijk. Het moeilijke hieraan, is de gebruiker van het |
256 |
290 |
programma op een begrijpelijke manier deze data te laten invoeren. |
257 |
291 |
Data worden ingevoerd via tekstvelden, omdat Swing geen zgn. "DatePicker" heeft. |
258 |
292 |
|
259 |
293 |
|
+ |
294 |
|
+ |
295 |
|
260 |
296 |
### GUI |
261 |
297 |
|
262 |
298 |
Swing biedt geen widget die zich specialiseert in het weergeven van informatie |
263 |
299 |
over een datum (een zgn. _calendar widget_). |
264 |
300 |
|
265 |
301 |
In de opdracht stond dat we geen "visueel aantrekkelijke GUI" hoeven te |
266 |
302 |
ontwerpen, maar wel een die "alle functionaliteit ondersteunt". |
267 |
303 |
|
268 |
304 |
Wegens het tijdsgebrek en de tekortkoming van Swing, heb ik besloten om data |
269 |
305 |
weer te geven in een simpel JTextField. |
270 |
306 |
Het biedt de beste oplossing voor wat gevraagd wordt in deze opdracht, en wegens |
271 |
307 |
de tijdslimiet tijdens de challenges zelf vormen ze een goede |
272 |
308 |
"kortetermijnoplossing". |
273 |
309 |
|
274 |
310 |
## Bedden |
275 |
311 |
|
276 |
312 |
Ik heb voor bedden een aparte class aangemaakt, omdat deze een bepaalde state |
277 |
313 |
bijhouden, nml. wanneer ze gereserveerd zijn. |
278 |
314 |
|
279 |
315 |
|
280 |
316 |
## Reservation |
281 |
317 |
Voor de reservaties heb ik besloten om enkel die data bij te houden die inherent |
282 |
318 |
gerelateerd is aan die reservatie: |
283 |
319 |
- Enkel gereserveerde bedden i.p.v. gereserveerde kamers. Qua uitbreidbaarheid |
284 |
320 |
heeft dit tot gevolg dat men gemakkelijk kan uitbreiden naar reservaties, |
285 |
321 |
gespreid over verschillende kamers. |
286 |
322 |
|
287 |
323 |
|
288 |
324 |
## ReservationView / WalkView |
289 |
325 |
Merk op hoe in ReservationView de data van de Reservation direct in de GUI wordt |
290 |
326 |
geplaatst. |
291 |
327 |
Dit staat toe om zeer gemakkelijk deze class te hergebruiken voor zowel het |
292 |
328 |
**aanmaken** als het **updaten** van de reservatie. |
293 |
329 |
|
294 |
330 |
Dezelfde werkwijze wordt toegepast ook WalkView. |
295 |
331 |
|
296 |
332 |
|
297 |
333 |
## Opmerkingen |
298 |
334 |
|
299 |
335 |
- Door het gebruik van de FlowLayout in de GUI, is het mogelijk dat sommige |
300 |
336 |
gedeelten van de GUI niet direct worden weergegeven, daar ze verborgen worden |
301 |
337 |
door de hoogte van het venster. Men kan deze tevoorschijn halen door zelf het |
302 |
338 |
venster in de hoogte te verstellen. |
303 |
339 |