![]() |
Zwei Klassen möglichst "OOP" kommunuzieren lassen
Da ich gerade eine Komponentengruppe entwickle und alle Visuellen Komponenten eine Grundfunktion haben, dachte ich mir, lagere ich diese Funktion aus. Gesagt getan... Alles wunderbar.
Meine Komponenten (LangEdit, LangLabel...) haben jetzt eine referenz auf LangMain. Nun kam noch eine Idee dazu: Wenn der Benutzer jetzt für alle seine Komponenten, die zu oben genannten Gruppe gehören, auf all seinen Formen bearbeiten will, müsste er einen Umständlichen Code programmieren von meheren Zeilen ausmaße. Also habe ich in LangMain die "ultimativmethode" geschrieben. Problem: Diese "Ultimativmethode" muss logischerweiße auf alle anderen Komponenten zugreifen. Aber wie mache ich das? Klar ich könnte unter implementation die Komponenten hinzufügen, aber das ist doch ein bisschen unnatürlich, weil wie kann eine Komponente eine andere noch nicht erstellte Komponente kennen (deshalb landet sie in implementation)... Wie kann ich nun das möglichst "Haltbar" umsetzen. Ich dachte an einen Observer: LangEdit, LangLabel... <===> Observer <===> LangMain Problem bleibt: <===> Habt ihr eine andere möglichkeit? @ Ultimativmethode: Die greift auf Funktionen von LangEdit/LangLabel zu (die auf LangMain zugreifen) |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Delphi-Quellcode:
Damit können sich deine Language-sensitiven komponenten jederzeit bei der Language-verwaltung mit einer zur designzeit vergebenen id registrieren und bekommen bei jeder änderung der sprache den neuen string (der sich problemlos mit platzhaltern versehen lässt -
type
TChangeLanguageEvent = procedure(Sender: TLangMain; NewString: string) of object; TLangMain = class ... procedure RegisterSensitiveControl(StringId: Integer; OnLanguageChange: TChangeLanguageEvent;); ![]() Musst du natürlich alles noch implementieren ;) |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Aha... Das ist sozusagen ein Event, das cih wie auslöse?
Und wie kann ich es "Empfangen"? Übrigends muss ich noch auf Daten der Komponenten zugreifen. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
HI,
ich glaube dein Bild vom Observerpattern ist noch etwas falsch. Ein Event ist etwas das man in diesem Pattern als Observable bezeichnen würde. Sauberer OOP ist es hier auf Zeiger zu verzichten und stattdessen eine Basisklasse mit der aufzurufenden Methode zu erstellen. Der Observer selbst ist kein zwischengeschaltetes Element. Du hast eine spezielle Klasse, die Events auslösen kann. Dies ist das Observable. Geschieht hier etwas bestimmtes, gibt sie allen registrierten Observern bescheid. Damit steht auch fest, sie muss Möglichkeiten zum registrieren und deregistrieren so wie zum Benachrichtigen haben. Die andere Art von Klasse sind die Observer. Diese haben eine definierte Schnittstelle über die ihnen ein Ereignis mitgeteilt wird. Du hast also eigentlich nur zwei Klassen (und nicht direkt etwas dazwischen). Ein grobes Beispiel könnte dass sein:
Delphi-Quellcode:
Ja, wie du hier siehst, wären die konkreten As einfache Observer. Alle diese Objekte sind Nachfahren von TAbstractA und können sich damit beim Observable registrieren. Registrierte Klassen können nun einfach benachrichtigt werden. Man muss nur die Liste durchlaufen und bei jedem Observer die Methode onFoo aufrufen. Aus den hier übergebenen Argumenten geht dann hervor, was genau pasiert ist. Natürlich kannst du hier auch mehr als ein Ereignis mit ganz anderen Parametern beobachten.
TAbstractA = class
public // Abstrakte Methode die zur Benachrichtigung über ein Ereignis aufgerufen wird // Das Ereignis geht aus den übergebenen Parametern hervor procudure OnFoo(...); virtual; abstract; end; TA1 = class(TAbstractA) public // Konkrete Behandlung bei Benachrichtigung über Ereignis procudure OnFoo(...); override; end; TA2 = class(TAbstractA) public // Andere konkrete Behandlung bei Benachrichtigung über Ereignis procudure OnFoo(...); override; end; ... TObservable = class private // Liste aller Observer FObserver : TObjectList; // kann natürlich auch anders aussehen protected // Methode die alle registrierten Observer benachrichtigt // hier kann dann einfach über FObserver iteriert werden procedure notifyObserver(...); public procedure registerObserver(const Observer : TAbstractA); procedure deregisterObserver(const Observer : TAbstractA); end; Das ganze ist relativ analog zur Benachrichtigung durch Methodenzeiger. Hier hast du allerdings einen deutlich mehr OOP Weg und ein einfaches Beispiel für die Umsetzung des Observerpattern! Gruß Der Unwissende |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Statt der abstrakten Klasse könnte man da doch auch n Interface nehmen, oder hab ich da was falsch verstanden? :gruebel: Das hätte dann nämlich noch den Vorteil, dass unterschiedliche Basistypen möglich wären. Was aber unter Delphi leider dadurch wieder relativiert wird, dass man von InterfacedObject ableiten muss...
mfg Christian |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Muss man nicht, allerdings muss man dann die drei Standardmethoden von IInterface implementieren.
Das hat auch den Vorteil, dass man das Instanzzählen abschalten kann. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Ein Interface hat aber den Nachteil, dass man (imho) nicht mehr so richtig gut mit der TObjectList arbeiten kann. Oder kenne ich da nur wieder den Weg nicht?
Ist aber natürlich prinzipiell möglich, in anderen Sprachen auch üblich! (wobei sich halt Delphi's COM Interfaces von einem "echten" Delphi Sprachfeature doch leicht unterscheiden). |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Zitat:
mfg Christian |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Oh man... das es SOOOO schwer ist, hab ich nicht gedacht :P
Also ich habe es so programmiert:
Code:
Das Problem ist ja (eigentlich wäre es schöner LangProcs und LangMain in einer zu Haben), dass LangProcs Informationen der Komponenten brauch. Und die Komponenten müssen auf Allgemiene Prozeduren zurgreifen (oder es wäre effektiver), da dort Code für jede Komponente steht.=============> LangMain (Allgemeine Prozeduren) LangLabel <=== LangProcs (Prozeduren, die Daten von den Komponenten benötigen |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Es gäbe da noch eine andere interessante Technik.
Alle Komponenten haben die virtuelle Methode Notification. Man kann diese Methode überschreiben und könnte automatisch Verbindungen zur MasterKomponente (LangMain) herstellen und trennen. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Zitat:
Zitat:
Damit ein Master also alle Kinder kennt müssen diese sich beim Master registrieren (=> Observerpattern gut geeignet). Natürlich kann man noch etwas drum rum bauen (z.B. eine Fabrik), die die Registrierung automatisch übernimmt. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Zitat:
Aber ich habe noch etwas experimentiert: Notification() wird schon von der VCL aufgerufen, bevor der Konstruktor komplett abgearbeitet ist. Das scheint mir dann doch nicht so günstig zu sein. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Das große Problem ist:
Die Komponenten (=Edit, Label nicht MAIN!) haben alle ein paar Funktionen gemeinsam... Deshalb habe ich sie in Main ausgelagert. Nun habe ich auch eine Methode geschrieben, die alle "Lang"-Komponenten auf der Form ansprechen soll. Und diese habe ich logischerweise in die Main geschrieben, aber ich werde wohl das alles umdenken (müssen)... Weil wie es bisher ist, ist es nicht schön und umständlich. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
implementiere diese Sachen doch in der basisklasse?!
Delphi-Quellcode:
So würds ich machen.
type
//TOnLanguageChange = procedure(NewString: string) of object; TLangSensObservable = class; //forward TLangSensComp = class(TComponent) //könnte natürlich auch ein Interface sein! Würde die mehrfachvererbung möglich machen... private FIdent: Integer; public procedure RegisterToObservable(Observable: TLangSensObservable); virtual; //ruft nur RegisterObserver auf... procedure UpdateLanguage; virtual; procedure OnLanguageChange(NewString: string); virtual; published property Identifier: Integer read FIdent write FIdent; end; TLangSensObservable = class(TObject); private FObservers: TObjectList; public procedure RegisterObserver(Observer: TLangSensComponent); function GetCurrentString(Ident: Integer): string; overload; procedure SetLanguage(lang: string); //geht über string, geht natürlich ebenso über index... ka wie du deine Daten speichern willst... end; Wenn du in mehreren Klassen, die von einer Basisklasse abgeleitet sind, gleichen Code schreiben musst, ist das auf jeden Fall falsch bzw. ein designfehler! Wenn du aber "bequem" von VCL-Kompos ableiten willst, ist es ideal, mit Interfaces zu arbeiten. Du kannst zwar nicht von TInterfacedObject ableiten (musst also die Referenzzählung selber einbauen, denke ich :gruebel: ), aber das ist trotzdem ein idealer Einsatzort für Interfaces. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Hab ich nur vergessen was mit Interface gemeint ist, oder hab ich das tatsächlich noch nie gehört? (Erkläre das mal bitte kurz)
So: Nun denke ich ist es langsam (*räusper*) an der Zeit mal euch das Grundgerüst zu geben: - Es gibt "Komponentenunit", die z.Zt. LangLabel und LangEdit sind (ich wollte erstmal die Kinderkrankheiten auskurieren) (Die Units sind von TLabel/TEdit abgeleitet) - Es gibt eine Klasse in der die Funktion ist, die alle benötigen (sozusagen deie Übersetzungsfunktion) - Es gibt eine Prozedur die auf alle Komponentenunits zugreifen muss Es sind alles verschiedene "*.pas"! Jetzt wüsste ich gerne: Was muss ich bei den Klassen dazuschreiben? Muss ich z.B. bei LangMain das hinzufügen:
Delphi-Quellcode:
Eins möchte ich hier noch loswerden: Ich komme mit euren Antworten irgendwie leider nicht weiter :( Deshalb kann es sein, dass ich schon beantwortete Fragen nochmal stelle... Sry
type
TLangSensObservable = class; //forward TLangSensComp = class(TComponent) //könnte natürlich auch ein Interface sein! Würde die mehrfachvererbung möglich machen... private FIdent: Integer; public procedure RegisterToObservable(Observable: TLangSensObservable); virtual; //ruft nur RegisterObserver auf... procedure UpdateLanguage; virtual; procedure OnLanguageChange(NewString: string); virtual; published property Identifier: Integer read FIdent write FIdent; end; TLangSensObservable = class(TObject); private FObservers: TObjectList; public procedure RegisterObserver(Observer: TLangSensComponent); function GetCurrentString(Ident: Integer): string; overload; procedure SetLanguage(lang: string); end; // Me/ain Code TLangMain = class(TObject); {...} end; |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
OK, is kein problem.
Interface: Die einzige Möglichkeit zur Mehrfachvererbung in Delphi. Folgender Code:
Delphi-Quellcode:
Du hast damit sozusagen eine Kompatibilitätsvorraussetzung geaschaffen. Alle ILangObserver implementierenden Klassen müssen die in der ILangObserver-Deklaration vereinbarten Methoden bieten:
type
ILangObserver = interface(IInterface) procedure NotifyLanguageChanged(NewString: string); function GetStringId: Integer; end;
Delphi-Quellcode:
Du musst nun diese Methoden (ja, das ist ein wenig suboptimal) in jeder Klasse implementieren.
type
TLanglabel = class(TLabel, ILangObserver) public {ILangObserver} procedure NotifyLanguageChanged(NewString: string); function GetStringId: Integer; {IInterface} ... //weiss ich jetzt grad nicht was da noch hinmuss end; TLangPanel = class(TPanel, ILangObserver) public {ILangObserver} procedure NotifyLanguageChanged(NewString: string); function GetStringId: Integer; {IInterface} ... //weiss ich jetzt grad nicht was da noch hinmuss end; TLangGroupBox = class(TGroupBox, ILangObserver) public {ILangObserver} procedure NotifyLanguageChanged(NewString: string); function GetStringId: Integer; {IInterface} ... //weiss ich jetzt grad nicht was da noch hinmuss end; Du kannst deine TLang*-Kompos jetzt (wie bei einer Elternklasse) auf das ILangObserver-Interface reduzieren:
Delphi-Quellcode:
Du kannst die Interfaces in dieser Methode dann in eine Liste schreiben (das ist nicht ganz so... unenmpfindlich)und benachrichtigen:
TLangObservable = class(TObject)
public procedure Register(Observer: ILangObserver); end;
Delphi-Quellcode:
Das als kurze Einführung in die Vorteile von Interfaces. Du hast auch noch eine Referenzzählung, das zur Vollständigkeit, und das macht das hantieren damit ein wenig gefährlich, weil dir eventuell irgendwo instanzen übrigbleiben, und du bei der herumreichung von zeigern viel falsch machen kannst.
procedure TLangObservable.LanguageChange(Language: string);
var Observer: ILangObserver; begin LanguageSet(Language); for i := 0 to List.Count-1 do begin Observer := List[i] as ILangObserver; Observer.NotifyLanguageChange(GetLangstring(Observer.GetStringId)); end; end; Dazu solltest du dich allerdings einlesen. Du kommst mit unseren Antworten nicht klar? Naja, ist ja auch alles hochprozentiges OOP ;) |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Cool! Danke, hat mir echt weitergeholfen! Noch ein paar Fragen bleiben noch:
- Muss ich den Code überalls schreiben, oder reicht der Kopf? - Was ist mit GetStringId? Ist das wichtig/Notwendig? - Kennen die Komponentenklassen die Interasceklasse? - Was hat es mit dem Register auf sich? |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
1. du hast in interfaces leider absolut keine möglichkeit, code zu hinterlegen. das ist auch nciht der sinn von interfaces, sowas wäre sainn von echter mehrfachvererbung ;) also ja, du musst den code überall neu schreiben.
2. klar ist das wichtig. damit weiss der sprachen-"server", welchen string er dir jeweils liefern soll. nehmen wir einmal an, wir haben es in XML:
XML-Code:
dann musst du, um einen string zu bekommen, Sprache und id haben.
<language name="de/de">
<string id="0">%s</string> <string id="1">Hallo, benutzer!</string> </language> <language name="en/us"> <string id="0">%s</string> <string id="1">Hello, user!</string> </language> 3. Wie kennen? Die Implementation eines Interfaces bedeutet, da ist ein Objekt, das verfügt über bestimmte Eigenschaften, und das absolut unabhängig von allen anderen Charakteristika der Klasse. Sozusagen ein black-box-modell: Du hast einen schwarzen Kasten, von dem du nicht weisst,was er macht, aber du hast bestimmte Schnittstellen (man übersetze mal das wort "interface" ;) ), von denen du genau weisst, wie sie funktionieren. In diesem schwarzen Kasten kann alles möglcihe drinsein, aber das ist dir völlig egal, wichtig ist (auf einer Seite) nur die Schnittstelle. 4. Damit registriert sich eine Komponente beim "Server", um beim Wechseln der Sprache auch den neuen string zu bekommen(Observer-Pattern! Bei Bedarf noch mal weiter oben nachlesen ;) ). Es wäre übrigens auch denkbar, dort bereits die string-id zu übergeben, damit die nicht immer nochmal gequeriet... äh... abgefragt werden muss. |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Der Sinn von Interfaces? Die Vereinbarung von Schnittstellen. Stell dir vor, du hast Klassen, die eine mathematische Funktion darstellen. dann könntest du so ein Interface machen:
Delphi-Quellcode:
Dann kannst du dir alle möglichen Klassen bauen, die eine beliebige Funktion darstellen und eben einfach über das Interface angesprochen werden können.
type
IMathFunction = interface(IInterface) function Calculate(X: Extended):Extended; Das ginge natürlich mit einer abstrakten Basisklasse auch:
Delphi-Quellcode:
Schau dir einfach mal das Interface-Tutorial auf dsdt an. (
type
TMathFunction = class {abstract} //abstract erst ab D2005 public function Calculate(X: Extended):Extended; virtual; abstract; end; ![]() |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
Hallo,
Zitat:
![]() Gruß xaromz |
Re: Zwei Klassen möglichst "OOP" kommunuzieren las
LOL?!
ja, genau das (!) hab ich gemeint... :lol: sorry... :oops: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:02 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