Friday 31 October 2008

MVC in Delphi (Part III)

As an experiment I have changed the DomainModel of my example, to be a more complex object, encapsulating an 'order'. This can be updated, calculated and reset with out the View and the Controller having to change.

The DomainModel now looks like this ..


TDomainObject = class
private
FClient : String;
FStock : String;
FBuySell : String;
FSize : extended;
FPrice : extended;
FDate : String;

FConsideration : extended;
public
property Client : String read FCLient write FClient;
property Stock : String read FStock write FStock;
property BuySell : String read FBuySell write FBuySell;
property Size : extended read FSize write FSize;
property Price : extended read FPrice write FPrice;
property Date : String read FDate write FDate;
property Consideration : extended read FConsideration write FConsideration;
end;

///A small implementation of the MVC architecture
TDomainModel = class (TObservable)
private
/// Underlying data object
FData : TDomainObject;

public
///Property that represents the underlying data
property Data : TDomainObject read FData write FData;

///Executes the given command
procedure DoCommand (cmd : TCommand); override;

///Creates the underlying data
constructor Create;

///Function that is passed to the CommandThread for execution
///TCommandThread
procedure ExecuteCommand (cmd : TCommand);

function ToString : String;
end;


In order to do this, some of the plumbing code had to change, but basically just to get the values in the views. I have define 3 views, 2 of which are associated with one form, and a more complex one that is associated with another form, as below;


TFormView = class (TInterfacedObject, IObserver)
private
Ffrm : TForm2;
public
{ Public declarations }
procedure UpdateView (obj : TObject);
constructor Create (frm : TForm2);
end;

TListView = class (TInterfacedObject, IObserver)
private
Fctrl : TListBox;
public
{ Public declarations }
procedure UpdateView (obj : TObject);
constructor Create (ctrl : TListBox);
end;

TTextView = class (TInterfacedObject, IObserver)
private
Fctrl : TStaticText;
public
{ Public declarations }
procedure UpdateView (obj : TObject);
constructor Create (ctrl : TStaticText);
end;


These are created and associated with the Model and Controller as follows;


procedure TForm1.FormCreate(Sender: TObject);
begin
Controller.AddView (TListView.Create (ListBox1));
Controller.AddView (TTextView.Create (StaticText1));
end;

...

procedure TTextView.UpdateView (obj : TObject);
begin
Fctrl.Caption := FloatToStr((obj as TDomainModel).Data.Size);
end;

procedure TListView.UpdateView (obj : TObject);
begin
Fctrl.AddItem('Order = ' + (obj as TDomainModel).ToString, nil);
end;

...

procedure TForm2.FormCreate(Sender: TObject);
begin
Controller.AddView (TFormView.Create(Form2));
end;

...

procedure TFormView.UpdateView (obj : TObject);
begin
// Fctrl.AddItem('Order = ' + (obj as TDomainModel).ToString, nil);
Ffrm.lblStock.caption := (obj as TDomainModel).Data.Stock;
Ffrm.lblClient.caption := (obj as TDomainModel).Data.Client;
Ffrm.lblSize.caption := FloatToStr((obj as TDomainModel).Data.Size);
Ffrm.lblPrice.caption := FloatToStr((obj as TDomainModel).Data.Price);
Ffrm.lblValue.caption := FloatToStr((obj as TDomainModel).Data.Consideration);
end;



This shows that a new view can be added, which out having to change anything in the Model or Controller.

The first two Views are associated with the main form, and show a single value from the Model, and a list view representing the changes to the order. The third View is the state of the 'whole' order.




No comments: