Newsgroup: it.comp.lang.delphi
Titolo: Aiuto su Observer Pattern cercasi

Ciao a tutti,
ho scaricato un po di esempi di observer pattern scritti da Marco
Cantu per poterne adattare uno alle mie esigenze, ovvero poter
aggiornare alcune label del FormMain dai vari package a runtime
inseriti nel progetto.
Ne ho trovato uno che mi sembrava molto vicino a quello che volevo
ottenere ma c'è un "intoppo" e non sono in grado di proseguire con la
customizzazione. Vi posto il codice:
unit observer_core;
interface
uses
  Classes;
type
//  IPublisher = interface
//  ['{A7AE1AC5-6642-4DAB-AF22-57BABD78BA0F}']
//    function GetData (strWhat: string);
//  end;
  ISubscribe = interface
    ['{586A80B2-0357-4023-A7F1-549881CC2F0C}']
    procedure Notify (strWhat: string; strData: string; data:
TObject);
  end;
  IPubAndSubcr = interface
    ['{DD875933-73E6-46C2-A95D-3B8632A91C23}']
//    procedure Publish (pub: IPublisher; strWhat: string);
    procedure SimplePublish (strWhat: string; strData: string; data:
TObject);
    procedure Subscribe (subscriber: TObject; strWhat: string);
  end;
type
  TObserver = class (TInterfacedObject, IPubAndSubcr)
  private
    sList: TStringList;
  public
    constructor Create;
    destructor Destroy; override;
    class function Instance: TObserver;
    procedure SimplePublish(strWhat: string; strData: string; data:
TObject);
    //procedure ObsDataPublish(strWhat: string; strData: string);
    procedure Subscribe(subscriber: TObject; strWhat: string);
  end;
implementation
uses
  SysUtils;
var
  _Obs: TObserver;
{ TObserver }
constructor TObserver.Create;
begin
  sList := TStringList.Create;
end;
procedure TObserver.SimplePublish(strWhat: string; strData: string;
data: TObject);
var
  I: Integer;
  intf: ISubscribe;
begin
  for I := 0 to sList.Count -1 do
  begin
    if SameText (strWhat, sList[I]) then
    begin
      sList.Objects[I].GetInterface(ISubscribe, intf);
      intf.Notify(strWhat, strData, data);
    end;
  end;
end;
procedure TObserver.Subscribe(subscriber: TObject; strWhat: string);
begin
  if Supports (subscriber, ISubscribe) then
    sList.AddObject(strWhat, subscriber);
end;
destructor TObserver.Destroy;
begin
  sList.Free;
  inherited;
end;
class function TObserver.Instance: TObserver;
begin
  if not (Assigned (_Obs)) then
    _Obs := TObserver.Create;
  Result := _Obs;
end;
end.
{
 ALTRA UNIT
}
uses
  StdCtrls, observer_core, sLabel;
type
  TsLabelFX = class (sLabel.TsLabelFX, ISubscribe)
  public
    procedure Subscr;
    procedure Notify (strWhat: string; strData: string; data:
TObject);
  end;
implementation
{ TLabel }
procedure TsLabelFX.Notify(strWhat: string; strData: string; data:
TObject);
begin
  //Caption := strWhat + ': ' + data.ClassName;
  Caption := strData;
end;
procedure TsLabelFX.Subscr;
begin
  TObserver.Instance.Subscribe(self, Hint);
end;
end.
{
Codice in MAINFORM inserito nel constructor Create
}
LabelAccettazioni.Subscr;
LabelCarico.Subscr;
LabelConsegne.Subscr;
LabelResi.Subscr;
{
Esempio di chiamata in MAINFORM
}
TObserver.Instance.SimplePublish('Accettazioni',IntToStr(ADOQueryAccettazioni.RecordCount),
self);
Ok, tutto questo funziona perfettamente se l'Observer viene richiamato
dal MainForm, viene eseguita la procedura di notifica e la caption
della relativa label viene modificata; il problema nasce dai singoli
package poichè quando effettuo la chiamata per notificare delle
modifiche e di conseguenza l'aggiornamento della relativa label nel
form main, l'istanza di TObserver risulta nil poichè si riferisce al
package e non al form e di conseguenza non riesce ad agganciare la
label corretta da aggiornare.
Quello che avrei bisogno è di capire come diavolo fare per gestire le
notifiche del mainform a prescindere da chi le richiama. E' possibile?
Inoltre, mi rendo conto che questo sia un po limitante e mi chiedevo
se esisteva qualcosa più generalizzato da poter sfruttare con altri
progetti che fanno uso di un solo Form con caricamento di bpl a
runtime.
Grazie mille

 runtime.
Vediamo se ricordo bene.
Tu hai una app divisa in package di cui alcuni potrebbero non essere
caricati.
L'esigenza e' mettere in piedi un sistema di notifica che permetta ai
form presenti nei package caricati di aggiornare i valori di alcune
property di alcuni oggetti visuali..Giusto??
A.

 A.
Esatto, in realtà nei package non è presente un form ma un frame che
viene caricato all'interno di un TPanel nella form principale. L'uso
dei package in questo caso è molto comodo e li carico dinamicamente in
base alla voce di menù cliccata dall'utente; il problema principale di
questo sistema è appunto la mancanza di dialogo tra il package
caricato (sempre uno alla volta) e il form main.
In sostanza il "problema" dell'observer postato è che getisce le
notifiche in base all'istanza da cui è richiamato; i package hanno una
loro istanza diversa da quella della form main, nonostante venga tutto
caricato al suo interno, e quindi viene impedito loro l'accesso ai
componenti che non fanno parte dell'istanza da cui sono stati
registrati. Il risultato è che i package appunto non hanno accesso ai
componenti registrati dal form principale. Mi sono spiegato da cani ma
spero si capisca.
Grazie

 spero si capisca.
Onestamente non mi è chiarissima l'implementazione di Observer da te
adottata.
Comunque il problema che riscontri è, se non sbaglio, di non poter
accedere ad oggetti della mainform (le label) da oggetti (frames) di
packages caricati a runtime.
Se non ho capito male, le label sono (dovrebbero essere) dei
ConcreteObservers e i frame dei ConcreteSubjects.
Ad un certo punto della loro esistenza, i subjects devono attaccarsi /
vengono attaccati ad uno o più observers. Da quel momento, quando lo
stato di un subject cambia, ai suoi observers è notificato il
cambiamento e si vanno a leggere il nuovo stato del soggetto e lo
mostrano.
La tua implementazione assomiglia più a a quella che su Gamma&al
coinvolge il ChangeManager, che mantiene la mappatura fra soggetti ed
osservatori, oltre a specificare quali sono le modifiche di ciascun
soggetto che interessano a ciascun osservatore.
Se gli oggetti del package devono usare l'istanza di TObserver nota al
main form, o questa passa loro direttamente l'istanza, oppure è
necessario che TObserver sia la stessa classe sia per la mainform, sia
per il package. Questo si può ottenere ad esempio tramite
GetClass('laMiaClasse'), il che implica di dover registrare la classe.
Vedi questi due link: 
http://edn.embarcadero.com/article/27178
 e
http://stackoverflow.com/questions/701049/is-there-a-way-to-instantiate-a-class-by-its-name-in-delphi
Saluti
Paolo Arosio