AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi OOP Problem: änderungen werden nicht übernommen

OOP Problem: änderungen werden nicht übernommen

Ein Thema von mimi · begonnen am 25. Dez 2005 · letzter Beitrag vom 1. Jan 2006
Antwort Antwort
Seite 2 von 3     12 3   
Der_Unwissende

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

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
mimi

Registriert seit: 1. Dez 2002
Ort: Oldenburg(Oldenburg)
2.008 Beiträge
 
FreePascal / Lazarus
 
#2

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 25. Dez 2005, 21:02
Vielen dank für den versuch mir das interfase verständlich zu erklären. Aber ich finde ein interfase würde sich doch für eine funktion garnicht lohnen. dann müsten da auch variabln und sowas rein kommen.

Was klassen sind weiß ich inzwischen. Sie enthalten ein Bauplan nach diesem plan wird diese klase aufgabaut. Bis auf das interfase habe ich alles verstanden.
das würde ja beudeten das ich dem interface alles gebe was das standart objekt haben soll z.b.

eine Lösch procedure
eine Hinzufüge procedure
eine Draw Procedure
onDraw
onAdd
onDel

die standart klasse also die 1. klasse von der alles gerebt werden muss würde dann die oben genanten funtkion enthalten und dann ihre eigenen wie z.b. Namen, Caption, X,Y,W,H und sowas.
Ich muss sie auch nicht mehr defnieren wenn ich die klasse so erstelle:
TStdObj = class(IInter) // TInter ist mal das interface

und dieser klasse kann ich also noch weiter variabeln/Funktionen/Proceduren/Ereignise hinzufügen.
Wenn ich das so machen wollte wie in Delphi d.h.
Delphi-Quellcode:
var
  b:TButton;
begin
  b:=TButton.Create
  b.Name
  b.Top
end;
werden ja die änderungen übernommen weil name eine eigenschaft ist wie bekomme ich das auch hin.
(Das war die eigentliche Frage*G*)
Aber das beispiel mit den Tieren war auch nicht schlecht in den meisten beispieln geht es um Autos.
Müste dann bei den Aurufe von Create auch alles in eine liste hinzugefügt werden oder kann ich das anderes lösen und beim zeichnen ?

Aber eins hat mir dein Text verständlich gemacht: das Formular ist die wichtiges Klasse bei Komponenten d.h. wenn kein Form erstellt wurde dann sollen auch keine komponenten installisiert werden.

Gemeinsame funktionen währen ja die alle komponenten haben sollen die der GUI angehöhren z.b. alle Edit, Button, Label, Menus, Listenboxen und soweiter müsten also diese eigenschaften haben:
- Namen, Caption
- x,y,w,h,tag(sollte ein array evlt. sein),ax,ay
- Parent(ist der wichtig ?)
- Destroy, Create, Draw

Wie geht es jetzt weiter ?
das Fenster muss also diese eigenschaften haben aber noch weitere wie z.b.
die Titel Position und Titel Größe, Farbe und sowas halt.
Sollen Scrollbalken angezeigt werden ?
Soll die Größe automatisch berechnet werden ?
Soll die position in der Mitte des Desktops sein ?
und sowas.
ich habe z.b. ein Formular und ein Button.
beide stammen von StdObj ab und haben eine standart Draw funktion die auch ausgeführt werden sollte. Es muss ein rechteck gezeichnet werden in beiden fällen. Aber in ein Form muss ja noch ein line weiter oben gezeichnet werden als Titel(richtig ?) d.h. ich brauche für das Fenster auch eine eigene Draw funktion geht das überhaupt ?
ich möchte das erst die standart funktion aufgerufen wird und sofern vorhanden die eigene die beim Form dabei sind denn beim Button brauche ich keine eigene Draw funktion.
Weißt du was ich meine ?

Vielen Dank für deine Mühe mir bei diesem schwirigen Problem zu Helfen.
Michael Springwald
MFG
Michael Springwald,
Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus)
  Mit Zitat antworten Zitat
Hansa

Registriert seit: 9. Jun 2002
Ort: Saarland
7.554 Beiträge
 
Delphi 8 Professional
 
#3

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 25. Dez 2005, 21:09
Ich beziehe mich hauptsächlich auf das hier :

Zitat von mimi:
Ich möchte mir ein eigene "kleine" GUI schreiben.
Die nur ein paar standart komponenten hat wie z.b.
- Button
- Edit
- Formular
ersteinmal, später kann ich sie ja noch erweitern.
Du willst also ein Formular mit einem Buton und einem Edit. Und später kommt da noch eine Listbox hinzu und eine Combobox, oder das 2. Formular soll andere Farbe erhalten usw. Richtig ? Ihr versucht da anscheinend die bereits vorhandene Objektablage auf hochgeistige Weise nachzubauen. Habe die risigen Texte auch nicht gelesen. 8) Darüber habe ich schon so viel geschrieben, daß mir langsam die Lust ausgeht. Wer das für solche Zwecke nicht macht, der ist eben selber dran schuld. 8) Und trotz Weihnachten deshalb nochmals, allerdings nur Source mit Kurzkommentar :

Formular mit Button und Edit :
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TGrundForm = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

end.
Das Teil ist bereits umbenannt und wird mit Rechtsklick in die Objektablage verfrachtet. Ich brauche das nächste Formular, welches noch die Listbox und die Combobox erhalten soll, alles andere bleibt gleich.

Neu -> weitere, Formular suchen und unten vererben nicht vergessen ! Ich pappe die Listbox und die Combobox da drauf. Ergibt das :

Delphi-Quellcode:
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TGrundForm2 = class(TGrundForm)
    ComboBox1: TComboBox;
    ListBox1: TListBox;
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  GrundForm2: TGrundForm2;

implementation

{$R *.dfm}

end.
Jetzt hat das Formular : Button, Edit (beide geerbt) und neu sind die Listbox und die Combobox. Das zweite Formular mache ich jetzt gelb und ganz klein Hier die DFM :

Delphi-Quellcode:
inherited GrundForm2: TGrundForm2
  Width = 174
  Height = 226
  Caption = 'GrundForm2'
  Color = clYellow
  OldCreateOrder = True
  PixelsPerInch = 96
  TextHeight = 13
  inherited Button1: TButton
    Left = 8
    Top = 8
  end
  inherited Edit1: TEdit
    Left = 8
    Top = 40
  end
  object ComboBox1: TComboBox
    Left = 8
    Top = 72
    Width = 145
    Height = 21
    ItemHeight = 13
    TabOrder = 2
    Text = 'ComboBox1'
  end
  object ListBox1: TListBox
    Left = 8
    Top = 96
    Width = 121
    Height = 97
    ItemHeight = 13
    TabOrder = 3
  end
end
Wie man sieht habe ich den Button und das Edit ganz in die Ecke geschoben und die ganze Form ist nur noch 174 Pixel breit. Es werden also nur die Differenzen zum Vorgänger neu gespeichert. Alles andere bleibt ! Na gut noch Anhang.
Angehängte Grafiken
Dateityp: jpg oa_211.jpg (127,8 KB, 18x aufgerufen)
Gruß
Hansa
  Mit Zitat antworten Zitat
Der_Unwissende

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

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 25. Dez 2005, 21:44
Hi Hansa,
erstmal frohe Weihnachten auch dir!

Ich weiß zwar (auch schon gelesen) wie sehr du die Objektablage magst (hüstel pushst), aber ich hatte eigentlich den Thread etwas anders verstanden. Ging doch eher um ein erstes OOP-Programm, dass eine GUI bereitstellen soll, die dann (wahrscheinlich) zur Laufzeit einen Designer zur Verfügung stellt. Und wenn man nicht gerade eigene dfms parst, sollte die Objektablage hier wenig bringen.

@mimi:
Ok, merk schon die Idee von Interfaces ist nicht so leicht zu erklären. Also ein Interface ist wie eine eingeschränkte Klasse. Du kannst keine Variablen anlegen und auch keine Sichtbarkeit festlegen, nur Sichtbare Methoden. Ein Interface regelt sozusagen die Kommunikationsmöglichkeiten einer Klasse. Wenn dein Interface eine Methode draw enthält, heißt dass, das jede Klasse die dieses Interface implementiert Draw versteht. Aber was genau die Instanz macht, wenn ihr draw gesagt wird, geht aus ihrer Klasse und ihrem aktuellen Zustand hervor. Jede Instanz kann nur einer Klasse angehören, aber beliebig viele Interfaces haben, anders gesagt, jede Klasse kann beliebig viele Sprachen sprechen.
Wenn du eine Klasse nimmst, die nur Methoden beinhaltet (und die sind alle public und abstract), dann hast du ein Interface (mehr oder weniger). Allerdings kannst du trotzdem nur von einer Klasse erben.

Da hast du auch gleich das wichtigste an einem Interface, die Methoden sind alle abstrakt. In der Klasse musst du sagen, was genau in der Methode gemacht wird, im Interface kannst du das nicht festlegen.
Für gemeinsame Eigenschaften solltest du dann eine abstrakte Klasse benutzen, diese kann dann die Methoden der Interfaces als abstract markieren. Aber auch hier musst du aufpassen, nicht jede Klasse hat z.B. eine Caption (ListBox mit Caption?).
Zudem darf natürlich nicht jede Klasse Kinder haben (also untergeordnete Elemente). Bei einem Panel macht das Sinn, bei einem Label aber nicht, also musst du dort noch eine eigene Untergruppe machen. Z.B. wäre folgendes denkbar IZeichenbar, IKannKinderhaben. TBasisKlasse(IZeichenbar) -> TKannKinderhaben(TBasisKlasse, IKannKinderHaben)
und daraus dann die einzelnen Klassen TBasisKlasse -> TDeinButton, TKannKinderhaben -> TDeinPanel, ...

Für weitere Eigenschaften solltest du ruhig weitere Verfeinerungen festlegen. Musst halt immer gucken ob es Elemente gibt, die z.B. keine Farbe haben brauchen. Wenn alle Elemente eine Farbe haben sollen, trägst du die einfach in die Basisklasse ein und alle Nachfahren haben natürlich gleich eine Farbe.

In jeder speziellen Klasse kannst du eine bereits vorhandene Methode (von irgendeinem Vorfahren) einfach überschreiben. Du schreibst nochmal die Methode in die Klassendeklaration und dahinter ein override. Wird eine Methode überschrieben, kannst du mit inherited <Methodenname> die Vorfahrfunktion aufrufen. Jede Klasse besitzt automatisch einen Konstruktor Create und einen Destruktor destroy (geerbt von TObject, jede Klasse erbt von TObject).
In beide Methode gehört ein inherited create als erste Zeile (bzw. inherited destroy). Diese sorgen erst für das allozieren (oder freigeben) von Speicher durch Windows.
  Mit Zitat antworten Zitat
mimi

Registriert seit: 1. Dez 2002
Ort: Oldenburg(Oldenburg)
2.008 Beiträge
 
FreePascal / Lazarus
 
#5

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 06:05
@Hansa
Danke für dein versuch abr das geht leider am beitrag vorbei.
Es geht hier im mein erstes richtiges OOP programm und in diesem zusammen hang um eine kleine GUI.
Und das was du da gezeigt hast auf dem bild ist natürlch sehr einfach.

@Der_Unwissende
Also ein Button braucht eigetnlich auch keiner Kinder das brauchst nur das panel z.b..
Was hast du gegen meine idee mit den zwei variabeln ?
Achso die sind dann natürlcih in jeder komponente, das ist ja klar.
Aber die grund komponente soll ja auch schon was machen. bzw. aus deinen letzten beitrag kamm mir die idee das alle fenster objekte d.h. das Formula(r) die Panels und und und evlt. das die nur die eigenschaften haben sollten um komponenten zu verwahlten. Aber dann ist die Frage wie verwalte ich die Komponenten am besten im speicher so das ich leicht drauf zugreifen kann intern und extern ?

In einen Interfaces werden also nur methoden defniert keine variabeln, ereignise eigenschaften ?
und du kannst also bei jeder klassen deniation dieses Interface mit angeben ?
Dann währe die frage was sollte da alles rein ?
evtl. dies:
Draw, add, del
wobei add soll ja Create ausführen bei jedem "fenster" und del soll ja Destroy werden.
Aber für mich ist im moment die verwaltung der Komponenten das größte problem. Wie soll ich dies am besten bewerkstilligen ? nagut die wirst warscheinlich keine 100 komponenten auf dein formular haben aber auch menupunkte z.b. beim mainmenu und Popupmenu und evlt. bei der Listbox wobei die listbox soll ja das erste menu werden sobalt die fertig ist wollte ich daraus ein Popupmenu basteln und ein Mainmenu. Habe ich shconmal gemacht aber leider ohne OOP.Und darum geht es mir ja hier.

Das heißt ja das die fenster klassen einer vollkommen andre klasse her abgeleitet werden müsten also die normalen Komponenten wie z.b. einen button, label, Checkbutton und soweiter.

Aber die verwaltung ist wie gesagt das größte problem.
Du hast jetzt 20 komponenten in deinem Fenster und möchte das 3 ändern z.b. über
Komoponente3.Color:=clred
das währe ja der vorteil bei oop.
wenn du alles in einer liste hättes müsste du ja ersteinmal den index raußfinden:
Item[3].Color:=clred.
das währe ja ein umdecken ervorderlich und genau das macht das ganze projekt so schwer.
Michael Springwald
MFG
Michael Springwald,
Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus)
  Mit Zitat antworten Zitat
Der_Unwissende

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

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 11:44
Ich seh schon, langsam nähern wir uns der OOP, aber es sind glaube ich noch ein paar Dinge nicht ganz klar. Heißt hier weder das du es nicht verstanden noch ich es schlecht erklärt habe oder so, sondern ein paar Dinge wurden (denke ich) einfach noch nicht gesagt und selbst dann ist die OOP schon etwas umfassender als das man mal ebend ein perfektes OO-Programm hinbekommt (falls das je der Fall ist).

Aber du befindest dich (imho) auf einem guten Weg.
Eine wirklich wichtige Sache an der OOP ist es und die ist grundlegender als alle Anderen, zu abstrahieren. Ich glaube du betrachtest deine Probleme noch ein wenig zu konkret. Z.B. die Idee, dass bei einem Add ein Create aufgerufen werden muss, stimmt so nicht ganz.
Du darfst dir immer nur einzelne Objekte anschauen, die kennen die Welt nicht sondern nur sich selbst. Sie kennen ihre Eigenschaften und können auf Kommunikation von Aussen reagieren, mehr nicht. Sie interessieren sich aber auch garnicht für die Welt.
Dein Panel ist auch so ein Objekt. Es kennt erstmal nur die eigenen Eigenschaften. Sagen wir mal (da sind wir uns ja einig), eine Eigenschaft ist, dass ein Panel andere Objekte aufnehmen kann (sie verwalten kann). Das weiß das Panel, das ist auch gut so. Aber wo die anderen Objekte herkommen und was für Objekte auf der Welt leben, dass braucht das Panel doch gar nicht zu wissen, also weiß es das auch nicht.
Dein eigentliches Programm ist wie gesagt die Welt. Da gibt es auch eine höhere Macht (den User), der die Welt beeinflusst und auf diese Beeinflussung reagiert deine Welt. Du definierst mit deinem Programmieren nur, wie sie worauf reagiert.

Also quasi üblich wäre es für ein Panel, dass ein Panel folgendes kann:
  • sich um eigene Eigenschaften (Farbe, Größe, Titel, ...) kümmern
  • Fremde Objekte speichern
  • aber nicht fremde Objekte erzeugen

Also könnte ein Panel folgende Form haben

Delphi-Quellcode:
type
  TDeinPanel
    private
      // alle privaten Eigenschaften / Felder
    protected
      // setter und getter für diese Felder
      // alles zur eigenen Verwaltung
    public
      // Methoden die von anderen angesprochen werden können
      procedure draw; // damit es sich selbst aktualisiert
      procedure add(Obj); // fügt irgendein Objekt ein
      procedure remove(Obj); // löscht irgendeinobjekt
  end;
Natürlich ist dies hier kein echter und vollständiger Code, aber von aussen gesehen sehe ich eigentlich doch nur, dass es ein draw, ein add und ein remove gibt. Es ist die Sache des TPanels was beim Aufruf gemacht wird. Sagen wir mal wir machen es etwas konkreter, wir kreieren eine Liste in Delphi (ohne Pointer, sondern mit Klassen). Ich denke es reicht sich hier auf das Add zu beschränken, daran kann ich schon alles zeigen was ich zeigen möchte (sollte es Namen schon in Delphi geben, ist dies nur Zufall und nichts aus der VCL)

Delphi-Quellcode:
type
  // Ein Interface, legt nur Methoden fest
  // die von aussen zugänglich sind
  IObjectList = Interface
    procedure Add(Obj : TObject);
  end;
  
  // erbt von TInterfaceObject, da Delphi Interfaces 3 Standard-Methoden haben
  // die ich hier nur nicht implementieren möchte
  // Und diese Klasse implementiert IObjectList (hat also alle Methoden des Interfaces)
  TObjectList_Array = class(TInterfacedObject, IObjectList)
    private
      Objects : Array of TObject;
    public
      procedure Add(Obj : TObject); // muss hier rein, da IObjectList implementiert wird!
  end;
 
  TObjectList_List = class(TInterfacedObject, IObjectList)
    private
      Objects : TObjectList;
    public
      procedure Add(Obj : TObject); // muss hier rein, da IObjectList implementiert wird!
  end;
Ok, soweit sind mal die Deklarationen der Klassen fertig. Wie du siehst haben beide Klassen die Methode Add und beide müssen Add haben, da sie IObjectList implementieren. Von Aussen sehe ich nur dieses Add. Intern hat eine Klasse noch ein dyn. Array, die Andere eine TObjectList.
Schauen wir uns nun das Add an.

Delphi-Quellcode:
procedure TObjectList_Array.Add(Obj : TObject);
begin
  setLength(self.Objects, length(self.Objects) + 1);
  self.Objects[length(self.Objects) - 1] := Obj;
end;

procedure TObjectList_List.Add(Obj : TObject);
begin
  self.Objects.Add(Obj);
end;
Wie du siehst machen beide nicht das gleiche. Eine Methode speichert das übergebene Obj in ein Array, das andere in eine Liste. Wenn du aber davon abstrahierst, würdest du sagen, beide speichern Obj. Gut, hier fehlen jetzt Methoden um ein Element auch wieder raus zu holen, aber denk dir die einfach. Auch diese sähen für ein Array anders aus als für eine Liste, aber beide würden dir ein Element rausholen.
Und dieses Abstraktere siehst du nur von aussen.

Wenn du jetzt eine Variable vom Typ IObjectList hast, dann weißt du, dass Add ein TObject aufnehmen kann. Wie das intern gespeichert wird weißt du nicht, aber musst du auch nicht.
Es handelt sich dabei um das so genannte Black-Box-Prinzip. Du hast eine schwarze Kiste. Die kannst du nicht öffnen, du weißt was du reinwerfen kannst und du weißt was rauskommt. Mehr nicht, aber das reicht dir.

Und auch das nochmal als Programm:

Delphi-Quellcode:
// Fügt Element in die Liste ein
procedure AddToList(Liste : IObjectList; Element : TObject);
begin
  // klappt, da jedes IObjectList eine Methode Add hat, der man ein TObject geben kann
  Liste.Add(Element);
end;

procedure Test;
var L1 : TObjectList_Array;
    L2 : TObjectList_List;
begin
  L1 := TObjectList_Array.Create;
  L2 := TObjectList_List.Create;
  
  AddToList(L1, TObject.Create);

  AddToList(L2, TObject.Create);
end;
Schau dir den Code mal gut an. Wie du siehst, kannst du Variablen vom Typ IObjectList übergeben. Die beiden TObjectList_xxx haben dieses Interface implementiert. AddToList weiß zu keinem Zeitpunkt ob Liste gerade eine TObjectList_Array oder TObjectList_List bekommen hat. Es weiß nur, dass es eine Methode Add gibt und die wird aufgerufen.
Soweit klar? Hier bitte wirklich an diesem Beispiel nachfragen wenn etwas unklar ist!

Also alles was geerbt wurde (irgendwann auf dem Weg von TObject -> TIrgendwas) bleibt erhalten. Das heißt wenn du ein Objekt T1 von TObject erben lässt, kannst du es auch überall dort verwenden wo ein TObject verlangt wird. Dann kannst du ein T2 ableiten (TObject -> T1 -> T2) und T2 kannst du überall dort benutzen wo ein TObject oder T1 gebraucht wird.
Dann kannst du ein T3 ableiten...

Wichtig ist, dass du immer nur die Eigenschaften hast, die der Typ (variable : TIrgendwas) festlegt.
Delphi-Quellcode:
type
  T1 = class(TObject)
    public
      color : TColor;
  end;
  
  T2 = class(T1)
    public
      count : Integer;
  end;

procedure setColor(t : T1);
begin
  t.Color := clRed;
end;

procedure resetCount(t : T2);
begin
  t.Count := 0;
end;

procedure resetCount2(t : T1);
begin
  t.Count := 0;
end;

procedure Test;
var inst1 : T1;
    inst2 : T2;
begin
  inst1 := T1.Create;
  inst2 := T2.Create;
  
  setColor(inst1); // klappt super
  setColor(inst2); // klappt auch

  resetCount(inst1); // geht nicht, da inst1 nicht vom Typ T2
  resetCount(inst2); // klappt super

  resetCount2(inst2); // klappt nicht
end;
So, interessant ist natürlich der Letzte Fall. Du übergibst ein T2, dass die Eigenschaft Count hat. Aber die Methode resetCount2 erwartet ein T1. Also werder von inst2 nur die Eigenschaften genommen, die auch ein T1 hat, count gehört nicht dazu. In der Methode siehst du also auch wirklich nur das, was der Variablentyp (T1) beschreibt.

Ok, hoffe das war soweit verständlich.
Was dein Problem mit der Verwaltung angeht, so sollte es eigentlich recht nahe an dem Beispiel der Listen dran sein. Modellier (am besten einfach auf Papier oder so) erstmal die Beziehung der Objekte untereinander.
Also wer hat welche Eigenschaften. Das macht vieles einfacher. Ich denke du würdest erstmal mit den zwei Interfaces auskommen, die ich schon genannt hatte. Wie sie funktionieren (also wie verwaltet wird) kannst du dir später überlegen, erstmal musst du dir überlegen was du alles brauchst.
Ein häufiger Fehler (hab ihn sehr sehr häufig gemacht) ist es, einfach los zu legen. Dann kommt irgendwann der Punkt wo einem klar wird, man hat etwas vergessen und man kann mehr oder weniger von Vorne anfangen (macht sich immer schlecht, ist immer teurer als erwartet und lässt sich fast immer vermeiden).
Deshalb modellier erstmal im Kopf / auf Papier in Ruhe durch. Der Hauptvorteil der OOP ist, dass du dir über die Details zu keinem Zeitpunkt Gedanken machen musst (ganz übertrieben gesagt). Wenn du in deiner schwarzen Kiste die Objekte statt in einem Array in einer TObjectList speicherst, dann ist das dem Programm egal. Es wird halt intern anders gespeichert, aber du wirfst noch das gleiche rein und bekommst noch das gleiche raus.
Hier liegt dann auch der Vorteil von Interfaces. Wenn du als Argument nur ein IObjectList eingetragen hast, ist es dem Programm egal ob die Instanz nun ein TObjectList_Array, eine TObjectList_List oder gar eine TObjectList_FibonacciHalde ist. Die Methode Add gibt es, das reicht.

