MVP in a Nutshell: Have We Got It Right?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

MVP in a Nutshell: Have We Got It Right?

Eric Taylor
Hello Forum,

The difference between a <View> behavior and an instance of a view is
clear to us now in terms of the Dolphin framework.

But we've noticed that some of the classes in the View hierarchy also
have associated with them a view instance.  Is this because not all
Views have formal Presenters (i.e. some Views, implementing the
presenter protocol, act as their own Presenters)?

Would it be correct to say that the MVP possibilities are as follows
(upper- and lowercase deliberate).

1) Model, view-Presenter;
2) Model, View, Presenter;
3) Model, View-view-<presenter protocol>.

Thinking out loud, in terms of using Dolphin, each of the above would
translate into the following (I use the term "specialize" instead of
"subclass" to indicate that one may need to sub-subclass, etc.):

1) Specialize a Model; specialize a Presenter; right-click on this
Presenter and associate a view instance in the familiar way;
2) Specialize a Model; specialize a View; specialize a Presenter;
right-click on this Presenter and associate an instance of this
specialized View by dropping it onto a view canvas;
3) Specialize a Model; specialize a View; right-click on this View and
associate a view instance in the familiar way; implement the presenter
protocol in the specialized View itself.

Have we got it right?

Thanks very much.


Cheers,

Eric


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Chris Uppal-3
Eric,

> 2) Specialize a Model; specialize a View; specialize a Presenter;
> right-click on this Presenter and associate an instance of this
> specialized View by dropping it onto a view canvas;

I'm not entirely sure that I understand what you are asking, but a couple of
observations which may (or may not) be relevant.

There is no direct way of creating the first View Resource for a new View
subclass from the View Composer (unless something has been added in D6).  The
VC works solely in terms of /existing/ templates, and allows you to compose
them to create new templates, but doesn't have a way to create the very first
template.

I use code like

     PresenterClass addView: ViewClass asResource: 'Default view'.

but that may no longer be the preferred method in D6 (this stuff changed
between D5 and D6, and has broken quite a lot of my specialised View code.)

Also, if your new View has specific requirements of its intended Presenter
subclass, then it /must/ implement that itself too -- whether or not there is a
dedicated Presenter subclass as well -- since views are used in
"self-presentered" mode in the View Composer.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Eric Taylor
Hello Chris,

>There is no direct way of creating the first View Resource for a new
View
>subclass from the View Composer (unless something has been added in
D6).

I went back to take a closer look at what's going on in the View
hierarchy, and now I'm a little more confused.  When you right-click on
various views in this hierarchy, the context-sensitive menu lights up
the "New..." option under the "Views>" submenu; however, selecting the
option produces an exception.  Right-clicking on
AXValueConvertingControlSite displays a context menu will all options
lit up, but the only one that doesn't produce an exception is the "Edit"
option.

So it would seem that D6 works the same way as D5 in this regard.  I
assumed that the enabled options on the context menu meant that they
were available in that context.  I think the only one I actually tested
was the "Edit" option, which happened to be the one that _is_ available.

>Also, if your new View has specific requirements of its intended
Presenter
>subclass, then it /must/ implement that itself too -- whether or not
there
>is a dedicated Presenter subclass as well -- since views are used in
>"self-presentered" mode in the View Composer.

Without going through the process first, I'm not sure I understand what
you mean here.  I'll have to digest this one a bit.  I would have to
wonder why, then, Views would need Presenters at all.  But this does
explain the presence of the presenter protocol in the View class.  I
need to explore the MoenTreeView a little further, see how
responsibilities are distributed between the TreePresenter and the
MoenTreeView classes.

Cheers,

Eric

