Projekt mit
1 €
unterstützen?
So geht das *:


Step 19 - OOP - Einführung

Um letztendlich ein eigenes "Objekt" zu erstellen, benötigen wir erstmal etwas Theorie:

Wer sich mit der Delphi-Objekthierachie bereits auskennt, kann den folgenden Abschnitt getrost überspringen.

Objekte sind, wie es Jan bereits in Step 08 bereits beschrieben hat, eine konzentrierte Sammlung von Methoden, Eigenschafften, und s.g. Events (Ereignissen), mit welchen wir uns jedoch nicht befassen werden. Objekte haben im Grunde immer eine Funktion: Die Arbeit beim Programmieren zu vereinfachen. Die VCL ist die größte "Objekte"-Sammlung im Turbo Delphi Explorer. Sie umfasst jedemenge "visuelle" und "non-visuelle" "Komponenten". Eine visuelle Komponente ist z.B. der Button. Diesen kann man von der Tool-Palette nehmen und auf einem Formular platzieren, wie man es möchte. Eine non-visuelle ist z.B. der Timer. Dieser ist zur Laufzeit unsichtbar und hat im Grunde nur eine Aufgabe: In einem gegebenen Intervall das OnTimer-Event auszuführen. Wer bereits einige VCL-Komponenten getestet hat, ist sicher schonmal in der Abteilung "Zusätzlich" auf den BitBtn ("BitButton") gestoßen. Der BitBtn ist, wie man im Objektinspector sehen kann, vom Typ TBitBtn und unterscheidet sich vom "normalen" Button in einigen Eigenschaften, wie z.B. dem Glyph. Hinter dieser Eigenschaft verbirgt sich die Funktion, dass man irgendein kleines Bitmap auf dem Button anzeigen kann, welches dann wiederum per Eigenschaft Layout positioniert werden kann. Worauf ich hinaus möchte, ist, dass dieser TBitBtn immernoch alle Eigenschaften vom Standard-TButton hat. Dies nennt man Vererbung. Genauergesagt handelt es sich hierbei um die Vererbung von Klassen. Die Klasse TButton ist die Elternklasse von TBitBtn. Eine Klasse Y kann also von einer anderen Klasse X vererbt werden. Es bekommt dann alle Eigenschafften, Methoden und Events, wie es die Eltern-Klasse hat. Diese können dann erweitert oder modifiziert werden (genaueres siehe später -> OOP für Fortgeschrittene). Klassen können dann "instanziert" werden, d.h. dass man eine Instanz einer Klasse - somit das eigentliche "Objekt" - erstellt. Es können bliebig viele Instanzen von einer Klasse erstellt werden.

Nun erstmal genug "pure" Theorie - nun etwas Praxis:

Wir starten Delphi und legen eine neue VCL-Formularanwendung an.
Klassen werden in Delphi normalerweise in neuen Units erstellt.
Also erstellen wir noch zusätzlich eine neue Unit:

Datei -> Neu -> Unit
(oder Delphi-Projekte -> Delphi-Dateien -> Unit)

Nun dürften wir folgendes in der Code-Ansicht (ggf. F12) sehen:

unit Unit2;

interface

implementation

end.

Dies sind die Grundbausteine jeder Unit. Wie in Step 05 bereits beschrieben, darf der Name der Unit (hier "Unit2") nicht verändern, wenn man nicht den Dateinamen gleichsam mitverändert (Groß- und Kleinschreibung ist hierbei wichtig!).

Als nächstes sollte ich vielleicht doch mal noch erklären, was wir genau vorhaben. Wir werden eine Klasse erstellen, mit dessen Hilfe wir ganz einfach einen Text mittels der Caesar-Verschlüsselung verschlüsseln können. Als zweiten Schritt habe ich mir gedacht das Objekt zum Vigenére-Verfahren zu vererben. Die Theorie zu den beiden Verschlüsselungs-Verfahren findet man sicher überall im Internet, daher werde ich nicht weiter darauf eingehen.

