AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language MVC + Observer Pattern Konzept / Was haltet Ihr davon
Thema durchsuchen
Ansicht
Themen-Optionen

MVC + Observer Pattern Konzept / Was haltet Ihr davon

Ein Thema von FAM · begonnen am 5. Feb 2015 · letzter Beitrag vom 6. Feb 2015
Antwort Antwort
Seite 3 von 3     123   
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#21

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 11:39
Zum Event-Parameter:

Ich habe mir einen generischen MultiCast-Event geschrieben und davon einen speziellen PropertyChanged-Event erzeugt. Dieser Event hängt an jedem ViewModel (im Model hat der nichts verloren )

Siehe die Methode OnPropertyChanged
Delphi-Quellcode:
unit MVVM.ViewModel.ViewModelBase;

interface

uses
  de.itnets.Events,
  de.itnets.References;

type
  TViewModelBase = class
  private
    FPropertyChanged: PropertyChangedEvent;
    FDisplayName: string;
    function GetPropertyChanged: IPropertyChangedEvent;
  protected
    procedure SetDisplayName( const Value: string );
    procedure OnPropertyChanged( const PropertyName: string = '' ); overload;
    procedure OnPropertyChanged( const PropertyNames: TArray<string> ); overload;
  public
    property DisplayName: string read FDisplayName;
    property PropertyChanged: IPropertyChangedEvent read GetPropertyChanged;
  end;

  TViewModelClass = class of TViewModelBase;

implementation

{ TViewModel }

function TViewModelBase.GetPropertyChanged: IPropertyChangedEvent;
begin
  Result := FPropertyChanged;
end;

procedure TViewModelBase.OnPropertyChanged( const PropertyNames: TArray<string> );
var
  LPropertyName: string;
begin
  for LPropertyName in PropertyNames do
    begin
      OnPropertyChanged( LPropertyName );
    end;
end;

procedure TViewModelBase.OnPropertyChanged( const PropertyName: string );
begin
  // Hier wird jetzt der Event durch die Gegend geschickt
  FPropertyChanged.Invoke( Self, TPropertyChangedArgs.Create( PropertyName ) );
end;

procedure TViewModelBase.SetDisplayName( const Value: string );
begin
  if FDisplayName <> Value
  then
    begin
      FDisplayName := Value;
      OnPropertyChanged( 'DisplayName' );
    end;
end;

end.
Da diese Methode protected ist, kann ich die von allen abgeleiteten ViewModels aufrufen.

Warum kein Notify im Model?

Wenn sich im Model etwas ändert, dann ist das durch das ViewModel passiert ... ja, äh dann soll bitte auch das ViewModel die Nachricht schicken.

Bei mir sind alle Models immutable (unveränderlich). Wenn ich etwas ändere, dann erzeuge ich mir eine neue Model-Instanz mit den aktuellen Werten. Nach dem Speichern sende ich dann diese neue Instanz in die Runde und jeder der sich dafür interessiert, kann dann diese neue Instanz übernehmen.

Exakt zu diesem Zweck gibt es die virtuelle Methode Delphi-Referenz durchsuchenTObject.Equals. Damit kann ich auf Gleichheit prüfen. Die Standard-Prüfung ist
Delphi-Quellcode:
function Object.Equals( Obj : TObject ) : Boolean;
begin
  Result := Self = Obj;
end;
Das kann ich aber auch überschreiben
Delphi-Quellcode:
TPerson = class
private
  FID : Integer;
  FName : string;
public
  constructor Create( const ID : Integer; const Name : string );
  function SameEntityAs( Other : TPerson ): Boolean;
  function Equals( Obj : TObject ) : Boolean; override;
  property ID : Integer read FID;
  property Name : string read FName;
end;

function TPerson.Equals( Obj : TObject ) : Boolean;
begin
  Result := ( Self = Obj ) or Assigned( Obj ) and ( Self.ClassType = Obj.ClassType ) and SameEntityAs( Obj as TPerson );
end;

function TPerson.SameEntityAs( Other : TPerson ): Boolean;
begin
  Result := (Self = Other ) or Assigned( Other ) and (Self.FID = Other.FID );
end;
Delphi-Quellcode:
procedure Test;
var
  LPerson1, LPerson2 : TPerson;
