720 likes | 901 Views
Delphi II - OOP. IFB Fortbildung Objektorientiertes Programmieren in Delphi: Einführung in das Klassenkonzept Tobias Selinger 15. 4. 2008. Klasse : ?. Drei Objekte. blauer Jaguar. roter Käfer. Pol-Ente. Objekte. Klasse : ?. Drei Objekte von einer gemeinsamen Klasse.
E N D
Delphi II - OOP IFB Fortbildung Objektorientiertes Programmieren in Delphi: Einführung in das Klassenkonzept Tobias Selinger 15. 4. 2008
Klasse: ? Drei Objekte ... blauer Jaguar roter Käfer Pol-Ente Objekte
Klasse: ? Drei Objekte von einer gemeinsamen Klasse TAuto Objekte: blauer Jaguar roter Käfer Pol-Ente
TAuto: Methoden? Was sollen Objekte der Klasse TAuto können? Welche Methoden können sie ausführen? ?
TAuto: Attribute? Was sollen Objekte der Klasse TAuto können? Welche Methoden können sie ausführen?Welche Attribute besitzen sie? ? fahren tanken km-Stand angeben Tankinhalt angeben prüfen, ob Inspektion nötig ist
? Attribute Methoden ? TAuto: UML-Modell Was sollen Objekte der Klasse TAuto können? Welche Methoden können sie ausführen?Welche Attribute besitzen sie? UML-Modell: km-Stand Tankinhalt Modell fahren tanken km-Stand angeben Tankinhalt angeben prüfen, ob Inspektion nötig ist
TAuto: UML-Modell Was sollen Objekte der Klasse TAuto können? Welche Methoden können sie ausführen?Welche Attribute besitzen sie? UML-Modell: km-Stand Tankinhalt Modell fahren tanken km-Stand angeben Tankinhalt angeben prüfen, ob Inspektion nötig ist Beachte: Die Methode erzeuge ist ein sog. Konstruktor, der von der Klasse selbst ausgeführt wird! Bsp: neuesAuto := TAuto.erzeuge(‘Manta 08/15‘)
Weitere Zugriffsrechte: ++published( = public + im Objekt- inspektor sichtbar)#protected( = privat + vererbbar) Zugriffsrechte und Notation Kapselung(Geheimnisprinzip):Attribute sind generell privat. Zugriffe nur über Zugriffsmethoden.(z.B. gibKmstand ) Zugriffsrechte: UML-Modell: private ( - ) Attribute öffentliche(public)Methoden ( + ) Bei Methoden zusätzlich: ! für Aufträge (Prozeduren) ? für Anfragen (Funktionen)
per UML-Editor(z.B. UMLed )automatisch erzeugbarbzw. rück-modellierbar ! TAuto in Delphi Delphi-Code: UML-Modell:
MVC-Konzept (strikt) mit zwei Views View 1 View 2 Modell Controller verwaltet das Modell & steuert die Views
MVC-Konzept mit kombiniertem GUI Controller + View: GUI * Modell Vorteil: nur einGUI-Fenster nötig, einfacher Datenaustausch, schnell programmiert Nachteil: unflexibel & verstrickt, konzeptionelle Trennung nur „im Kopf“ des Programmierers! (*) GUI: Graphical User Interface
2 3 1 MVC-Beispiel mit kombiniertem GUI: Auto Controller + View (GUI) Modell unit Unit1; uses ... TAuto; unitTAuto procedure TForm1.TankenButtonClick(Sender: TObject);beginauto.tanke(strToFloat(InputBox('Tanken', 'Menge: ', '30')));aktualisiereAnzeige;end;
Exkurs: Nützliche Dialoge in Delphi • Nützlich für kurze Dialoge (d.h. kleine Ein-/Ausgabefenster) sind diefolgenden Prozeduren und Funktionen der Unit Dialogs (per uses einbinden!): • Ausgabefenster: • ShowMessage(<Meldung>)zeigt eine einfache Meldung an: • MessageDlg (<Meldung>,<Typ>, <Buttons>,0) • zeigt eine konfigurierbare Meldung an. Eingabefenster: InputBox(<Titel>,<Frage>,<Defaultwert>)erfragt einen einzugebenden String: InputQuery(<Titel>,<Frage>,<Variable>) liefert true / false (für Erfolg / Abbruch)und belegt die Variablemit dem eingegebenen String. mtWarning, mtError, mtInformation, mtConfirmation, ... [ mbYes, mbNo, mbCancel, mbOK, ... ]
Delphi-„tückisches“ Einige typische Delphi-Fehler: • Element nicht bekannt:a) falsche Schreibweise (Tippfehler?)b) fehlende Zugriffsrechte? (privat?)c) zugehörige Unit nicht eingebunden? ( uses ...; z.B. Dialogs oder eigene Unit) • Ausgabedatei kann nicht erstellt werden:a) läuft das Projekt gerade? ( Abbrechen! s.u.)b) fehlen Zugriffsrechte? (richtiger Ordner? eigene Kopie?) • Programm-Absturz:a) bei Objektzugriff? entsprechendes Objekt vorher erzeugen! (nur Formularkomponenten werden automatisch erzeugt)b) bei Arrayzugriff? Indexbereich überprüfen! (Min/Max? Beginn mit 0?) • Programm „hängt“: Abbrechen:Entweder im Delphi-Menü: Start – Programm zurücksetzen,oder im Taskmanager: laufendes Projekt suchen – dessen Task beenden!
Aufgaben zum Auto-Projekt • Kopieren Sie den Ordner Auto-Aufgabe in Ihre eigenen Dateien. (Immer den ganzen Ordner!) • Öffnen Sie das Projekt und lassen Sie sich auch die Auto-Unit anzeigen!(zu finden in der uses-Klausel, dann per Kontextmenü „Datei beim Cursor öffnen“) • Bearbeiten Sie die folgenden Aufgaben (jeweils in der Haupt-Unit oder Auto-Unit): • Fahren-Button: Ereignisprozedur ergänzen (vgl. Tanken-Button) ! • fahre-Methode: Auch den km-Stand erhöhen! • Auto-Modell: Zugriffsfunktion auf den Modellnamen hinzufügen ( - ist nötig für d) ! • Erzeugen-Button: Auch den Modellnamen im GUI anzeigen! • im Auto-Modell: Ein Attribut „naechsteInspektionBeiKm“ hinzufügen und diesen Wert im Konstruktor mit 1000 initialisieren! Die Funktion „istInspektionNoetig“ soll true liefern, falls der aktuelle km-Stand höher liegt! • Fahren-Button: Jeweils überprüfen, ob eine Inspektion nötig ist und ggf. das Inspektionslabel (als Hinweis) sichtbar machen! Fahren Sie nur los, falls das Benzin ausreicht...! • Inspektion-Button: Den Inspektions-Hinweis zurücknehmen (d.h. unsichtbar machen) und im Auto-Modell eine Methode „macheInspektion“ aktivieren, die den km-Stand für die nächste Inspektion geeignet erhöht! • im Auto-Modell: Ein Attribut Tankvolumen ergänzen und beim Erzeugen per Parameter festlegen! • Per UML-Editor: Mittels UMLed ein UML-Diagramm der Auto-Klasse erzeugen!
Normale Uhr und Spezialisierungen „Normale“ Uhr : Uhrwerk / Zählerfür Sek., Min., Std, 1/100, ... Taktgenerator Spezialisierungen: StoppuhrStart, Stopp, Reset Alarm-UhrAlarmzeit einstellen, Alarm an/aus Schach-UhrStart,Stopp A,Stopp B
Stoppuhr: Modularisierung? Aufteilung in Module?
Sekunden Hundertstel Uhrwerk Taktgeber Stoppuhr: Klassendiagramm Erforderliche Klassen?
Sekunden Hundertstel Uhrwerk TUhrwerk TZähler TTimer TForm Stoppuhr: Klassendiagramm modulo n (Delphi-Komponente)
Stoppuhr: Klassendiagramm Sekunden Hundertstel Uhrwerk TUhrwerk TZähler modulo n TTimer TForm (Delphi-Komponente)
Stoppuhr: Klassendiagramm Sekunden Hundertstel Uhrwerk TUhrwerk TZähler modulo n TTimer TForm (Delphi-Komponente) ? z.B. ‘07‘ z.B. ’13:07‘ in ms
Stoppuhr: Klassendiagramm Sekunden Hundertstel Uhrwerk TUhrwerk TZähler modulo n TTimer TForm (Delphi-Komponente) ? z.B. ‘07‘ z.B. ’13:07‘ in ms
Stoppuhr: Klassendiagramm Sekunden Hundertstel Uhrwerk TUhrwerk TZähler modulo n TTimer TForm (Delphi-Komponente) z.B. ‘07‘ z.B. ’13:07‘ in ms
Aufgabe: Stoppuhr implementieren • Bilden Sie Zweier- oder Dreier-Teams zur Implementierung der Klassen (*): • Haupt-Unit mit Formular und Timer-Komponente (aus Registerkarte: System). • TUhrwerk. • TZaehler. • Nutzen Sie für 2) und 3) den UML-Editor zur Erstellung der jeweiligen Klasse: • Erstellen Sie ein entsprechendes UML-Modell. • Exportieren Sie den zugehörigen Code in eine entsprechende Delphi-Unit. • Öffnen Sie diese Unit in Delphi und ergänzen Sie den nötigen Methoden-Code. • Speichern Sie ihre Unit in den Projekt-Ordner der Haupt-Unit, fügen Siedie Unit dem Projekt hinzu, und ergänzen Sie die nötigen uses-Deklarationen. Compilieren und testen Sie - viel Erfolg !!
Aufgabe: Weitere Uhren modellieren • Entwickeln Sie in UML - zunächst auf Papier - geeignete Klassen für einea) 24-Stunden-Uhr (mit „Stell“-Möglichkeit)b) Uhr mit Weckfunktionc) Schachuhrd) Countdown-Uhr (Tipp: TZaehler um eine Methode „erniedrige“ erweitern!) • Implementieren Sie eine dieser Uhren! (Nutzen Sie den UML-Editor!) • Hausaufgabe: Realisieren Sie diese Uhr als analoge Zeigeruhr (d.h. mit alternativer View)!
Lösung: 24-Stunden-Uhr mit Alarm (UML) Die Klasse TZaehler wird unverändert benutzt!
Lösung: Schach-Uhr (UML) Tipp zur Implementierung:Zwei 24-Std-Uhrwerke erzeugen,dann das aktuelle Uhrwerk jeweils mitdem entsprechenden Uhrwerk belegenund „ticken“ lassen! UhrA aktuelleUhr UhrB
Vererbung: Alarmuhr erstmal ohne ... Vergleich ? doppelter Code !
Vererbung: Alarmuhr erstmal ohne ... Ohne Vererbung: Die komplette bisherige Klasse T24StdUhrwerkplus weiterem Code für die Alarm-Funktion.Nachteil: Code-Duplikation! Besser geht es mit Vererbung...
protected (vererbbar) Alarmuhr mit Vererbung Oberklasse(vererbt an) Ohne Vererbung: Die komplette bisherige Klasse T24StdUhrwerkplus weiterem Code für die Alarm-Funktion.Nachteil: Code-Duplikation! Unterklasse (erbt von...) Mit Vererbung: Durch T24StdUhrwerkals Oberklasse ist nur noch der Code für die Alarm-Funktion nötig!
protected (vererbbar) Alarmuhr mit Vererbung in Delphi: Delphi-Code für Vererbung: Oberklasse Mit Vererbung: Durch T24StdUhrwerkals Oberklasse ist nur noch der Code für die Alarm-Funktion nötig! ruft die ererbte erzeuge-Methode auf
Stoppuhr mit integriertem Timer ? TUhrwerk TTimer TForm
mit TStoppUhr Stoppuhr mit integriertem Timer TUhrwerk TTimer TForm
die Ereignisprozedur wird hier nur als Name eingetragen, aber hier mit Sender-Parameter deklariert! Stoppuhr: Implementierung des Timers Der Timer: Einfach im Konzept, aber tückisch in der Implementierung: constructor TStoppuhr.erzeuge; ...Timer := TTimer.Create(nil); Timer.Enabled := false; Timer.OnTimer := TimerTick; procedure TStoppUhr.TimerTick(Sender: TObject); begin ticke; end; der Timer-Konstruktor verlangt einen „Eigentümer“ Und nicht vergessen, die Timer-Unit einzubinden: uses ExtCtrls; Aufruf der eigentlichen Stoppuhr-Methode!
Schritte für Mögl.2: Und nicht vergessen, für TEdit die Standard-Unit einzubinden: uses StdCtrls Stoppuhr: Kommunikation mit GUI Kommunikation - oder: Wie erfährt das GUI von der aktuellen Zeit ? Mögl.1: Das GUI fragt ständig nach der Zeit („Polling“)! Öde Endlosschleife... Mögl.2: Das Modell (Stoppuhr) informiert das GUI über Änderungen! Die Stoppuhr muss hierfür auf das Anzeigefeld zugreifen. (Das verletzt allerdings die Unabhängigkeit der Stoppuhr...) ein privates Attribut für (einen Zeiger auf) das GUI-Anzeigefeld einbauen beim Erzeugen (einen Zeiger auf) dieses Anzeigefeld mitgeben
Und nicht vergessen, für TEdit die Standard-Unit einzubinden: uses StdCtrls Stoppuhr: Kommunikation mit GUI im GUI: in TStoppuhr: Schritte für Mögl.2:
Aufgaben: Stoppuhr mit Timer • Erstellen Sie eine Kopie des bisherigen Stoppuhr-Ordnersund integrieren Sie dort einen Timer in die Stoppuhr-Klasse. • Testen Sie die Funktion mittels a) manuellem Polling (Abfrageknopf auf GUI)b) zyklischem Polling (Schleife oder weiterer Timer im GUI) • Implementieren Sie den direkten Zugriff der Stoppuhrauf das GUI-Anzeigefeld! • Bewerten Sie die Vor- und Nachteile dieser Verfahren!
Ticketautomat: Beziehung? GUI-Klasse Fach-Klasse Beziehung? Aufrufe
Ticketautomat: Assoziation GUI-Klasse „kennt“-Beziehung: Assoziation Fach-Klasse Aufrufe
Ticketautomat: Units GUI-Klasse unit Unit1; uses Windows, Forms, ...mTTicketautomat; type TForm1 = class(TForm) „kennt“-Beziehung: Assoziation Fach-Klasse unit mTTicketautomat; Uses Dialogs, SysUtils; type TTicketautomat = class Aufrufe
Benutzer-Dialog • Wer – d.h. welche Klasse? – führt den Dialog mit dem Benutzer ? GUI-Klasse oder Fachklasse ?
Benutzer-Dialog • Wer – d.h. welche Klasse? – führt den Dialog mit dem Benutzer ? Die GUI-Klasse: - Buttons, Edit-Felder, Dialog-Boxen etc. (typischerweise) - Ereignisbehandlungs-Prozeduren - Aufruf der Methoden der bekannten Fachklasse - Behandeln / Anzeigen der Rückgabewerte
Benutzer-Dialog • Wer – d.h. welche Klasse? – führt den Dialog mit dem Benutzer ? Die GUI-Klasse: - Buttons, Edit-Felder, Dialog-Boxen etc. (typischerweise) - Ereignisbehandlungs-Prozeduren - Aufruf der Methoden der bekannten FachklasseBeispiel: - Behandeln / Anzeigen der Rückgabewerte procedure TForm1.GeldEinwerfenButtonClick(Sender: TObject); var betrag: integer; begin betrag := strtoint(InputBox('Geld einwerfen', 'Betrag', '0')); Automat.geldEinwerfen(betrag); AktuelleAnzahlungEdit.Text:=inttostr(Automat.gibAktuelleAnzahlung);end;
Zum Testen: Dialoge innerhalb der Fachklasse • Mitunter kann es hilfreich sein, eine Fachklasse alleine zu testen,d.h. a la BlueJ unabhängig von einer GUI-Klasse. • Für kurze Dialoge (kleine Ein-/Ausgabefenster) bietet die Unit Dialogsfolgende „unabhängige“ Prozeduren und Funktionen: • Ausgabefenster: • ShowMessage(<Nachricht>) zeigt einen String an. • Eingabefenster: • InputBox(<Titel>, <Frage>, <Defaultwert>) liefert den eingegebenen String. • InputQuery(<Titel>, <Frage>, <String-Variable>) liefert Erfolg (true/false) und belegt ggf. die Variable mit der Benutzer-Eingabe.