-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
475 lines (464 loc) · 39.9 KB
/
index.html
File metadata and controls
475 lines (464 loc) · 39.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoBuilder</title>
<link rel="stylesheet" href="style.css">
<link rel="shortcut icon" href="assets/gb.ico" type="image/x-icon">
<meta property="og:url" content="https://geo-objects.netlify.app/">
<meta property="og:type" content="website">
<meta property="og:title" content="GeoBuilder">
<meta property="og:description" content="Zusammenfassung vom Arbeitsblatt GeoObjects des RP Freiburg.">
<meta property="og:image" content="https://geo-objects.netlify.app/assets/og-img.png">
</head>
<body>
<div class="container">
<div class="page">
<div class="basics" style="margin-bottom: 10px">
<div class="icon">INFO</div>
<p>Zur Basics-Section geht es hier: <a href="/basics.html">Basics</a>.</p>
</div>
<div class="basics">
<div class="icon">INFO</div>
<p>Eine kurze Einführung in UML findest du hier: <a href="/uml.html">UML</a>.</p>
</div>
<div class="section">
<h2>Disclaimer</h2>
<p>Ich besitze keinerlei Rechte an dem folgenden Gedankengut, da dies zu einem Großteil auf der Arbeit vom RP Freiburg und seiner Version dieses ABs basiert.</p>
</div>
<div class="section">
<h2>Stufe 1</h2>
<p>Wir beginnen mit dem Erstellen des einfachsten geometrischen Objektes: Dem Punkt. Dabei soll ein jeder Punkt die folgenden Eigenschaften haben:</p>
<p>Eine <code>x</code> und <code>y</code> Koordinate und eine Darstellungscolor <code>color</code>.</p>
</div>
<div class="section">
<h2>Testen_GPoint.java</h2>
<p>Eine neue Datei <code>Testen_GPoint.java</code> erstellen, dann den folgenden Quellcode einfach kopieren und einfügen und abschließend <code>Testen_GPoint.java</code> compilieren und ausführen.</p>
<p>Wird in der Konsole <code>Test was successful! Good job!</code> ausgegeben, dann funktioniert deine Methode perfekt. Siehst du stattdessen eine rote Fehlermeldung, dann stimmt leider etwas noch nicht ganz mit deiner Methode.</p>
<pre>
<code>
public class Testen_GPoint {
private GPoint p;
public int x, y;
public Testen_GPoint(){
this.x = 2;
this.y = 2;
p = new GPoint(x, y);
}
public void test() throws Exception {
if(!(p.abstandZu(4, 4) == this.distanceTo(4, 4))) throw new Exception();
else System.out.println("Test was successful! Good job!");
}
public double distanceTo(int mx, int my){
double dx, dy, d;
dx = (double) mx - this.x;
dy = (double) my - this.y;
d = Math.sqrt(dx * dx + dy * dy);
return d;
}
public static void main(String[] args) throws Exception {
Testen_GPoint pointTest = new Testen_GPoint();
pointTest.test();
}
}
</code>
</pre>
</div>
<div class="section">
<h2>Stufe 5: Neue Punkte erstellen</h2>
<p>
Die Punkte sollen nun ja, nicht wie zum Ende der vorherigen Stufe beschrieben im Konstruktor der Board-Klasse, vom Benutzer erzeugt werden. So soll bei einem Mausklick ein neues <code>GPoint</code> Objekt (mit den Koordinaten der Stelle des Klicks auf der Zeichenfläche) erstellt und in die Liste <code>geoObjects</code> eingefügt werden.
</p>
<p>
Um dieses Mausklick-Event "abzufangen", können wir die Methode <code>Board.mousePressed(MouseEvent e)</code> benutzen. Diese wird von JAVA nämlich genau dann aufgerufen, wenn die primäre (also die linke) Maustaste gedrückt wurde.
</p>
<p>
Also schreiben wir in diese <code>mousePressed()</code> Methode unseren Quellcode, der einen neuen Punkt erzeugt und der <code>geoObjects</code> Liste hinzufügt (Nach demselben Prinzip, wie wir es bereits im Konstruktor von <code>Board</code> gemacht haben).
</p>
<p>
Zur Initialisierung eines neuen <code>GPoint</code> Objektes benötigen wir eine x und y Koordinate, die diesem dann "zugewiesen" (In anderen Worten: Dem Konstruktor übergeben) wird. Diese Koordinaten liefert uns der Parameter <code>MouseEvent e</code>. Ganz einfach formuliert übergibt das "System" (Was auch immer hier genau das System sein mag) der <code>mousePressed()</code> Methode ein Objekt vom Typ <code>MouseEvent</code>, in welchem z.B. die Koordinaten des Mauszeigers bei Auslösung des Events gespeichert sind.
</p>
<p>
Um nun in der Methode mit diesen Koordinaten etwas anfangen zu können, die in dem <code>e</code> Objekt (Das ja eigentlich der übergebene Parameter der Methode ist) enthalten sind, benutzen wir folgendes Code-Snippet:
</p>
<pre>
<code class="JAVA">
int mx = e.getX();
int my = e.getY();
</code>
</pre>
<p>
So sind nach diesen beiden Zeilen die x und y Koordinate der Mausposition in <code>mx</code> und <code>my</code> gespeichert.
</p>
<div class="exercise">
<p>
Stelle eine Kopie deines Projekts in einem neuen Verzeichnis <code>GeomObj_D</code> her. Ergänze die <code>mousePressed()</code> Methode in der <code>Board</code> Klasse dann so, dass bei einem Mausklick an eine leere Stelle der Zeichnung ein neuer Punkt erzeugt wird.
</p>
<p>
Dieser Punkt muss dann natürlich auch noch in die <code>geoObjects</code> Liste hinzugefügt werden.
</p>
<p>
Zusätzlich sollte nach dem Einfügen eines neuen Punktes das <code>Board</code> neu geladen werden, damit der neue Punkt auch angezeigt wird. Dies lässt sich ganz einfach über einen Aufruf der Methode <code>repaint()</code> ganz am Ende deines Codes in der <code>mousePressed()</code> Methode erreichen. Wunder dich nicht, dass du nirgendwo direkt eine Definition dieser <code>repaint()</code> Methode siehst, <code>Board</code> erbt diese nämlich von <code>JPanel</code>.
</p>
</div>
<div class="exercise">
<p>
Verschiebe den Quellcode für das Einfügen von Punkten vorübergehend(!) von <code>mousePressed()</code> nach <code>mouseReleased()</code> bzw <code>mouseDragged()</code>. Was ändert sich dabei jeweils am Verhalten des Programms?
</p>
<p>
Wenn du etwas mehr wissen möchtest, dann kannst du über den Befehl <code>System.out.println(geoObjects.size());</code> im Konsolenfenster ausgeben lassen, wie groß die <code>geoObjects</code> Liste zum jeweiligen Zeitpunkt ist, d.h. wie viele Punkte schon hinzugefügt wurden.
<br>
Füge diesen dafür einfach in den Methoden <code>mousePressed()</code>, <code>mouseReleased()</code> bzw <code>mouseDragged()</code> ein.
</p>
<p>
Zu guter Letzt musst du nun den verschobenen Quellcode wieder zurück in die <code>mousePressed()</code> Methode verschieben (und ggf. den <code>System.out...</code> Befehl wieder entfernen)
</p>
</div>
</div>
<div class="section">
<h2>Stufe 6: Jetzt wird's dynamisch!</h2>
<p>
Nun können wir Punkte per Mausklick hinzufügen. Allerdings wollen wir nun diese plazierten Punkte auch verschieben können.
Dies regeln wir wie folgt:
</p>
<p>
Wenn die Maustaste auf einen schon vorhandenen Punkt p (oder hinreichend dicht bei p) gedrückt wird, dann soll dieser Punkt als <code>dragPoint</code>, also als "zu verziehender" Punkt gespeichert werden.
Wird nun die Maus bei weiterhin gedrückter Taste bewegt, soll dieser Punkt der Mausbewegung folgen, bis die Maustaste wieder losgelassen wird.
</p>
<p>
Hierfür müssen wir natürlich erst einmal wissen, ob die Koordinaten des Mauszeigers beim Klicken in der Nähe, bzw. "ganz dicht" an einem bereits existierenden Punkt liegen.
</p>
<p>
Dies erledigen wir in der Methode <code>mousePressed()</code> von <code>Board</code>, die ja immer dann aufgerufen wird, wenn die primäre Maustaste gedrückt wurde.
</p>
<p>
Dann untersuchen wir als erstes einfach alle Punkte der <code>geoObjects</code> Liste darauf, wie dicht sie bei der aktuellen Mausposition liegen. Eine dafür benötigte Schleife findest du bereits in der <code>Board.paint()</code> Methode oder hier:
</p>
<pre>
<code class="JAVA">
for (GPoint p: geoObjects) {
// Anweisungen hier einfügen
}
</code>
</pre>
<p>
In dieser Schleife ist eine Art "Laufvariable" definiert, <code>p</code> von Typ <code>GPoint</code>. Diese Schleife "läuft" über die Liste <code>geoObjects</code> und ihre Laufvariable <code>p</code> nimmt in jedem Durchlauf den Wert an der Stelle der Liste an, an der die Schleife momentan steht.
<br>
D.h. wenn wir annehmen, dass an der nullten Stelle von <code>geoObjects</code> das Objekt <code>GPoint point1 = new GPoint(1, 1)</code> steht, so würde die Laufvariable <code>p</code> im allerersten Schleifendurchlauf die Referenz des Objekts <code>point</code> annehmen.
</p>
<p>
Hier die ganze Erklärung in Code ausgeschrieben:
</p>
<pre>
<code class="JAVA">
ArrayList<GPoint> geoObjects = new ArrayList<GPoint>();
GPoint point1 = new GPoint(1, 1);
geoObjects.add(point1);
GPoint point2 = new GPoint(2, 2);
geoObjects.add(point2);
for (GPoint p: geoObjects) {
System.out.println("X: " + p.getX() + "; Y: " + p.getY());
}
// Gibt in der Konsole aus:
// X: 1; Y: 1
// X: 2; Y: 2
</code>
</pre>
<p>
Wie groß der Abstand zwischen zwei Punkten, also des Punktes <code>p</code> und der aktuellen Mausposition, ist, können wir über einen Aufruf der bereits in Schritt 3 geschriebenen Methode <code>abstandZu()</code> der Klasse <code>GPoint</code> berechnen.
</p>
<p>
Hierfür übergeben wir dieser Methode einfach die Maus-Koordinaten, welche uns wie bereits in Schritt 5 erwähnt, über den Parameter <code>e</code> des Typs <code>MouseEvent</code> übergeben werden: <code>e.getX()</code> und <code>e.getY()</code> geben die jeweilige Koordinate zurück.
</p>
<p>
Wir erinnern uns: Da die Methode <code>abstandZu()</code> zur Klasse <code>GPoint</code> gehört und wir sie als <code>public</code> definiert haben, können wir sie über jedes Objekt vom Typ <code>GPoint</code> aufrufen. So dann auch über die Laufvariable <code>p</code> der Schleife, da diese ja vom Typ <code>GPoint</code> ist und in jedem Durchlauf das Element in der <code>geoObjects</code> Liste an der momentanen Stelle in ihr gespeichert wird. Darüber lässt sich ganz einfach der Abstand zwischen einem jeden Punkt, der auch ein Element in <code>geoObjects</code> ist und dem Mauscursor berechnen, indem wir über das Objekt <code>p</code> (Die Laufvariable) <code>abstandZu()</code> aufrufen und dieser Methode die Koordinaten des Mauscursors, also <code>e.getX()</code> und <code>e.getY()</code> übergeben.
</p>
<p>
Dieser Methodenaufruf liefert der Methodendefinition nach einen Wert von Typ <code>double</code>, den Abstand zwischen den beiden Punkten, also genau das, was wir eigentlich brauchen.
</p>
<p>
Ist der Abstand nun kleiner als 4 (Pixel), so weisen wir der Variablen <code>dragPoint</code> den Punkt p zu; andernfalls behält <code>dragPoint</code> den Wert <code>null</code> (D.h. wir müssen ihn nicht erneut auf <code>null</code> setzen).
</p>
<p>
Anschließend überprüfen wir dann, ob <code>dragPoint</code> nicht <code>null</code> ist. Wenn dies der Fall ist, soll dieser Punkt so lange der Maus folgen, bis die Maustaste wieder losgelassen wird.
</p>
<p>
Dies erreichen wir, indem wir in der <code>mouseDragged()</code> Methode den gezogenen Punkt, also <code>draggedPoint</code> stets an die aktuelle Mausposition setzen.
<br>
Auch in dieser Methode erhalten wir die Koordinaten des Mauszeigers über den übergebenen Parameter <code>e</code> des Typs <code>MouseEvent</code>:
</p>
<pre>
<code class="JAVA">
dragPoint.setX(e.getX());
dragPoint.setY(e.getY());
</code>
</pre>
<p></p>
<p>
Nun sind zwar die Koordinaten des gezogenen Punktes aktualisiert, jedoch wird diese Änderung noch nicht angezeigt.
<br>
Auch hier müssen wir die Methode <code>repaint()</code> zum Neuladen, also zum Aktualisieren des Fensters aufrufen.
</p>
<p>
Was aber, wenn <code>dragPoint</code> gleich <code>null</code> ist? Dann wurde die primäre Maustaste an einer freien Stelle des Zeichenbreichs gedrückt. In dieser Situation sollten wir also einen neuen Punkt hinzufügen - Also genau das tun, was wir bereits in Schritt 5 getan haben!
</p>
<p>
Baue also den schon vorhandenen Quelltext zur Erzeugung eines neuen Punktes so in die neue <code>mousePressed()</code> Methode ein, dass er nur noch im Falle <code>dragPoint == null</code> ausgeführt wird.
</p>
<p>
Abschließend müssen wir noch den Zugvorgang beim Loslassen der Maustaste beenden.
<br>
Hierfür muss einfach in <code>mouseReleased()</code> die Variable <code>dragPoint</code> auf null zu setzen.
</p>
<div class="exercise">
<p>
Erstelle eine Kopie deines Projektes in einem neuen Verzeichnis <code>GeomObj_E</code> und ergänze die Maus-Methoden in der Klasse <code>Board</code> wie oben beschrieben.
</p>
</div>
<div class="tipp" style="font-family: monospace !important">
<p>
In <code>mousePressed()</code> {
</p>
<p style="padding-left: 20px">if-Schleife ("<i>Ist <code>dragPoint</code> nicht gleich null?</i>") {</p>
<p style="padding-left: 40px">for-Schleife {</p>
<p style="padding-left: 60px">if-Schleife (" <i> Ist der Abstand zwischen Punkt <code>p</code>(der Laufvariablen der Schleife) und dem Punkt mit den Koordinaten (<code>e.getX()</code>|<code>e.getY()</code>) kleiner als 4?" </i>) { </p>
<p style="padding-left: 80px">Setze <code>dragPoint</code></p>
<p style="padding-left: 60px">}</p>
<p style="padding-left: 40px">}</p>
<p style="padding-left: 40px">repaint</p>
<p style="padding-left: 20px">}</p>
<p>}</p>
</div>
<div class="tipp" style="font-family: monospace !important">
<p>
In <code>mouseDragged()</code> {
</p>
<p style="text-indent: 20px">Setze neue Koordinaten für <code>dragPoint</code></p>
<p>}</p>
</div>
<div class="tipp" style="font-family: monospace !important">
<p>
In <code>mouseReleased()</code> {
</p>
<p style="text-indent: 20px">Reset <code>dragPoint</code> (auf <code>null</code>)</p>
<p>}</p>
</div>
</div>
<div class="section">
<h2>Stufe 7: Andere geometrische Objekte: Strecken</h2>
<p>
Nun wollen wir in unserer Applikation ja nicht nur mit Punkten arbeiten, sondern mit "komplexeren" geometrischen Objekten, wie z.B. Strecken.
</p>
<p>
Hierfür benötigen wir logischerweise auch gleich eine neue Klasse <code>GLine</code>.
</p>
<p>
Genau wie die Punkte, sollen sich auch die Strecken mit Hilfe einer <code>draw()</code> Methode im Zeichenfenster darstellen können.
</p>
<p>
Strecken haben stets einen Anfangs- und einen Endpunkt und (genau wie die Punkte) eine Farbe, somit ist das folgende UML-Diagramm für die Strecken naheliegend:
</p>
<div class="center">
<img src="./assets/dia1.png" alt="" srcset="">
</div>
<p>
Nun stoßen wir allerdings schnell auf ein großes Problem: Wenn wir die Klasse <code>GLine</code> fertig implementiert und eine Instanz dieser Klasse, also ein Strecken-Objekt erzeugt hätten, müssten wir dieses ja auch in die <code>geoObjects</code> Liste eintragen.
</p>
<p>
Hier stellt sich jedoch JAVA quer - in die <code>geoObjects</code> Liste können ja nur Objekte vom Typ <code>GPoint</code> hinzugefügt werden.
<br>
Gleichzeitig gibt es keine Möglichkeit, in einer solchen <code>ArrayList</code> mehrere Objekte von verschiedenen Typen, also mehrere Instanzen verschiedener Klassen zu speichern.
</p>
<p>
Somit brauchen wir eine Art gemeinsame "Oberklasse" für <code>GPoint</code> und <code>GLine</code>. Beide Klassen beschreiben ja GeoObjekte, also bietet sich für den Namen der übergeordneten Oberklasse <code>GeoObject</code> prima an.
</p>
<p>
Diese Klasse soll nun alle Eigenschaften (also Attribute) und Methoden beinhalten, die im oberen UML Diagramm sowohl in <code>GPoint</code> und <code>GLine</code> vorhanden sind.
</p>
<p>
Alle anderen Eigenschaften, in denen sich die beiden Klassen unterscheiden, bleiben damit in ihnen selbst. Damit sind <code>GPoint</code> und <code>GLine</code> jeweils Spezialisierungen von <code>GeoObject</code>.
</p>
<p>
Jedoch gleichen sich <code>GPoint</code> und <code>GLine</code> nicht zu 100%. Damit müssen sie sich voneinander und vor allem von der Oberklasse unterscheiden. Die Eigenschaften, die sie spezialisieren werden also jeweils in (und nur in) <code>GPoint</code> und <code>GLine</code> definiert.
</p>
<p>
Damit sind die beiden Klassen <code>GPoint</code> und <code>GLine</code> Spezialisierungen von <code>GeoObject</code>. Sie sollen alle Eigenschaften und Fähigkeiten (Also Attribute und Methoden) von <code>GeoObject</code> besitzen und darüber hinaus noch eigene Eigenschaften ergänzen.
</p>
<p>
Wenn eine Unterklasse die Eigenschaften einer Oberklasse haben soll, spricht man von Vererbung.
</p>
<p>
Um diese Vererbung in Code auszudrücken, verwenden wir das <code>extends</code> Schlüsselwort in der Klassendefinition.
</p>
<p>Damit würde die neue Klassendefinition von <code>GPoint</code> nun wie folgt aussehen:</p>
<pre>
<code class="JAVA">
public class GPoint extends GeoObject { ... }
</code>
</pre>
<p>Möchte man sich unbedingt durch die Hölle des Java-Editors schlagen, so ist es möglich einen solchen Ausdruck auch automatisch zu erzeugen, indem man im UML-Klassen-Editor auf der Registerkarte <i>Klasse</i> im Feld <i>Abgeleitet von</i> die Klasse, von der geerbt werden soll, eingeträgt - In unserem Fall also für <code>GPoint</code> und <code>GLine</code> jeweils <code>GeoObject</code>.</p>
<div class="center">
<img src="./assets/pic1.png" style="width: 60%;">
</div>
<p>Andererseits (was durchaus um einiges empfehlenswerter ist), lässt sich natürlich der Quellcode auch direkt bearbeiten und die Klassendefinitionen von <code>GPoint</code> und <code>GLine</code> durch <code>extends GeoObject</code> wie in dem oberen Code-Snippet gezeigt, ergänzen.</p>
<p>Befindest du dich noch in der UML-Ansicht, dann mache einfach einen Rechtsklick auf die entsprechende Klasse und klicke dann <i>Quelltext öffnen</i>.</p>
<p>Wenn du nun den Code richtig über eine der beiden zueben genannten Methoden ergänzt hast und anschließend zur UML-Ansicht zurückkehrst, sollte diese nun in etwa so aussehen (WICHTIG: Du kannst diesen Schritt erst durchführen, wenn du die Klasse <code>GeoObject</code> bereits geschrieben hast, was aufgrund der dämlichen Struktur dieses ABs leider noch nicht passiert sein kann):</p>
<div class="center">
<img src="./assets/pic2.png" alt="" srcset="">
</div>
<p>Hier führt nun jeweils ein fetter Pfeil von jeder abgeleiteten Unterklasse, die von <code>GeoObject</code> erbt zu <code>GeoObject</code>, der das Vererbungs-Verhältnis ausdrückt.</p>
<p>So kann man diese Pfeile als <i>... ist ein ...</i> lesen. Für uns also:</p>
<ul>
<li>Ein <code>GPoint</code> Objekt ist ein <code>GeoObject</code> ...</li>
<li>Ein <code>GLine</code> Objekt ist ein <code>GeoObject</code> ...</li>
</ul>
<p>... das heißt, dass sie jeweils über alle Attribute und Methoden von GeoObject verfügen, diese allerdings noch durch ihre eigenen speziellen Attribute und Methoden ergänzen.</p>
<p>Anmerkung eine Erklärung der Bedeutung der einzelnen Pfeile und deren Bezeichnung findest du in der <a href="/uml.html">Einführung zu UML</a>.</p>
<p>Nun werfen wir noch einen genaueren Blick auf die <code>GeoObject</code> Klasse im UML-Diagramm:</p>
<div class="center">
<img src="./assets/pic3.png" style="width: 52%;">
</div>
<p>Hierbei fällt vor allem eine Sache auf: "GeoObject", also der Klassenname und die <code>draw()</code> Methode sind beide kursiv geschrieben. Was bedeutet das?</p>
<p>Um das zu verstehen, müssen wir uns nun erst einmal überlegen, was die <code>draw()</code> Methode eigentlich ist.</p>
<p>In ihr stehen jeweils die Anweisungen, die einen Punkt, bzw. eine Strecke auf das Board zeichnen.</p>
<p>Damit implementieren <code>GPoint</code> und <code>GLine</code> diese Methode auf zwei grundlegend verschiedene Arten. Ein Punkt muss ja offensichtlich anders gezeichnet werden, als eine Strecke.</p>
<p>Gleichzeitig soll aber auch jede Klasse, die von <code>GeoObject</code> eine Art von <code>draw()</code> Methode implementieren, da wir diese ja später in <code>Board</code> zum eigentlichen Zeichnen des Objektes aufrufen müssen.</p>
<p>Weiter hängt die genaue Implementierung dieser Methode in einer erbenden Klasse in jedem Fall von dem jeweiligen Objekt ab, das gezeichnet werden soll.</p>
<p>Deshalb ist es auch nicht wirklich sinnvoll eine Art "Standart-Implementierung" direkt in <code>GeoObject</code> vorzunehmen und diese dann später in den erbenden Klassen zu überschreiben (Falls dir das Überschreiben von Methoden noch kein Begriff ist, melde dich gerne bei mir 😄. Eventuell ist zu diesem Zeitpunkt dann auch schon eine detaillierte Erklärung auf dieser Website verfügbar).</p>
<p>Die Implementierung ist sowieso in allen von <code>GeoObject</code> erbenden Klassen verschieden.</p>
<p>Deshalb legen wir in <code>GeoObject</code> lediglich fest, dass eine von ihr erbende Klasse eine <code>draw()</code> Methode implementieren muss, die <code>public</code> sein soll, den Rückgabetyp <code>void</code> hat und als einzigen Parameter <code>gr</code> vom Typ <code>Graphics</code> entgegennimmt.</p>
<p>Wie genau die <code>draw()</code> dann sonst noch aussieht, d.h. wie genau das Objekt dann gezeichnet wird, wird den jeweiligen erbenden Klassen überlassen.</p>
<p>Um diese spezielle Eigenschaft der <code>draw()</code> Methode in Code auszudrücken, müssen wir sie als <code>abstract</code> definieren.</p>
<p>Eine als <code>abstract</code> definierte Methode hat in der Klasse, in der sie definiert wird (also im Fall von <code>draw()</code> die Klasse <code>GeoObject</code>) <i>keinen</i> Methodenrumpf, womit statt einem geschweiften Klammerpaar der Methodendefinition nur noch ein Semikolon folgt:</p>
<pre>
<code class="JAVA">
public abstract void draw(Graphics gr);
</code>
</pre>
<p>Sobald eine Methode einer Klasse als <code>abstract</code> definiert wurde, muss diese Klasse ebenfalls als <code>abstract</code> definiert werden, da nun keine Instanz (kein Objekt) dieser Klasse mehr erstellt werden kann. Es fehlt ja eine vollständige Definition der abstrakten Methode.</code></p>
<p>Zusammengefasst gilt für die <code>GeoObject</code> Klasse also folgendes:</p>
<p>Sie besitzt...</p>
<ul>
<li>ein Attribut <code>color</code>, das als <code>private</code> definiert wird</li>
<li>einen Konstruktor <code>GeoObject()</code>, der keinen Parameter entgegennimmt. In ihm wird das Attribut <code>color</code> intialisiert. </li>
<li>einen Getter <code>getColor()</code> und Setter <code>setColor(Color colorNew)</code> für <code>color</code></li>
<li>eine abstrakte Methode <code>draw(Graphics gr)</code>, die den Rückgabetyp <code>void</code> besitzt, als <code>public</code> definiert wird und einen Parameter <code>gr</code> vom Typ <code>Graphics</code> entgegenimmt.</li>
</ul>
<p>Damit haben wir nun alle Information, die wir benötigen, um die <code>GeoObject</code> Klasse zu implementieren.</p>
<p>Auch hier gibt es wieder zwei Möglichkeiten, die Eigenschaft <code>abstract</code> festzulegen.</p>
<p>Für alle, die den ungemütlichen Java-Editor, bzw dessen UML-Editor verwenden wollen (und sich damit durch diesen ganzen Prozess begleitet von unzähligen Fehlermeldungen quälen werden): </p>
<p>Im UML-Diagramm: Rechtsklick > Neue Klasse. In dem Fenster anschließend <i>GeoObject</i> als Klassenname angeben und <i>Speichern</i> klicken. In dem nun neugeöffneten Fenster unter der Registerkarte <i>Klasse</i> ein Häkchen bei <i>abstrakt</i> setzen.</p>
<p>Anschließend das Attribut <code>color</code> (Wobei der Getter und Setter automatisch erstellt werden können) und die abstrakte Methode <code>draw(Graphics gr)</code> hinzufügen.</p>
<p>Die Optionen für die beiden abstrakten "Neuheiten":</p>
<div class="center">
<img src="./assets/pic4.png" style="width: 52%;">
<img src="./assets/pic5.png" style="width: 52%;">
</div>
<p>Alternativ kannst du natürlich auch diese ganzen Dinge im rohen Quellcode einfügen.</p>
<p>Dazu einfach die beiden Klassendefinitionen von <code>GPoint</code> und <code>GLine</code> durch ein <code>extends GeoObject</code> ergänzen. Und die folgende Methode einfügen, bzw. ein <code>@Override</code> über der alten <code>draw()</code> Methode hinzufügen:</p>
<pre>
<code class="JAVA">
@Override
public void draw(Graphics gr){
// Anweisungen
}
</code>
</pre>
<p>Beachten solltest du hierbei noch, dass du nun natürlich auch alle überflüssigen Attribute in den erbenden Klassen entfernen solltest, da diese ja nun schon in <code>GeoObject</code> definiert sind. In unserem Fall betrifft das genau ein Attribut, nämlich <code>color</code>.</p>
<div class="exercise">
<p>Stelle eine neue Kopie deines Projektes in einem neuen Verzeichnis <code>GeomObj_F</code> her. Bevor du <code>GeoObj.uml</code> wieder öffnest, schließe erst einmal alle Dateien im JavaEditor.</p>
<p>Erstelle gemäß den Beschreibungen des Textes dieses Kapitels eine neue abstrakte Klasse <code>GeoObject</code>.</p>
<p>Noch einmal der Hinweis, dass du unbedingt darauf achten solltest, die Klasse <code>Color</code> überall dort, wo sie verwendet wird (also auch in <code>GeoObject</code>) auch zu importieren. </p>
<p>Der Konstruktor von <code>GeoObject</code> hat sogar auch schon etwas zu tun: Er soll das Attribut <code>color</code> initialisieren. Dafür kannst du dir prinzipiell eine beliebige Farbe aussuchen, jedoch empfiehlt sich natürlich der Einfachheit halber Schwarz.</p>
<p>Bearbeite dann wie bereits beschrieben die schon vorhandene Klasse <code>GPoint</code>, indem du alle nicht mehr benötigten Deklarationen entfernen und die Klasse von <code>GeoObject</code> erben lassen.</p>
<p>Hierbei gibt es noch eine Besonderheit zu beachten:</p>
<p>Der Konstruktor von jeder von <code>GeoObject</code> erbenden Klasse muss als erste Anweisung <code>super()</code> beinhalten. Dieses <code>super()</code> ruft den Konstruktor der Superklasse, also der Elternklasse <code>GeoObject</code> auf, was ja durchaus nötig ist, um <code>color</code> zu initialisieren.</p>
<p>Teste nun, ob dein Programm immer noch so funktioniert wie vor den Änderungen!</p>
</div>
<div class="exercise">
<p>Erstelle eine weitere neue Klasse <code>GLine</code>, die dem oben angegebenen UML-Diagramm entspricht.</p>
<p>Wenn du hier nicht ganz weiterkommst, werf einfach einen Blick auf den folgenden grün markierten Tipp-Block.</p>
<p>Auch hier solltest du nicht vergessen als erste Anweisung im Konstruktor <code>super()</code> aufzurufen (wie bei <code>GPoint</code>).</p>
<p>Implementiere abschließend die <code>draw(Graphics gr)</code> Methode, indem du in ihrem Inneren die Anweisung zum Zeichnen einer Linie einfügst.</p>
<p>Das als Parameter übergebene Objekt <code>gr</code> enthält eine Methode <code>drawLine()</code>. Schau dir um zu verstehen, was man ihr alles übergeben muss, einmal den folgenden Screenshot aus der Dokumentation der Klasse <code>Graphics</code> an oder gehe selbst auf die <a href="https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#drawLine(int,%20int,%20int,%20int)">Website von Oracle</a>:</p>
<div class="center">
<img src="./assets/pic6.png" style="width: 100%;">
</div>
</div>
<div class="tipp">
<p>Attribute und Methoden von <code>GLine</code>:</p>
<ul>
<li><code>point1</code> und <code>point2</code> vom Typ <code>GPoint</code>, jeweils als <code>private</code> deklariert.</li>
<li><code>GLine(GPoint point1, GPoint point2)</code> als Konstruktor, der zwei Parameter jeweils des Typs <code>GPoint</code> entgegennimmt.</li>
<li>eine überschriebene <code>draw()</code> Methode, deren Signatur bereits im zuvorigen Text erklärt wurde.</li>
</ul>
</div>
</div>
<div class="section">
<h2>Stufe 7 - Der letzte Act: Strecken verwenden</h2>
<h3>Strecken Objekte erzeugen</h3>
<p>Nun wollen wir ja unsere neu implementierte <code>GLine</code> Klasse auch in Aktion erleben.</p>
<p>Damit wir nun aber überhaupt erst <code>GLine</code> Objekte in <code>geoObjects</code> hinzufügen können, müssen wir noch einige Dinge ändern:</p>
<p>Zunächst müssen wir den Typ von <code>geoObjects</code> ein wenig anpassen: Diese Liste soll ja nicht mehr nur <code>GPoint</code> Objekte beinhalten (Was durch <code>ArrayList<GPoint></code> bisher noch ausgedrückt wird), sondern alle Objekte, die von <code>GeoObject</code> erben.</p>
<p>Ändere hierfür einfach <code>ArrayList<GPoint></code> um zu <code>ArrayList<GeoObject></code> und ab sofort kannst du auch <code>GLine</code> Objekte in <code>geoObjects</code> speichern.</p>
<p>Um nun unsere <code>GLine</code> Klasse zu testen, wollen wir im Konstruktor von <code>Board</code> gleich ein neues <code>GLine</code> Objekt erzeugen.</p>
<p>Der Konstruktor von <code>GLine</code> erwartet zwei <code>GPoint</code> Objekte. Da wir im Konstruktor von <code>Board</code> ja bereits mehrere solcher Objekte erzeugt haben (Bzw. eigentlich ja nur genau eins, dieses dann aber immer wieder neu initialisiert und dann in <code>geoObjects</code> eingefügt), können wir einfach diese verwenden.</p>
<p>Wie aber schon angedeutet, müssen wir zuvor den Quellcode zum Erzeugen der Punkte so abändern, dass wir nicht immer einen einzigen Punkt neu initialisieren, sondern vier <code>GPoint</code> Objekte <code>p1</code>, <code>p2</code>, <code>p3</code> und <code>p4</code>.</p>
<p>Dann kannst du dem Konstruktor von <code>GLine</code> ganz einfach zwei dieser vier <code>GPoint</code> Objekte übergeben und schon haben wir ein <code>GLine</code> Objekt.</p>
<p>Jedoch muss auch dieses Objekt dann natürlich noch in die <code>geoObjects</code> Liste hinzugefügt werden.</p>
<h3>Strecken Objekte zeichnen</h3>
<p>Damit die neuen <code>GLine</code> Objekte auch alle richtig gezeichnet werden, müssen wir nun noch die For-Schleife in der <code>Board.paint()</code> Methode anpassen, in welcher ja jeweils die <code>draw()</code> Methode der Objekte, die in <code>geoObjects</code> gespeichert sind, aufgerufen wird.</p>
<p>Die Laufvariable muss nun also abgeändert werden, da jetzt ja nicht mehr nur <code>GPoint</code> Objekte in <code>geoObjects</code> enthalten sind, sondern Objekte vom Typ <code>GeoObjects</code>.</p>
<p>Dafür müssen wir also den Datentypen der Laufvariablen von <code>GPoint</code> zu <code>GeoObject</code> ändern. Da diese Variable dann ja immer ein <code>GeoObject</code> und nicht nur einen <code>GPoint</code> beinhaltet, benennen wir sie auch gleich von <code>p</code> (was ja für Point stand) zu <code>go</code> (für GeoObject) um.</p>
<p>Damit wird ...</p>
<pre>
<code class="JAVA">
for(GPoint p: geoObjects){
p.draw(gr);
}
</code>
</pre>
<p>... zu ...</p>
<pre>
<code class="JAVA">
for(GeoObject go: geoObjects){
go.draw(gr);
}
</code>
</pre>
<h3>Weiter Punkte in <code>mousePressed()</code> hinzufügen</h3>
<p>In <code>mousePressed()</code> wird nach einem Punkt in der <code>geoObjects</code> Liste gesucht, der sich dicht an der Maus befindet.</p>
<p>Hierfür läuft eine for-Schleife über alle Einträge der Liste. Jedoch haben wir hier nun das gleiche Problem, wie bereits in der for-Schleife in der <code>Board.paint()</code> Methode. In <code>geoObjects</code> sind ja nicht mehr nur <code>GPoint</code> Objekte enthalten, sondern <code>GeoObject</code> Objekte im Allgemeinen.</p>
<p>Damit müssen wir auch hier die Laufvariable von <code>GPoint p</code> zu <code>GeoObject go</code> abändern.</p>
<p>Nun gibt es ja aber auch die Möglichkeit, dass das <code>go</code> Objekt gar kein <code>GPoint</code> Objekt mehr ist und wir damit die Distanzüberprüfung nicht mehr auf jedem Objekt in der <code>geoObjects</code> Liste aufrufen können, da <code>GLine</code> die Methode <code>abstandZu()</code> ja gar nicht erst implementiert.</p>
<p>Also darf der Schleifenrumpf nur dann ausgeführt werden, wenn das aktuelle Listenobjekt, was ja in der Laufvariable <code>go</code> gespeichert ist, wirklich vom Typ <code>GPoint</code> ist.</p>
</div>
<div class="section">
<h2>Polymorphie</h2>
<p>Zuletzt möchte ich in diesem Abschnitt noch auf eine weitere Besonderheit, die sich aus der <code>Board.paint()</code> Methode erkennen lässt, eingehen.</p>
<p>Wenn wir hierbei kurz einen Blick auf diese werfen, fällt uns etwas auf (Die ersten 4 Zeilen der Methode sind für uns erst einmal irrelevant):</p>
<pre>
<code class="JAVA">
public void paint(Graphics g) {
super.paint(g);
BasicStroke stroke2 = new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
((Graphics2D) g).setStroke(stroke2);
for (GeoObject go : geoObjects ) {
go.draw(g);
}
}
</code>
</pre>
<p>Mithilfe der for-Schleife iterieren wir über die <code>ArrayList</code> <code>geoObjects</code>. Wie bereits erklärt, nimmt die Laufvariable <code>go</code>, die vom Typ <code>GeoObject</code> sein muss, da in <code>geoObjects</code> ja sowohl <code>GPoint</code> als auch <code>GLine</code> oder andere GeoObjekte gespeichert werden können, in jedem Schleifendurchlauf den Wert des momentanen Elements in dieser <code>ArrayList</code> an.</p>
<p>Das Objekt <code>go</code> kann somit sowohl ein <code>GPoint</code>, als auch ein <code>GLine</code> oder anderes von <code>GeoObject</code> erbendes Objekt sein.</p>
<p>Das heißt, dass die <code>draw()</code> Methode jeweils aus einem anderen "Kontext" aufgerufen werden kann. In unserem Fall sind das bisher die Methoden: <code>GPoint.draw()</code> und <code>GLine.draw()</code>.</p>
<p>Da sich die Implementierung der beiden genannten <code>draw()</code> Methoden unterscheidet, da beide Klassen ja ihre eigene Version von <code>draw()</code> implementieren, spricht man hier von <b>Polymorphie</b>.</p>
<p>Polymorphie meint einfach nur, dass sich hinter einem Ausdruck (hier: <code>go.draw()</code>) verschiedene Methoden-Implementierungen verbergen können, weil jedes von <code>GeoObject</code> erbende Objekt die Methode <code>draw()</code> überschreiben und damit seine eigene Version implementieren muss.</p>
</div>
</div>
</div>
</body>
</html>