Lösungen zum Übungsblatt 12 - AG Softwaretechnik

Werbung
Prof. Dr. A. Poetzsch-Heffter
Dipl.-Inform. N. Rauch
Dipl.-Inform. C. Stenzel
Technische Universität Kaiserslautern
Fachbereich Informatik
AG Softwaretechnik
Lösungen zum Übungsblatt 12: Entwicklung von
Softwaresystemen I (WS 2003/04)
Aufgabe 1 Abstrakte Klassen
(10 Punkte)
a) Die Deklaration der Klassen Fahrzeug, Auto und Motorrad als abstrakt hat zur Folge, dass keine Objekte
dieser Klasse erzeugt werden können. Der Vorteil solcher Hierarchie abstrakter Klassen ist, dass sie die Klassifikation der Fahrzeuge nach ihren Eigenschaften wiederspiegeln. Zum Beispiel ist die Fahrgestellnummer eine
Eigenschaft, die gemeinsam für die Klassen Auto, Motorrad und Fahrrad gilt. Deshalb ist es sinnvoll, eine
abstrakte Oberklasse Fahrzeug über den Klassen Auto, Motorrad und Fahrrad einzuführen, und die Eigenschaft Fahrgestellnummer dieser Klasse zuzuordnen. Die Eigenschaft wird dann von den Unterklassen
einheitlich vererbt.
(2 Punkte)
b) class PKW extends Auto {
class LKW extends Auto {
public LKW(int fzNr, int geschw){
super(fzNr, geschw, 6);
}
public boolean istPKW(){
return false;
}
}
public PKW(int fzNr, int geschw){
super(fzNr, geschw, 4);
}
public boolean istPKW(){
return true;
}
}
(3 Punkte)
c) Die Klasse Motorfahrzeug müsste als Unterklasse der Klasse Fahrzeug und Oberklasse der Klassen Auto
und Motorrad eingeordnet werden.
(1 Punkt)
d) Die Attribute in der neuen Klassenhierarchie müssen neu eingeordnet werden.
1
2
3
4
5
6
7
8
9
abstract class Fahrzeug {
protected int fahrgestellnummer;
public Fahrzeug(int fgNr) {
fahrgestellnummer = fgNr;
}
public int getFahrgestellNummer(){
return fahrgestellnummer;
}
}
10
11
12
13
14
15
17
19
20
21
22
23
24
25
26
30
31
32
33
abstract class Motorfahrzeug
extends Fahrzeug {
protected int geschwindigkeit;
protected int leistung;
public Motorfahrzeug
(int fzNr, int g, int l){
super(fzNr);
geschwindigkeit = g;
leistung = l;
}
getGeschwindigkeit(){
geschwindigkeit;
getLeistung(){
leistung;
}
34
35
37
38
39
40
41
42
16
18
29
36
class Fahrrad extends Fahrzeug {
public Fahrrad(int fzNr) {
super(fzNr);
}
}
public int
return
}
public int
return
}
27
28
43
44
45
46
47
abstract class Auto
extends Motorfahrzeug {
protected int anzahlraeder;
public Auto(int fzNr,int g,
int r, int l){
super(fzNr,g,l);
anzahlraeder = r;
}
public int getAnzahlRaeder() {
return anzahlraeder;
}
public abstract boolean istPKW();
}
48
49
50
51
52
abstract class Motorrad
extends Motorfahrzeug {
protected boolean beiwagen;
public Motorrad(int fzNr, int g,
boolean b, int l) {
super(fzNr,g,l);
beiwagen = b;
leistung = l;
53
54
55
56
}
public boolean hatBeiwagen(){
return beiwagen;
}
57
58
59
60
61
}
63
64
65
66
67
70
71
72
73
74
75
76
62
77
class PKW extends Auto {
public PKW(int fzNr, int geschw,
int leist){
super(fzNr, geschw, 4, leist);
}
public boolean istPKW(){
return true;
}
68
69
78
79
80
}
class LKW extends Auto {
public LKW(int fzNr, int geschw,
int leist){
super(fzNr, geschw, 6, leist);
}
public boolean istPKW(){
return false;
}
}
81
(3 Punkte)
e) Die neue Klassenhierarchie graphisch dargestellt:
Fahrzeug
fahrgestellnummer : int
Motorfahrzeug
geschwindigkeit : int
leistung : int
Auto
Motorrad
anzahlraeder : int
LKW
Fahrrad
beiwagen : int
PKW
(1 Punkt)
Aufgabe 2 Schnittstellentypen (praktisch)
(14 Punkte)
a) public interface AnklickbareFigur extends Figur,Anklickbar {}
(1 Punkt)
b) Für einen Kreis ist der Test, ob ein Punkt innerhalb der Figur liegt, einfach: Man muss nur prüfen, ob der Abstand
des Punktes (px , py ) zum Mittelpunkt (mx , my ) kleiner oder gleich dem Radius r ist. Also liefert die Testmethode
true, wenn (px − mx )2 + (py − my )2 ≤ r2 .
public class KreisLsg implements AnklickbareFigur {
int x,y,r;
// ...siehe Blatt 11
public boolean istAnklickbarBei(int x, int y) {
int dx = x-this.x,
dy = y-this.y;
return (dx*dx+dy*dy <= r*r);
}
}
2
Etwas schwieriger ist die Behandlung des Dreiecks. Hier muss man prinzipiell überprüfen, wie der Punkt relativ
zu den drei Seiten bzw. den entsprechenden Geraden liegt. Nehmen wir an, das Dreieck M P 1 P2 P3 sei entgegen
dem Uhrzeigersinn beschrieben. Dann muss der Punkt P links von jeder Seite liegen (bzw. darf nicht rechts von
einer Seite liegen, wenn man die Kanten auch zur Figur rechnet).
−−−→ −−→
−−−→ −−→
−−−→ −−→
Anders ausgedrückt: Die Winkel ^ P1 P2 , P1 P , ^ P2 P3 , P2 P und ^ P3 P1 , P3 P müssen positiv (0 bis
180◦ ) sein. Um dies schnell zu prüfen, kann auf das Vektorenprodukt im dreidimensionalen Raum zurückgegriffen
werden.






