|
Antwort |
Registriert seit: 1. Dez 2002 Ort: Oldenburg(Oldenburg) 2.008 Beiträge FreePascal / Lazarus |
#11
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:
werden ja die änderungen übernommen weil name eine eigenschaft ist wie bekomme ich das auch hin.
var
b:TButton; begin b:=TButton.Create b.Name b.Top end; (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) |
Zitat |
Registriert seit: 9. Jun 2002 Ort: Saarland 7.554 Beiträge Delphi 8 Professional |
#12
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. Formular mit Button und Edit :
Delphi-Quellcode:
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.
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. Neu -> weitere, Formular suchen und unten vererben nicht vergessen ! Ich pappe die Listbox und die Combobox da drauf. Ergibt das :
Delphi-Quellcode:
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 :
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.
Delphi-Quellcode:
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.
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
Gruß
Hansa |
Zitat |
Registriert seit: 13. Dez 2003 Ort: Berlin 1.756 Beiträge |
#13
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. |
Zitat |
Registriert seit: 1. Dez 2002 Ort: Oldenburg(Oldenburg) 2.008 Beiträge FreePascal / Lazarus |
#14
@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) |
Zitat |
Registriert seit: 13. Dez 2003 Ort: Berlin 1.756 Beiträge |
#15
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:
Also könnte ein Panel folgende Form haben
Delphi-Quellcode:
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)
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;
Delphi-Quellcode:
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.
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; Schauen wir uns nun das Add an.
Delphi-Quellcode:
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.
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; 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:
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.
// 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; 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:
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.
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; 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ß |
Zitat |
Registriert seit: 1. Dez 2002 Ort: Oldenburg(Oldenburg) 2.008 Beiträge FreePascal / Lazarus |
#16
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:
ein Create in einer Procedure aufruftst. das war irgenwie neu für mich.
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; 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:
das währe mein Grundobjekt(ersteinmal ohne interface da ich den sinn noch nicht so ganz verstehe)
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; 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) |
Zitat |
Registriert seit: 13. Dez 2003 Ort: Berlin 1.756 Beiträge |
#17
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.
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 |
Zitat |
Registriert seit: 1. Dez 2002 Ort: Oldenburg(Oldenburg) 2.008 Beiträge FreePascal / Lazarus |
#18
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:
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 ?
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;
Michael Springwald
MFG Michael Springwald, Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus) |
Zitat |
Registriert seit: 9. Jun 2002 Ort: Saarland 7.554 Beiträge Delphi 8 Professional |
#19
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:
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 :
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 = 'TPanel' then 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.
Delphi-Quellcode:
Wenn auch die Theorie hier Vorfahrt zu haben scheint, es gibt auch Mitleser ! Und das Ergebnis : siehe Anhang.
implementation
uses Unit3; {$R *.dfm} procedure TGrundForm.Button1Click(Sender: TObject); begin GrundForm3.Show; end;
Gruß
Hansa |
Zitat |
Registriert seit: 1. Dez 2002 Ort: Oldenburg(Oldenburg) 2.008 Beiträge FreePascal / Lazarus |
#20
ist das jetzt eine eigene GUI oder benutzt du delphi komponenten ?
Ich möchte nämlich eine komplet eigene GUI entwickeln die dann in Spielen z.b. verwendet werden können. ich möchte diese GUI mit OOP entwickeln da gibt es nur einige probleme mit der Klassenstrucktur. Wir haben zwar schon ausfürhlich darüber geredet aber ich weiß immer noch nicht wie z.b. das Grundobjekt ausehen sollte. von denn alle Komponenten abgeleitet werden sollen. z.b. dachte ich jetzt das das grundobjekt nur paar variablen/eigenschaften/methoden haben sollte. Mehr nicht. wie z.b. name, Caption, X,Y,W,H,Visible,onMouseMove,OnMouseDown, OnMouseUp. die ereignise zu schreiben ist kein problem und das aber was ein problem ist eine optimale klassenstrucktur zu entwickeln.
Michael Springwald
MFG Michael Springwald, Bitte nur Deutsche Links angeben Danke (benutzte überwiegend Lazarus) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |