Thursday, August 25, 2005

MVP Notes

Prenseter = Model + View + Command + CommandSet + Selection + Interactor

Model is Subject of View in general

View is Observer to Model

Selection is Visited (Visitor element in GOF Visitor pattern catalog)

Selection know Model: This is optional

Command bind Selection: To perform action on the selection's item.

Command is Visited: Used by ICommandVisitor.VisitCommand. Examples of some ICommandVisitor implementation are MenuItem, Button, any control that want to wired up with the command. The CommandSet will deliver the Visitor to the Command:

procedure TCommandSet.Accept(const Visitor: IVisitor);
var
i: Integer;
begin
for i := 0 to Pred(fItems.Count) do
(fItems[i] as IVisited).Accept(Visitor);
end;

procedure TCommand.Accept(const Visitor: IVisitor);
begin
(Visitor as ICommandVisitor).VisitCommand(self);
end;


CommandSet is a collection of Command

CommandSet is Observer of Selection: any change of Selection will notify CommandSet.

CommandSet is Subject. Some example of CommandSet observer are Command Menu, Pop up menu, SpeedButton Set or a set of UI controls that bind to CommandSet.

CommandSet is Visited: Act is a proxy visitor to deliver the Visitor to each Command item.

----------------------------------------
Presenter:

  1. Constructing Presenter
  2. We may pass a Model and a View to Presenter. This is option as the Presenter may create the Model or View itself.
  3. Presenter attach View to Model via Observer pattern.
Model:
  1. When the Model value changed, it will notify View via Observer pattern.
View:

Selection:
  1. Selection may contain zero, one or many items in Model
  2. When Selection is updated, it will notify CommandSet via Observer pattern
CommandSet:
  1. When CommandSet is notified, it will notify CommandMenu via Observer pattern. CommandMenu build Command from CommandSet.
  2. Create a CommandMenuItemVisitor. CommandSet accept the CommandMenuItemVisitor and deliver the Visitor to all Command item.
  3. Each Command will visit the Visitor.

Wednesday, August 24, 2005

Value Type Framework

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Wed, 25 Aug 2004 17:17:33 +0100
Local: Thurs, Aug 26 2004 12:17 am
Subject: Re: Attribute Framework

I now call it a Value Type framework as it avoids conflicts with the .NET idea of Attributes.

Essentially it is a sophisticated version of RTTI that allows you to implement metadata accessible without having to make all properties published.

First, you need a simple metadata type...

IMetadata = interface
[GUID]
function GetName: string;
function GetType: TValueTypeType;
end;

...then you can add type specific additional data...

IStringMetadata = interface(IMetadata)
[GUID]
function GetLength: Integer;
function GetCase: TStringCase;
end;

IFloatMetadata = interface(IMetadata)
[GUID]
function GetSize: Integer;
function GetPrecision: Integer;
end;

Then comes the main Value Type types from which all business objects are built...

IValueType = interface
[GUID]
procedure Assign(const Other: IValueType);
function Clone: IValueType;
function GetAsString: string;
function GetFormatString: string;
function GetMetadata: IMetadata;
function GetName: string;
function GetRequired: Boolean;
function IsNull: Boolean;
procedure SetAsString(const Value: string);
procedure SetFormatString(const Value: string);
procedure SetNull;
procedure SetRequired(Value: Boolean);
property AsString: string
read GetAsString
write SetAsString;
property FormatString: string
read GetFormatString
write SetFormatString;
property Required: Boolean
read GetRequired
write SetRequired;
end;

Once again, we can add type specific information to the generic case.

e.g.

IIntegerValueType = interface(IValueType)
[GUID]
function GetValue: Integer;
procedure SetValue(Value: Integer);
property Value: Integer
read GetValue
write SetValue;
end;

IStringValueType = interface(IValueType)
[GUID]
function GetValue: string;
procedure SetValue(const Value: string);
property Value: string
read GetValue
write SetValue;
end;

All Business Objects that contain properties/attributes are derived from IObjectValueType, which is designed to contain a list of Value Types, one for each property about which you wish to obtain metadata...

IObjectValueType = interface(IValueType)
[GUID]
function GetValueType(const Name: string): IValueType;
function GetValueTypes: IValueTypeList;
function GetType: TGUID;
function GetValue: IInterface;
property Value: IInterface
read GetValue;
end;

