1 |
# Maarten Vangeneugden - 1438256
|
2 |
|
3 |
## GRASP |
4 |
Om zoveel mogelijk tegemoet te komen aan de GRASP-richtlijnen, heb ik volgende |
5 |
ontwerpkeuzes toegepast voor de verschillende richtlijnen. |
6 |
|
7 |
### High cohesion & Low coupling |
8 |
- Veel classes zijn totaal van elkaar losgekoppeld. Een goed voorbeeld hiervan |
9 |
is Bed; Bed weet niet over dat er Reservaties bestaan, zelfs niet in welke |
10 |
Room het 'staat'. |
11 |
- Als er toch gekoppeld moet worden, dan blijft dit tot een minimum beperkt; Een |
12 |
Room weet wel welke Bedden het heeft, maar niet hoe deze gebruikt worden, een |
13 |
Reservation weet wel welke Bedden voor hem gereserveerd zijn, maar heeft geen |
14 |
weet van welke kamer dat deze staat. |
15 |
- Uitzondering op deze regel vormen de Controller-classes; omwille van hun taken |
16 |
zijn deze vaak verplicht om van veel verschillende classes op de hoogte te |
17 |
zijn. |
18 |
|
19 |
### Indirectie |
20 |
- In plaats van functionaliteit toe te wijzen aan class X om met class Y te |
21 |
interageren, wordt er vaak gebruik gemaakt van een class Z, die deze |
22 |
verantwoordelijkheid op zich neemt. |
23 |
- Dit wordt toegepast d.m.v. een MVC-structuur, waarin de \*Controller-classes |
24 |
als 'tussenpersoon' werken. |
25 |
- Dit komt ook tegemoet aan de eigenschappen van Information Expertise. |
26 |
|
27 |
### Creator |
28 |
- Controllers staan ook in als zgn. Creators van de models die ze beheren. Ze |
29 |
voldoen dan ook aan de eigenschappen zoals |
30 |
[hier](https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)#Creator) |
31 |
opgesomd wordt. |
32 |
|
33 |
|
34 |
# Veranderingen t.o.v. Challenge 1 |
35 |
- Verkrijgen van een vrije kamer is verplaatst van ReservationView naar |
36 |
RoomController. |
37 |
- Toevoeging van veel documentatie, vooral in de vorm van Javadoc, maar ook |
38 |
_inline commenting_ waar nodig/gewenst. |
39 |
- Toevoeging van een systeem om wandelingen te organiseren (1 testwandeling als |
40 |
voorbeeld incluis). |
41 |
- Nieuw zoekvenster, speciaal gericht op het behandelen van wandelingen. |
42 |
- Zoekvensters zijn nu haast volledig operationeel, en geven bruikbare |
43 |
antwoorden en data terug. |
44 |
- Eerder gemaakte reservaties/wandelreservaties kunnen later aangepast en/of |
45 |
verwijderd worden. |
46 |
- Prijsberekening op orde gesteld. |
47 |
- Toevoeging van een automatische _room generator_ in Main, om snel en met |
48 |
willekeurige data het programma te testen. |
49 |
- Toepassing van contractueel programmeren in vele methods; vaak wordt de method |
50 |
nog niet uitgevoerd als het contract niet 100% wordt nageleefd. |
51 |
|
52 |
### Vereiste aanpassingen |
53 |
De aanpassingen die ik heb moeten doorvoeren om wandelingen toe te voegen, zijn |
54 |
grotendeels beperkt gebleven tot de plekken in de code die sowieso van meerdere |
55 |
onderdelen van het programma op de hoogte zijn; |
56 |
Zo is er niks veranderd aan Reservation of Room, maar WalkController en |
57 |
ReservationController hebben elkaar nu wel als member. |
58 |
|
59 |
Aan de GUI-kant was het slechts 1 knopje toevoegen in het hoofdmenu. |
60 |
|
61 |
Ik heb het zo opgesteld dat een groepsnaam die overeenkomt met een |
62 |
hostelreservering, wordt aanzien als een reservering van dezelfde persoon. |
63 |
Als de namen niet overeen komen, dan zijn het verschillende groepen. |
64 |
|
65 |
Deze conventie laat mij toe om het programma veel beter te laten voldoen aan |
66 |
high cohesion/low coupling, zonder daarvoor veel voordelen voor hoeven in te |
67 |
leveren. |
68 |
|
69 |
Ik was origineel van plan om de zoekfunctionaliteit bij het bestaande |
70 |
zoekvenster te plaatsen, maar ik vond dat dat nogal onduidelijk ging worden. Ik |
71 |
heb dus voor wandelmanagement een apart zoekvenster gemaakt, te bereiken vanuit |
72 |
het hoofdmenu. |
73 |
|
74 |
Uiteindelijk ben ik blij van de beperkte aanpassingen in het eerste gedeelte van |
75 |
de challenge. Ik heb slechts hier en daar een extra member moeten toevoegen, en |
76 |
het geheel is nog steeds degelijk gesplitst. |
77 |
|
78 |
# Algemene keuzes ter bevordering codekwaliteit |
79 |
|
80 |
## Strings, integers, ... i.p.v. Enums/Eigen classes |
81 |
In het programma zijn er sommige members die doen vermoeden dat ze beter als |
82 |
Enums aangeduid kunnen worden; Het type van een kamer, faciliteiten, ... |
83 |
|
84 |
In plaats van een speciale class hiervoor te maken, heb ik gekozen om deze |
85 |
gewoon te behandelen als simpele Strings in lijsten. |
86 |
|
87 |
Het voordeel aan deze werkwijze, is dat Strings in Java SE quasi universeel |
88 |
voorkomen; data kunnen geconverteerd worden naar Strings (en vice versa), veel |
89 |
gebruikte GUI-libraries (waaronder Swing, JavaFX, ...) gebruiken Strings voor |
90 |
tekst voor te stellen in de GUI-widgets, ... |
91 |
Daarnaast weet iedereen die ook maar een beetje geschoold is in Java, direct hoe |
92 |
deze members behandeld kunnen worden. Had ik een class gemaakt die bv. |
93 |
RoomFacilities heette, dan is die parate kennis over de taal zelf nutteloos. |
94 |
|
95 |
Strings zijn ook voorzien van extreem veel methods, en zijn dus zeer flexibele |
96 |
classes om te gebruiken. Het feit dat ze zo makkelijk doorheen het programma te |
97 |
gebruiken zijn is dus een groot pluspunt. |
98 |
|
99 |
Als dat nog niet genoeg is: Het gebruiken van de hulpmiddelen uit de _standard |
100 |
library_ is niet enkel waarvoor libraries überhaupt bestaan, het beperkt ook |
101 |
het benodigde aantal classes in de eigen software, waardoor het geheel |
102 |
uiteindelijk veel overzichtelijker blijft, en bijgevolg, makkelijk uit te |
103 |
breiden, te testen, te begrijpen, ... |
104 |
|
105 |
Al deze voordelen graan grotendeels verloren als ik beslis om zelf |
106 |
gespecialiseerde classes op te stellen. |
107 |
Men kan misschien stellen dat "de voordelen die het schrijven van eigen |
108 |
classes veel beter tegemoet komt aan de essentie van object-georiënteerd |
109 |
programmeren, dan zich _beperken_ tot slechts een handvol generieke classes, die |
110 |
misschien niet volledig aan de benodigdheden beantwoorden". |
111 |
|
112 |
Toch laat ik mijn afweging overslaan in het voordeel van minder classes, en meer |
113 |
uniformiteit. _Sometimes, less is more._ Meer classes betekent ook: |
114 |
- Meer onderhoud |
115 |
- Meer kans op bugs |
116 |
- Groter programma |
117 |
|
118 |
En al die problemen resulteren op lange termijn in: |
119 |
- Slechtere uitbreidbaarheid |
120 |
- Slechtere onderhoudbaarheid |
121 |
- Slechtere schaalbaarheid |
122 |
|
123 |
Zijn al die problemen de moeite van een (zogezegd) "beter object-georiënteerd |
124 |
design" wel waard? |
125 |
Veel mensen stellen juist dat OOP net voordelig is als men op zoek is naar |
126 |
- Uitbreidbaarheid |
127 |
- Onderhoudbaarheid |
128 |
- Modulariteit |
129 |
- Schaalbaarheid |
130 |
|
131 |
Als de voordelen van het paradigma verdwijnen als ik dat "beter design" volg, |
132 |
is dat design dan wel echt beter? |
133 |
|
134 |
|
135 |
**Conclusie: Uniforme classes zijn volgens mij soms beter dan een stel |
136 |
gespecialiseerde classes. Daarom dat ik mij soms behelp met Strings, intergers, |
137 |
... i.p.v. zelf een oplossing te schrijven.** |
138 |
|
139 |
|
140 |
### Null pointers |
141 |
Het valt misschien op dat ik doorheen mijn programma in veel contracten eis dat |
142 |
geen enkele parameter een zgn. *null pointer* is. |
143 |
|
144 |
Het gebruik van null pointers staat garant voor een overvloed aan moeilijk te |
145 |
vinden bugs die (door het design van object-georienteerd programmeren) enorm diep |
146 |
kunnen doorpropageren. |
147 |
|
148 |
Ik maak er een kerntaak van dat, als er aan mijn programma gewerkt wordt, de |
149 |
programmeur zichzelf steeds kan garanderen dat **alle** data die hij |
150 |
ontvangt, valide data is, tenzij dat expliciet anders wordt aangegeven. |
151 |
Op deze manier valt er een hele last van de schouders van de programmeur; een |
152 |
reeks fouten worden voorkomen, simpelweg door een strikt schema aan te houden. |
153 |
|
154 |
Het controleren op null pointers wordt op 2 manieren gedaan: |
155 |
- Gebruik van *methods* aanwezig in het gegeven type. Als de gegeven variabele |
156 |
een null pointer is, zal het programma direct crashen, en een |
157 |
NullPointerException geven. |
158 |
- Expliciet testen of *var == null*. Wordt toegepast op parameters die direct |
159 |
als members opgeslagen dienen te worden. |
160 |
|
161 |
Een uitzondering op het vermijden van null pointers vormen sommige methods die |
162 |
slechts 1 object teruggeven. In deze gevallen kan het voorvallen dat het te |
163 |
zoeken object niet gevonden wordt. Om de ontwikkelaar hiervan op de hoogte te |
164 |
stellen, wordt dan soms een null teruggestuurd. |
165 |
Dit wordt steeds expliciet vermeld. |
166 |
|
167 |
Deze (contractuele) controle laat toe dat, mocht er een null pointer gebruikt |
168 |
worden, het programma de programmeur hiervan direct op de hoogte stelt, en dit |
169 |
op het laagst mogelijke niveau (namelijk de eerste method waar deze waarde |
170 |
gebruikt wordt). |
171 |
|
172 |
### Cloning |
173 |
members 'private' maken (encapsuleren) is volledig nutteloos als men getters en |
174 |
setters op deze members toepast; In Java worden references doorgegeven (m.u.v. |
175 |
primitives), die de hele notie van encapsulatie voorbijgaan (bij sommige types). |
176 |
Een voorbeeld hiervan is het privatiseren van een Set<T>-member: men kan daar |
177 |
een 'getSet()'-method op plaatsen, en dan toch de inhoud van deze 'private' |
178 |
aanpassen. |
179 |
|
180 |
Ik heb geopteerd om, waar van toepassing, deze variabelen te 'clonen', om zo |
181 |
exacte kopieën terug te geven. |
182 |
Deze manier van werken brengt enkele voordelen teweeg: |
183 |
- Zeer defensief programmeren; De ontwikkelaar kan geen members aanpassen als |
184 |
dat niet de bedoeling was |
185 |
- Duidelijkheid code: getSet().clear() zal de member niet meer leegmaken. Om dat |
186 |
te doen, moet men gebruikmaken van de method die daarvoor bedoeld is: |
187 |
setSet(clearedSet) |
188 |
|
189 |
### Inheritance |
190 |
Overerving is een goed concept over het algemeen, maar **niet** in OOP. |
191 |
De problemen omtrent impliciet gedrag en onnodige *state* zijn al te vaak |
192 |
beschreven met OOP-inheritance. |
193 |
|
194 |
Ik heb in mijn programma geen gebruik gemaakt van inheritance, exact omwille van |
195 |
de problemen die het voortbrengt, zeker in termen van herbruikbaarheid en |
196 |
robuustheid, wat toch zware vereisten waren voor deze opdracht. |
197 |
|
198 |
Ik heb al mijn problemen makkelijk kunnen oplossen d.m.v. compositie. |
199 |
|
200 |
### Benaming variabelen |
201 |
Doorheen mijn programma maak ik heel veel gebruik van dezelfde benamingen. |
202 |
Bijvoorbeeld: Een variabele van het type Reservation zal haast altijd |
203 |
'reservation' heten, een Set van een bepaald type zal de naam van datzelfde type |
204 |
dragen, in het meervoud, ... |
205 |
|
206 |
Sommige programmeurs gebruiken liever afkortingen (bv. 'reservation' --> |
207 |
'resv'), omdat dit sneller schrijft. |
208 |
|
209 |
Naar mijn mening moet men bij deze werkwijze inleveren aan leesbaarheid, vooral |
210 |
wanneer iemand die nog nooit met de code gewerkt heeft, dit programma moet |
211 |
overnemen. |
212 |
|
213 |
Daarnaast zorgt de consistentie van woordgebruik ervoor dat een andere |
214 |
programmeur, doorheen het hele programma dezelfde context in zijn/haar gedachten |
215 |
kan gebruiken. |
216 |
|
217 |
Nu, ik maak hier soms een uitzondering op, omdat het soms zo lang wordt dat de |
218 |
code in zijn geheel moeilijker leesbaar wordt. |
219 |
Een voorbeeld van hoe dit uitmondt is Objective-C. Haast **alles** wordt voluit |
220 |
genoteerd, waardoor van de eigenlijke essentie van het programma haast niks |
221 |
overblijft. |
222 |
|
223 |
De controllers heb ik daarom in sommige gevallen toch tot hun initialen |
224 |
afgekort. Ik vond dat het verlies aan expressiviteit meer dan goed gemaakt werd |
225 |
door het winnen aan leesbaarheid. |
226 |
|
227 |
|
228 |
## Toepassing types/classes |
229 |
Doorheen het programma heb ik getracht zoveel mogelijk gebruik te maken van |
230 |
types/classes die |
231 |
- Veelvoorkomend zijn in de Java STL (Zoals String, Set, List, ...) |
232 |
- primitief zijn (ints, ...), omdat deze operatoren hebben en de code |
233 |
leesbaarder maken |
234 |
|
235 |
Een goed voorbeeld hiervan zijn bv. de faciliteiten: |
236 |
I.p.v. een aparte "Facility"-class te maken, heb ik de verschillende |
237 |
faciliteiten voorgesteld door een simpele String. De voordelen van deze aanpak |
238 |
zijn ook direct duidelijk: |
239 |
- Betekenis is direct duidelijk; de faciliteit letterlijk in de code vernoemd |
240 |
- Makkelijke interactie met GUI, die sowieso Strings vraagt voor bv. JLabel |
241 |
- Uitbreidbaarheid wordt bekomen door simpelweg een nieuwe String te |
242 |
introduceren |
243 |
|
244 |
## View en GUI |
245 |
Werken met GUI's is vaak tijdrovend en veroorzaakt snel errors, zeker met bv. |
246 |
anonieme methods, exceptions, ... |
247 |
Alhoewel mijn programma grotendeels in een MVC-stijl is geschreven, maken de |
248 |
view-classes (RegistrationView, SearchView, ...) achterliggend gebruik van een |
249 |
zelfgemaakt framework om makkelijk vensters te maken. |
250 |
Dit kleine framework is een persoonlijk hobbyproject dat ik JSugar noem. |
251 |
Het biedt een heleboel voordelen, vergeleken met elk GUI-venster zelf opstellen: |
252 |
- Vaak gebruikte GUI-widgets (zoals een label, textfield) worden aangemaakt en |
253 |
toegevoegd door slechts 1 method op te roepen |
254 |
- JSugar maakt gebruik van reflectie om op een leesbare en uitbreidbare manier |
255 |
knoppen te activeren: |
256 |
public JButton createButton(String text, String action, String methodName, Object triggerObject) |
257 |
'methodName' is een simpele String, en 'triggerObject' is het object waar deze |
258 |
method moet worden opgeroepen. |
259 |
- Automatische uitlijning van widgets middels de standaard FlowLayout |
260 |
Voor meer informatie kunt u JSugar-readme.md raadplegen. |
261 |
|
262 |
## java.util.Date |
263 |
|
264 |
Doorheen het programma maak ik vaak gebruik van de Date-class uit de Java STL, |
265 |
om de volgende redenen: |
266 |
- Uitbreidbaarheid: Door overal eenzelfde type te gebruiken, is het gemakkelijk |
267 |
om nieuwe modules toe te voegen, zonder dat daarvoor abstractielagen etc. |
268 |
nodig zijn. |
269 |
- Uniformiteit: Eenzelfde type uit de STL laat de ontwikkelaar toe om door het |
270 |
hele programma heen hetzelfde denkpatroon aan te houden; een |
271 |
Stringvoorstelling hier, een integer daar, ... veroorzaken problemen en bugs, |
272 |
die met deze class voorkomen worden. |
273 |
|
274 |
De datavelden in de GUI komen altijd met de huidige datum op het veld genoteerd. |
275 |
Geef data in in hetzelfde formaat, en de achtergrond doet de rest. |
276 |
|
277 |
### Deprecation |
278 |
Ik besef dat ik in java.util.Date veel gebruik maak van _deprecated methods_. |
279 |
Nu, deze methods bieden mij wel de kans om snel en gemakkelijk met data om te |
280 |
kunnen gaan. |
281 |
Zeker het tijdsgebrek dat komt kijken bij de challenges zorgen ervoor dat ik |
282 |
mijn niet de luxe kan veroorloven om een speciaal voor mij gemaakte dataclass te |
283 |
maken. |
284 |
|
285 |
### Ontbijtdata |
286 |
|
287 |
Het moeilijkste om te implementeren waren de data voor ontbijt. |
288 |
Er wordt voor de reservaties steeds een set bijgehouden van de data waarop |
289 |
ontbijt besteld is. |
290 |
Op zich is dit niet zo moeilijk. Het moeilijke hieraan, is de gebruiker van het |
291 |
programma op een begrijpelijke manier deze data te laten invoeren. |
292 |
Data worden ingevoerd via tekstvelden, omdat Swing geen zgn. "DatePicker" heeft. |
293 |
|
294 |
Ontbijt wordt doorheen het programma geserveerd om 9 uur. |
295 |
|
296 |
|
297 |
### GUI |
298 |
|
299 |
Swing biedt geen widget die zich specialiseert in het weergeven van informatie |
300 |
over een datum (een zgn. _calendar widget_). |
301 |
|
302 |
In de opdracht stond dat we geen "visueel aantrekkelijke GUI" hoeven te |
303 |
ontwerpen, maar wel een die "alle functionaliteit ondersteunt". |
304 |
|
305 |
Wegens het tijdsgebrek en de tekortkoming van Swing, heb ik besloten om data |
306 |
weer te geven in een simpel JTextField. |
307 |
Het biedt de beste oplossing voor wat gevraagd wordt in deze opdracht, en wegens |
308 |
de tijdslimiet tijdens de challenges zelf vormen ze een goede |
309 |
"kortetermijnoplossing". |
310 |
|
311 |
## Bedden |
312 |
|
313 |
Ik heb voor bedden een aparte class aangemaakt, omdat deze een bepaalde state |
314 |
bijhouden, nml. wanneer ze gereserveerd zijn. |
315 |
|
316 |
|
317 |
## Reservation |
318 |
Voor de reservaties heb ik besloten om enkel die data bij te houden die inherent |
319 |
gerelateerd is aan die reservatie: |
320 |
- Enkel gereserveerde bedden i.p.v. gereserveerde kamers. Qua uitbreidbaarheid |
321 |
heeft dit tot gevolg dat men gemakkelijk kan uitbreiden naar reservaties, |
322 |
gespreid over verschillende kamers. |
323 |
|
324 |
|
325 |
## ReservationView / WalkView |
326 |
Merk op hoe in ReservationView de data van de Reservation direct in de GUI wordt |
327 |
geplaatst. |
328 |
Dit staat toe om zeer gemakkelijk deze class te hergebruiken voor zowel het |
329 |
**aanmaken** als het **updaten** van de reservatie. |
330 |
|
331 |
Dezelfde werkwijze wordt toegepast ook WalkView. |
332 |
|
333 |
|
334 |
## Opmerkingen |
335 |
|
336 |
- Door het gebruik van de FlowLayout in de GUI, is het mogelijk dat sommige |
337 |
gedeelten van de GUI niet direct worden weergegeven, daar ze verborgen worden |
338 |
door de hoogte van het venster. Men kan deze tevoorschijn halen door zelf het |
339 |
venster in de hoogte te verstellen. |
340 |
|