Und nochmal konkret auf deinen Fall bezogen, ja es ist schon richtig, du trägst in die Interfaces ein Add und Remove (und was auch immer dazu gehört) ein. Hier musst du nur drauf achten, dass jedes Interface genau das beinhaltet, was zusammen gehört. Ein Interface mit nur Add macht keinen Sinn (siehe oben ), aber genauso wenig macht ein Interface mit nur einem Remove Sinn. Eines das Hinzufügen und Entfernen kann, ist hingegen schon geeigneter, oder?
Und so kannst du weiter machen, erstmal die Grundlegenden Eigenschaften in Interfaces (also nur in Hinblick darauf, dass es irgendwann mal irgendein Objekt geben wird, dass diese Methoden braucht).
Dann geht es mit abstrakten Klassen weiter. Hier kommen dann Methoden, Ereignisse und Variablen rein, die spätere konkrete Klassen gemeinsam haben (z.B. wird alles was Sichtbar ist auch eine Position haben, ...). Aber auch hier nur dass was auch zusammen gehört. Wir hatten ja schon, dass nicht jedes Objekt mal einen Titel (Caption) haben muss (aber die einen haben kann man wieder von einer gemeinsamen Klasse ableiten).

Das alles sind natürlich nur Tipps, ich würde sagen es ist immer einer guter Weg so an ein Problem ran zu gehen. Du bleibst flexibel. Es ist gerade bei am Anfang kleineren Projekten schon ein gewisser Mehraufwand hinter und man fragt sich immer mal lohnt sich das? Aber ehrlich, wenn du später was neues einfügst und gut modelliert wurde, ein Traum! Und wenn du mal Code umschmeißen musst, weil nicht gut modelliert wurde... (und das umschmeißen kostet deutlich mehr Zeit/Geld als das bisschen mehr Planung am Anfang).

Falls du dich für die Modellierung entscheidest, würde ich die gerne sehen, dann kann ich dir da sicher weiterhelfen (falls ich dir bisher helfen konnte )
Lass ruhig Dinge bei denen du dir unsicher bist noch etwas aussen vor (wäre hier eigentlich nur das Hauptformular) oder bau es so ein wie du denkst. Muss auch nicht vollständig sein, geht ja nur ums Prinzip.
Und denk noch nicht zu sehr über alle speziellen Eigenschaften und Beziehungen nach. Was man später in ein oder zwei Variablen, mit Listen oder ohne, ... macht ist für die schwarze Kiste erstmal egal. Die guckt man sich später immer schön einzeln an und dann hat man schon den Vorteil dass man weiß was reinkommt und was rauskommt.

Ach ja, wie du später an ein bestimmtes Objekt kommst, ist viel leichter als du vielleicht gerade glaubst, aber ich habe es erstmal aussen vor gelassen, da wie gesagt die Modellierung vorher dran kommen sollte und du nie über Dinge nachdenken brauchst (in der OOP) die noch nicht aktuell sind. Du kannst schließlich sehr sehr leicht erweitern!

Gruß
  Mit Zitat antworten Zitat
mimi

Registriert seit: 1. Dez 2002
Ort: Oldenburg(Oldenburg)
2.008 Beiträge
 
FreePascal / Lazarus
 
#7

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 13:53
Modellierung damit meinst du die Klassenstrucktur aufbauen also wie die grundklasse ausehen soll.
Aber ich glaube ich habe verstanden worauf du hinnaus wolltes:
Die objekte arbeiten für sich alleine.
Das interface hat nur public funktionen/proceduren die in jeder abgeleitetn klassen zu sehen sind hier noch mal eine frage:
Können die Klassen die nicht abgeleitet werden die proceduren/funktionen aus dem Interface sehen ? oder können "nur" abgeleitet klassen diese proceduren/funktionen sehen ?

was mich überrascht hat war das du
Delphi-Quellcode:
procedure Test;
var L1 : TObjectList_Array;
    L2 : TObjectList_List;
begin
  L1 := TObjectList_Array.Create;
  L2 := TObjectList_List.Create;
 
  AddToList(L1, TObject.Create);

  AddToList(L2, TObject.Create);
end;
ein Create in einer Procedure aufruftst. das war irgenwie neu für mich.

aber ich denke ich entscheide mich für das Modellieren von Klassen.
also was alle Komponenten auf jedenfall gemeinsamm haben währen bei einer GUI z.b.:

- add, del, draw, repaint
- onDraw, onAdd, onDel, onrepaint
wobei auch Mausereignise könnten ja auch noch jede komponente habe würde ich mal sagen oder ?
wie z.b.:
onMouseDown
OnMouseMove
onMouseUp
OnMouseLave(wenn du mit der Maus die komponenten verlässt)
wobei die tastertur ereignise nicht umebdingt jede komponete haben sollte. z.b. ein Panel braucht keine Taster ereignise aber ein TEdit.

aber was ich an dieser sache immer noch nicht verstehe ist warum das verwalen erst zum schluss kommt.
angenommen du hast jetzt ein paar komponenten geschrieben und die sind soweit fertig. du kannst von jeder dieser komponenten ein instanz erzeugen und benutzten. aber wenn du mehre benutzen möchte musst du die ja auch verwalten z.b. alle zeichnen(evlt. nur die die geändert wurden), bestimmte komponenten löschen oder hinzufügen. evlt. möchtes du von einer einen caption ändern oder so.

Was ich auch nicht verstehe ist warum bei den standart komponenten von delphi der Panel ein Caption hat. den braucht er doch garnicht.

Alle komponenten haben die methoden von dem inteface, aber sie brauchen ja auch noch eigene z.b. ein Memo braucht scollbalken wobei ein Edit keine braucht.(währe auch nicht schlecht).

Wenn ich dein Beitrag verstanden habe geht es darum ersteinmal alle komponenten die ich brauche zu enfernen von standart obj dann TEdit, TLabel, TButton, ..... habe ich entwurfen. dann muss ich mir gedanken machen wie ich sie verwalte, sehe ich das so richtig ?
z.b.
Delphi-Quellcode:
TStdObj = class
private
  FName, FCaption, FTitel:String;
  Fx,Fy,Fw,Fh,Ftag:Integer;
  fbgColor, fvorColor:TColor
public

published
  property Caption; read FCaption; write FCaption
  property name;read fName; write fName
  property Titel;read fTitel; write fTitel
end;
das währe mein Grundobjekt(ersteinmal ohne interface da ich den sinn noch nicht so ganz verstehe)
von dieser Klasse leitet ich nun die TEdit komponente aber TEdit braucht ja kein Caption sondern Text wo man ein Text eingeben kann weil Caption soll ja nur als anzeige dinnen. muss ich jetzt für jeden fall eine weite grund komponente schreiben oder kann ich den Caption verstecken und durch Text ersetzten.

mir geht es ja ersteinaml nur darum eine Klassenstrucktur zu finden die Optimal für eine GUI währe und dann um dann die mit Leben zu füllen.

Ich würde schon sagen das du mir weiter Hilfe auf die einer oder andre art und weise.
z.b. bei jedem beitrag wird mir was Klarer *G*.

z.b. bei deinem letzten: das mit der Schwartzen kiste das ich z.b. mehre komponenten geschrieben habe und diese alle das "gleiche" create haben und somit das gleiche Create von der grundklasse aufgerufen wird wobei hat dies eigetnlich vorteile ?
weil wenn ich in der Grundkomponete ein Item defniere wird es ja überalle die item geben.

PS:
ich muss leider um ca 18 Uhr wieder nach Hause und da habe ich kein internet. währe schön wenn wir uns nochmal im IRC treffen könnten oder so weil ICQ habe ich nicht.
Michael Springwald
MFG
Michael Springwald,
Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus)
  Mit Zitat antworten Zitat
Der_Unwissende

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

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 14:43
Kein Thema, hab auch kein ICQ (und auch nicht vor in nächster Zeit etwas daran zu ändern). In welchem Channel finde ich dich denn?

Zitat:
aber was ich an dieser sache immer noch nicht verstehe ist warum das verwalen erst zum schluss kommt.
Die Details kommen zum Schluß. Die Herangehensweise ist eigentlich denkbar einfach, du gehst immer von unabhängigsten Objekt (oder einem der unabhängigsten Objekte, wenn es mehrere gibt) zum nächsten.
Wenn du sagen wir einen BaseButton, einen Button, einen SpeedButton hast, wobei dies auch die Spezialisierung ist (BaseButton -> Button -> SpeedButton), dann kannst du nicht mit dem SpeedButton anfangen. Du könntest dort zwar alles Eigenschaften des SpeedButton reinschreiben, aber die von einem Button hat ein Speedbutton schließlich auch.
Würdest du mit dem SpeedButton anfangen, könntest du dort alle Eigenschaften reinschreiben, die die auch ein Button hat wieder löschen und den Button entwerfen, hier dann die entfernen, die auch ein BaseButton hat...

Schöner ist mit dem kleinsten gemeinsamen anzufangen. Das kannst du dann in kleinen Schritten erweitern. Wenn dein BaseButton erstmal nur ein Rechteck ist, ist das super. Ein Rechteck lässt sich leicht implementieren. Ist das fertig, kommt vielleicht ein Text in den BaseButton, wieder nur ein kleiner Schritt. Kleine Schritte bedeuten weniger Fehler.
Wenn du erst alle Eigenschaften festlegst und TopDown arbeitest, erschlägt dich das bei einem Speedbutton. Es gäbe soviel zu tun, dass du nicht weißt wo du anfangen solltest (aber du kannst es machen).

Ähnlich ist es mit der Modellierung. Bevor du etwas verwalten kannst, muss etwas zum verwalten existieren. Deshalb fängst du mit etwas ganz einfachem an, was eigentlich nichts braucht (ausser dem Delphi gegegebenen). Basiert etwas auf dieser Klasse, ist das kein Problem, die Klasse hast du dann ja schon fertig.
Wenn du alle Lebewesen der Welt kennst, kannst du leicht eine Welt erschaffen, auf der sie alle Leben können, du weißt ja schon was für Lebewesen existieren. Würdest du sonst eine Welt schaffen, die Wasser hat (weil du Fische planst), dann würdest du in Ruhe die Fische entwerfen, dann die Wale, dann die Ameisen, OH, Moment, Ameisen brauchen Land. Ok, kein Thema du schaffst auch noch Land. Dann schaffst du die Affen, Mensch, jetzt brauchst du auch noch Bananen und Bäume, ...

Verstehst du was ich meine? Du kannst auch mit der Verwaltung anfangen, aber es wäre schwerer im Nachhinein anzupassen. An sich kannst du auch eine Verwaltung planen, aber nicht konkret. Bleib da einfach flexibel. Merk dir nur, dass es eine Art zu speichern gibt, nicht mehr. Du weißt man speichert indem man etwas hinzufügt, etwas entfernt und sich mal anguckt was es schon gibt. Das ist alles. Wie man genau hinzufügt oder entfernt ist doch erst wichtig wenn du wirklich speichern möchtest.

Ich häng dir mal ein sehr einfaches UML-Diagramm an. Weiß nicht ob du mal damit gearbeitet hast, die sind glaube ich nur Teilweise intuitiv. Es soll dir einfach nur die Modelierung erklären. Die Pfeile würden die meisten Menschen immer in umgekehrter Richtung setzen wollen. Zur Erklärung, sie heißen Verallgemeinerung (man könnte sie so auffassen). Du zeigst immer auf eine Verallgemeinerung. Also würde ein Schimpanse auf Affe Zeigen, Affe auf Säugetier, Säugetier auf Tier, ...
Interfaces sind Schnittstellen, die +, - und # Zeichen stehen nur für public, private und protected.

Hoffe es hilft dir überhaupt. Wäre nur eine Möglichkeit es zu modelieren. Wichtig ist, es gibt immer viele Arten der Modellierung, aber es ist nicht nur eine richtig!
Verwende immer (auch beim Diagramm) den Grundsatz für alle Probleme der Informatik : Teile und Herrsche. Schau dir nicht alles auf einmal an, sondern wirklich in kleinen Stücken, dass macht es leichter.