> -----Original Message-----
> From: Chris Uppal [mailto:[hidden email]]
> Posted At: Monday, June 05, 2006 3:29 AM
> Posted To: comp.lang.smalltalk.dolphin
> Conversation: MVP in a Nutshell: Have We Got It Right?
> Subject: Re: MVP in a Nutshell: Have We Got It Right?
>
> Eric,
>
> > 2) Specialize a Model; specialize a View; specialize a Presenter;
> > right-click on this Presenter and associate an instance of this
> > specialized View by dropping it onto a view canvas;
>
> I'm not entirely sure that I understand what you are asking, but a
couple
> of
> observations which may (or may not) be relevant.
>
> There is no direct way of creating the first View Resource for a new
View
> subclass from the View Composer (unless something has been added in
D6).
> The
> VC works solely in terms of /existing/ templates, and allows you to
> compose
> them to create new templates, but doesn't have a way to create the
very
> first
> template.
>
> I use code like
>
>      PresenterClass addView: ViewClass asResource: 'Default view'.
>
> but that may no longer be the preferred method in D6 (this stuff
changed
> between D5 and D6, and has broken quite a lot of my specialised View
> code.)
>
> Also, if your new View has specific requirements of its intended
Presenter
> subclass, then it /must/ implement that itself too -- whether or not
there
> is a
> dedicated Presenter subclass as well -- since views are used in
> "self-presentered" mode in the View Composer.
>
>     -- chris


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Eric Taylor
In reply to this post by Chris Uppal-3
Chris,

>Also, if your new View has specific requirements of its intended
Presenter
>subclass, then it /must/ implement that itself too -- whether or not
there
>is a dedicated Presenter subclass as well -- since views are used in
>"self-presentered" mode in the View Composer.

O.K., I think I've got it.  The key phrase in your statement is
"specific requirements."  Since both Views and Presenters implement the
presenter protocol, that functionality is already there.  But a
particular View may have to override one or more of these methods to
provide its own, specific functionality.

But how do Presenters and Views combine their implementations?  Consider
for a moment what we're wrestling with here:

1) TreePresenter implements the treePresenter protocol, which contains
the #collapse: method;
2) The MoenTreeView implements the treeView protocol, which contains the
#collapse: method;
3) Both TreePresenter and View implement the presenter protocol;
4) The TreePresenter contains a view instance called "Moen tree," which
contains a single component called MoenTreeView.

Imagine, then, that the #collapse: message is sent to an instance of
MoenTreeView.  Where does the #collapse: message in an instance of
TreePresenter fit in?  MoenTreeView doesn't seem to pass it up to the
TreePresenter.

I wonder who thought this approach was easier than multiple inheritance
:).

Cheers,

Eric


> -----Original Message-----
> From: Chris Uppal [mailto:[hidden email]]
> Posted At: Monday, June 05, 2006 3:29 AM
> Posted To: comp.lang.smalltalk.dolphin
> Conversation: MVP in a Nutshell: Have We Got It Right?
> Subject: Re: MVP in a Nutshell: Have We Got It Right?
>
> Eric,
>
> > 2) Specialize a Model; specialize a View; specialize a Presenter;
> > right-click on this Presenter and associate an instance of this
> > specialized View by dropping it onto a view canvas;
>
> I'm not entirely sure that I understand what you are asking, but a
couple
> of
> observations which may (or may not) be relevant.
>
> There is no direct way of creating the first View Resource for a new
View
> subclass from the View Composer (unless something has been added in
D6).
> The
> VC works solely in terms of /existing/ templates, and allows you to
> compose
> them to create new templates, but doesn't have a way to create the
very
> first
> template.
>
> I use code like
>
>      PresenterClass addView: ViewClass asResource: 'Default view'.
>
> but that may no longer be the preferred method in D6 (this stuff
changed
> between D5 and D6, and has broken quite a lot of my specialised View
> code.)
>
> Also, if your new View has specific requirements of its intended
Presenter
> subclass, then it /must/ implement that itself too -- whether or not
there
> is a
> dedicated Presenter subclass as well -- since views are used in
> "self-presentered" mode in the View Composer.
>
>     -- chris


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Chris Uppal-3
In reply to this post by Eric Taylor
[replying to both Eric's posts]

Eric,

> Without going through the process first, I'm not sure I understand what
> you mean here.  I'll have to digest this one a bit.  I would have to
> wonder why, then, Views would need Presenters at all.

If you look at it solely from the POV of the View itself, then it has little
need for a Presenter.  From that POV, the Presenter is little more than a
pluggable strategy object (in the sense of the Strategy Pattern) to make it
more configurable/flexible.  The View's #interactor -- which defaults to the
#presenter -- is used similarly but more dynamically.

The real point, though, is that the View has only a limited understanding of,
or responsibility for, the whole component.  In fact (IMO) it is subordinate to
the Presenter -- i.e. the Presenter "owns" (or even /is/) the presentation
layer, and the View is something of an implementation detail.  The View is the
generic rendering/user-interaction code factored out from the Presenter to
allow more flexibility in assembling components.   (There are, I'm sure, other
ways of thinking about MVP, but that's how I see it.)

As I mentioned before, it is rare for a View class to correspond to a
complicated Presenter class (although it is possible within the overall
framework).  And that sort of falls out from the above relationship between a
View and a Presenter.  If, say, you have a View which wraps some Windows
control, then it and its Presenter between them (i.e. considered as a single
component) will do very little except what the control does -- that's what the
component is /for/.  So most of the code, in this instance, will be in the View
(since most of the code is about wrapping the Windows control's appearance and
user-interactions), and the Presenter will be very little more than an empty
shell.  So, in this case, the View will have most of the code, and will require
very little from the Presenter except to be the pluggable bit I mentioned above
(plus, of course, generic Presenter responsibilities like initialisation).

More of pluggability below...


> But how do Presenters and Views combine their implementations?

There are two directions to consider.  What messages can be sent /to/ the
component from the rest of the application, and what actions can by invoked on
the component by the user.  These have to be treated separately.

Start with the first case, and assume we are still keeping with the pattern of
a trivial or dumb Presenter connected to a smart View.  Or, more accurately, a
trivial Presenter which can be connected to any of a choice of smart Views
(differently configured instances of the same class, or instances of completely
different classes).  In this case, there /may/ be a subset of functionality
which is inherent to the kind of data being displayed, in which case all the
candidate Views should expose that functionality via the same set of messages
(i.e. polymorphically).  In this case, it /may/ be advantageous to put some
handy utility methods on the Presenter too, so that that functionality is
exposed directly, rather than always having go via the View. I.e:

    aPresenter collapse.

in preference to always having to write:

    aPresenter view collapse.

That is just following the Occasionally Good Advice of Demeter (which some call
the Law of Demeter).  But that is only a convenience, and there is no good
reason (despite the so-called Law) to try to achieve blanket coverage of every
possible shared feature of the Views (and in any case, there will still be
View-specific features, so it is not possible to hide everything).

Carrying on from that, I believe that this is also part of why it is rare to
attach complicated Presenters attached to complicated Views.  It is /possible/
to create a special-purpose Presenter, with significant logic, which connects
to a complicated, but generic View.  The only example I can think of in the
image is MethodBrowser.  In such cases, it may be /necessary/ to "re-expose"
features of the View as features of the Presenter because the Presenter wants
to modify or reinterpret what they mean (e.g.
MethodBrowser>>selection:ifAbsent:).  I think that necessity, and the
awkwardness of satisfying it, is symptomatic of [ab]using inheritance when
composition would do the job better.

Now, about user interaction and pluggability.  Remember that I think of the
View as subordinate to the Presenter.  The Presenter "owns" the UI, but
delegates most of the work to the View.  But that means that the Presenter has
lost control over the UI -- which is wrong since it still owns it (and the MVP
framework discourages application-specific View classes).  Solution: the View
consults the Presenter before it interprets user-actions.  Put more generally,
the View informs the Presenter of all user-interactions, but provides sensible
default handling for them.

As far as I understand it, the idiomatic sequence for interpreting a user-input
is:

1) User does something, X.

2) Windows tells the View about it.  If the notification from Windows isn't
purely to do with that view's implementation then it tells the Presenter about
it by sending #onX (or #onX: or whatever) to its Presenter.  The default
implementation in the Presenter class is just to pass it back to the View.  So
the presenter's onX method just reads:
    ^ self view onX.

3)  The View's own onX method (which is invoked directly if the View instance
happens to be "self-presentered" or indirectly by the default Presenter code),
triggers a notification off the presenter:
    self presenter trigger: #onX.
and also does any default handling which is needed for that input.

It's rather a complicated little dance, but it does provide considerable
flexibility.  In particular, since input is handled both by Smalltalk message
sends (nothing to do with Windows "messages"), and by Smalltalk event
notification, the behaviour can be extended by both subclassing and by
composition.  Notice that the Presenter is acting as a Strategy object for
interpreting user input (from the View's POV).

Actually, in the above I've glossed over the Interactor/Presenter distinction.
It would be more accurate to say that the View's Interactor is the pluggable
strategy object, but since (for most Views, for most of the time) the
interactor /is/ the Presenter, the difference is small.

It's instructive to follow through the sequence starting at
View>>wmLBDblClk:wParam:lParam: for an example of this.


> Imagine, then, that the #collapse: message is sent to an instance of
> MoenTreeView.  Where does the #collapse: message in an instance of
> TreePresenter fit in?  MoenTreeView doesn't seem to pass it up to the
> TreePresenter.

It might help to remember that the View and Presenter are not peers.  Any View
will assume that it is controlled by some Presenter (not necessarily its own
Presenter, since it could be paired with a dumb presenter as a sub-component of
something bigger) . Hence if it is told to #collapse: then it assumes that all
the important parties already know it is going to #collapse: and all it has to
do is get on with drawing the graphics, or updating the Windows control, or
whatever.  (Actually, this specific case is a little more complicated since
#collapse: can result in selection changes as a side-effect).  Contrast that
with the case of user input.  Here the View is the first to be told (by
Windows) about what the user is doing, so it has to pass that information on to
its masters before it can safely act on it itself.


> I wonder who thought this approach was easier than multiple inheritance
> :).

<grin/>


BTW, one thing that I've left out of this discussion is to emphasise how much
all this is about composing components from combinations of other components.
If we focus on the relationship between a "primitive" View (such as a wrapper
for a Windows control), and its Presenter, then we miss how very much the
architecture is based around the idea of composition -- because our example
doesn't /do/ any composition.  A lot of the design only really makes sense when
you think of the components being used to create other components, and so on.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Schwab,Wilhelm K
Chris, Eric,

> As I mentioned before, it is rare for a View class to correspond to a
> complicated Presenter class (although it is possible within the overall
> framework).  And that sort of falls out from the above relationship between a
> View and a Presenter.  If, say, you have a View which wraps some Windows
> control, then it and its Presenter between them (i.e. considered as a single
> component) will do very little except what the control does -- that's what the
> component is /for/.  So most of the code, in this instance, will be in the View
> (since most of the code is about wrapping the Windows control's appearance and
> user-interactions), and the Presenter will be very little more than an empty
> shell.  So, in this case, the View will have most of the code, and will require
> very little from the Presenter except to be the pluggable bit I mentioned above
> (plus, of course, generic Presenter responsibilities like initialisation).

Seconded.  I will go a bit further, by suggesting that a complicated
view is a code smell.  As Chris suggests, wrapping a control is a
somewhat unusual situation, so do what you must.  For just about
anything else (aka a custom view), I recommend trying an ImagePresenter
to display the contents.  You can search for graphics, debugging, etc.
to turn up old posts with my approach to this.

Again, if you must wrap a control, all bets are off.  Note that you will
benefit from a good presenter layer.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Chris Uppal-3
In reply to this post by Eric Taylor
Eric Taylor wrote:

> When you right-click on
> various views in this hierarchy, the context-sensitive menu lights up
> the "New..." option under the "Views>" submenu; however, selecting the
> option produces an exception.

Bug.  I presume it's intended to do the same as it would for a Presenter
subclass.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Eric Taylor
In reply to this post by Chris Uppal-3
Hello Chris,

I apologize in advance.  This is a rather long response, rhetorical in
form, that demonstrates our wrestling with MVP.  It would seem that
we're following in the footsteps of many who are now offering
heavyweight, expert answers to questions about MVP ;).  I wanted to
capture these thoughts in the forum for the sake of others who come
after.  It's interesting how many of us come to the same sticking point
in MVP.

Yesterday, I went through DSDN, copy-and-pasted about two dozen articles
into MS Word, and printed this monolithic tome for study.  Thrice I've
read your post below, and it's interesting to see how _your_
understanding of the subject has grown over time.  I found a number of
wonderful posts that you made some time ago on this same subject.  I had
found [some of] them before, but the exigencies of the design matter at
hand didn't really come to bear until the last few days.  As I'm new to
Smalltalk, and to Dolphin, your words a few weeks ago simply weren't
registering.

I was up until 2:00 a.m. last night (something I rarely do anymore)
really going through the posts, reading and re-reading Ted Bracht's
book, and using Dolphin's discovery features to explore the pertinent
classes.  I think it's fair to say that, yesterday, I ate, drank, and
slept MVP.

Smalltalk has a way of dazzling someone new to it, even to such an
extent that one apprehends applying even the most fundamental
programming principles.  What is clear in one's native programming
language, and in reality is equally clear in Smalltalk, becomes
unnecessarily clouded by over-thinking and second-guessing.

It took me a while to realize that I'm simply not adept at applying the
GoF Decorator pattern.  Since Eiffel is a language that fully supports
multiple inheritance, I haven't often had a need to apply the "wrapping"
technique (there are still times when it is the preferred approach, even
in Eiffel, but it is not the norm).

So, after brushing up on the Decorator pattern, I went through the View
class and the Presenter class method by method and your words now
register:

>I.e:
>
>     aPresenter collapse.
>
> in preference to always having to write:
>
>     aPresenter view collapse.

which indicates an arrangement where the Presenter class "wraps" and
instance of the View class.  Using the language of Eiffel (could be any
language, really), for the benefit of others who may be migrating from a
multiple-inheritance language, one can make the following comparison:

Multiple Inheritance
====================================

deferred class PRESENTER_VIEW inherit
        PRESENTER
        VIEW
end

Composition (the Decorator Approach)
====================================

deferred class PRESENTER
        view: VIEW
end

The Presenter class would have to be able to "pass on" at least the
functionality of the View class, and then tack on whatever additional
functionality it needs to support, functionality that would _not_ get
passed on to the instance of View.

In the multiple-inheritance example above, the glue that binds PRESENTER
and VIEW is a new class called a PRESENTER_VIEW.  In the composition
example, the glue is the <presenter> protocol: Both the Presenter class
and the View class must support this protocol in order for the two
classes to "stick" together.

Now the issue of where to put the logic--in the View or in the
Presenter--of a particular MVP component is much clearer.  The design
question to ask is "Is there anything that the view needs to do that the
presenter doesn't need to know about?"  If we look at it from another
perspective, we could say that the presenter is the interface boundary
between the MVP component and the application.  So, if we think a
particular method belongs in the Presenter instead of the View, we
should ask the question "Would the application ever need to send that
message directly to the presenter?"

I can see the application itself needing to collapse a node in a tree,
but the actually graphical manifestation of that collapse should be
handled by the view.  The presenter says to the view, "Hey, the user
just told me to collapse this node, so now I'm telling you to collapse
the node. How you wish to draw that on the screen is your business."
The intervening role of the Presenter is clear: It may not be
appropriate for the application to directly tell a view to do something.
The presenter might want to get in the middle of this.  For example,
what if there were a user preference called "Allow collapsing of nodes"
and the user had chosen "no"?  In this case, the presenter may want to
check this option before passing the message onto the view.  The
dialogue would be this, then: "Hey, the user just told me to collapse a
node.  Now I know you can do this, but I'm afraid I'm going to have to
withhold this request since the user told the application not to allow
collapsing of nodes (and the developer was daft enough not to hide the
+/- icon in the first place ;)."

I think the logic-location question is straightforward now.  The next
question is the View-view question.  Do we create a new View or a new
instance of View?  I think it's misleading to cast this question in
terms of genericity, asking the question, "Is the View going to be doing
something that could be considered generic?"  How does the neophyte
interpret "generic" into a concrete feature set, and at the same time
avoid reinventing a portion of the framework?  Borrowing from Andy's
reply to one of my posts, I think the better question might be, "Is the
View either going to draw something that has been draw before, but in a
fundamentally new way (e.g. the Moen tree), or is it going to draw
something completely novel (e.g. the Scribble example)?  But there
again, a loaded word creeps in: "novel" relative to what?  Scribbling
isn't really novel in software, but it _is_ novel relative to the
Dolphin framework, and so demands a new View.

Whew!

Well, I hope we're on the right track here.


Cheers,

Eric


Reply | Threaded
Open this post in threaded view
|

Re: MVP in a Nutshell: Have We Got It Right?

Eric Taylor
In reply to this post by Schwab,Wilhelm K
Bill,

>Again, if you must wrap a control, all bets are off.  Note that you
will
>benefit from a good presenter layer.

I think this statement hits the nail on the head.  An ActiveX control is
a bit of a hack anyways, a multi-faceted one, so it only makes sense
that it may need to be hacked in, and in equally various ways.

>For just about anything else (aka a custom view), I recommend trying an
>ImagePresenter to display the contents.  You can search for graphics,
>debugging, etc. to turn up old posts with my approach to this.

ImagePresenter?  Hmmm...I'll have to take a look at this.

Thanks very much.

Cheers,

Eric

> -----Original Message-----
> From: Bill Schwab [mailto:[hidden email]]
> Posted At: Tuesday, June 06, 2006 6:38 AM
> Posted To: comp.lang.smalltalk.dolphin
> Conversation: MVP in a Nutshell: Have We Got It Right?
> Subject: Re: MVP in a Nutshell: Have We Got It Right?
>
> Chris, Eric,
>
> > As I mentioned before, it is rare for a View class to correspond to
a
> > complicated Presenter class (although it is possible within the
overall
> > framework).  And that sort of falls out from the above relationship
> between a
> > View and a Presenter.  If, say, you have a View which wraps some
Windows
> > control, then it and its Presenter between them (i.e. considered as
a
> single
> > component) will do very little except what the control does --
that's
> what the
> > component is /for/.  So most of the code, in this instance, will be
in
> the View
> > (since most of the code is about wrapping the Windows control's
> appearance and
> > user-interactions), and the Presenter will be very little more than
an
> empty
> > shell.  So, in this case, the View will have most of the code, and
will

> require
> > very little from the Presenter except to be the pluggable bit I
> mentioned above
> > (plus, of course, generic Presenter responsibilities like
> initialisation).
>
> Seconded.  I will go a bit further, by suggesting that a complicated
> view is a code smell.  As Chris suggests, wrapping a control is a
> somewhat unusual situation, so do what you must.  For just about
> anything else (aka a custom view), I recommend trying an
ImagePresenter
> to display the contents.  You can search for graphics, debugging, etc.
> to turn up old posts with my approach to this.
>
> Again, if you must wrap a control, all bets are off.  Note that you
will
> benefit from a good presenter layer.
>
> Have a good one,
>
> Bill
>
> --
> Wilhelm K. Schwab, Ph.D.
> [hidden email]