Einzelnen Beitrag anzeigen

FAM

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

MVC + Observer Pattern Konzept / Was haltet Ihr davon

  Alt 5. Feb 2015, 12:30
Delphi-Version: XE
Hallo Zusammen,

ich habe mir mal zum Thema MVC + Observer-Pattern einen konzeptionellen Entwurf überlegt ...

Grundüberlegung war folgende:

Im View werden unterschiedliche Events ausgelöst, über den Controller werden diese dann ausgewertet und die entsprechende Funktion werden dafür aufgerufen. Die Funktionen werden zuvor am jeweiligen Controller registriert.
Verwendungsmöglichkeit: einfaches EventDispatching (Komponentenunabhängig), z.B. GUI-Update

Unabhänig davon sollen Datenmodelle mit "integrierten Observer Pattern" implenetiert werden können.
Verwendungsmöglichkeit: Bei Datenänderung im Model werden entsprechende EventHandlers ausgelöst.

Ich finde das charmante daran ist die Event-Trennung (Kapselung) - Controller gesteuerte Events (MVC) + auto. Model-gesteuerte Events (ObserverPattern)


Was haltet Ihr davon? - möchte nur der "Betriebsblindheit" mal vorbeugen


VIEW

Delphi-Quellcode:
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, event.fam.types, model.stockpile, controller, StdCtrls, ExtCtrls;

type

  TForm1 = class(TForm)
    LabelEventHandlerDebug: TLabel;
    RadioGroup1: TRadioGroup;
    RadioButton1: TRadioButton;
    RadioButton2: TRadioButton;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure SetApplicationBackground;
    procedure Button1Click(Sender: TObject);
    procedure RadioButton1Click(Sender: TObject);
    procedure EventHandlerViewLabelUpdate;
    procedure EventHandlerModelStockpileUpdate;
    procedure RadioButton2Click(Sender: TObject);

  private
    { Private-Deklarationen }

    controller: TController;

    FAMEvents: TEvents;

    StockpileModel: TStockpileModel; // Stockpile model
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  data: TArray<Double>;
begin

  StockpileModel.SetData(data);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

  { Create eventtype }
  FAMEvents := TEvents.Create();

  { Create controller instances }
  controller := TController.Create();

  { Register Event-Handler }
  controller.OnUpdateUI := EventHandlerViewLabelUpdate;
  controller.OnApplicationBackground := SetApplicationBackground;

  { Create object instances }
  StockpileModel := TStockpileModel.Create();

  { Register Event-Handler for StockpileModel }
  StockpileModel.registerOn(EventHandlerModelStockpileUpdate);

end;

procedure TForm1.RadioButton1Click(Sender: TObject);
begin
  controller.DispatchEvent(FAMEvents.Name.OnUpdateUI);
end;

procedure TForm1.RadioButton2Click(Sender: TObject);
begin
  controller.DispatchEvent(FAMEvents.Name.OnApplicationBackground);
end;

procedure TForm1.SetApplicationBackground;
begin
  self.Color := clBlue;
end;

procedure TForm1.EventHandlerModelStockpileUpdate;
begin
  ShowMessage('EventHandlerModelStockpileUpdate');
end;

procedure TForm1.EventHandlerViewLabelUpdate;
begin
  LabelEventHandlerDebug.caption := ('TListener has been OnUpdateUI.');
end;

end.
CONTROLLER

Delphi-Quellcode:
unit controller;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, event.fam.types;

type

  { Define a procedural type }
  TFAMEvent = procedure of object;

  TController = class
  private
    FUpdateUI: TFAMEvent;
    FApplicationBackground: TFAMEvent;
  public
    FAMEvents: TEvents;
    Constructor Create;
    procedure dispatchEvent(const FAMEventName: String);
    property OnUpdateUI: TFAMEvent read FUpdateUI write FUpdateUI;
    property OnApplicationBackground: TFAMEvent read FApplicationBackground
      write FApplicationBackground;
  end;

implementation

{ TController }

constructor TController.Create;
begin

  { Create eventtype }
  FAMEvents := TEvents.Create();

end;

procedure TController.dispatchEvent(const FAMEventName: String);
begin

  if ((FAMEventName = FAMEvents.Name.OnUpdateUI) and Assigned(FUpdateUI)) then
    FUpdateUI();

  if ((FAMEventName = FAMEvents.Name.OnApplicationBackground) and
    Assigned(OnApplicationBackground)) then
    OnApplicationBackground();

end;

end.
Model

Delphi-Quellcode:
unit model.stockpile;

interface

uses
  model;

type

  { Stockpile Model }
  TStockpileModel = class(TModel)
  private
    data: TArray<Double>;
  public
    function GetData: TArray<Double>;
    procedure SetData(data: TArray<Double>);
  end;

implementation

{ TStockpileModel }

function TStockpileModel.GetData: TArray<Double>;
begin
  result := self.data;
end;

procedure TStockpileModel.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 (Eltern - Klasse)
Delphi-Quellcode:
unit model;

interface

type
  TEvent = procedure of object;

  TModel = class(tObject)
  protected
    // interne Liste
    OnChange: array of TEvent;
    // Aufruf aller Routinen der Liste
    procedure notify;
  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
procedure TModel.notify;
var
  i: integer;
begin
  for i := Low(OnChange) to High(OnChange) do
    OnChange[i];
end;

end.

Events-Types


Delphi-Quellcode:
unit event.fam.types;

interface

type

  Events = record
    onUpdateUI: string;
    onApplicationBackground: string;
  end;

  TEvents = class
  private
    FEvents: Events;
  public
    Constructor Create;
    property Name: Events read FEvents;

  end;

implementation

{ EventTypes }

constructor TEvents.Create;

var
  _FEvents: Events;

begin

  _FEvents.onUpdateUI := 'onUpdateUI';
  _FEvents.onUpdateUI := 'onApplicationBackground';

  FEvents := _FEvents;

  Finalize(_FEvents);
  FillChar(_FEvents, SizeOf(_FEvents), 0);

end;

end.

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