Einzelnen Beitrag anzeigen

Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#10

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 25. Dez 2005, 19:36
Ok,
hier wären wir schon fast wieder bei den Interfaces. Ist eigentlich gar nicht so schwer, du musst dir nur etwas mehr überlegen welche Klassen was gemeinsam haben.
Ich hoffe ich bin hier nicht zu sprunghaft oder unverständlich oder so, beschwer dich einfach immer wenn das der Fall ist und frag nach! Ist wichtig und richtig!

Da ich nicht weiß wie gut dein Tutorial zu OOP war (und ehrlich gesagt hab ich echt einige gebraucht um den Sinn zu verstehen), möchte ich nochmal ein wenig auf die Ideen eingehen.
Die OOP ist eigentlich nur ein einfacher Versuch, das reale Leben auf die Programmierung abzubilden. Unnötig zu sagen, dass es nicht trivial ist.
Bei der Beobachtung des "realen Leben" viel auf, dass wir alles in Klasen aufteilen können. Diese Unterteilungen kann man dann auch noch verfeinern (also sowas wie Tier -> Säugetier -> Affe -> Schimpanse -> Chita (wie auch immer man die schreibt)).
Man weiß also sehr viel über einen sehr speziellen Schimpansen, aber vor allem auch, dass Chita ein Schimpanse ist. Du weißt also das Chita Fell hat, ein Menschenaffe ist, Bananen ist, ...
Das kannst du nicht über alle Säugetiere sagen, aber alles was du über ein Säugetier sagen kannst gilt halt auch für Chita. Wichtig ist es also, dass du immer alle Eigenschaften in einer Verfeinerung bei behälst.

Die andere wichtige Sache dieser Unterteilung ist, dass ich auch andere Säugetiere haben kann (Tier -> Säugetier -> Bär -> Balu -> ...). Hab ich ein Säugetier, weiß ich dass es Sauerstoff zum atmen braucht, das gilt sicherlich für Chita und Balu, aber eben auch für jedes andere Säugetier. Aber gilt dass auch für jedes Tier? (Tier -> Fisch -> Nemo) Wohl eher nicht.

Also fasse ich alles nach bestimmten Eigenschaften zusammen und kann dabei beliebig verfeinern. In der OOP unterscheidet man nicht die Stufe der Verfeinerung, alles ist einfach ein Klasse (nichts Familie, Art, Gattung, ...). Alles was existiert ist dann eine Instanz. Etwas anschaulicher kannst du sagen, Klassen sind der Bauplan, Instanzen/Objekte sind das Gebaute.
Also sind Tier, Fisch, Säugetier, Affe, ... alles nur Klassen die gewisse Eigenschaften festlegen. Chita, Nemo und Balu sind hingegen Instanzen, sie wurden nach dem jeweiligen Bauplan angefertigt. Natürlich kann man beliebig viele Instanzen einer Klasse anlegen (z.B. könnte auch Charlie ein Schimpanse sein).

Das ist der eine Teil der OOP. Du erstellst einfach immer einen Bauplan, der genau die Eigenschaften von etwas festlegt. Aber mehr hast du nicht, nur den Bauplan. Beim Programmstart erstellst du dann aus den einzelnen Bauplänen konkrete Instanzen. Dein Programm ist dabei ihre Welt. Eine Instanz führt immer ein begrenztes Leben. Sie kann erst nach der Welt (dem Programmstart) entstehen und endet auch mit der Welt (Programmende). Länger kann nichts leben, aber früher sterben geht immer (Destruktor aufruf).
Jede Instanz führt dabei auch ein ganz eigenes Leben und kann seine Eigenschaften individuell ändern. Aber jede Instanz hat halt auch nur genau die Eigenschaften, die in der Klasse stehen.
Da jede Instanz ihr eigenes Leben hat, interessieren sich Instanz erstmal nicht weiter führ einander, sie leben halt irgendwo auf der Welt. Was genau in ihnen vorgeht weiß man nicht, interessiert einen aber auch nicht.
Wenn man eine Instanz findet, gibt es die Möglichkeit mit dieser zu kommunizieren. Dabei ist aber (durch die Klasse) festgelegt, wie diese Kommunikation aussieht. Alles was öffentlich ist (public oder published) kann jeder zur Kommunikation benutzen. Dinge die privat sind, kann nur die Instanz selbst sehen, Dinge die geschützt sind (protected), sehen nur Nachfahren oder Instanzen der gleichen Klasse.
Die Kommunikation entspricht also dem Aufruf von Methoden (proceduren / funktionen).

Das war's eigentlich schon so ziemlich.


Hoffe es war soweit verständlich. Ist wie gesagt nur grob die Grundidee. Daraus hervor gehen tun die typischen Punkte, was OOP möchte (die findest du mit google).

Was du jetzt machen möchtest lässt sich geradezu ideal in OOP modellieren. Du hast ein Hauptformular (dein GUI). Hier möchtest du Elemente platzieren. Diese Elemente sind sichtbar. Einige von ihnen (und das Hauptformular) können andere Elemente aufnehmen.