Finally, you can build something like a Customer interface and class...

ICustomer = interface
[GUID]
function GetCode: string;
function GetName: string;
function GetTotalOnOrder: Double;
function GetTotalShipped: Double;
procedure SetCode(const Value: string);
procedure SetName(const Value: string);
procedure SetTotalOnOrder(Value: Double);
procedure SetTotalShipped(Value: Double);
property Code: string
read GetCode
write SetCode;
property Name: string
read GetName
write SetName;
property TotalOnOrder: Double
read GetTotalOnOrder
write SetTotalOnOrder;
property TotalShipped: Double
read GetTotalShipped
write SetTotalShipped;
end;

TCustomer = class(TObjectValueType, ICustomer)
private
// ICustomer
function GetCode: string;
function GetName: string;
function GetTotalOnOrder: Double;
function GetTotalShipped: Double;
procedure SetCode(const Value: string);
procedure SetName(const Value: string);
procedure SetTotalOnOrder(Value: Double);
procedure SetTotalShipped(Value: Double);
end;

Property accessors usually look like this...

function TCustomer.GetName: string;
begin
Result := (GetValueTypes['Name'] as IStringValueType).Value;
end;

procedure TCustomer.SetName(const Value: string);
begin
(GetValueTypes['Name'] as IStringValueType).Value := Value;
end;

This structure allows you to address the properties of an ICustomer as simple properties, but also as the sub value types of an object value type complete with all their metadata.

Normal code uses the ICustomer interface, whilst OPFs and MVP frameworks can make use of the Value Type information for persistence and display purposes - much more sophisticated than 'data-aware'. :-))

Presenter

Joanna Carter (TeamB) Nov 23 2004, 6:31 pm hide options
Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Tue, 23 Nov 2004 10:31:41 -0000
Local: Tues, Nov 23 2004 6:31 pm
Subject: Re: Correct design ?

In the business layer of the application. The theory is that it should be possible to write the business logic of an application as if there were no forms or database; so creation, alteration and destruction of BOs happens entirely in the BO layer.

Of course, most applications have a starting UI, but that should really reflect the Application object.

e.g.

TBusinessApplication = class
...
public
procedure BrowseEmployees;
procedure BrowseCustomers;
...
end;

procedure TBusinessApplication.BrowseEmployees;
begin
var
EmployeeList: TObjectCollection; // generic list of TObjectValueTypes
ListPresenter: TListPresenter; // generic UI for choosing objects
Employee: TEmployee;
Presenter: TObjectPresenter; // base class for UI to edit single objects
begin
EmployeeList := TObjectStore.RetrieveCollectionForType(TEmployee);

ListPresenter := TListPresenter.Create(EmployeeList);

Employee := TEmployee(ListPresenter.SelectedItem);

ListPresenter.Free;

Presenter := TEmployeePresenter.Create(Employee);

Presenter.Free;
end;

The application .dpr file would look something like this :

var
BusinessApp: TBusinessApplication;
begin
Application.Initialize;
BusinessApp := TBusinessApplication.Create(Application);
Application.Run;
end.

Passing Application to the BusinessApp is necessary to ensure that it can be passed to the Presenter that will create the Main Form of the app; otherwise that form will never show and the app will appear not to start.

Somewhere on the main form, you will have a button or a menu item that will call the methods of TBusinessApplication like BrowseEmployees

Interactor

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter"
Date: Sat, 9 Feb 2002 14:27:48 -0000
Local: Sat, Feb 9 2002 10:27 pm
Subject: Re: Model View Presenter

Interactor is a specialised Handler and as such is usually held in the Presenter. The main difference between a Handler and an Interactor is that an Interactor usually copes with a timeline and is usually some kind of state machine.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Thu, 27 Nov 2003 10:01:46 -0000
Local: Thurs, Nov 27 2003 6:01 pm
Subject: Re: Feedback from a Selection


As you may have gathered, I use Interactors to respond to user gestures and these are responsible for telling the Selection to change.

Because an Interactor can be a State Machine, you can detect whether the Ctrl and/or Shift keys are pressed and Clear/Add/Replace the Selection accordingly from an appropriate State class of the Interactor.

Once the Selection has changed, the Model should then notify any Observers.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Mon, 24 Nov 2003 09:35:34 -0000
Local: Mon, Nov 24 2003 5:35 pm
Subject: Re: Linkage between Interactor, Commands and the Selection


Buttons on a form can be used in at least two different contexts:

1. They can be used to invoke Commands on the Model that is being presented in the form and, in that case, should be hooked up directly to Commands.

2. They can be used to spawn other Presenters that will display other aspects of the Model e.g. you are displaying a Customer and you want to display a list of all their Orders; The Order List is not a property of the Customer, but displaying the list is a facet of presenting a Customer.

I would hook such buttons up to simple event handlers within the Presenter for the object being displayed in the form.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Mon, 24 Jan 2005 17:12:15 -0000
Local: Tues, Jan 25 2005 1:12 am
Subject: Re: Validate data between Model and Controller in MVC


The real difference between MVC and MVP is that the 'message flow' is handled via two different 'channels', one for each direction.

The user makes a Gesture in the View

An Interactor responds to that Gesture (that Interactor may delegate reponsibility to nested Interactors, dependent on the complexity of the Gesture).

If that Gesture is intended to alter the visual selection in the View, the Interactor notifies the Selection in the Model.

Changes to the Selection are observed but he View and it is updated to match.

The Selection then notifies any Observers, one of which will be the View which will update itself accordingly, the other is the Command Set.

The Command Set updates itself based on the Selection to accomodate things like Commands not being available when more than one/only one item are/is selected.

Assuming that the Selection is now up to date, the Interactor translates subsequent Gestures, determines which Command on the Model is to be called and calls this Command.

Commands can be undone and redone and can alos support the concept of a history if required.

If executing the Command raises an exception, then the Interactor is responsible for creating a Presenter for that exception and directing the UI to a suitable focus point.

Assuming the Command succeeds, the Model, which contains the Value Type is passes the change to the Value Type.

The Value Type responds to the change by notifying any Observers, one of which would be the View.

This structure is more flexible in that Interactors and Commands can be relatively freely substituted depending on the nature of either the View or the Model.

MVPs can also be nested, so you get a MVP 'component' for a BO, the View of which will be a form, but this component will contain other MVP components, one for each of the properties of the BO, the views of which will be the edits on the form.

The inner MVP components can be created at runtime based on the names of the BO properties matching the 'PropertyName' of the relevant edit on the form.

I tend to talk about creating Presenters for Value Types; this then means that your client code usually creates/retrieves a Value Type (a BO is a composite Value Type) and then simply passes that Value Type to the constructor of a Presenter. The Presenter is responsible for creating a form or other display and will self-destruct when the form closes.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Tue, 23 Nov 2004 10:31:41 -0000
Subject: Re: Correct design ?

If you look at the articles on MVP, you will see that any forms that you create should have absolutely no code on them. Any events generated by buton or mouse clicks are handled in Interactors; thus allowing you to redisgn or replace forms without having to rewrite handler code.

It is the job of the Presenter to create an instance of an appropriate form and show it. The above code assumes that all Presenters create forms that are shown modally; you could not call Free for non-modal use, you would have an event handler in the business layer that would free off such a Presenter when the form closed.

As well as creating a form, the Presenter also populates the form from the object and hooks up the event handlers of the form to Interactor objects that respond by talking to the business object to update its state.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Thu, 2 Jun 2005 09:13:32 +0100
Local: Thurs, Jun 2 2005 4:13 pm
Subject: Re: How Interactor spawn others?

From my research, it should not be necessary to have a top level Interactor for the form if it is just to handle Buttons. Each of the buttons on a form should be wired up directly to a Command in the Model for the object being displayed on the form. In my MVP framework, Buttons implement the ICommandGuiObject, as do Menu items. Due to the simplicity of anticipated interaction between the Button and its related command, there really is no need for the complexity of an Interactor. Any Button or MenuItem should use the Observer pattern to detect when a Command is enabled/disabled; other than that, there is only the need to hold a reference to a Command in the Button and override the Click method of the Button or MenuItem to call the Execute method of the Command.

But when it comes to handling of Edits, Lists, etc; this relies on the Interactors that are listening to the controls also surfacing events or being Subjects that are observed by the Form's Interactor.

