Einführung in die objektbasierte Programmierung mit Delphi
Klaus Becker
iMedia 2007
2
Orientierung
Der Einstieg in die Programmierung mit Delphi bereitet so mancherlei Schwierigkeiten. Auch einfachste Programme sind zunächst schwer zu durchschauen. Kompliziert ist insbesondere das Zusammenspiel zwischen GUI-Objekten und algorithmischen Elementen.
Ziel der folgenden Ausführungen ist es, ein didaktisches Konzept
vorzustellen, bei dem von Anfang an objektbasierte Erklärungsmodelle benutzt werden, um das Verständnis der recht komplexen Delphi-
Programme zu erleichtern.
Diese Vorgehensweise hat zweierlei Vorzüge: Zum wird mit Hilfe der Erklärungsmodelle die "Logik der Programme" vereinfachend dargestellt und somit der Quelltext strukturell geordnet. Zum anderen werden
hierdurch bereits wichtige Konzepte der objektorientierten Modellierung und Programmierung für eine später zu erfolgende Vertiefung eingeführt.
Letzteres erfordert, dass die Grundideen und die Terminologie der
Objektorientierung von Beginn an korrekt und konsistent verwendet
werden.
3
Teil 1
"Hallo Welt"
-
objektbasierte Benutzungsoberflächen
4
"Hallo Welt"
5
GUI-Objekte
Eine Benutzungsoberfläche wird mit Hilfe von sog. GUI-Objekten aufgebaut. (GUI: Graphical User Interface)
GUI-Objekt
Die Eigenschaften eines Objekts kann man sich mit dem Objektinspektor anzeigen lassen.
GUI-Objekt
GUI-Objekt
GUI-Objekt GUI-Objekt
6
Objekt
Ein Objekt hat (in der Regel) einen Namen und wird einer Klasse zugeordnet. Die Klasse legt dabei den Typ des Objekts fest.
iBaer1:
TImage
Im Objektinspektor werden der Name und die
zugehörige Klasse des markierten Objekts angezeigt.
iBaer1:
TImage
iBaer2:
TImage
bTag:
TButton bNacht:
TButton pAnzeige:
TPanel
GUI:
TGUI
7
iBaer1:
TImage
Aufbau und Zustand eines Objekts
iBaer1: TImage Visible = False ...
Klasse
Attribut Attributwert
Der Aufbau eines Objekts wird mit Hilfe von Attributen (Eigenschaften) festgelegt. Die Attributwerte legen den aktuellen Objektzustand fest.
Attribut
Attributwert Name
Klasse
Name
8
Objektdiagramme
iBaer1: TImage Visible = False ...
Um die Struktur von Objekten zu beschreiben, benutzt man sog.
Objektdiagramme.
Objektdiagramm
iBaer2: TImage Visible = True ...
bTag: TButton Caption = 'Tag' ...
bNacht: TButton Caption = 'Nacht' ...
pHinweis: TPanel Caption = 'Wer ...' Color = clGreen ...
GUI: TGUI
Caption = 'Knut ...' Color = clWhite ...
9
Ein Blick in die Datei „uTagNacht.dfm“
object GUI: TGUI Left = 307
Top = 360
BorderIcons = [biSystemMenu]
BorderStyle = bsSingle Caption = 'Knut tut gut' ClientHeight = 410
ClientWidth = 760 Color = clWhite ...
PixelsPerInch = 96 TextHeight = 16
object iBaer1: TImage Left = 392
Top = 48 Width = 334 Height = 255 AutoSize = True Picture.Data = {
0A544A504547...
...}
Visible = False end
object iBaer2: TImage Left = 32
Top = 48 ...
Visible = False end
object bTag: TButton ...
Caption = 'Tag' TabOrder = 0
OnClick = bTagClick end
object bNacht: TButton ..
end
object pHinweis: TPanel Left = 280
Top = 320 Width = 201 Height = 41 TabOrder = 2 end
end
10
„verwaltet“
Objekthierarchie
FGUI:
TFGUI
iBaer1:
TImage iBaer2:
TImage bTag:
TButton bNacht:
TButton pHinweis:
TPanel
...
...
...
object GUI: TGUI Left = 307
Top = 360
BorderIcons = [biSystemMenu]
BorderStyle = bsSingle Caption = 'Knut tut gut' ClientHeight = 410
ClientWidth = 760 Color = clWhite ...
PixelsPerInch = 96 TextHeight = 16
object iBaer1: TImage Left = 392
Top = 48 Width = 334 Height = 255 AutoSize = True Picture.Data = {
0A544A504547...
...}
Visible = False end
11
Veränderung von Objektzuständen
Attributwerte eines Objekts kann man mit Hilfe von Wertzuweisungen verändern.
pHinweis.Caption := 'Pst, ich schlafe!';
pHinweis.Color := clRed;
pHinweis: TPanel Caption = 'Wer ...' Color = clGreen ...
pHinweis: TPanel Caption = 'Pst ...' Color = clRed ...
Objektzustand vorher
Objektzustand nachher
Wertzuweisungen
12
Ereignisbasierte Veränderung
Ein Ereignis ist eine Zustandsänderung in einem Objekt, die von Interesse ist und daher mit einer Prozedur zur Ereignisverarbeitung verknüpft
werden kann.
(K. Merkert)Ereignis
Mausklick auf den Button mit der
Aufschrift "Tag" löst das Ereignis
"bTag.onClick" aus
Mausklick auf den Button mit der Aufschrift "Nacht"
löst das Ereignis
"bNacht.onClick" aus
Ereignisverarbeitung
procedure TGUI.bTagClick(Sender: TObject);
begin
GUI.Color := clWhite;
iBaer1.Visible := false;
iBaer2.Visible := true;
pHinweis.Caption := 'Wer spielt mit mir?';
pHinweis.Color := clGreen;
end;
procedure TGUI.bNachtClick(Sender: TObject);
begin
GUI.Color := clBlack;
iBaer1.Visible := true;
iBaer2.Visible := false;
pHinweis.Caption := 'Pst, ich schlafe!';
pHinweis.Color := clRed;
end;
13
Delphi-Quelltext (uTagNacht.pas)
unit uTagNacht;
interface uses
...
type
TGUI = class(TForm) bTag: TButton;
bNacht: TButton;
iBaer1: TImage;
iBaer2: TImage;
pHinweis: TPanel;
procedure bTagClick(Sender: TObject);
procedure bNachtClick(Sender: TObject);
private
{ Private-Deklarationen } public
{ Public-Deklarationen } end;
var GUI: TGUI;
Deklaration eines
Formulars
mit GUI-Objekten
14
Delphi-Quelltext (uTagNacht.pas)
implementation {$R *.DFM}
procedure TGUI.bTagClick(Sender: TObject);
begin
GUI.Color := clWhite;
iBaer1.Visible := false;
iBaer2.Visible := true;
pHinweis.Caption := 'Wer spielt mit mir?';
pHinweis.Color := clGreen;
end;
procedure TGUI.bNachtClick(Sender: TObject);
begin
GUI.Color := clBlack;
iBaer1.Visible := true;
iBaer2.Visible := false;
pHinweis.Caption := 'Pst, ich schlafe!';
pHinweis.Color := clRed;
end;
end.
Implementierung
Ereignisverarbeitung der
15
Teil 2
Bär auf Wanderschaft -
Datenhaltung mit lokalen Variablen
16
Bär auf Wanderschaft
17
Bär, komm her!
procedure TGUI.bAktualisierenClick(Sender: TObject);
var
xposPfleger, yposPfleger: integer;
begin
// Eingabe: Übernahme der Anzeige xPosPfleger := StrToInt(eXpos.Text);
yPosPfleger := StrToInt(eYpos.Text);
// Ausgabe: Aktualisierung der Anzeige iPfleger.Left := xPosPfleger;
iPfleger.Top := yPosPfleger;
end; procedure TGUI.bRechtsClick(Sender: TObject);
var
xPosBaer, yPosBaer: integer;
begin
// Eingabe: Übernahme der Anzeige xPosBaer := iBaer.Left;
yPosBaer := iBaer.Top;
// Verarbeitung: Veränderung der Daten xPosBaer := xPosBaer + 10;
// Ausgabe: Aktualisierung der Anzeige iBaer.Left := xPosBaer;
pXposWertBaer.Caption := IntToStr(xPosBaer);
pYposWertBaer.Caption := IntToStr(yPosBaer);
end;
lokale Variable lokale Variable
18
yXposwertBaer: TPanel Caption = '218'
Caption = '208'...
...
pXposwertBaer: TPanel
Doppelte Buchführung
procedure TGUI.bRechtsClick(Sender: TObject);
var
xPosBaer, yPosBaer: integer;
begin
// Eingabe: Übernahme der Anzeige xPosBaer := iBaer.Left;
yPosBaer := iBaer.Top;
// Verarbeitung: Veränderung der Daten xPosBaer := xPosBaer + 10;
// Ausgabe: Aktualisierung der Anzeige iBaer.Left := xPosBaer;
pXposWertBaer.Caption := IntToStr(xPosBaer);
pYposWertBaer.Caption := IntToStr(yPosBaer);
end;
xPosBaer: 208 iBaer: TImage Visible = True Left = 208 Top = 176 ...
yPosBaer: 176
xPosBaer: 218 yPosBaer: 176
yXposwertBaer: TPanel Caption = '218'
...
iBaer: TImage Visible = True Left = 218 Top = 176 ...
pXposwertBaer: TPanel Caption = '218'
...
19
yXposwertBaer: TPanel Caption = '218'
Caption = '208'...
...
pXposwertBaer: TPanel
EVA-Prinzip
procedure TGUI.bRechtsClick(Sender: TObject);
var
xPosBaer, yPosBaer: integer;
begin
// Eingabe: Übernahme der Anzeige xPosBaer := iBaer.Left;
yPosBaer := iBaer.Top;
// Verarbeitung: Veränderung der Daten xPosBaer := xPosBaer + 10;
// Ausgabe: Aktualisierung der Anzeige iBaer.Left := xPosBaer;
pXposWertBaer.Caption := IntToStr(xPosBaer);
pYposWertBaer.Caption := IntToStr(yPosBaer);
end;
xPosBaer: 208 iBaer: TImage Visible = True Left = 208 Top = 176 ...
yPosBaer: 176
xPosBaer: 218 yPosBaer: 176
yXposwertBaer: TPanel Caption = '218'
...
iBaer: TImage Visible = True Left = 218 Top = 176 ...
pXposwertBaer: TPanel Caption = '218'
...
Viele Eingabe-Verarbeitung-Ausgabe-Systeme lassen sich mit Hilfe von
lokalen Variablen realisieren.
20
Teil 3
Bär auf Wanderschaft -
Datenhaltung mit globalen Variablen
21
Bär, komm her!
Schwierigkeit:
Die Ereignisverarbeitung zum Ereignis "bVorwaerts.Click" hängt von der
momentanen Bewegungsrichtung des Bären ab. Diese Information könnte
man aus den GUI-Objekten gewinnen, indem man nachschaut, welches
Bild gerade sichtbar ist. Besser und übersichtlicher wird das Programm,
wenn man ein internes Datenmodell mit "globalen Variablen" erstellt, auf
die jede Ereignisverarbeitungsprozedur zugreifen und die sie verändern
kann.
22
Bär, komm her!
...
type
TGUI = class(TForm) bRechts: TButton;
...
procedure bRechtsClick(Sender: TObject);
...
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen } xpos, ypos: integer;
oben, unten, links, rechts: boolean;
procedure ausgangslage;
procedure vorwaerts;
procedure rueckwaerts;
procedure rechtsdrehen;
procedure linksdrehen;
procedure baerZeigen;
public
{ Public-Deklarationen } end;
Formular-Attribut als
"globale Variable"
Formular-Methode als Hilfsprozedur
23
Trennung: GUI – Datenmodell
type
TGUI = class(TForm) bRechts: TButton;
...
procedure bVorwaertsClick(Sender: TObject);
...
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen } xpos, ypos: integer;
oben, unten, links, rechts: boolean;
procedure ausgangslage;
procedure vorwaerts;
procedure rueckwaerts;
procedure rechtsdrehen;
procedure linksdrehen;
procedure baerZeigen;
public
{ Public-Deklarationen } end;
procedure TGUI.vorwaerts;
begin
if oben = true then ypos := ypos - 10
else if unten = true then ypos := ypos + 10
else if links = true then xpos := xpos - 10
else if rechts = true then xpos := xpos + 10;
end;
procedure TGUI.bVorwaertsClick(...);
begin
// Datenmodell aktualisieren vorwaerts;
// Anzeige aktualisieren baerZeigen;
end;
Datenmodell
24
Trennung: GUI – Datenmodell
type
TGUI = class(TForm) bRechts: TButton;
...
procedure bVorwaertsClick(Sender: TObject);
...
private
{ Private-Deklarationen } xpos, ypos: integer;
oben, unten, links, rechts: boolean;
procedure ausgangslage;
procedure vorwaerts;
procedure rueckwaerts;
procedure rechtsdrehen;
procedure linksdrehen;
procedure baerZeigen;
public
{ Public-Deklarationen } end;
procedure TGUI.vorwaerts;
begin
if oben = true then ypos := ypos - 10
else if unten = true then ypos := ypos + 10
else if links = true then xpos := xpos - 10
else if rechts = true then xpos := xpos + 10;
end;
Bei komplexeren Systemen ist es günstig, ein von der GUI getrenntes Datenmodell zu entwickeln.
Datenmodell
25
Teil 4
Zusammenfassung: Programmmuster
26
iBaer1: TImage Visible = False ...
iBaer2: TImage Visible = True ...
bTag: TButton Caption = 'Tag' ...
bNacht: TButton Caption = 'Nacht' ...
GUI: TGUI
Caption = 'Knut ...' Color = clWhite ...
Muster 1
GUI-Objekte regeln Anzeige und Kontrolle.
procedure TGUI.bTagClick(Sender: TObject);
begin
GUI.Color := clWhite;
iBaer1.Visible := false;
iBaer2.Visible := true;
pHinweis.Caption := 'Wer spielt mit mir?';
pHinweis.Color := clGreen;
end;
pHinweis: TPanel Caption = 'Wer ...' Color = clGreen ...
27
Muster 2
EVA - Verarbeitung mit einem temporären Datenmodell
yXposwertBaer: TPanel Caption = '218'
Caption = '208'...
...
pXposwertBaer: TPanel
procedure TGUI.bRechtsClick(Sender: TObject);
var
xPosBaer, yPosBaer: integer;
begin
// Eingabe: Übernahme der Anzeige xPosBaer := iBaer.Left;
yPosBaer := iBaer.Top;
// Verarbeitung: Veränderung der Daten xPosBaer := xPosBaer + 10;
// Ausgabe: Aktualisierung der Anzeige iBaer.Left := xPosBaer;
pXposWertBaer.Caption := IntToStr(xPosBaer);
pYposWertBaer.Caption := IntToStr(yPosBaer);
end;
xPosBaer: 208 iBaer: TImage Visible = True Left = 208 Top = 176 ...
yPosBaer: 176
xPosBaer: 218 yPosBaer: 176
yXposwertBaer: TPanel Caption = '218'
...
iBaer: TImage Visible = True Left = 218 Top = 176 ...
pXposwertBaer: TPanel Caption = '218'
...
28
Muster 3
Internes Datenmodell mit "globalen Variablen"
type
TGUI = class(TForm) bRechts: TButton;
...
procedure bVorwaertsClick(Sender: TObject);
...
private
{ Private-Deklarationen } xpos, ypos: integer;
oben, unten, links, rechts: boolean;
procedure ausgangslage;
procedure vorwaerts;
procedure rueckwaerts;
procedure rechtsdrehen;
procedure linksdrehen;
procedure baerZeigen;
public
{ Public-Deklarationen } end;
procedure TGUI.vorwaerts;
begin
if oben = true then ypos := ypos - 10
else if unten = true then ypos := ypos + 10
else if links = true then xpos := xpos - 10
else if rechts = true then xpos := xpos + 10;
end;
Datenmodell
29
Muster 4
Eigenständiges Datenmodell mit neuen Objekten
TRoboter xPos
yPos richtung ...
erzeugen initialisieren schritt
linksDrehen rechtsDrehen markeSetzen markeLoeschen ...
Osten Westen
Süden Norden
(4,3)