begin
  LPerson1 := TPerson.Create( 1, 'Foo' );
  LPerson2 := TPerson.Create( 2, 'Foo' );

  if LPerson1.Equals( LPerson2 ) then
    WriteLn('Gleiche Person ist gemeint');

  LPerson2 := TPerson.Create( 1, 'Bar' );

  if LPerson1.Equals( LPerson2 ) then
    WriteLn('Gleiche Person ist gemeint');

end;
Und noch etwas weitergedacht, kann ich sogar das ViewModel vergleichen und zwar mit einem anderen TPersonViewModel und einem TPerson :
Delphi-Quellcode:
TPersonViewModel = class
private
  FPerson : TPerson;
public
  function Equals( Obj : TObject ) : Boolean;
end;

function TPersonViewModel.Equals( Obj : TObject ) : Boolean;
begin
  Result := (Self = Obj ) or Assigned( Obj ) and (
    ( ( Self.ClassType = Obj.ClassType ) and ( Self.FPerson.Equals( ( Obj as TPersonViewModel ).FPerson ) ) )
    or
    ( Self.FPerson.Equals( Obj ) )
  );
end;
Das auch noch auf die View übertragen und ich kann quasi alles mit jedem vergleichen.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)

Geändert von Sir Rufo ( 6. Feb 2015 um 11:46 Uhr)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#22

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 12:28
hmm irgendwie klappt das doch nicht so wie ich mir das dachte....

wenn ich aus der Main heraus das Model ändere (über die ModelView) dann wird TForm1.ViewModelStockpilePropertyChanged ausgeführt, wenn ich aber aus dem ModelView das Model ändere passiert nichts... eigentlich logisch und denn wieder auch nicht

jemand eine idee? ich vermute ich muss noch ein Event vom ModelView in Richtung View dispatchen?!


Main
Delphi-Quellcode:
...
  // Create reference of ViewModelStockpile (Scope to Model.Stockpile)
  FViewModelStockpile := TViewModelStockpile.Create;

  // Register PropertyChanged-Methode if changing the
  // Datamodel in ViewModelStockpile
  FViewModelStockpile.registerOn(ViewModelStockpilePropertyChanged);

...

procedure TForm1.ViewModelStockpilePropertyChanged;
begin
  EditContentbarStartMarker.Text := FViewModelStockpile.Foo;
  ShowMessage('Model wurde geändert');
end;
...

view.model.stockpile
Delphi-Quellcode:
unit view.model.stockpile;

interface

uses
  model, model.stockpile;

type

  { View Model for Stockpile }

  TViewModelStockpile = class(TModel)
  private
     FModel: TStockpileModel;
    data: TArray<Double>;
    cdsBioLife: String;

    FBar: String;
    FFoo: string;
    function GetBioLife: String;

    procedure SetBar(const Value: String);
    procedure SetFoo(const Value: string);

  public
    constructor Create;
    function GetData: TArray<Double>;
    procedure SetData(data: TArray<Double>);


    property BioLife: String read GetBioLife;
    destructor destroy; override;

    property Foo: string read FFoo write SetFoo;
    property Bar: String read FBar write SetBar;

  end;

implementation

{ TViewModelStockpile }

constructor TViewModelStockpile.Create;
begin
 inherited Create;
  FModel := TStockpileModel.Create;
  Foo := 'test';
end;

destructor TViewModelStockpile.destroy;
begin
   FModel.Free;
  inherited;
end;

procedure TViewModelStockpile.SetBar(const Value: String);
begin
  if FBar <> Value then
  begin
    FBar := Value;
    notify;
  end;
end;

procedure TViewModelStockpile.SetFoo(const Value: string);
begin
  if FFoo <> Value then
  begin
    FFoo := Value;

    ShowMessage('notify');
    notify;
    // OnPropertyChanged('Foo');
  end;
end;

function TViewModelStockpile.GetBioLife: String;
begin
  Result := cdsBioLife;
end;

function TViewModelStockpile.GetData: TArray<Double>;
begin
  Result := self.data;
end;