Das sind alles Eigenschaften. Du musst jetzt nur noch die Eigenschaften geschickt zusammen fassen. Es sollte immer alles was zusammen gefasst werden kann auch zusammen gefasst werden (natürlich kann man alles übertreiben, meine implizit natürlich sinnvoll).
Du hast hier also einmal eine Klasse von Objekten, die Elemente aufnehmen können.
Dann hast du eine Klasse von Elementen die gezeichnet werden können.
Eventuell hast du ja auch noch eine Klasse von Elementen, die nicht gezeichnet werden können.

Nehmen wir nur die ersten beiden Eigenschaften.
Du hast also dein Hauptformular, dass kann Objekte aufnehmen.
Die Objekte, die du darauf ablegst können alle gezeichnet werden, aber nur einige davon können Objekte aufnehmen.

Mit zwei Klassen ist es etwas schwer Interfaces zu rechtfertigen, aber ich möchte hier noch nichts als Beispiel konstruieren, verwirrt wohl eher. Die Idee eines Interfaces noch einmal:
Du könntest jetzt einfach Eigenschaften in ein Interface packen. Du tust dort einfach alles rein, was es an Gemeinsamkeiten gibt. Objekte aufnehmen bringt nur etwas, wenn diese Objekte auch wieder entfernt werden können. Und wenn du suchen möchtest, musst du natürlich alle Objekte bekommen können.
Das alles ist doch super und daraus bauen wir einfach mal ein Interface (einen Bauplan, aus dem aber nichts direkt gebaut werden kann).

Delphi-Quellcode:
type
  IKannObjekteAufnehmen = Interface
    procedure fuegeObjektHinzu(const neuesObjekt : TObject);
    procedure entferneObjekt(const Objekt : TObject);
    function getListeDerObjekte : TObjectList; // entschuldige das get, aber ist ja fast Konvention
  end;
Ok, dies wäre einfach nur eine Beschreibung von Eigenschaften. Alles was IKannObjekteAufnehmen (führendes I weil Interface) implementiert hat diese drei Methoden. Was genau ein Objekt macht, wenn ich fuegeObjektHinzu aufrufe ist mir hier vollkommen egal.
Am Beispiel der Säugetiere, jedes Säugetier hat eine Lunge um zu atmen, aber jedes Säugetier atmet unterschiedlich. Was in der Lunge passiert und wie groß das Lungenvolumen ist und und und ist mir egal.

Hättest du hier eine Klasse, müsstest du für die Klasse die Implementierung angeben. Das hier kannst du in eine eigene Datei in den Interface-Teil schreiben und die Datei wäre fertig.

Die andere Eigenschaft ist natürlich das man etwas zeichnen kann. Dazu benötigt man nur eine Funktion, die für das zeichnen zuständig ist. Also
Delphi-Quellcode:
type
  IKannGezeichnetWerden = Interface
    procedure draw;
  end;
Ok, jetzt siehst du gleich (ausnamhsweise beabsichtigt) einen klassischen Designfehler. Es würde so funktionieren, wie es hier steht, aber IKannObjekteAufnehmen kann halt jedes Objekt aufnehmen, ohne Einschränkung. Du möchtest aber doch eigentlich nur deine (zeichenbaren) Objekte aufnehmen. Hm, wenn wir also erst festgelegt hätten, was zeichenbar ist...

Also erst letzte Deklaration von IKannGezeichnetWerden erstellen und dann :

Delphi-Quellcode:
type
  IKannObjekteAufnehmen = Interface
    procedure fuegeObjektHinzu(const neuesObjekt : IKannGezeichnetWerden);
    procedure entferneObjekt(const Objekt : IKannGezeichnetWerden);
    function getListeDerObjekte : TObjectList; // entschuldige das get, aber ist ja fast Konvention
  end;
Jetzt übergibst du nur noch IKannGezeicnetWerden-Objekte. Das heißt alles was du übergibst hat immer die Methode draw (du weißt nicht was sonst noch, aber die hat es).
Ja, jetzt siehst du vielleicht schon, warum ich vorhin über dein Hauptformular / die GUI nicht gesagt habe, dass sie zeichenbar ist. Das mag zwar sein, aber die möchtest du sicherlich nicht sich selbst hinzufügen können (und erst recht nicht einem Panel oder ähnlichem).

Deine Verwaltung kannst du nun auf diese Art und Weise vornehmen. Du legst dir erst Interfaces mit den zusammengehörigen Eigenschaften an und implementierst diese dann. Wenn etwas ein Interface implementiert, kann es auch dort übergeben werden, wo ein solches Interface verlangt wird (wenn ich einen Affen will, ist Chita ok, auch wenn ich ein Säugetier will. Möchte ich aber einen Hund, tut es Chita nicht, kein bellen, ...).

Ich glaube das reicht erstmal. Hoffe habe hier nicht zuviel auf einmal gepostet, frag gerne nach!

Gruß Der Unwissende
  Mit Zitat antworten Zitat