Nun ist es Lohnenswert, gleich beim Abspeichern sich folgendes anzueignen (was allerdings jedem selbst überlassen ist, wie es genannt werden soll):
Wichtig beim Abspeichern ist immer: keine Leer- oder Sonderzeichen eingeben! Nun klicken wir auf das Symbol für alles speichern in der Symbolleiste. Unit speichern als "Unit1" - hier, da es die Unit des Hauptformulars ist: "MainUnit" Unit speichern als "Unit2" - da es sozusagen unser Elternobjekt werden soll speichern wir es als: "mCaesar" Projekt speichern als "Projekt1" - Wir nennen es mal: "OOPProject"

Nun müssen wir in unserer mCaesar-Unit (das Modul "m"Caesar, welches das Objekt Caesar enthält) noch das Objekt anlegen:

  • zwischen interface und implementation werden wir es wie folgt definieren.
  • Wir geben mit einer Zeile Abstand zu interface und implementation den Bezeichner "type" ein
  • dannach in einer neuen Zeile und etwas eingerückt das Wörtchen "class" und drücken Tab. (Groß- und Kleinschreibung beachten!)
Nun, wenn alles richtig eingegeben wurde, seid ihr Zeugen des Live-Template-Systems von Turbo-Delphi geworden :-)

Das Ganze sollte nun so aussehen:

unit mCaesar;

interface

type
  T = class(T)
  private

    { private-Deklarationen }
  protected
    { protected-Deklarationen }
  public
    { public-Deklarationen }

  published

    { published-Deklarationen }
  end;

implementation

end.

An den Stellen, an denen das Live-Template uns auffordert etwas einzugeben (per Tab kommt man weiter), geben wir Caesar und Object ein:

  TCaesar = class(TObject)
  private

    { private-Deklarationen }
  protected
    { protected-Deklarationen }
  public
    { public-Deklarationen }
    // hier geben wir gleich wieder etwas ein...

  published
    { published-Deklarationen }
  end;

...aber zunächst eine kurze Beschreibung, was die einzelnen Abschnitte bedeuten:

class(TObject)
Hierbei leiten wir die "Urklasse" TObject ab, und erstellen unsere eigene Klasse.
Die folgenden Bezeichner dienen zur Verwaltung der Sichtbarkeit von Methoden und s.g. "Feldern" (im Grunde Variablen).

privat
Methoden die hier deklariert werden, werden nach dem Instanzieren nicht "gesehen".
Hier sollten außer den Methoden alle Felder ( = objekteigene Variablen), beginnend mit einem "f" (für "field") eingetragen werden. Dabei zuerst die Felder, und dann die einzelnen Methoden. (Die Reihenfolge gilt generell)

protected
Hier ist das selbe, wie bei privat, nur dass auch bei der Vererbung auf die Methoden und Felder zugegriffen werden kann.
Felder und Methoden, die unter private deklariert sind, können somit, wenn man die Klasse vererbt, nichtmehr verwendet werden!

public
Hier werden dann letztendlich die Eigenschaften (durch Bezeichner "property" eingeleitet) sowie sichtbare Methoden deklariert.

published
Bei der Komponentenentwicklung ist dies wichtig. Hier stehen dann die Eigenschaften und "Events", welche dann im Objektinspektor angezeigt werden.

So wieder mal genug Theorie für den Anfang :-)
Weiter im Programm:

Der published-Abschnitt kann somit getrost gelöscht werden.
Um später eine Instanz unserer Klasse "erzeugen" zu können, benötigen wir einen Constructor:

  public
    { public-Deklarationen }
    
    constructor Create;
  end;

implementation


// Constructor implementieren:
constructor TCaesar.Create;
begin
  // Constructor von Elternobjekt ausführen:
  inherited Create;
end;


end.

Nun müssen wir im Constructor die Buchstaben-Liste laden.
Hierzu müssen wir ein Feld, genauer ein Array of Char oder besser gesagt einen String einführen:

  private
    fAlphabet : String;