If you try to pass interaction from a top level Interactor to sub-Interactors, then this, I believe, is the wrong way round. Interactors are reactive, not proactive and only listen for Gestures rather than generating them, which is what passing control to sub-Interactors from a top level Interactor would imply.

Now, within any Interactor system, there needs to be the ability to start an interaction from one control to end in another, as in drag-dropping from a control on one form to a control on another form. This involves the use of a ScrapItem which is passed to an application scope "Scrapboard" (clipboard) by the Interactor of the originating control and then picked up by the Interactor of the target control when it detects a MouseOver message with the mouse button pressed.

I think you are trying to design how to detect when a Control gains focus, either by mouse or keypress.

Mouse activation is accomplished by using the Interactor attached to the Control receiving focus to detect the mouse click; this Interactor also publishes an event picked up by the Form's Interactor, which then sets focus to the clicked Control.

Using the Tab key to move between Controls means picking up the Tab keypress in the currently focused Control and publishing this as an event which is picked up by the Form Interactor.

You also have to bear in mind that most Windows controls already have a mechanism for handling control focus change and interfering with this could be more hassle than it is worth. It would be better to activate a Control's Interactor on a SetFocus message and deactivate it on KillFocus message.

Do these ramblings answer your questions, or am I missing your point ?

  1. MVP Interactor ... how to link
  2. How Interactor spawn others?

Tuesday, August 23, 2005

Validation

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter"
Date: Sat, 30 Aug 2003 08:46:35 +0100
Local: Sat, Aug 30 2003 3:46 pm
Subject: Re: Opinions wanted


The validation of objects can happen in many different places and ways.

In the case of an Sales Order , the issue of cascading occurs when the Order is deleted and, because it owns the Order Lines, so should they. Such a cascade rule is enforced in the Order itself as it is solely responsible for the lifetime management of its owned objects.

In the case of a Customer and its Orders, the deletion is not a function of the Customer but of a 'Manager'; There may be rules that state that Orders have to be kept in archive rather than truly deleting, this kind of functionality could well be the domain of an Order Book class.

Certainly all classes in a system are not persistent and that is where the table metaphor is most lacking. You also need 'rules' and 'manager' classes which are not persistent and that have nothing to do with the management of collections of objects (tables).

Model

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Wed, 28 Apr 2004 10:20:48 +0100
Local: Wed, Apr 28 2004 5:20 pm
Subject: Re: Who's watching who?


I have managed to determine that the Model is there essentially to provide a set of rules and validations for anything that may happen to the Value object it maintains.

e.g.

ICustomer = interface
function GetCode: string;
function GetName: string;
procedure SetCode(const Value string);
procedure SetName(const Value string);
end;

TCustomerModel = class
public
class function ValidateCustomer(const Customer: ICustomer): Boolean;
class function StoreCustomer(const Customer: ICustomer): Boolean;
class function DeleteCustomer(const Customer: ICustomer): Boolean;
...
public
constructor Create(const Customer: ICustomer);
function GetValue: ICustomer;
procedure SetValue(const Value: ICustomer);
end;

If someone changes the Value object held in the Model, then the Model notifies the View that the whole display is invalid.

If someone changes an attribute in the Value object, then the Attribute notifies the Value object that it has changed as well as notifying the edit view that is displaying it. Subsequently, the Value object notifies the form view in case there are other aspects of the form that need updating like buttons/menus that need enabling/disabling.

The Model seems to play very little part in mediating between the Value/View/Value; it is rather there as an extension of the Value object. This means that the basic description of, say, a Customer can remain the same, but the effects of handling a Customer can be modified in the Customer Model without affecting the Customer itself.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Tue, 4 May 2004 15:36:19 +0100
Local: Tues, May 4 2004 10:36 pm
Subject: Re: Who's watching who?

ICustomer = interface
...
end;

TCustomer = class(..., ICustomer)
// class (static) data and methods
...
// instance data and methods
...
end;

IMVPModel = interface
function GetCommandSet; ICommandSet;
function GetCurrentSelection: ISelection;
end;

TMVPModel = class(..., IMVPModel)
...
end;

TCustomerModel = class(TMVPModel)
// class (static) methods
class function Validate(const Customer: ICustomer; out Errors:
IErrorList): Boolean;
class function Store(const Customer: ICustomer): Boolean;
...
// instance data and methods
GetValue: ICustomer;
end;

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Fri, 25 Feb 2005 13:15:01 -0000
Local: Fri, Feb 25 2005 9:15 pm
Subject: Re: MVP Questions


