![]() |
Observer-Pattern Implementation
Im Zuge eines etwas größeren Projekts, hat mich stahli (dicken Dank für deine Gedult und Mühe an der Stelle) auf die Idee gebracht, für meine Anwendung das Observer-Pattern zu nutzen.
Dazu hatte ich einige Vorraussetzungen: a) Die Implementierung der notwendigen Funktionen sollte möglichs eigentständig sein Da einiges an Objekten zusammenkommt, wollte ich vermeiden, jedesmal das gleiche zu machen. Andererseits müssen bei den Unterschiedlichen konkreten Objekten aber auch unterschiedliche Daten übertragen werden. b) Da das o.g. Projekt später auch über ein Plugin-System erweiterbar werde wird, muss auch gewährleistet sein, das ich ggf. Elemente aus einer externen DLL bekomme. b heißt also Interfaces, da das der mir einzig bekannte Weg ist, sowas wie Objekte aus einer DLL zu bekommen, bzw anzusprechen. a heißt, Basisklassen die die Daten als Pointer liefern. Hier will ich nu meine Implementation präsentieren: Interface
Delphi-Quellcode:
Basisklassen:
TYPE
ISubject = INTERFACE; IListener = INTERFACE Procedure RegisterMe(Const aSubject:ISubject); Procedure UnregisterMe(Const aSubject:ISubject); procedure Notification(const data:Pointer); END; ISubject = Interface Procedure RegisterListener(const obj:IListener); Procedure UnregisterListener(Const obj:IListener); Procedure NotifyListener(const data : Pointer); End;
Delphi-Quellcode:
und schließlich meine "Realen" Klassen aus dem Testprojekt:
unit BaseClasses;
interface uses windows,classes,contnrs,BaseInterfaces; TYPE TBaseSubject = class; TBaseListener = class(TInterfacedPersistent,IListener) private protected published public Procedure RegisterMe(Const aSubject:ISubject); Procedure UnregisterMe(Const aSubject:ISubject); //Wird erst in der Ableitung befüllt. procedure Notification(const data:Pointer);virtual;abstract; end; TBaseSubject = Class(TInterfacedPersistent,ISubject) PRIVATE flist : TList; procedure ClearList; PROTECTED PUBLISHED Constructor Create; Destructor Destroy;override; PUBLIC Procedure RegisterListener(Const obj:IListener); Procedure UnregisterListener(Const obj:IListener); Procedure NotifyListener(const data : Pointer); End; implementation { TBaseListener } procedure TBaseListener.RegisterMe(aSubject: ISubject); begin aSubject.RegisterListener(self); end; procedure TBaseListener.UnregisterMe(aSubject: ISubject); begin aSubject.UnregisterListener(self); end; { TBaseSubject } procedure TBaseSubject.ClearList; begin while (flist.count > 0) do flist.Delete(0); end; constructor TBaseSubject.Create; begin flist := TList.create; end; destructor TBaseSubject.Destroy; begin ClearList; flist.free; inherited; end; procedure TBaseSubject.NotifyListener(const data:pointer); var i : integer; begin for I := 0 to flist.count - 1 do IListener(flist.items[i]).Notification(data); end; procedure TBaseSubject.RegisterListener(obj: IListener); begin flist.Add(Pointer(obj)); end; procedure TBaseSubject.UnregisterListener(obj: IListener); begin flist.Remove(Pointer(obj)); end;
Delphi-Quellcode:
(unit28 is lediglich die Form der Application mit Buttons und einem Memo drauf :))
uses
windows,classes,BaseClasses; TYPE TRealListener = Class(TBaseListener) PRIVATE PROTECTED PUBLIC procedure Notification(const data:pointer);override ; PUBLISHED End; TRealSubject = Class(TBaseSubject) PRIVATE fcaption : string; procedure setCaption(const Value: string); PROTECTED PUBLIC PUBLISHED property Caption:string read fcaption write setCaption; End; implementation uses unit28; { TRealListener } procedure TRealListener.Notification(const data: pointer); begin form28.memo1.lines.append(string(data^)); end; { TRealSubject } procedure TRealSubject.setCaption(const Value: string); begin fcaption := Value; NotifyListener(@fcaption); end; end. Außer dem Einsatz von Generics, fällt mir im Moment keine Optimierung mehr ein (und da ich Turbo Delphi einsetze, geht das im Moment nicht :)) Was haltet ihr davon ? |
AW: Observer-Pattern Implementation
Als ich mit Delphi angefangen habe dachte ich auch erst das Observer-Pattern so umsetzen zu müssen, da ich es in Java (vor Java 8) so gelernt hatte.
Vielleicht habe ich es mir jetzt nicht genau angesehen, aber das Interface "Listener" ist doch eigentlich überflüssig, oder? Warum muss mein Objekt eine bestimmte Schnittstelle implementieren, nur um etwas mitzubekommen? Man könnte dem Subject stattdessen eine
Delphi-Quellcode:
verpassen mit welcher jeder auf dem Observable einmal z.B. sagen kann
procedure register(onNotification: TProc)
Delphi-Quellcode:
Man könnte sich auch mehrmals registrieren. Sehe ich mit dieser Implementation nicht wie das ginge.
someObservable.register(
procedure() begin ShowMessage(someObservable.someValue); end ); Das ist glücklicherweise wie man das in letzter Zeit auch häufig ab Java 8 und C++ 11 sieht. Ob das "Turbo Pascal" auch kann weiß ich nicht, ich weiß noch nichtmal was das ist. Allen Bauer hier auch mal so etwas in die Richtung gebaut: ![]() |
AW: Observer-Pattern Implementation
Zitat:
Es ist tatsächlich so, daß sich viele Design-Patterns mittels moderner Sprachkonstrukte in einem aktuellen Delphi wesentlich schlanker und eleganter umsetzen lassen, als es in den Lehrbüchern beschrieben ist. |
AW: Observer-Pattern Implementation
Zitat:
Zitat:
reichen. Zitat:
Zitat:
Das Konzept anonymer Funktionen kenn ich von js und anderen Sprachen. Und auch wie "verpönt" sie sind :) |
AW: Observer-Pattern Implementation
Zitat:
Java hat so etwas ja erst extrem spät bekommen, viele Observer-Pattern Implementationen in Delphi sehen so aus als hätte man sie aus Java 1:1 übernommen Zitat:
Ich meinte aber eher: Wenn sich ein Objekt bei zwei Observables vom gleichen Typ registrieren will - Dann rufen beide zwangsläufig die gleiche Methode auf, obwohl man das sicher nicht will. |
AW: Observer-Pattern Implementation
Zitat:
Doch, genau das will ich, zumindest in dem geplanten Anwendungsfall. Der Unterschied besteht hier lediglich in den übergebenen Daten bei der Notification (daher auch Pointer !) :) |
AW: Observer-Pattern Implementation
Liste der Anhänge anzeigen (Anzahl: 1)
So dale, noch mal ein Update.
Hab das ganze nochmal komplet umgebaut. Source im Anhang. Achtung hab da FastMM4 eingebunden, um zu checken ob Memory-Leaks entstehen :) Generics wären schön, insbesondere bei der Methodlist bzw. deren Nachfahren. Habsch aber net.:( Jetzt ist wieder euer Meinung gefragt :) |
AW: Observer-Pattern Implementation
Hi,
ich hatte mal für ein privates Projekt Observer-Komponenten geschrieben. Du kannst da mal rüber schauen :) Mit einer List hatte ich nicht gearbeitet, sondern mit einer TInterfacedList, das wäre sicher eine Optimierung :thumb: Der TMBObserver ist eigentlich nur eine Basiskomponente, man könnte auch einfach das Interface IMBObserver zum Beispiel einem Form hinzufügen.
Delphi-Quellcode:
unit MBSubject;
{$mode objfpc}{$H+} interface uses Classes, SysUtils, LResources, MBObserver; type IMBSubject = interface ['{A89B6DD9-711C-4444-BF29-1AB0335A489C}'] procedure AddObserver(Observer: IMBObserver); procedure RemoveObserver(Observer: IMBObserver); procedure ClearObservers; procedure NotifiyObservers; end; { TMBSubject } TMBSubject = class(TComponent, IMBSubject) private FEnabled: Boolean; FObservers: TInterfaceList; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure AddObserver(Observer: IMBObserver); procedure RemoveObserver(Observer: IMBObserver); procedure ClearObservers; procedure NotifiyObservers; published property Enabled: Boolean read FEnabled write FEnabled default True; end; procedure Register; implementation procedure Register; begin {$I mbsubject_icon.lrs} RegisterComponents('MBComponents',[TMBSubject]); end; { TMBSubject } constructor TMBSubject.Create(AOwner: TComponent); begin inherited Create(AOwner); FObservers:=TInterfaceList.Create; FEnabled:=True; end; destructor TMBSubject.Destroy; begin if Assigned(FObservers) then FreeAndNil(FObservers); inherited Destroy; end; procedure TMBSubject.AddObserver(Observer: IMBObserver); begin if FObservers.IndexOf(Observer) = -1 then FObservers.Add(Observer); end; procedure TMBSubject.RemoveObserver(Observer: IMBObserver); begin FObservers.Remove(Observer); end; procedure TMBSubject.ClearObservers; begin FObservers.Clear; end; procedure TMBSubject.NotifiyObservers; var i: Integer; Observer: IMBObserver; begin if not FEnabled then Exit; for i := 0 to Pred(FObservers.Count) do if Supports(FObservers.Items[i], IMBObserver, Observer) then Observer.SubjectChanged(Self); end; end.
Delphi-Quellcode:
unit MBObserver;
{$mode objfpc}{$H+} interface uses Classes, SysUtils, LResources; type IMBObserver = interface ['{512FD47A-6122-4EFF-9ED4-09AC795EF794}'] procedure SubjectChanged(Sender: TObject); end; { TMBObserver } TMBObserver = class(TComponent, IMBObserver) private FOnSubjectChanged: TNotifyEvent; protected procedure SubjectChanged(Sender: TObject); published property OnSubjectChanged: TNotifyEvent read FOnSubjectChanged write FOnSubjectChanged; end; procedure Register; implementation procedure Register; begin {$I mbobserver_icon.lrs} RegisterComponents('MBComponents',[TMBObserver]); end; { TMBObserver } procedure TMBObserver.SubjectChanged(Sender: TObject); begin if Assigned(FOnSubjectChanged) then FOnSubjectChanged(Sender); end; end. |
AW: Observer-Pattern Implementation
Erstmal Danke für deine Anmerkungen :)
Da ich keine Interfaces speichere und auch ganz gern auf das automatische Instanzhandling verzichten möchte, hab ich mich ganz bewußt gegen TInterfaceList entschieden. :) Stattdessen speichere ich nur noch Methodenzeiger in folgender Liste bzw einer davon abgeleiteten Klasse:
Delphi-Quellcode:
Damit laufe ich nicht Gefahr, das mir die Liste plötzlich mein Objekt unterm Allerwertesten wegzieht :)
TYPE
PMethod = ^TMethod; TMethodList = Class(TObject) PRIVATE flist : TList; function getMethod(Index: integer): TMethod; procedure setMethod(Index: integer; const Value: TMethod); PROTECTED function getCount:integer; function indexOf(proc:TMethod):integer; PUBLIC Constructor Create; Destructor Destroy;override; Procedure Clear; procedure Add(const proc:TMethod); procedure Remove(const proc:TMethod); property Items[Index:integer]:TMethod read getMethod write setMethod; PUBLISHED Property Count:integer read getCount; End; |
AW: Observer-Pattern Implementation
Als kleiner Tipp: Es gibt sehr gute Implementierungen der ganzen Sache (also Obersever Pattern und Iterator Pattern) mit einem größtenteils sehr einheitlichen Interface:
![]() Leider ist hier (noch? ;-) ) keine Bibliothek für Delphi dabei. Aber auch wenn ich es jetzt gerade nicht geschafft habe Dich zu überreden diese zu erstellen *g*, so empfehle ich auf jeden Fall, dass Du Dir dort mal anguckst, wie das in anderen Sprachen realisiert wurde. Rx hat sehr viele Anhänger, und aus meiner Sicht nicht umsonst. Damit zu arbeiten empfinde ich (zumindest in C# und JavaScript) als sehr angenehm. Von daher lohnt es sich auf jeden Fall mal, sich das mal intensiver anzuschauen und sich ggf. von dort inspirieren zu lassen, bevor man sonst eine eigene Lösung ohne diesen Input baut, die dann sonst möglicherweise Schwächen hat die Rx schon an anderen Stellen ausgemerzt hat. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:16 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