Um nun die Buchstaben erzeugen zu können, sollte an dieser Stelle vielleicht eine weitere Besonderheit von Delphi genannt werden, nämlich, dass man auch eine for-Schleife mit Char-Werten ausführen kann:

constructor TCaesar.Create;
var
  c:Char;
begin
  fAlphabet:='';
  for c:='a' to 'z' do
    fAlphabet:=fAlphabet+c;
       // damit wir später keine Probleme beim Verschieben bekommen.
  fAlphabet:=fAlphabet+fAlphabet;

Und nun haben wir ganz einfach das kleine Alphabet erstellt.
Im Grunde können wir jetzt schon an die Funktion DeCode gehen, in der letztendlich die Caesar-codierung (& -decodierung) durchgeführt wird:
Wir deklarieren unter protected eine function des oben genannten Namens mit den Parametern orig, vom Typ String, sowie move, vom Typ Byte, und einem String als Rückgabewert. Im implementation-Abschnitt implementieren wir dann den Algorithmus fürs Codieren:

  protected
    function DeCode(orig:String; move:Byte):String;

  //...

function TCaesar.DeCode(orig:String; move:Byte):String;
begin
  
end;

Und nun zum Algorithmus:
Es soll geschaut werden, an welcher Stelle ein Buchstabe orig[i] im Alphabet ist, um ihn anschließend um move Schritte zu verschieben. Wem die Schreibweise orig[i] nicht bekannt ist, sollte nun bei Step 11 (for-Schleife) das Beispiel 4 betrachten. Dies geht ganz automatisch indem wir zwei verschachtelte for-Schleifen verwenden. Die erste for-Schleife zählt den Index des gerade zu verschlüsselnden Chars hoch. Die zweite for-Schleife zählt von 1 bis zur Länge des Alphabetes hoch. Hiernach muss dann nur noch geschaut werden ob der Buchstabe bei Index des Original-Strings gleich dem Buchstaben des Alphabetes ist, und anschließend soll um move-Schritte verschoben werden. Somit wäre coded:=coded+fAlphabet[alpha_index+move] die Verschiebung. Da allerdings für Werte über 24 zumindest für das 'z' die Rechnung schief geht, muss hier noch ein Modulo investiert werden: coded:=coded+fAlphabet[alpha_index+move mod Length(fAlphabet)];

Somit sehen wir, dass es funktioniert und implementieren nun den Rest:

var
  orig_index, alpha_index:Integer;
  coded: String;
begin
  coded:='';
  for orig_index := 1 to Length(orig) do
    for alpha_index := 1 to Length(fAlphabet) do
      if orig[orig_index]=fAlphabet[alpha_index] then
      begin
        coded:=coded+fAlphabet[alpha_index+move mod Length(fAlphabet)];
      end;
  result:=coded;
end;

Nun, testen wir den Alorithmus mal schnell gedanklich mit folgendem Beispiel-Text:
'Hallo, ich bin ein Beispiel'
Verschiebung um eine Stelle sollte theoretisch folgendes Resultat bringen:
'Ibmmp, jdi cjo fjo Cfjtqjfm'
Es kommt bei uns aber folgendes raus:
'bmmpjdicjofjofjtqjfm'

Was ist also passiert?
Genau! Die Groß-buchstaben sowie andere Zeichen, die nicht verschlüsselt werden gingen verloren. Damit dies nichtmehr passiert, müssen wir den Algorithmus so verändern, dass zum einen die Großbuchstaben auch verschoben werden, was mittels UpperCase(fAlphabet) möglich ist, und zum Anderen alle übrigen Zeichen gradewegs übernommen werden.

Das passiert nach Anpassung zu folgendem Algorithmus:

var
  orig_index, alpha_index:Integer;
  coded: String;
  isInAlphabet:Boolean;
begin
  coded:='';
  for orig_index := 1 to Length(orig) do
  begin
    isInAlphabet:=false;
    for alpha_index := 1 to (Length(fAlphabet) div 2) do // da sonst 2xAlphabet
    if orig[orig_index]=fAlphabet[alpha_index] then
    begin
      coded:=coded+fAlphabet[alpha_index+move mod (Length(fAlphabet) div 2)];
      isInAlphabet:=true;
    end
    else if orig[orig_index]=UpperCase(fAlphabet[alpha_index]) then
    begin
      coded:=coded+UpperCase(fAlphabet[alpha_index+move mod (Length(fAlphabet) div 2)]);
      isInAlphabet:=true;
    end;
    if not isInAlphabet then
      coded:=coded+orig[orig_index];
  end;
  result:=coded;
end;

Bei der Implementierung müsstet ihr Theoretisch einen Fehler vom Live-Debugger bekommen: Nicht definierter Bezeichner 'UpperCase'.
In dem Fall suchen wir eben nach der Unit, bzw wir lassen suchen: Rechtsklick drauf, Refactoring -> Unit suchen... und schon kommt ein kleines Fenster, in dem SysUtils.UpperCase als einziger Eintrag steht.
Dann auf OK, und schon ist unsere Klasse fertig :-)

Ein letztes Strg+S und nun kommen wir zum Instanzieren.
Hierfür gehen wir in die MainUnit, bzw. zur Formularansicht.
Nun ist die Bezeichnung des "Hauptformulars" Form1 etwas unschön. Daher bennennen wir es erstmal zu "MainForm" um.
Hiernach benötigen wir drei Edit-Felder und einen Button.
Die ersten Zwei Edits sind für die Text Ein- und Ausgabe zuständig, das 3. Edit beinhaltet nur die Zahl, um welche sich alles verschieben soll.
Die Edits und der Button werden umbenannt zu "EdEingabe", "EdAusgabe", "EdMoveCount" und "BtnCaesar".
Per Doppelklick auf den Button kommen wir in die BtnCaesarClick-Procedure.

Hier deklarieren wir gleichmal eine Instanz der Klasse TCaesar:

procedure TMainForm.BtnCaesarClick(Sender: TObject);
var
  Caesar:TCaesar;
begin

end;

Nun sehen wir wieder, dass er TCaesar nicht kennt.
Aber dank Refactoring, dürfte auch das kein Problem mehr sein.

Nun wenden wir nochmal ein Live-Template zwischen begin und end an:
try
hiernach sollte dann folgendes stehen:

begin
  := T.Create(Self);
  try
    
  finally
    .Free;
  end;
end;

Und dies füllen wir noch mit Caesar aus und dann kommt die eigentliche Tat ;-)
Wir weisen EdAusgabe.Text das Ergebnis der Caesar-funktion zu:

  Caesar:=TCaesar.Create;
  try
    EdAusgabe.Text:=Caesar.DeCode(ParamX,ParamY);
  finally
    Caesar.Free;
  end;

Und nun Preisfrage (was zum selber denken): was kommt nun als Parameter hier noch rein? :-)
Falls ihr soweit gekommen seid, dann schafft ihr das locker alleine ;-)

Caesar herunterladen.

 

Weiter geht es im Kapitel Vererbung Step 20 ;-)


Da das Kommentarmodul dieser Seite zur Zeit neu überarbeitet werden muss, sendet bitte alle Fragen, Anregungen oder Probleme mit Betreff zu welchem Thema es sich handelt an folgende Mailadresse:


Hinweis:

Dieses Kommentar-Modul teilt seinen Inhalt mit dem gleichnamigen Kapitel im Delphi-Anfängerkurs. Daher kann es vorkommen, dass Fragen zu Lazarus im Anfängerkurs im Delphi-Anfängerkurs und umgekehrt stehen. Wenn ihr euch registriert, werdet ihr aber immer an die richtige Stelle weitergeleitet.
Zu dieser gemeinsamen Nutzung der Datenbank habe ich mich entschlossen, weil die Sprachelemente in Lazarus genauso wie in Delphi genutzt werden können.

www.marco-hetzel.de
www.delphi-lernen.de
www.lazarus-lernen.de

© 2006-2019 by Marco Hetzel , last modified 01.11.2018, 11:28

(* Unterstützung dient der Aufrechthaltung laufender Kosten für dieses Projekt.)