Ich versuch dir mal hier das Diagramm zu erklären. Es ist wie gesagt nicht vollständig.
Fang am Besten oben Links an, da hast du das Interface Drawable (Zeichenbar). Es hat eine Funktion Draw, dass war's auch schon. Nun nimm den Pfeil mit der gestrichelten Linie, der auf dieses Interface zeigt und folge ihm. Er kommt von TVisibleBaseClass. Die Klasse ist in zwei Teile geteilt. In der oberen Hälfte stehen die Variablen, das Minuszeichen heißt dass sie alle private sind.
In der unteren Hälfte stehen die Funktionen. Ein plus heißt sie sind public, eine # protected. Was für Felder es hier gibt und was für Methoden ist eigentlich erstmal egal, sollte nur grob was zeigen. Wichtiger ist der Pfeil. Du zeigst auf eine Generalisierung oder anders gesagt, TVisibleBaseClass ist ein spezielles IDrawable (es kann alles was IDrawable kann und mehr). Hier heißt dass, das jede TVisibleBaseClass immer ein Draw beherrscht.
Das macht natürlich nur Sinn, wenn auch jedes Sichtbare Element von dieser Klasse erbt (dafür musst du dann halt sorgen).

Schau dir nun die anderen beiden Schnittstellen an (ohne die Pfeile). Es sind die zwei weiteren obersten Kästchen, IContainer und IContainable. Fangen wir mit letzterer an. IConainable ist leer. Das ist auch beabsichtigt.
IContainable sollen einfach alle Elemente werden, die abgelegt werden können. Warum dieses Interface? Ganz einfach, auch deine GUI wird viele Eigenschaften der Sichtbaren Elemente haben. Sie muss auch gezeichnet werden, hat auch Position und Breite sowie Höhe, ...
Aber auf ein Panel wirst du sicherlich keine Form legen wollen, oder? Ich würde es jetzt so wie in Delphi machen und nein sagen. Deshalb muss es eine Eigenschaft geben, die eine GUI eben nicht hat. Dies ist durch das Interface gegeben. Folge dem gestrichelten Pfeil von IContainable zur TContainableBaseClass. Es heißt, dass jede TContainableBaseClass auch Containable ist. Von dieser Klasse (die keine eigenen Methoden braucht) geht ein Pfeil nach oben zur TVisibleBaseClass. Dieser durchgezogene Pfeil heißt, TContainableBaseClass erbt von TVisibleBaseClass. Alles was also in TVisibleBaseClass steht, hat TContainableBaseClass auch.
Beachte nur kurz, dass das Kästchen TGui keinen Pfeil zu TContainableBaseClass hat und keinen zum Interface Containable. Damit fehlt TGUI also diese Eigenschaft.

Ok, zurück nach oben zum Interface in der Mitte. IContainer ist das Interface, dass alle Objekte implementieren sollen, wenn sie andere aufnehmen/verwalten können. Was braucht man um verwalten zu können? Na ja, eine Möglichkeit etwas hinzu zu fügen, etwas zu entfernen und natürlich die Möglichkeit aufs Gespeicherte zu zu greifen. Es muss nicht so aussehen wie hier! Die Möglichkeiten könnte man (sollte man) deutlich flexibler wählen.

Zwischen IContainer und IContainable gibt es eine Linie mit einem Karo dran. Da steht auch noch ein 1 * dran. Ist das Karo wie hier innen weiß, so nennt man das eine Aggregation. Das bedeutet soviel wie : IContainer besteht unter anderem aus Elementen vom Typ IContainable. Ist hier eigentlich falsch, da nur Klassen Elemente aufnehmen können, wollte aber nicht noch extra eine Klasse zwischen setzen.
Die 1 * heißt, dass 1 IContainer beliebig viele (auch 0) IContainable aufnehmen kann.

Das wäre die oberste Zeile, die drei Interfaces. Schau dir nun die beiden gestrichelten Linien die von IContainer weggehen an. Sie gehen zu TGui und TVisiblePanel. Anders gesagt, alle Objekte die später andere Aufnehmen können müssen das Interface IContainer implementieren (es gilt auch umgekehrt, alles was IContainer implementiert kann später Objekte aufnehmen). Es ist auch schon festgelegt was für Objekte, es müssen welche vom Typ IContainable sein.

Nun bleiben eigentlich nur noch die Pfeile die von TContainableBaseClass weggehen übrig. Diese kommen von TVisibleButton und TVisiblePanel. Wie du siehst, sind diese Elemente damit vom Typ IContainable (erben sie von TContainableBaseClass) und vom Typ TVisibleBaseClass (auf gleiche Weise geerbt), TGui erbt nur von TVisibleBaseClass.
Ein TVisiblePanel kann zu dem als Container dienen, die TGui auch, ein TVisibleButton hingegen nicht. Ihm fehlt eine Beziehung zu IContainer.

Ich weiß nicht, ob das jetzt wirklich anschaulicher ist oder nicht, aber das ist der Gedanke den du vervollständingen müsstest. Es ist noch überhaupt nicht festgelegt, ob ein Button später ein Rechteck, ein Dreieck oder ein Kreis ist. Aber es ist im Moment noch nicht wichtig. Auch die Verwaltung ist hier schon geplant, aber eben noch nicht konkret. So modellierst du erstmal so weit wie es dir möglich ist (es sollte halt ein einfaches Modell bleiben). Man kann sich natürlich auch tot modellieren, aber du hattest ja eine Vorstellung, was du für den Anfang haben möchtest, wie gesagt Erweitern ist sicher nicht das Problem.
Wenn du dass erstmal modelliert hast, dann geht es erst an die Implementierung und auch dort jede Klasse für sich. Wenn du dann etwas implementierst, brauchst du nur dein Modell umzusetzen, das überlegen über mögliche Abhängigkeiten und sowas wie, wie kann die Gui Elemente aufnehmen, die erst noch geschrieben werden entfällt. Würdest du jetzt ein TVisibleMemo schreiben, musst du nur gucken wovon es erbt und es könnte ohne Änderung der Gui von dieser oder einem Panel verwaltet werden (wenn es ein IContainer ist).
Damit kannst du die Implementierung sehr viel einfacher durchführen.

Die ist dann aber eine andere Sache. Dort fängst du dann übribens immer mit den Klassen / Interface an, auf die nur gezeigt wird. Die anderen besitzen eine Abhänigkeit von diesen Klassen/Interfaces (kann nur von Dingen die es gibt erben).

Gruß Der Unwissende
Angehängte Dateien
Dateityp: pdf visio-zeichnung1__1__162.pdf (44,8 KB, 8x aufgerufen)
  Mit Zitat antworten Zitat
mimi

Registriert seit: 1. Dez 2002
Ort: Oldenburg(Oldenburg)
2.008 Beiträge
 
FreePascal / Lazarus
 
#9

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 15:33
Das stimmt, es ist einfacher wenn ich von vorrein weiß wie die objekte aussehen und welche ich haben. um sie zu verwalten. Von UML habe ich schon mal gehört aber noch nicht mit gearbeitet.
Aber aus deinen diagramm leitet ich folgendes ab:
alle komponenten sind von IContainer abgeleitet. Alle komponenten die sichtbar sein soll werden von TVisibleBaseClass abgeleitet der button scheint von TContainableBaseClass abgeleitet zu sein.
d.h. es muss dann noch eine zweite TVisibleBaseClass gebebn die dann z.b. andre funktionen bereit stellt wie z.b. gruppenbarsit. die einen brauchen diese klassen die anderen wiederum diese und soweiter.

ich habe mir meine gui in entwar so vorgestellt:
das fenster soll ein einfaches Rechteck sein wo oben eine line gezeichnet wird als titel so ca 10(y) pixel unter dem anfang vom Rechteck.
das soll der header sein, der jeder kompoenten haben sollte.
genauso wie die Scrollbalken das sind alles weiter komponenten die überalle eingesetzt werden sollen.

Der Buttonsoll ersteinmal ein rechteck sein mit einen Text drin mehr nicht.später soll der erweiter werden.

Schritweise vorzugehen ist warscheinlich das besten. D.H. Wie sollen die 3 ersten komponenten aussehn.
Was du mit deiner Welt beschrieben hast meine ich jetzt. Das ich erst die komponenten erstelle und dann versuche sie zu verwalten.

gut. was haben ein Button, ein Label, ein fenster gemeinsamm und was nicht ?

zum Zeichnen eine procedure
zum Löschen der Komponente eine procedure
zum updaten eine Komponenten

variabeln z.b. Name, Caption, x,y,w,h,tag, taborder,visible,Autosize,Image,Form(z.b. rund, eckig)
TMyStyl(für das aussehen der komponente, z.b hintergrund/vordergrund farbe, schriftart, bitmapfont und soweiter)
wobei das fenster noch VisibleScollbalken, VisibleTitel haben sollte

aber wie sollte das am besten defniniert werden ?

Delphi-Quellcode:
tStdObj1 = class
  private
    fCaption, fName:String
    fx,fy,fw,fh,ftag,ftaborder:Integer;
  public;
   property Caption
   property name
   property x
   property y
   property w
   property h
   property Tag
   property Taborder
end;
und bei der zweiten klasse sollte statt caption dann text stehen. nur wie speicher ich dann sowas. Währe hier ein interface praktisch ? von den beide klassen abgeleitet werden ? oder soll caption und text ersteinaml in andren klassen defniert werden ?
Michael Springwald
MFG
Michael Springwald,
Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus)
  Mit Zitat antworten Zitat
Hansa

Registriert seit: 9. Jun 2002
Ort: Saarland
7.554 Beiträge
 
Delphi 8 Professional
 
#10

Re: OOP Problem: änderungen werden nicht übernommen

  Alt 26. Dez 2005, 16:26
Seid ihr immer noch an der Theorie dran ? Aber mein Beispiel war echt zu einfach. Folgendes hat mir nicht gepaßt : die Form war zu klein, die Farbe der gelben Form zu ätzend. Für die Nachfahr-Forms soll außerdem ein Panel eingeführt werden. Und weils so schön ist soll das gleich abgerundete Ecken haben. Außerdem sollen die vorhandenen Controls nach rechts. Das geht so :

Delphi-Quellcode:
unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Unit2, ExtCtrls, StdCtrls, Grids;

type
  TGrundForm3 = class(TGrundForm2)
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  GrundForm3: TGrundForm3;

implementation

{$R *.dfm}

procedure TGrundForm3.FormCreate(Sender: TObject); // (C) swissdelphicenter bzw. Beitragsschreiber
const
  bgcolor = $00FFDDEE;
  linecolor = $00554366;
var
  img: array of TImage;
  reg: hrgn;
  i: Integer;
begin
  inherited;
  for i := 0 to ComponentCount - 1 do
  begin
    if Components[i].ClassName = 'TPanelthen
    begin
      setlength(img, Length(img) + 1);
      img[i] := TImage.Create(Self);
      img[i].Width := (Components[i] as TPanel).Width;
      img[i].Height := (Components[i] as TPanel).Height;
      img[i].Parent := (Components[i] as TPanel);
      img[i].Canvas.Brush.Color := clSkyBlue; // Änderung !!
      img[i].Canvas.pen.Color := bgcolor;
      img[i].Canvas.Rectangle(0,0,img[i].Width, img[i].Height);
      img[i].Canvas.pen.Color := linecolor;
      img[i].Canvas.RoundRect(0,0,img[i].Width - 1,img[i].Height - 1,20,20);
      reg := CreateRoundRectRgn(0,0,(Components[i] as TPanel).Width,
        (Components[i] as TPanel).Height, 20,20);
      setwindowrgn((Components[i] as TPanel).Handle, reg, True);
      deleteobject(reg);
    end;
  end;
end;

procedure TGrundForm3.Button1Click(Sender: TObject);
begin
  inherited;
  showmessage ('blubb');
end;

end.
Jo, die Farbe des Panels wurde noch geändert und der ButtonClick hat jetzt auch eine Funktion. Die GrundForm3 wird übrigens in Unit1 so aufgerufen :
Delphi-Quellcode:
implementation

uses Unit3;

{$R *.dfm}

procedure TGrundForm.Button1Click(Sender: TObject);
begin
  GrundForm3.Show;
end;
Wenn auch die Theorie hier Vorfahrt zu haben scheint, es gibt auch Mitleser ! Und das Ergebnis : siehe Anhang.
Angehängte Grafiken
Dateityp: jpg oa2_732.jpg (24,0 KB, 12x aufgerufen)
Gruß
Hansa
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:34 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz