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 |
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 |
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 > 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 |
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 > 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 |
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 |
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] |
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 |
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 |
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 > > 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 > 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] |
Free forum by Nabble | Edit this page |