![]() |
Delphi-Version: 2007
Observer:
Hallo zusammen,
ich spiele gerad ein bischen mit dem Observer Muster rum und habe folgende Klassen erstellt:
Delphi-Quellcode:
Ich habe mir ein Formular etstellt, in welchem ich die beiden Beobachter erstelleIBeobachter = interface ['{965C77DA-CA00-4915-8133-2D865B8F8182}'] procedure Aktualisieren(iTemperatur, iFeuchtikgkeit, iDruck: real); procedure Anzeigen; end; ISubject = interface ['{EB06F989-902A-43C1-8A58-DC671744DD2E}'] procedure RegistriereBeobachter(iIBeobachter: IBeobachter); procedure EntferneBeobachter(iIBeobachter: IBeobachter); procedure BenachrichtigeBeobachter; end; TWetterDaten = class(TInterfacedObject, ISubject) private Temperatur, Feuchtikgkeit, Druck : real; FLstBeobachter : TInterfaceList; // !!!!!!!!!!! public constructor Create; destructor Destroy; override; procedure RegistriereBeobachter(iIBeobachter: IBeobachter); procedure EntferneBeobachter(iIBeobachter: IBeobachter); procedure BenachrichtigeBeobachter; procedure MesswerteGeaendert; procedure SetMesswerte(iTemperatur, iFeuchtikgkeit, iDruck : real); end; TTemperaturAnzeigen = class(TInterfacedObject, IBeobachter) private FWetterDaten : ISubject; Temperatur, Feuchtikgkeit, Druck : real; public procedure Aktualisieren(iTemperatur, iFeuchtikgkeit, iDruck: real); procedure Anzeigen; constructor Create(iISubject : ISubject); destructor Destroy; override; // Zum Testen !!!! end; TFeuchtigkeitAnzeigen = class(TInterfacedObject, IBeobachter) private FWetterDaten : ISubject; Temperatur, Feuchtikgkeit, Druck : real; public procedure Aktualisieren(iTemperatur, iFeuchtikgkeit, iDruck: real); procedure Anzeigen; constructor Create(iISubject : ISubject); end; Die beiden Beobachter erstelle in in meinem Formular bei Programmstart:
Delphi-Quellcode:
procedure TfrmMain.FormCreate(Sender: TObject);
begin FWetterDaten := TWetterDaten.Create; FWetterDaten.SetMesswerte(30,65,30); FTemperaturAnzeigen := TTemperaturAnzeigen.Create(FWetterDaten); FFeuchtigkeitAnzeigen := TFeuchtigkeitAnzeigen.Create(FWetterDaten); end; Über Buttons will ich die Beobachter "Ein/Aus Schalten"
Delphi-Quellcode:
Mein Problem ist aber, das nach dem Entfernen des Beobachters, diese Beobachter frei gegeben wird. Das sollte aber nicht sein.
procedure TfrmMain.Button2Click(Sender: TObject);
begin FWetterDaten.RegistriereBeobachter(FTemperaturAnzeigen); end; procedure TfrmMain.Button3Click(Sender: TObject); begin FWetterDaten.RegistriereBeobachter(FFeuchtigkeitAnzeigen); end; procedure TfrmMain.Button4Click(Sender: TObject); begin FWetterDaten.EntferneBeobachter(FTemperaturAnzeigen); end; procedure TfrmMain.Button5Click(Sender: TObject); begin FWetterDaten.EntferneBeobachter(FFeuchtigkeitAnzeigen); end; procedure TfrmMain.Button6Click(Sender: TObject); begin FWetterDaten.BenachrichtigeBeobachter; end; Die Instanz des Beobachters habe ich doch in meinem Formular erstellt. Warum wird sie zerstört, nachdem ich sie aus der InterfaceList raus nehme? Hier noch mein mein Subject und ein Beobachter:
Delphi-Quellcode:
{ TWetterDaten }
constructor TWetterDaten.Create; begin FLstBeobachter := TInterfaceList.Create; end; destructor TWetterDaten.Destroy; begin inherited; end; procedure TWetterDaten.BenachrichtigeBeobachter; var i : integer; begin for i := 0 to FLstBeobachter.Count - 1 do IBeobachter(FLstBeobachter[i]).Aktualisieren(Temperatur, Feuchtikgkeit, Druck); end; procedure TWetterDaten.EntferneBeobachter(iIBeobachter: IBeobachter); begin FLstBeobachter.Remove(iIBeobachter); end; procedure TWetterDaten.RegistriereBeobachter(iIBeobachter: IBeobachter); begin FLstBeobachter.Add(iIBeobachter); end; procedure TWetterDaten.SetMesswerte(iTemperatur, iFeuchtikgkeit, iDruck: real); begin Temperatur := iTemperatur; Feuchtikgkeit := iFeuchtikgkeit; Druck := iDruck; MesswerteGeaendert; end; procedure TWetterDaten.MesswerteGeaendert; begin BenachrichtigeBeobachter; end;
Delphi-Quellcode:
procedure TTemperaturAnzeigen.Aktualisieren(iTemperatur, iFeuchtikgkeit, iDruck: real);
begin Temperatur := iTemperatur; Feuchtikgkeit := iFeuchtikgkeit; Druck := iDruck; Anzeigen; end; procedure TTemperaturAnzeigen.Anzeigen; begin ShowMessage('Aktuelle Wetterbedingungen: Temperatur:' + format('%f',[Temperatur])); end; constructor TTemperaturAnzeigen.Create(iISubject : ISubject); begin FWetterDaten := iISubject; end; destructor TTemperaturAnzeigen.Destroy; begin ShowMessage('Schade'); inherited; end; |
AW: Observer:
Interfaces haben eine Referenzzählung.
Wenn keine Interfaceinstanzen mehr existieren, dann wird das Objekt freigegeben ... das ist der Sinn von Interfaces. PS: aus diesesm Grund mischt man auch Interfacezugriffe und Objektzugriffe nicht miteinander, da es so schnell mal zu Problemen kommen kann. Also wenn das Interface/Objekt noch nicht zersört werden soll, dann mußt du weiterhin eine Instanz davon irgendwo gespeichert/abgelegt haben. |
AW: Observer:
Hi,
Danke erst mal! Zitat:
Delphi-Quellcode:
Anscheinend habe ich da was falsch verstanden. Aber was?
TfrmMain = class(TForm)
Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); private FWetterDaten : TWetterDaten; FTemperaturAnzeigen : TTemperaturAnzeigen; // ... eine Instanz davon irgendwo gespeichert/abgelegt ..... Oder nicht ??? FFeuchtigkeitAnzeigen : TFeuchtigkeitAnzeigen; public { Public-Deklarationen } end; ..... procedure TfrmMain.FormCreate(Sender: TObject); begin FWetterDaten := TWetterDaten.Create; FTemperaturAnzeigen := TTemperaturAnzeigen.Create(FWetterDaten); FFeuchtigkeitAnzeigen := TFeuchtigkeitAnzeigen.Create(FWetterDaten); end; Ich gehe davon aus, durch FTemperaturAnzeigen im Formular ich die Instanz erst "verliere" wenn ich das Formular zerstöre |
AW: Observer:
Zitat:
Deine Variablen sollten auch Interfaces sein, also zum Beispiel IBeobachter...
Delphi-Quellcode:
{ ... }
FTemperaturAnzeigen : IBeobachter; // ... eine Instanz davon irgendwo gespeichert/abgelegt ..... Oder nicht ??? FFeuchtigkeitAnzeigen : IBeobachter; public |
AW: Observer:
**Schuppen von den Augen fallen**
Mist habe ich übersehen. Stimmt. Es heißt ja auch immer in meinem Buch Zitat:
|
AW: Observer:
Ich sagte doch "nicht interfaces mit Objekten mischen" :warn:
Objekte = keine Referenzzählung Interfaces = ja TTemperaturAnzeigen ist das Objekt und wird demnach nicht mitgezählt. hier gibt es also nur 2-3 Lösungen: - entweder du meldest beim Interface (._AddRef) diese Referenz an - oder besser, du nimmst hier auch statt TTemperaturAnzeigen das IBeobachter. - oder du umgehst die Referenzzählung (siehe #7 Uwe Raabe, bzw. implementierst dir eine eigene "Referenzzählung", welche auf deine Bedürfnisse abgestimmt ist) |
AW: Observer:
So, wie du es machst, steckst du in einem Dilemma: um der Interface-Referenzzählung zu genügen, solltest du nur Interfaces als Instanz-Variablen haben, aber ein ISubject hat leider nicht die Methoden und Eigenschaften eines TWetterDaten, was die Manipulation einer Interface-Instanz verhindert.
Ausweg 1: Du deklarierst zu jeder Klasse ein passendes Interface, das von ISubject abgeleitet ist. => Viel Schreiberei und meher Aufwand bei Änderungen, aber sauber bezüglich Referenzzählung. Ausweg 2: Du leitest statt von TInterfacedObject von TInterfacedPersistent ab. Damit umgehst due die Referenzzählung und kannst wieder Instanzen der Klassen (statt der Interfaces) verwenden. Du musst aber selber dafür sorgen, daß die Instanzen in der richtigen Reihenfolge freigegeben werden, aber das sollte einem Delphianer ja nicht fremd sein. Da TInterfaceList von TInterfacedObject abgeleitet ist, musst du auch eine IInterfaceList-Instanz verwalten, sonst kracht es irgendwann. Also FLstBeobachter als IInterfaceList deklarieren! |
AW: Observer:
meinst du mit
Zitat:
Delphi-Quellcode:
sagen soll? :oops:
...
private FWetterDaten : TWetterDaten; FTemperaturAnzeigen : IBeobachter; FFeuchtigkeitAnzeigen : IBeobachter; public ... und wenn ich das von Uwe Raabe lese, da erscheint es mir dann (fast) ratsamer mit abstrakten Klassen zu arbeiten. |
AW: Observer:
Zitat:
Zitat:
|
AW: Observer:
So ein Problem hatten wir auch. Wir haben es über TNotifyEvent gelöst.
Dein
Delphi-Quellcode:
machst du zu einer Liste von TNotifyEvents.
FLstBeobachter : TInterfaceList;
BenachrichtigeBeobachter; Schickt dann nur die Nachricht in den eigentlichen TTemperaturAnzeigen... dort kannst du den Sender dann auf TWetterDaten prüfen, carsten und die relevanten Daten benutzen. Gruß David |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 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