More correctly, The Model holds a Value which can be anything from a simple value type like Integer or string to a business object. The Value will have its own rules and logic; the Model can hold additional rules and logic where those rules extend the effects of the Value beyond the limits of the Value.

e.g.

SalesOrder
This is a valid Value and all the rules that pertain to internal handling of a Sales Order like totaling lines, discounts, etc are implemented in here.

SalesOrderModel
When a Sales Order is created/altered it also affects the Customer and Product classes; the code to govern the outside interactions resulting from, say, storing a Sales Order should be in a static method of the SalesOrderModel class

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Tue, 16 Mar 2004 14:13:21 -0000
Local: Tues, Mar 16 2004 10:13 pm
Subject: Re: Hiding public elements

In the case of MVP, I use Interfaces to define business domain behaviours: Customer, Order, Invoice, etc.. This allows me to create classes that not only implement the business interface but that can also implement metadata and identity behaviours, not normally visible to the application programmer. This ability to treat an object in different ways, depending on the POV is possibly what I find most attractive about interfaces from a design perspective. The business classes may seem a bit like pure data classes, but, in fact, they also contain consequential effect and calculation code.

Now when it comes to the Model part of MVP, I have now improved how I see this working. This next bit may ramble, but see if you can make sense of it :-)

You need a Model to correlate the Value object, the Selection and the Command Set.

You need an instance of a Command Set within the Model which will make available appropriate Commands for an instance of the Value type, dependent on the User, Security, Scenario, etc..

The Command Set may need to see more than just the Value type (e.g. Customer) in order to build itself, and it is this need to see outside the Value type that I believe is beyond the remit of the standard Value type classes.

The Model can be said to represent a Use Case or Scenario; and the use of the Model allows the Scenario to change without necessarily changing the Value type.

Whereas, methods like Store and Delete definitely don't belong in the Value type, I do believe they are valid methods of the Model. But they should be class methods that take an instance of the Value type as parameter.

This allows the Model class to have 'command' methods which can be called from within the Command Set and that are related to and strictly typed to the Value type that the Model contains.

So you would have:

IOrder = interface
property Number: string;
Customer: ICustomer;
Lines: IOrderLineList;
...
end;

TOrder = class(TInterfacedObject, IOrder)
// implements IOrder
end;

TOrderModel = class
class procedure Store(const Order: IOrder);
class procedure Delete(const Order: IOrder);
class procedure DeleteLine(const Order: IOrderLine);
...
end;

Methods like Delete and Store have consequences, in that the Customer's balance will have to be adjusted as well as the Stock levels of the Products included in the Order Lines; it is these side effects that seem to fit very nicely in the use of the Model to enforce rules that apply not to an instance of a Value type but to the type itself.

In any case, feel free to pull apart this ramble :-))

Selection / Command / CommandSet

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Wed, 26 Nov 2003 09:04:24 -0000
Local: Wed, Nov 26 2003 5:04 pm
Subject: Re: How to map self-induced model changes to a selection?


I think what is needed here is to use the Interactor to detect when an editing keystroke happens in the View and suspend the View's ability to update itself until the edit is complete. At which point the View would once again start reacting to updates in the Time model.

You might also want to consider splitting the concept of Time into Hours, Minutes and Seconds 'sub-models' and having the View also contain 'sub-views' ??

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Wed, 26 Nov 2003 14:17:43 -0000
Local: Wed, Nov 26 2003 10:17 pm
Subject: Re: How to map self-induced model changes to a selection?


Then, because the Model has changed state, it has to inform the Command Set that it owns; just because the Command Set is an Observer of the Selection doesn't mean that other objects can't talk to it.

If you implement such Models as State Machines, then you can stop further changes to the Selection/Command Set happening.

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Fri, 20 Feb 2004 09:58:09 -0000
Local: Fri, Feb 20 2004 5:58 pm
Subject: Re: MVP again - how to implement a view of a selection


You might consider using Aspects to enhance the behaviour of the Observer.

Here is a rough outline :

IAspect = interface
function CompareTo(const Other: IAspect);
funstion ToString: string;
end;

TSelectionAspect = class
class function Single: IAspect;
class function Multiple: IAspect;
class function Empty: IAspect;
end;

IAspectObserver = interface
procedure Update(const Subject: IInterface; const Aspect: IAspect);
end;

IAspectSubject = interface
procedure Attach(const Observer: IAspectObserver);
procedure Detach(const Observer: IAspectObserver);
procedure Notify(Subject: IInterface; const Aspect: IAspect);
end;

TMySelection = class
private
fSubject: IAspectSubject;
public
procedure AddItem(const Item: TMyClass);
procedure RemoveItem(const Item: TMyClass);
...
end;

procedure TMySelection.AddItem(const Item: TMyClass);
begin
fItems.Add(Item);
case fItems.Count of
0:
fSubject.Notify(nil, TSelectionAspect.Empty);
1:
fSubject.Notify(fItems[0], TSelectionAspect.Single);
else
fSubject.Notify(fItems, TSelectionAspect.Multiple);
end;
end;

procedure TMySelection.RemoveItem(const Item: TMyClass);
begin
fItems.Remove(Item);
DoNotify;
end;

TSelectionView = class
procedure Update(const Subject: IInterface; const Aspect: IAspect);
...
end;

procedure TSelectionView.Update(const Subject: IInterface; const Aspect:
IAspect);
// subject can be either a single object or a list
begin
// clear display
if Aspect.CompareTo(TSelectionAspect.Single) then
// display single object
else
if Aspect.CompareTo(TSelectionAspect.Multiple) then
// display list object
end;

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter"
Date: Sat, 30 Aug 2003 08:46:35 +0100
Local: Sat, Aug 30 2003 3:46 pm
Subject: Re: Opinions wanted


There are times when you want to deal with a single object rather than a collection. There may be different rules that apply to a collection or an object.

For example, in the MVP framework we have the concepts of Selection and Command Set.

The Selection for a single object like a Customer can just be the single object that makes up the Model, but in the case of a Document, the Selection is going to be the currently selected text; but in the case of a collection of Documents, the Selection is going to be none, one or more documents.

The Command Set for a single object may be Edit, View, Save and Print, but the Command Set for a collection would typically include Add, Insert, Delete, Sort, Move, etc. Because a Command is executed against the Current Selection, each model can determine exactly what happens when that command is executed.

It is the purpose of the Model in MVP to provide support for validation that exceeds the boundary of the Value object that it is responsible for managing. Using MVP design patterns allows you to create generic editors for single objects and list presenters for collections.


MVP concept

Newsgroups: microsoft.public.dotnet.languages.csharp
From: "Joanna Carter \(TeamB\)"
Date: Thu, 14 Apr 2005 09:48:42 +0100
Local: Thurs, Apr 14 2005 4:48 pm
Subject: Re: Document/View Model - Advice Sought!

As Jeff has indicated, this kind of relationship cannot be resolved when you only have a Model and a View.

This is why someone came up with the concept of MVC (Model View Controller) and later with MVP (Model View Presenter), a more sophisticated and flexible version of MVC

In MVC, the Controller acts as a Mediator between the Model and the View and can ensure that, if a message comes from the View, that the resultant action invoked on the Model does not cause infinite recursion.

MVP takes things further by splitting out the responsibilities for handling changes even better.

I have had extensive practice in implementing an MVP framework using Delphi which adds a couple more ideas to keep things better encapsulated than the MVC.

The Model, not only holds an instance of the Value Type (the object to be editied), but it also holds a Current Selection and a Command Set. Instead of relying on a UI to maintain a selection, the Model is notified via an Interactor when a user makes a Gesture in the UI; the Current Selection can vary from being a sub-list in the case of a multi-item object to just the single object in the case of a basic ValueType like a string or integer.

When the Selection changes, the Command Set is notified and changes itself to only allow Commands that are viable for the given Selection. In the case of a list of objects, if the Selection is empty, then the Add Command is enabled but the Insert, Delete, Edit, Cut and Copy Commands are disabled. Changing the Selection to a single item will enable all Commands, while a multiple selection will disable Edit and Insert.

The Command Set can be visualised in the UI by a popup context menu or maybe a MS Money style 'what to do' bar.

The View is made to be an Observer of the Value Type held in the Model and will respond to changes in the Value Type by updating itself to reflect the current state of the object it is displaying.