ax
bx
a y · bz − a z · by
Zur Erinnerung: Sei a =  ay  und b =  by . Dann ist a × b definiert als  az · bx − ax · bz  Der
az
bz
a x · by − a y · bx
Vektor a × b steht dann senkrecht auf a und b, und alle drei Vektoren bilden ein Rechtssystem.
Bei zwei Ebenen-Vektoren a und b (z-Komponente jeweils 0) zeigt a × b also nach „oben“ (positive z-Komponente), wenn a und b einen positiven Winkel bilden, anderenfalls nach „unten“.
Für den Test, ob ein Punkt innerhalb des Dreiecks liegt, müssen wir also die z-Komponenten der Kreuzprodukte
der oben genannten drei Vektorenpaare bilden (die anderen Komponenten sind ohnehin 0). Sind die Eckpunkte
entgegen dem Uhrzeigersinn angegeben, liegt der Punkt im Inneren des Dreiecks, wenn keine dieser Komponenten
negativ ist. Bei einer Angabe der Eckpunkte im Uhrzeigersinn ist es umgekehrt: Dann liegt der Punkt im Inneren,
wenn keine der Komponenten positiv ist.
Um beide Fälle einzuschließen testet man einfach, ob die Vorzeichen der drei Komponenten gleich sind (wobei
eine 0 als neutral zu behandeln ist). Das funktioniert deshalb, weil es keinen Punkt in der Ebene gibt, der mit allen
drei Seitenvektoren einen negativen Winkel bildet. So muss ein Punkt, der außerhalb des Dreiecks liegt, mindestens
zu einem Vorzeichenunterschied führen.
public class DreieckLsg implements AnklickbareFigur {
private int[] xPoints, yPoints;
// ...siehe Blatt 11
/* Implementiert die Signum-Funktion
*/
private int sig(int i) {
if (i > 0) return 1;
else if (i == 0) return 0;
else return -1;
}
/* Testet, ob zwei Werte das gleiche Vorzeichen haben, wobei 0 als
* vorzeichenneutral gewertet wird, also für alle x liefert
* istVorzeichenGleich(x,0) true. (Wird gemacht, damit auch Klicks
* auf den Rand akzeptiert werden.)
*/
private boolean istVorzeichenGleich(int a, int b) {
int sa = sig(a),
sb = sig(b);
return (sa == 0 || sb == 0 || sa == sb);
}
public boolean istAnklickbarBei(int x, int y) {
int nz1 = (xPoints[1]-xPoints[0])*(y-yPoints[0])
-(yPoints[1]-yPoints[0])*(x-xPoints[0]),
nz2 = (xPoints[2]-xPoints[1])*(y-yPoints[1])
-(yPoints[2]-yPoints[1])*(x-xPoints[1]),
nz3 = (xPoints[0]-xPoints[2])*(y-yPoints[2])
-(yPoints[0]-yPoints[2])*(x-xPoints[2]);
return (istVorzeichenGleich(nz1,nz2) &&
istVorzeichenGleich(nz2,nz3));
}
}
Für Vierecke kann man das beim Dreieck benutzte Verfahren verallgemeinern. Schwierig wird es dann allerdings,
wenn man auch konkave Figuren zulässt (Innenwinkel mit mehr als 180 ◦ ). Da wir in dieser Aufgabe aber zei3
gen wollten, dass zum Funktionieren nicht alle Figuren anklickbar sein müssen, lassen wir die Klasse Viereck
..
unverändert. ^
(3 Punkte)
c) Für die oben angegebene Variante sieht die Subtypbeziehung folgendermaßen aus:
Figur
Anklickbar
AnklickbareFigur
Viereck
Kreis
Dreieck
(1 Punkt)
d) und e)
Zur Realisierung der Verschiebefunktion mit der Maus werden zwei Attribute eingeführt:
private Figur active = null;
private int lastX,lastY;
Die Variable active enthält die aktuell angewählte Figur, solange die Maustaste gedrückt ist, ansonsten null.
Die Variablen lastX und lastY speichern die letzten Koordinaten des Mauszeigers während einer Verschiebeoperation.
Damit kann das Anwählen und Verschieben folgendermaßen realisiert werden:
private void mausGedrueckt(int x, int y) {
int size = figures.size();
for (int i = size-1; i >= 0; i--) {
Figur f = (Figur)figures.get(i);
if (f instanceof AnklickbareFigur) {
if (((AnklickbareFigur)f).istAnklickbarBei(x,y)){
figurNachVorne(i+1);
active = f;
lastX = x;
lastY = y;
return;
}
}
}
}
private void mausGezogen(int x, int y) {
if (active != null && (lastX != x || lastY != y)) {
active.verschiebe(x-lastX,y-lastY);
lastX = x; lastY = y;
p.repaint();
}
}
private void mausLosgelassen() {
active = null;
}
(4+5 Punkte)
4
Aufgabe 3 Statische und dynamische Bindung
Die Ausgabe des Programms:
10
10
1000
1000
Programmzeile
Zeile 15:
Zeile 16:
Zeile 17:
Zeile 18:
Zeile 19:
Zeile 20:
Aufgerufene Methode /
verwendetes Attribut
Konstruktor Klasse A
Konstruktor Klasse B
Attribut this.i in B
Attribut i in A
Attribut i in A
Methode m in A
Attribut i in A
Methode n in A
Attribut i in A
Methode m in B
Attribut i in B
Methode m in A
Attribut i in A
Methode n in B
Attribut i in B
definiert
in Zeile
3
9
8
2
2
5
2
4
2
11
8
5
2
10
8
Bindungsart
statisch
statisch
statisch
statisch
statisch
dynamisch
statisch
dynamisch
statisch
dynamisch
statisch
statisch
statisch
dynamisch
statisch
5
(6 Punkte)
Herunterladen