2.3 Wir erstellen eine neue Klasse THaus

Transcrição

2.3 Wir erstellen eine neue Klasse THaus
Seite 11
Informatik 10
[email protected]
2.3 Wir erstellen eine neue Klasse THaus
zX
Menschen kommen heim, öffnen die Haustür, schalten
das Licht an, ...
x
Die Grundidee der objektorientierten Programmierung
(OOP) ist es, ein Modell der Wirklichkeit zu schaffen
mit Hilfe von Klassen gleichartiger Objekte. Die Objekte
müssen intelligent genug sein, Aufgaben in eigener Verantwortung zu erledigen. Sie müssen miteinander kommunizieren können, Aufträge ausführen oder Anfragen
beantworten. Dabei kann man einem Objekt sagen, was
es tun soll, aber nicht, wie es etwas tun soll. Das muss
das Objekt selber wissen (das heisst: der Programmierer
muss diese Fähigkeiten der Klasse hinzugefügt haben).
In diesem Abschnitt entwickeln wir eine Klasse THaus, deren Objekte so intelligent sind, dass sie sich selber zeichnen
können, dass sie das Licht anschalten können und die Türe
öffnen und schließen. Und sie sollen Fragen beantworten:
„Ist das Licht an?“ und „Ist die Türe offen?“
Breite div 5
zY
y
Breite (= Höhe)
Erstellt man in Delphi eine neue Objektklasse, so tut man dies normalerweise in einer eigenen Unit. Denn
eine neue Klasse hat ja nicht direkt etwas mit unserer Unit1 und dem Hauptformular Form1 zu tun. Das
gesamte Delphi-System besteht aus vielen Modulen, den Units, in denen die Objektklassen deklariert und
implementiert wurden.
►
Erstelle zunächst eine neue Delphi-Anwendung und speichere das Projekt in einem neuen
Ordner. Öffne dann noch eine neue Unit (Datei/Neu.../Unit). Diese trägt den Namen Unit2.
Ändere diesen in der erste Zeile in:
unit Gebaeude1;
Speichere diese Unit in den gleichen Ordner wie das
ganze Projekt. Der Dateiname der Unit muss exakt
gleich lauten wie der gerade festgelegte Name ’Gebaeude1’. Jetzt wechsle in die Unit1 und füge Gebaeude1 bei der Uses-Klausel hinzu, damit später das
Hauptformular Form1 die Objekte der Klasse THaus
verwenden kann.
Damit ein Haus-Objekt die verlangten Fertigkeiten ausführen
kann, muss es sich verschiedene Dinge merken. Die Zeichenfläche, auf die es sich zeichnen soll, gehört einem anderen Objekt (FBesitzer), zum Beispiel einem Fenster von der
Klasse TForm. Das Haus muss dieses Objekt kennen, dazu
die Position (FX, FY) und seine eigene Größe (FBreite) und
Farbe (FFarbe). Beim Zeichnen der Fenster muss es wissen, ob
gerade das Licht an ist oder nicht (FLichtAn). Die Werte dieser
Attribute bestimmen den Zustand des Objekts.
THaus = class(TObject)
<<Attribute>>
- FX, FY: integer
- FBreite:integer
- FFarbe: TColor
- FBesitzer: TForm
- FLichtAn: boolean
<<Methoden>>
+ Create() {constructor}
+ Zeichnen()
+ LichtEinschalten()
+ LichtAusschalten()
+ IstLichtAn(): boolean
Es ist eine stille Übereinkunft zwischen den Delphi-Programmierern, die Attribut-Bezeichner mit ’F’ (Field) zu beginnen
Klassendiagramm von THaus, für den
und die Klassen-Bezeichner mit ’T’ (Type).
Anfang noch nicht vollständig
Jede Klasse braucht eine Methode, welche die Objekte im
[email protected]
Informatik 10
Seite 12
Speicher des Computers erstellt. Diese nennt man Konstruktor. Ihr Bezeichner lautet meistens create().
Bisher haben wir Delphi-Objekte erzeugt, indem wir mit der Maus z.B. ein Button1 aus der Komponentenpalette geholt haben. In diesem Moment hat Delphi die Methode TButton.create ausgeführt und dem
neuen Objekt den Bezeichner Button1 gegeben. Da wir Haus1, Haus2 ... nicht mit der Maus holen, müssen wir später an geeigneter Stelle der Unit1 dafür sorgen, dass THaus.create aufgerufen wird.
►
Aufruf in einer Anwendung erklären, zB. TButton.Create oder TShape.Create :
Aber wir wissen doch gar nicht, wie man ein Objekt erzeugt!?! Das ist auch nicht nötig, weil eine neue
Klasse sehr elegant durch Vererbung von einer schon vorhandenen Klasse abgeleitet („vererbt“) wird.
Die Klasse TForm1 einer Anwendung wird von der Klasse TForm vererbt. TForm ist der Vorfahre.
TForm1 hat alle Eigenschaften und Methoden, die TForm besitzt, und dann kommen in unserer Unit1
noch weitere Eigenschaften, Objekte, Methoden dazu. Wie ist das mit unserer Klasse THaus?
type
THaus = class (TObject)
Die Klasse TObject ist die Basis-Klasse für alle anderen Delphi-Klassen. Sie hat fast nichts! Aber eine
Methode constructor create. Diese kann ein Objekt der Klasse erzeugen. Wenn wir im Konstruktor von
THaus als erstes die geerbte Methode aufrufen mit inherited create, so brauchen wir nur noch das festlegen, was unsere Klasse zusätzlich hat: FX, FY, FBreite ... .
constructor THaus.create(xPos,yPos,Breite: integer; F: TColor; Besitzer: TForm);
begin
inherited create;
FX:= xPos;
FY:= yPos;
FBreite:=Breite;
...
[email protected]
Informatik 10
Seite 13
private oder public?
Attribute und Methoden, die ich im Klassendiagramm mit einem ’-’ versehen habe, wollte
ich unter private deklarieren, die mit einem ’+’
unter public. Für andere Objekte ist die Privatsphäre eines THaus-Objekts nicht sichtbar. Man
kann also von Form1 aus nicht das Attribut FX
ansprechen mit Haus1.FX, wohl aber seine Methode zeichnen aufrufen mit Haus1.zeichnen.
Der Zweck ist ganz einfach: Wenn du die Unit
Gebaeude1 fertig hast, soll nicht ein anderer
Benutzer dieser Unit auf die Attribut-Werte zugreifen können. Er kennt ja meist gar nicht deren Bedeutung und würde ein heilloses Durcheinander anrichten. Nur über die public-Methoden des Objekts sollen die Attributwerte verändert werden
können. Nur der Programmierer der Unit hat vollen Zugriff auf alle Attribute und Methoden. Merke:
Attribute werden in der Regel unter private deklariert, Methoden unter public, sodass andere
Objekte Zugriff auf die Attribut-Werte eines Objekts nur über seine Methoden erhalten.
(Prinzip der Datenkapselung)
[email protected]
►
Informatik 10
Seite 14
Gib den Quelltext der Unit Gebaeude ein. Beschränke dich zuerst auf die Prozeduren create,
zeichnen und zeichneWand. Bevor du weiterschreibst, teste deine neue Klasse THaus: Füge
in der Unit1 der Klasse TForm1 unter private die Bezeichner
Haus1, Haus2: THaus
bei. Benutze das onClick-Ereignis eines Button (oder besser: das onCreate-Ereignis von
Form1), um die beiden Objekte zu erschaffen:
Haus1:=THaus.create(...);
Haus2:=THaus.create(...);
Wenn alles richtig ist, solltest du jetzt auf dem Formular zwei Rechtecke sehen. Das sind die
noch unvollständigen Objekte Haus1 und Haus2.
►
Schrittweises Fertigstellen der Klasse THaus
►
Besprechen: Parameter-Übergabe, Unterschied zwischen procedure (Methode) und function
(Funktions-Methode)
►
Gib Form1 ein ganzes Array von Häusern
H: array[1..5] of THaus;
und initialisiere das Array mit
for i := 1 to 5 do H[i]:=THaus.create(...)
Dabei musst du die Position der Häuser von der Zählvariablen i abhängen lassen.
►
Füge Lichtschalter hinzu.
Aufgabe:
►
Programmiere die Methoden TuereOeffnen() und TuereSchließen(), und die Funktionsmethode IstTuereOffen:boolean .
Orientiere dich dabei an LichtEinschalten() . Bedenke, dass
man durch eine offene Tür auch sieht, ob das Licht an ist.
Informatik 10
[email protected]
Seite 15
2.4 Wir modellieren eine einfache Bank
(Vergleiche Diagramm S. 1 !)
Systemanalyse: Was macht eine Bank?
Abgrenzung:
Unsere Bank soll nur Giro-Konten haben und nur mit ganzen €-Werten rechnen.
Abstraktion:
Wir brauchen mehrere Konten (meinKonto, Konto2 ...) und ein Objekt, das (zunächst)
nur meinKonto verwaltet, nennen wir es Bank.
Aggregation:
Wir bilden eine Klasse TKonto und eine Klasse TBank.
Idealisierung:
TKonto braucht die Attribute Nummer, Inhaber, Kontostand und Methoden für Ein-/
Auszahlung, Überweisung und Einsichtnahme, also um den Kontostand zu erhöhen oder
zu erniedrigen und mitzuteilen und den Kontostand eines anderen zu erhöhen.
TBank braucht einen Automaten mit Buttons, Eingabefenster und die Fähigkeit, Konto-Objekte zu erzeugen und die Methoden der Konto-Objekte zu nutzen. (Später wird
die Bank dazu eine Liste der Konto-Objekte führen müssen.)
Strukturanalyse: Eine Bank verwaltet mehrere Konten auf diese Art.
Modellbildung: Wir benutzen die Delphi-IDE als Programmierumgebung und definieren dort eine neue
Delphi-Klasse TKonto in einer eigenen Unit. Als Bank benutzen wir ein gewöhnliches
Delphi-Formular, nennen1 die Klasse TBank (statt TForm1) und das Objekt Bank
statt Form1 und geben ihr Konten, Buttons, ein Edit (später eine ComboBox) und die
Überschrift „Automat für mein Bank-Konto“. Dann bekommen TKonto und TBank
noch ihre Methoden. TBank legen wir so einfach an, dass sie zunächst nur meinKonto
verwaltet und von diesem auf ein zweites überweisen kann.
TKonto = class(TObject)
<<Attribute>>
<<Eigenschaften>>
- FNummer: string
(viele geerbte Eigenschaften von TForm)
- FInhaber: string
meinKonto, Konto2...: TKonto
- FKontostand: integer
Edit1: TEdit
<<Methoden>>
EinzButton: TButton
+ constructor create (nr,inh:string)
AuszButton: TButton
+ einzahlen(Betrag:integer)
UeberwButton: TButton
+ auszahlen(Betrag:integer)
...
+ ueberweisen(Empfaenger:TKonto;
Betrag:integer)
<<Methoden>>
+ gibStand: string
+ gibNummerUndInhaber:string
Beachte: Objekte von TKonto sind unsichtbare Objekte, im Gegensatz zu den Objekten
der bisher besprochenen Delphi-Klassen.
1
TBank = class(TForm)
(viele geerbte Methoden von TForm)
EinzButtonClick
AuszButtonClick
UeberwButtonClick
FormCreate
Die Hauptklasse einer Delphi-Anwendung heißt standardmäßig immer TForm1 und das zugehörige Fenster-Objekt
hat den Bezeichner Form1. Wenn du diese, wie oben vorgeschlagen, tatsächlich umbenennen willst, musst du das in
Unit1.pas, in Unit1.dfm und in Project1.dpr machen.
[email protected]
Informatik 10
Seite 16
► Die Unit Konten.pas vorstellen und besprechen, zusammen mit den Schülern schreiben;
Teile können die Schüler selber schreiben.
► Neues Projekt erstellen, die Unit „dem Projekt hinzufügen“. Uses-Klausel
► Dann das Formular ’Bank’ Schritt für Schritt aufbauen und immer sofort testen. (Auch
Abschreiben von Code bringt Lerneffekte.)
Erst wenn die Anwendung in dieser Form funktioniert, dann:
Aufgaben:
1.
Auch von Konto2 soll beim Programmstart Nummer und Inhaber angezeigt werden.
2.
Nach jeder Aktion der Bank soll das Editfenster gelöscht werden.
3.
Füge noch ein drittes Konto hinzu, auf das man mit einem weiteren Button überweisen
kann.
Wer schon Programmierkenntnisse hat,
- kann die Geldbeträge auch mit zweistelligen Dezimalzahlen machen.
- der Bank eine Kontenliste (Objekt der Klasse TComboBox) hinzufügen, aus welcher
der Empfänger einer Überweisung ausgewählt wird.
[email protected]
Informatik 10
Quellcode der Unit Konten mit der Klasse TKonto
Seite 17
[email protected]
Quellcode der Unit1 mit der Klasse TBank
Informatik 10
Seite 18
[email protected]
Informatik 10
Seite 19
anspruchsvolle Aufgabe:
Folgendes Problem gibt es in vielerlei Versionen: Ein Weinhändler hat einen 8-Liter-Krug und einen 3Liter-Krug. Er möchte aus einem großen Fass genau 1 Liter Wein abfüllen. Wie muss er vorgehen?
(Zu beachten ist: Die Krüge haben keinerlei Liter-Marken, und man hat auch sonst keine Hilfsmittel.
Man kann nur vom Weinfass Wein auslaufen lassen, bis ein Krug randvoll ist oder von einem Krug in den
anderen umfüllen, bis dieser voll oder der erste leer ist, oder den Krug ins Fass zurückschütten.)
Das Umfüllen des Weins soll in einem Delphi-Programm simuliert werden. Dazu soll eine Klasse TGefaess in einer Unit Gefaesse deklariert werden, ähnlich zu der Klasse TKonto oder THaus. In einer Delphi-Anwendung sollen kleinerKrug, grosserKrug und Fass Objekte der Klasse TGefaess sein. Du kannst
so vorgehen wie in THaus, wo die Objekte sich auf dem Canvas von Form1 zeichnen. Einfacher geht es
jedoch, wenn du TGefaess von einer sichtbaren Delphi-Klasse vererbst, z.B. von TProgressBar. Diese
hat schon die Fähigkeit sich zu zeichnen aber den Nachteil, dass sie rechteckig aussieht. Als Füllhöhe des
Gefässes kannst du ihre Eigenschaft Position benutzen. Die Klassendeklaration lautet dann:
TGefaess = class(TProgressBar)
public
constructor create(Besitzer:TForm; Volumen, Menge:integer; xPos, yPos:integer);
procedure umfuellen(Ziel: TGefaess);
end;
Ein Objekt von der Klasse TProgressBar muss wie alle sichtbaren Delphi-Objekte seinen Besitzer (z.B.
Form1) kennen, damit es sich auf dessen Canvas zeichnen kann. Deswegen übergeben wir den Besitzer
auch in der create-Methode von TGefaess. Es hat außerdem alle Eigenschaften und Methoden, die TProgressBar hat, also z.B. Left, Top, Width, Max ... das kannst du wunderbar brauchen! Du kannst also in der
create-Methode das Volumen an die Eigenschaft Max übergeben und die Füllmenge an die Eigenschaft
Position. Dann zeichnet sich die Progressbar automatisch mit der richtigen Füllmenge.
Du musst dann in der create-Methode als erstes die geerbte create-Methode von TProgressbar aufrufen.
In der Hilfe siehst du, dass diese Methode als Parameter ebenfalls den Besitzer braucht.
inherited create(Besitzer);
parent:=Besitzer;
Sichtbare Delphi-Objekte haben alle das Attribut parent (wie unser THaus das Attribut zBesitzer), in der sie
sich ihren Besitzer merken. Deine Delphi-Anwendung braucht drei Objekte von der Klasse TGefaess, nämlich kleinerKrug, grosserKrug und Fass. In der Unit1 kannst du dann solche Objekte erschaffen z.B. so:
kleinerKrug.create(Form1, 3, 0, 100,50);
Damit erzeugst du einen Krug mit dem Volumen 3Liter, der Startmenge 0Liter an der Stelle (100,50).
Mit mehreren Buttons soll man dann vom Fass in die Krüge umfüllen können und auch vom einen in den
anderen Krug oder ins Fass zurück.
Informatik 10
[email protected]
Seite 20
2.5 Objektbeziehungen und Klassenbeziehungen
Die objektorientierte Denkweise geht davon aus, dass ein System nur aus Objekten und ihren Beziehungen untereinander besteht. Gleichartige Objekte werden zu einer Klasse zusammengefasst und zwar
nicht nur so, wie man Briefmarken in ein Album gibt oder gleichaltrige Schüler in eine Schulklasse. Eine
Objekt-Klasse stellt den Bauplan für alle von ihr erzeugten Objekte dar. Diese sind absolut gleichartig
und unterscheiden sich nur in den Attribut-Werten.
Zwischen den Objekten bestehen Beziehungen. Form1 besitzt die Objekte Button1, Button2 und Edit1,
verwaltet diese und benutzt ihre Methoden. Button1 sendet beim Ereignis onClick eine Botschaft an
Form1. Form1 kann mit einer Ereignis-Methode darauf reagieren. Bank erzeugt und verwaltet mehrere
TKonto-Objekte. Haus1 kennt seinen Besitzer Form1 und benutzt dessen Canvas, um sich darauf zu
zeichnen. Manche Objekte können gar nicht ohne andere existieren: Jedes sichtbare Objekt wie Button1
braucht ein Objekt der Klasse TWinControl, von dem es verwaltet wird. Die Klasse TForm ist ein TWinControl (weil TForm in mehreren Schritten aus der Klasse TWinControl vererbt wurde). Objekte der
Klasse TForm können deswegen dieses Aufgabe übernehmen.
Um Beziehungen zwischen Objekten darzustellen benutzt man Klassendiagramme der Unified Modelling Language (UML). Diese Diagramme sind beim Entwurf umfangreicherer Programme unentbehrlich.
Die Kennt-Beziehung (auch: Benutzt-Beziehung, in UML: „Assoziation“)
Die Objekte unserer Klasse THaus kenTForm1
THaus
nen ihren Besitzer, ein Objekt der Klasse
1
+Canvas:
TCanvas
-FBesitzer:
TForm
TForm1. Ihre Methode zeichnen() benutzt
zeichnet sich auf
...
...
die Zeichenfläche Canvas des Besitzers,
*
...()
ohne dass die Zeichenfläche die THaus- +zeichnen()
...()
Objekte kennen muss. Diese Kennt-Beziehung ist eine gerichtete Assoziation
zwischen den Objekten der beiden Klassen. An den Pfeilenden wird die Kardinalität der Beziehung
angegeben: In unserem Programm benutzen beliebig viele (*) THaus-Objekte genau ein (1) TForm-Objekt.
Die Hat-Beziehung (auch: Besitzt-Beziehung, in UML: „Aggregation“)
Die Verbindungslinie mit der Raute zeigt an, dass ein Objekt der Klasse TForm jeweils ein Objekt der
Klassen TCanvas, TBrush, TFont ... besitzt und verwaltet. Das gleiche gilt für Objekte, die wir in der
Klassendeklaration von TForm1 hinzufügen. Ein Objekt der Klasse TForm1 (nämlich Form1) verwaltet
sieben THaus-Objekte, ein TButton-Objekt und fünf TCheckbox-Objekte. Und weil TForm1 von TForm
vererbt wurde, verwaltet Form1 auch ein TCanvas-Objekt, ein TBrush-Objekt usw.. Diese Hat-Beziehung bedeutet, dass das besitzende Objekt für die Existenz der anderen Objekte zuständig ist. Die Linien
TForm
TForm1
...
1
TCanvas
1
TBrush
1
TFont
...
7
THaus
1
TButton
5
TCheckbox
Informatik 10
[email protected]
Seite 21
beschreiben gleichzeitig auch die möglichen Nachrichtenwege: Das besitzende Objekt hat die Möglichkeit, den verwalteten Objekten Nachrichten zu schicken, also ihre Methoden aufzurufen. Das bedeutet
auch: Die Hat-Beziehung umfasst die Kennt-Beziehung.
Insbesondere bedeutet Verwalten eines Objekts: Das besitzende Objekt führt das verwaltete Objekt in einer Objektvariablen oder in der Komponentenliste, erzeugt es durch Aufruf des Konstruktors der Klasse
und ist auch verantwortlich dafür, dass es irgendwann aus dem Speicher entfernt wird. Von außerhalb des
Besitzers ist ein verwaltetes Objekt nur über den Besitzer ansprechbar, z.B.
Form1.Haus3.LichtAusschalten;
oder: Paintbox1.Canvas.Moveto(20,20);
Die Ist-Beziehung (auch: Is-a-Beziehung)
Die Ist-Beziehung beschreibt die Hierarchie bei der Vererbung. Während „Hat“ und „Kennt“ Beziehungen zwischen zwei Objekten darstellen, beschreibt die Ist-Beziehung den Zusammenhang zwischen
zwei Klassen: Die Klasse TForm1 ist ein TForm, weil sie von dieser Klasse abgeleitet wurde. TForm ist
ein TWinControl, weil sie über mehrere Stufen von dieser Klasse abstammt. TWinControl ist Vorfahre
(Oberklasse), TForm Nachkomme (Unterklasse). Der Nachkomme hat alle Merkmale des Vorfahren
aber darüber hinaus noch zusätzliche Merkmale (Spezialisierung).
TWinControl
TScrollingWinControl
TForm
+Name: string
+Caption: string
+Color: TColor
...
+Create()
+Destroy()
+Close()
+Print()
...()
TObject
über mehrere
Stufen
+Create()
+Destroy()
TCustomform
THaus
TForm1
TForm
TForm1
+Name: string
+Caption: string
+Color: TColor
+Button1: TButton
...
+Create()
+Destroy()
+Close()
+Print()
+Button1Click()
...()
-FX, FY: integer
-FBreite: integer
-FFarbe: TColor
-FBesitzer: TForm
-FLichtAn: boolean
+Create()
+Destroy()
+Zeichnen()
+LichtEinschalten()
+LichtAusschalten()
+IstLichtAn(): boolean
► Delphi kann die Hat-Beziehungen zwischen den verwendeten Objekten im DiagrammFenster anzeigen. Hole dazu mit der Maus die Objekte aus dem Objekthierarchie-Fenster
ins Diagramm.
► Studiere in der Delphi-Hilfe die Klassenhierarchie einiger Klassen.
► Zeichne zu unserem Bankprogramm die Klassendiagramme.
► Komplexere Strukturen werden häufig durch UML-Diagramme dargestellt. Ein UML-Modell von Wetterstationen findest du unter:
http://ifgivor.uni-muenster.de/vorlesungen/Geoinformatik/kap/kap4/k04_2.htm
Informatik 10
[email protected]
Seite 22
2.6 Objekte und Klassen in einem Vektorgrafik-Programm
Grafik-Programm ’Dia’ (Freeware)
Klasse Ellipse
Klasse Bogen
<<Attribute>>
<<Attribute>>
Liniendicke
Liniendicke
Linienfarbe
Linienfarbe
Füllfarbe
Startpfeil
...
Endepfeil
<<Methoden>>
...
verschieben()
<<Methoden>>
strecken()
verschieben()
kopieren()
strecken()
...
biegen()
kopieren()
...
Die Eigenschaftswerte der Objekte werden mit der Maus eingestellt oder im Fenster ’Properties’ (Doppelclick auf das Objekt!). Die Methoden der Objekte erreicht man ebenfalls mit Maus oder Tastatur.
► UML-Klassen-Diagramm zeichnen
► Zustandsdiagramm (sh. 11.Kl.) zeichnen (z.B. für Parallelschaltung)
► ER-Diagramm zeichnen (sh. Datenbank-Skript)