Einzelnen Beitrag anzeigen

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, 10: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 10:46 Uhr)
  Mit Zitat antworten Zitat