The Presenter, which holds a reference to each of the Model and View, also holds one or more Interactors.

An Interactor is essentially an object that has one or more event handlers or delegates that are listening for changes in the UI (View). The Interactor responds to a Gesture, by interpreting it into : a change in the Selection or a sending of change of content in the edit to the Value Type. This includes UI elements like radio groups or checkboxes that are used to represent sets or boolean values

UI elements like buttons and menu items are not usually handled by an Interactor, but are linked directly to a relevant Command; the OnClick event handled directly in the associated Command and the button or menu item enabled or disabled according to the state of the Command.

Now comes the bit that keeps the Model and View apart in terms of stopping recursion.

Interactors can be enabled or disabled; they can enable or disable themselves. Which means that if they instigate a change in the Value Type that is known to propogate to a change in the UI, the Interactor can prevent itself from receiving further notifications until the change has completed.

Interactors can also be nested and can propogate user input to other, more sophisticated, Interactors to handle things like dragging an item from one control to another or even to another application.

Ultimately, the MVP framework allows for the nesting of MVP 'Components' so that each property of an object has its own MVP to link it to an edit/checkbox/combo on a form and these are all contained and maintained within an overall Presenter which links the object in the Model to the form.

Once the nested MVP framework is completed, it is then possible to write a business class and design a form where each edit has the name of the property it is to represent, but where there is absolutely no code on the form itself. Then by simply instantiating a Presenter, passing the business object to the constructor, a Model will be created for you, a View (form) will be created for you, they will be linked together, all the properties will have MVPs created for them and linked to the controls on the form via Interactors; and finally the form will be shown and managed by the Presenter.

Regardless of whether you want to achieve this level of sophistication in your current situation, you at least need to implement some kind of Controller to act as a Mediator between your Model and View, taking into account the idea of enabling and disabling used in my Interactor classes.

If you are interested in further reading on MVP, then I have some articles on my website www.carterconsulting.org.uk, or you can Google in the news groups for "joanna carter interactor" for more discussion on the finer points on this part of the MVP which are not covered in the articles.

Joanna
--
Joanna Carter
Consultant Software Engineer

Newsgroups: borland.public.delphi.oodesign
From: "Joanna Carter \(TeamB\)"
Date: Fri, 4 Feb 2005 12:51:32 -0000
Local: Fri, Feb 4 2005 8:51 pm
Subject: Re: Best way to model this?

The real difference between MVC and MVP is that the 'message flow' is handled via two different 'channels', one for each direction.

The user makes a Gesture in the View

An Interactor responds to that Gesture (that Interactor may delegate reponsibility to nested Interactors, dependent on the complexity of the Gesture).

If that Gesture is intended to alter the visual selection in the View, the Interactor notifies the Selection in the Model.

Changes to the Selection are observed by the View and the visualisation is updated to match.

The Selection then notifies any Observers, one of which will be the View which will update itself accordingly, the other is the Command Set.

The Command Set updates itself based on the Selection to accomodate things like Commands not being available when more than one/only one item are/is selected.

Assuming that the Selection is now up to date, the Interactor translates subsequent Gestures, determines which Command on the Model is to be called and calls this Command.

Commands can be undone and redone and can also support the concept of a history if required.

If executing the Command raises an exception, then the Interactor is responsible for creating a Presenter for that exception and directing the UI to a suitable focus point.

Assuming the Command succeeds, the Model, which contains the Value Type, passes the change to the Value Type.

The Value Type responds to the change by notifying any Observers, one of which would be the View.

This structure is more flexible, in that Interactors and Commands can be relatively freely substituted depending on the nature of either the View or the Model.

MVPs can also be nested, so you get a MVP 'component' for a BO, the View of which will be a form, but this component will contain other MVP components, one for each of the properties of the BO, the views of which will be the edits on the form.

The inner MVP components can be created at runtime based on the names of the BO properties matching the 'PropertyName' of the relevant edit on the form.

I tend to talk about creating Presenters for Value Types; this then means that your client code usually creates/retrieves a Value Type (a BO is a composite Value Type) and then simply passes that Value Type to the constructor of a Presenter. The Presenter is responsible for creating a form or other display and will self-destruct when the form closes.



This page is powered by Blogger. Isn't yours?