Sunday, 11 January 2009

Actually implemented

I have started to build a proof of concept that actually implments code that does stuff, so can't actually post that code here, but basically it follows the same structures, with a command being sent via the Controller, and then being run across the model (and hence the data). The notification of errors is done by the Notify mechanism, with whatever view knowing what to do with the errors statuses returned for each possible input.

Once a change to client, stock, buy/sell, etc has been entered, then at the the moment the Notify mechanism is directly fired, but what should happen is that the whole order should be validated at this point, allowing for cascadimg errors to be flagged.

We will see on monday!

Tuesday, 6 January 2009

MVC updated

I have had a small rest over Christmas and have decided that the code below needs to be refactored. I broke down all of the interfaces and implementations of them into different units, and begun to separate out the stuff that is MVC and stuff which used by the model.

Specifically I broke down the command stuff and implemented descendants of TCommand that know what to do to the model - i.e. the model doesn't do anything other than respond to the data changes (or not as is the current state).

This means I have ended up with code like this ..

TCalculateCommand = class (TCommand)
procedure Run (model : tObject); override;
end;
..

{ TCalculateCommand }

procedure TCalculateCommand.Run (model : tObject);
begin
(model as TDomainModel).Data.Price := self.Params[0].Value;
(model as TDomainModel).Data.Consideration := ((model as TDomainModel).Data.Size *
(model as TDomainModel).Data.Price) + (model as TDomainModel).Data.Charges;
end;


This is a lot simpler in my view.

However it doesn't solve the problem with how to indicate that a field is invalid?

I might have to register each control with the model, so it knows which fields map to each control. That sounds like a lot to faff, but I don't know how else to do it.

Friday, 7 November 2008

MVC Update

In the real world, which I obviously can't post here, I have a problem where the result of the validation request does three things;

1) Validates the input values
2) Returns other information based on the inputs (i.e. Other client, stock details)
3) Set whether a specific field input is valid or not.

Sunday, 2 November 2008

More MVC!

A client search from would look like this ..


type
TForm3 = class(TForm, IObserver)
Button1: TButton;
Edit1: TEdit;
ListBox1: TListBox;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure UpdateView (obj : TObject);
end;

...

procedure TForm3.Button1Click(Sender: TObject);
var
c : TCommand;
o : TCommandParam;

begin
c := TCommand.Create ('SEARCH');

o := TCommandParam.Create;
o.Name := edit1.Text;

c.AddParam(o);

Controller.DoCommand(c);
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
ModalResult := mrOK;
end;

procedure TForm3.Button3Click(Sender: TObject);
begin
ModalResult := mrCancel;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
Controller.AddView (self);
end;

procedure TForm3.UpdateView (obj : TObject);
var
val : String;

begin
// Update the view here
if ((obj as TDomainModel).ClientSearchResult <> nil) then
begin
for val in (obj as TDomainModel).ClientSearchResult do
begin
ListBox1.AddItem(val, nil);
end;
end;
end;



I have merge the View classes with the actual form, in order to make the code relationships a little clearer.

I'm really happy with all of the, and I think it will be applicable for the requirements I have at work.

Friday, 31 October 2008

MVC questions

Having done all of the below, I am still left with a list of questions

  1. Should I be using threads to do this ?
  2. When the command requires interaction (i.e. if I want a form to search of a client or a stock item), should it be owned by the Model, Controller or View ?
To answer 2., the view owns the form, and and is updated by the model in the same way as before.

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.




Tuesday, 28 October 2008

MVC in Delphi (Part II)

The TCommand is clearly a Command, and should be slightly different, but I will sort that out later.