AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Observer:

Ein Thema von GroHae · begonnen am 21. Jul 2010 · letzter Beitrag vom 21. Jul 2010
Antwort Antwort
Seite 1 von 2  1 2   
GroHae

Registriert seit: 19. Apr 2007
Ort: Nabburg
83 Beiträge
 
Delphi 2007 Enterprise
 
#1

Observer:

  Alt 21. Jul 2010, 10:42
Delphi-Version: 2007
Hallo zusammen,

ich spiele gerad ein bischen mit dem Observer Muster rum und habe folgende Klassen erstellt:
Delphi-Quellcode:

  IBeobachter = 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;
Ich habe mir ein Formular etstellt, in welchem ich die beiden Beobachter erstelle
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:
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;
Mein Problem ist aber, das nach dem Entfernen des Beobachters, diese Beobachter frei gegeben wird. Das sollte aber nicht sein.
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;
Grüße

Thomas
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.017 Beiträge
 
Delphi 12 Athens
 
#2

AW: Observer:

  Alt 21. Jul 2010, 10:58
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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
GroHae

Registriert seit: 19. Apr 2007
Ort: Nabburg
83 Beiträge
 
Delphi 2007 Enterprise
 
#3

AW: Observer:

  Alt 21. Jul 2010, 11:17
Hi,

Danke erst mal!

Zitat:
Also wenn das Interface/Objekt noch nicht zersört werden soll, dann mußt du weiterhin eine Instanz davon irgendwo gespeichert/abgelegt haben.
Stimmt! Un genau deswegen habe ich in meinem Hauptformular die instanz erstellt
Delphi-Quellcode:
 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;
Anscheinend habe ich da was falsch verstanden. Aber was?
Ich gehe davon aus, durch FTemperaturAnzeigen im Formular ich die Instanz erst "verliere" wenn ich das Formular zerstöre
Grüße

Thomas
  Mit Zitat antworten Zitat
schlecki

Registriert seit: 11. Apr 2005
Ort: Darmstadt
148 Beiträge
 
Delphi XE2 Enterprise
 
#4

AW: Observer:

  Alt 21. Jul 2010, 11:33
Stimmt! Un genau deswegen habe ich in meinem Hauptformular die instanz erstellt
Delphi-Quellcode:
 TfrmMain = class(TForm)
   { ... }
    FTemperaturAnzeigen : TTemperaturAnzeigen; // ... eine Instanz davon irgendwo gespeichert/abgelegt ..... Oder nicht ???
    FFeuchtigkeitAnzeigen : TFeuchtigkeitAnzeigen;
  public
    { Public-Deklarationen }
  end;

.....
Anscheinend habe ich da was falsch verstanden. Aber was?
Ich gehe davon aus, durch FTemperaturAnzeigen im Formular ich die Instanz erst "verliere" wenn ich das Formular zerstöre
Nicht falsch verstanden, nur falsch deklariert
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
  Mit Zitat antworten Zitat
GroHae

Registriert seit: 19. Apr 2007
Ort: Nabburg
83 Beiträge
 
Delphi 2007 Enterprise
 
#5

AW: Observer:

  Alt 21. Jul 2010, 11:41
**Schuppen von den Augen fallen**

Mist habe ich übersehen. Stimmt. Es heißt ja auch immer in meinem Buch
Zitat:
Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung
1000 Danke!
Grüße

Thomas
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.017 Beiträge
 
Delphi 12 Athens
 
#6

AW: Observer:

  Alt 21. Jul 2010, 11:48
Ich sagte doch "nicht interfaces mit Objekten mischen"

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)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (21. Jul 2010 um 11:53 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.934 Beiträge
 
Delphi 12 Athens
 
#7

AW: Observer:

  Alt 21. Jul 2010, 11:49
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!
Uwe Raabe
  Mit Zitat antworten Zitat
GroHae

Registriert seit: 19. Apr 2007
Ort: Nabburg
83 Beiträge
 
Delphi 2007 Enterprise
 
#8

AW: Observer:

  Alt 21. Jul 2010, 12:14
meinst du mit

Zitat:
Ich sagte doch "nicht interfaces mit Objekten mischen"
dass ich nicht
Delphi-Quellcode:
...
  private
    FWetterDaten : TWetterDaten;
    FTemperaturAnzeigen : IBeobachter;
    FFeuchtigkeitAnzeigen : IBeobachter;
  public
...
sagen soll?

und wenn ich das von Uwe Raabe lese, da erscheint es mir dann (fast) ratsamer mit abstrakten Klassen zu arbeiten.
Grüße

Thomas
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.934 Beiträge
 
Delphi 12 Athens
 
#9

AW: Observer:

  Alt 21. Jul 2010, 12:27
Delphi-Quellcode:
...
  private
    FWetterDaten : TWetterDaten;
    FTemperaturAnzeigen : IBeobachter;
    FFeuchtigkeitAnzeigen : IBeobachter;
  public
...
FWetterDaten sollte IWetterDaten werden (Ausweg 1) oder von TInterfacePersistent abgeleitet werden.

und wenn ich das von Uwe Raabe lese, da erscheint es mir dann (fast) ratsamer mit abstrakten Klassen zu arbeiten.
Also, so schwer ist das wirklich nicht. Man muss halt wissen, was man tut. Interfaces sind eine feine Sache und machen den Code oft viel übersichtlicher, aber ich verzichte in den meisten Fällen auf die automatische Referenzzählung und kümmere mich selbst um die Freigabe der Objekte.
Uwe Raabe
  Mit Zitat antworten Zitat
David Martens

Registriert seit: 29. Sep 2003
205 Beiträge
 
Delphi XE Enterprise
 
#10

AW: Observer:

  Alt 21. Jul 2010, 12:41
So ein Problem hatten wir auch. Wir haben es über TNotifyEvent gelöst.
Dein FLstBeobachter : TInterfaceList; machst du zu einer Liste von TNotifyEvents.

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
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

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 21:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz