1.1 Manuelles
Polling über GUI-Komponenten
Die Aktionen werden durch getrennte GUI-Komponenten realisiert, was
bei den meisten Anwenderprogrammen angebracht ist.
Die Fachklasse
TPerson hat nur die notwendigen Set- und Get-Methoden.
TPerson = class(TObject)
private
Name : string;
Groesse : real;
Gewicht : real;
public
...
procedure SetName (n : string);
function GetName : string;
... |
Die GUI-Klasse übernimmt Control- und Viewaufgaben.
unit uFenster;
INTERFACE
//
======================================================================
uses
...
uPerson;
TFensterFrm = class(TForm)
GroesseLbl: TLabel;
...
procedure updModelBtnClick (Sender:
TObject);
procedure updViewBtnClick(Sender: TObject);
procedure AbnehmBtnClick(Sender: TObject);
private
Person : TPerson;
// --- Model PERSON
procedure Init;
procedure DatenAktualisieren; //
-- hier der Datentransport
procedure MaskeAktualisieren;
end;
IMPLEMENTATION
//
======================================================================
....
Procedure TFensterFrm.DatenAktualisieren;
//
----------------------------------------------------------------------
// Eingaben aus den Editfeldern an das Model-Objekt übergeben
begin
Person.SetName (NameEdt.Text);
Person.SetGroesse (StrToFloat (GroesseEdt.Text));
Person.SetGewicht (StrToFloat (GewichtEdt.Text));
end;
procedure TFensterFrm.updModelBtnClick(Sender: TObject);
//
----------------------------------------------------------------------
// Ereignis starten
begin
DatenAktualisieren;
Init;
// Eingaben im Fenster löschen
end;
procedure TFensterFrm.MaskeAktualisieren;
//
----------------------------------------------------------------------
// Objektdaten an die View-Komponenten übergeben
begin
NameEdt.Text := Person.GetName;
GroesseEdt.Text := FloatToStr (Person.GetGroesse);
GewichtEdt.Text := FloatToStr (Person.GetGewicht);
BmiEdt.Text := FloatToStr (Person.HatBMI);
end;
procedure TFensterFrm.updViewBtnClick(Sender: TObject);
//
----------------------------------------------------------------------
// Ereignis starten
begin
MaskeAktualisieren
end;
... |
|
1.2 Manuelles
Polling (halbautomatisch) über GUI-Komponenten
Wenn die Ausgabe von Ergebnissen und
Daten unmittelbar nach der Eingabe erwünscht ist, kann man den Aufruf
MaskeAktualisieren in die upDateModel-Komponente legen und auf die
upDateView-Komponente verzichten. Der Rest bleibt gleich.
procedure
TFensterFrm.updModelBtnClick(Sender:TObject);
//
------------------------------------------
// Ereignis starten
begin
DatenAktualisieren;
Init;
MaskeAktualisieren;
end; |
|
Bild 2
|
2. Observer Pattern Design
Bild 3 - [Klicken Sie für den Quelltext auf die Klasse]
Reichlich Aufwand, um eine Klasse Person zu observieren ...
Durch die Auslagerung von Observer-Code in die Oberklasse bleibt dann
aber nur der RunObservers-Aufruf in den Set-Methoden.
procedure
TPerson.SetName (n : string);
//----------------------------------------------
begin
fName := n;
fObserverMgr.RunObservers;
// Message absenden
end; |
und im GUI
procedure
TFensterFrm.FormShow(Sender: TObject);
// ---------------------------------------------
begin
Person := TPerson.Create;
Person.AddObserver(MaskeAktualisieren,self);
end; |
|
Bild 4
Dem GUI ist ein zweiter View hinzugefügt, der vom Observer mit
bedient wird.
|
|
3. Automatische Aktualisierung durch
selbstgeschriebene Ereignisroutinen
Die
Fachklasse hat außer den üblichen Set.. / Get-Methoden
zusätzlich Code zur Ereignisbehandlung. Will man sicherstellen, daß
die Änderung eines jeden Attributwertes verarbeitet wird, so braucht
jede betreffende Methode die Variable OnChanged, die in der
GUI-Klasse
referenziert wird und die Aktualisierung anstößt.
Hier wird mit einem selbstdefinierten Typ TNotifyChange gearbeitet.
Delphi stellt einen Typ TNotifyEvent zur Verfügung, der mit
Properties ähnlich arbeitet.
|
Bild 5 |
unit uPerson;
INTERFACE
//
======================================================================
type
TNotifyChange = procedure of object;// Deklaration eines Methodenzeigertyps
TPerson = class(TObject)
private
Name : string;
...
public
OnChanged : TNotifyChange;
// Vereinbarung einer
...
// Referenzvariablen/Ereignismethode
end;
IMPLEMENTATION
//
======================================================================
...
procedure TPerson.SetGroesse (gr : real);
(* -----------------------------------------
*)
begin
Groesse := gr;
if assigned(OnChanged)
// Ereignis auslösen
then OnChanged;
end;
procedure TPerson.Abnehmen;
(* -----------------------------------------
*)
begin
Gewicht := Gewicht - 1;
if assigned(OnChanged)
// Ereignis auslösen
then OnChanged;
end;
...
END. // ----- UNIT ------- |
In der GUI-Klasse sorgt die Methode Person.OnChanged in FormCreate
dafür, daß bei jedem Event die GUI-Methode MaskeAktualisieren ausgeführt
wird.
Leider kann das in bestimmten Fällen zu Problemen führen. Editfelder,
die sich gut für kombinierte Ein- und Ausgabe eignen, dürfen nicht für die
Ausgabe von Rechenoperationen angesprochen werden; es sind also andere
GUI-Komponenten dafür erforderlich. Die korrekte Initialisierung von
Gewicht und Groesse
mit 0 führt zu einer Exception wegen der unerlaubten
Division durch Null bei Errechnung des BMI.
Reine Textfelder sind unproblematisch.
unit uFenster;
INTERFACE
//
======================================================================
uses
...
uPerson;
type
TFensterFrm = class(TForm)
GroesseLbl : TLabel;
...
procedure FormCreate (Sender: TObject);
procedure updModelBtnClick (Sender:
TObject);
procedure AbnehmBtnClick (Sender: TObject);
procedure EndeBtnClick (Sender: TObject);
private
Person : TPerson;
// --- Model PERSON
procedure Init;
procedure DatenAktualisieren;
// --- hier der Datentransport
procedure MaskeAktualisieren;
public
end;
var
FensterFrm: TFensterFrm;
IMPLEMENTATION
//
======================================================================
{$R *.DFM}
procedure TFensterFrm.FormCreate(Sender: TObject);
//
----------------------------------------------------------------------
begin
FensterFrm.Init;
Person := TPerson.Create;
Person.OnChanged := MaskeAktualisieren;
// Methodenzeiger mit Methode
end;
// verknüpfen
...
procedure TFensterFrm.DatenAktualisieren;
//
----------------------------------------------------------------------
// wie vorher
...
procedure TFensterFrm.updModelBtnClick(Sender: TObject);
//
----------------------------------------------------------------------
begin
DatenAktualisieren;
end;
procedure TFensterFrm.MaskeAktualisieren;
//
----------------------------------------------------------------------
begin
NameEdt.Text := Person.GetName;
BmiEdt.Text := FloatToStr(Person.HatBMI);
end;
procedure TFensterFrm.AbnehmBtnClick(Sender: TObject);
//
----------------------------------------------------------------------
begin
Person.Abnehmen;
end;
end. // ------ UNIT ------- |
Bewertung
1. Polling
Manuelles Polling (1.1. und 1.2) haben sich seit 10 Jahren im
Unterricht bewährt und sind unaufwendig. An unserer Schule werden alle
Programme, beginnend im Anfangsunterricht mit einer Fachklasse bis zu
großen Projekten, in dieser Weise von den Schülern bearbeitet. Die
Datenklassen sind frei von jeder technischen Erweiterung. Das erlaubt eine
sehr einfache und klare OOA und Implementierung.
2. Observer Pattern Design
Viel Technik, wenig Erkenntnisgewinn (s.o.). Der Programmieraufwand pro
observiertem Objekt ist so umfangreich, daß er sich für den Unterricht
verbietet, wenn Schüler den kompletten
Observer-Code schreiben sollten, zumal das durchaus nicht trivial ist.
Eine Möglichkeit wäre, das ganze Paket als Bibliothek abzulegen.
Bei dieser als auch der dritten Variante bleiben Fachklassen nicht von
proprietärem Code frei, der sie mit dem Betriebssystem verknüpft, was einer Portierung im Wege steht. Der schöne Anspruch, das
in Fachklassen auch nur Anwendungsdaten und Anwendungslogik enthalten sein
sollen, ist dahin.
3. Selbstgeschriebene Ereignisroutinen
Die selbstgeschriebene Ereignissteuerung ist eher eine
Bastlerlösung. TNotifyChange ist ein Prozedurtyp, ein ebenso mächtiges wie
undurchsichtiges Konstrukt. Onchange eine Prozedurvariable, also
gleichermaßen Attribut und Prozedur und damit proprietär belastet. Die o. g. Restriktionen und Fehler lassen Zweifel
an einen
routinemäßigen Einsatz im Unterricht aufkommen.
-------------------
Literatur u. Links
Hilfehandbuch Delphi 7
Amrhein, B.: Designpattern für Graphische Benutzeroberflächen
http://www.hta-be.bfh.ch/~amrhein/Skripten/Swing/Pattern.pdf
Höstklint, C.: Simple Observer Pattern
http://www.delphi3000.com/member.asp?ID=9713
Paulus, H.: Das MVC-Konzept. (Schöne Präsentation)
http://informatikag.bildung-rp.de/assets/download/D1-Paulus-Farben.pps
|