procedure TViewModelStockpile.SetData(data: TArray<Double>);
begin
  self.data := data;
  // alle Ereignis-Behandlungs-Routinen der Liste aufrufen
  // wurde mit registerOn an das Model regestriert
  notify;
end;

end.

Model(Base)


Delphi-Quellcode:
unit model;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, jpeg, StdCtrls, pngimage, JvPanel, JvExExtCtrls,
  JvExtComponent, Series, TeeShape, TeEngine, TeeProcs, Chart, Math, TeeGDIPlus,
  ComCtrls, JvExComCtrls, JvComCtrls, dOPCIntf, dOPCComn, dOPCDA, dOPC;

type
  TEvent = procedure of object;

  TModel = class
  protected
    // interne Liste
    OnChange: array of TEvent;
    // Aufruf aller Routinen der Liste
    procedure notify;
    destructor destroy; override;
  public
    // neuer 'Event-Handler' in Liste
    procedure registerOn(routine: TEvent);
    // 'Event-Handler' aus Liste entfernen
    procedure registerOff(routine: TEvent);
  end;

implementation

// registriert neue routinen an den controller
procedure TModel.registerOn(routine: TEvent);
var
  n: integer;
begin
  n := Length(OnChange);
  SetLength(OnChange, n + 1);
  OnChange[n] := routine;

end;

// de-registriert routinen vom controller
procedure TModel.registerOff(routine: TEvent);
var
  i, j: integer;
begin
  i := Low(OnChange);
  while i <= High(OnChange) do // High liefert -1 bei leerem Array
  begin
    if @OnChange[i] = @routine // mit '@' nur Adressen vergleichen
    then
    begin
      for j := i to High(OnChange) - 1 do
        OnChange[j] := OnChange[j + 1];
      SetLength(OnChange, Length(OnChange) - 1);
    end
    else
      i := i + 1;
  end;
end;

// alle Ereignis-Behandlungs-Routinen der Liste aufrufen
destructor TModel.destroy;
begin
//
  inherited;
end;

procedure TModel.notify;
var
  i: integer;
begin
  for i := Low(OnChange) to High(OnChange) do
    OnChange[i];
end;

end.

Geändert von FAM ( 6. Feb 2015 um 12:49 Uhr)
  Mit Zitat antworten Zitat
FAM

Registriert seit: 22. Dez 2014
47 Beiträge
 
Delphi XE Enterprise
 
#23

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 13:02
fehler gefunden ...

Delphi-Quellcode:
constructor TViewModelStockpile.Create(const EventHandler: TEventHandler);
begin
  inherited Create;
   FEventHandler := EventHandler;

  FModel := TStockpileModel.Create;
  //Foo := 'TViewModelStockpile.Create => Value1'; <<<<<<<<<<<< FEHLER!
end;
Main
Delphi-Quellcode:
procedure TForm1.ViewModelStockpilePropertyChanged;
begin
  EditContentbarStartMarker.Text := FViewModelStockpile.Foo;
   ShowMessage('Model wurde geändert');
end;
läuft im Kreis ...

Geändert von FAM ( 6. Feb 2015 um 13:31 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#24

AW: MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 6. Feb 2015, 13:38
Tja, was da nicht so richtig tut gute Frage.

Einen Fehler habe ich aber schon entdeckt:
Delphi-Quellcode:
procedure TEventHandler.setPropertyChanged(const Value: TEvent);
begin
  if @FPropertyChanged <> @Value then
  begin
  ShowMessage('setPropertyChanged');
    FPropertyChanged := Value;
  end;
end;
Du vergleichst hier if @FPropertyChanged <> @Value then die Adresse der Methode. Das reicht aber nicht aus, denn bei unterschiedlichen Instanzen der gleichen Klasse ist diese Adresse gleich!

Du musst das nach Delphi-Referenz durchsuchenTMethod casten und dann TMethod.Code und TMethod.Data vergleichen

Meine Erfahrung mit MVVM haben mir allerdings auch gezeigt, dass die normalen Delphi-(SingleCast)-Events da nicht ausreichen und ein MultiCast-Event zwingend erforderlich ist. Ein und dasselbe ViewModel kann an mehreren Views hängen (Liste und Detail) und da wäre es doof, wenn nur eine View aktualisiert wird.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   

 

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 13:55 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