In the Flex 3.x world, I adopted the Presentation Model pattern and applied it just about everywhere. Separating the non-view logic made perfect sense, far better than the code-behind approach, and cleaned up the view components wonderfully.

Then Spark came along in Flex 4, and things have changed. The framework now has built-in support for separating the "view" (skin) from the "controller" (host component). As I've been using skinning more and more, I've come to wonder if or how the presentation model approach applies in the Spark skin world.

To try and answer that question, I created a fairly simple but non-trivial test application that creates the same views and behavior in three ways. I've heard each of these three ways mentioned at various times by various people, so I used them as the most commonly proposed options:

  • Supply a presentation model to both the skin and the host component, and let it act as a bridge between the two. In other words, the skin knows nothing about the host component, and the host component knows nothing about the skin. Create the PM as a self-contained set of methods and properties that can be applied to any relevant skin or host component. In essence, if the skin is the "view" and the component is the "controller", the PM is the "model" in this mini-MVC configuration.
  • Supply a presentation model to only the host component, and have the host component push state into the skin. In this case, the skin knows nothing about the host component, but the host component it tightly coupled to the skin.
  • Drop the presentation model approach completely. All behavior and properties are held in the host component, and the skin binds to host properties and invokes methods on the host. In this setup, the host knows nothing about the skin, but the skin is tightly coupled to the host.

After building all of these, there are pros and cons to each approach. And some definitely seem to have more pros or more cons than others. The one thing I tend to focus on is what kind of change and what kind of reuse each approach allows for. If you view the running test application, you'll see I've noted some thoughts on each in the sidebar. View source is also enabled, so if you want to look through the code or download it, feel free.

I'm very interested to hear other people's thoughts on this as well. So if you have an opinion on this, or any issues with how I've built any of these versions, by all means please comment below!

Comments Comments (18) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 2148 Views

Comments

  • # Posted By ash | 7/14/11 3:51 PM

    Mxml views are typically composites and interaction and state between components needs to be managed. So, we move this code into the PM because flat, dumb looking views make us happy inside. The view is coupled to the PM through bindings and we work hard to make the PM know nothing about the view it's trying to manage. Really? How is it possible to manage something you know nothing about? Just because we manipulate the components indirectly, doesn't mean we aren't making assumptions about the components used.

    I'm in favor of dropping PM, it seems like a good compromise. Having views completely decoupled is a great goal for framework and component developers but for app developers? I dunno, views should be fairly cheap to create and throw-away.

  • # Posted By Brian Kotek | 7/14/11 6:44 PM

    Hi, Ash. The main issue I see with eliminating the PM is the fact that any logic moved to the component itself can't be reused. As you may see from the example app, both UserInfoNoPM and UserListNoPMItemRenderer have a number of common needs: properties, event handlers, methods, injections, and so on. But because these are trapped in the Flex component inheritance hierarchy, there's no way to push that logic into a common superclass. That's because UserInfoNoPM extends SkinnableContainer, and UserListNoPMItemRenderer extends SkinnableItemRenderer. That's what I mean about being "trapped" in the component inheritance hierarchy. And this is a simple example. If the common logic were much more complex, that's a lot of duplication.

    On the other hand, as you can see from UserInfo and UserListPMItemRenderer, there is no duplication. It's all part of the PMs that these use. And better, you can see that UserListPM extends UserInfoPM, so it's easy to extend the PMs to add specialization if necessary. Again, that's because the PMs are in a separate, *developer-controlled* inheritance hierarchy.

    The last point is just that using PMs means that neither the skin nor the host have any knowledge of each other at all. Which is a good thing in my book. Without the PM acting as the intermediary "model" for the component and skin, the skin is highly coupled to the component, limiting reuse potential.

    Thoughts? Does that help explain my thinking on this?

    Thanks,

    Brian

  • # Posted By Sam A. | 7/14/11 9:34 PM

    I'm in the middle of my first Flex 4 project so I think I'm still forming an opinion. I've been using no-PM for this project. This was largely due to the fact that I like my packages/classes/code to be very specific in nature and purpose and am very frugal about making things reusable. My tolerance for duplication is also likely higher than others. There is some reused logic that I put into a bean and toss around. So far it's been going well for me.

    Though for a multi-device project I'd definitely go for PM-coupling. PM's and interfaces shared across all devices. Some sharing of view related code. No sharing of skin code. Something like that.

    Regarding Ash's note on framework/component vs app development, I think that's a good point. I imagine larger apps require their own 'framework'. I haven't built one of those (yet). For me overall I think the choice depends on the size of the project and style of the architect. It's good to have choices at least.

  • # Posted By Brian Kotek | 7/14/11 10:28 PM

    Hmmm...yes, my tolerance for duplication is very, very low, and my desire to make things reusable is very high. For me, it's not just about whether the project is multi-platform or not. But even in a standard web app, coupling the skin to the host component reduces the reusability of the skin. So I guess my mental cost/benefit analysis goes something like:

    - The logic has to live SOMEWHERE, be it host component or PM
    - Keeping it in a PM takes virtually no extra effort compared to keeping it in the host component
    - Keeping it in a PM eliminates duplication across host components
    - Keeping it in a PM leaves the skin free of any coupling to the host components
    - Using an independent inheritance tree means it can be extended at will

    So it looks like you gain a lot of benefits and flexibility for very little (essentially zero) cost. I personally LOVE calculations like that. ;-)

  • # Posted By Sam A. | 7/14/11 10:55 PM

    Interesting, the cost doesn't seem so little to me: creating a new class/file, registering it as bean, injecting it. I just like the idea of avoiding creating dozens/hundreds of (extra) files. Then again the more you reuse the less PMs you'd need.

    But I don't think my current project lends itself to much PM reuse. So worst case I'd have as many PMs as I do views. Now that I think about there are 1 or 2 places where reuse would make sense, but largely the views are different enough where reusing PMs would feel forced. Then again my style and preference comes before me and I start talking in circles ... the whole reuse/finding-patterns/modularization topic is a whole other existential topic, heh.

  • # Posted By Brian Kotek | 7/15/11 12:13 AM

    Yeah, I guess to me the cost is really small. Creating a new class takes, what, 5 seconds? Beyond that you're typing the same thing that you would into the host component (less, probably, because you won't have to duplicate it). And adding a bean and injecting it might take an extra 30 seconds. So I'm looking at less than a minute of extra effort to gain the advantages described earlier. So it seems like a pretty easy choice from my viewpoint. However, I appreciate the comments! This is exactly the sort of thing I wanted to hear from different people to see if I might be missing some big drawback!

  • # Posted By Bob Costas | 7/15/11 3:11 AM

    Would love to see the example but I'm getting a framework error:
    Error #2032: Stream Error. URL: http://www.briankotek.com/blog/files/pmskinning/fr...
    Were the dependencies deployed as well?

  • # Posted By Brian Kotek | 7/15/11 8:50 AM

    Hmm, are you using this url: http://www.briankotek.com/blog/files/pmskinning/Sk...

    ?

    That loads and runs fine on my end. Let me know. Thanks.

  • # Posted By ash | 7/15/11 1:08 PM

    Maybe I'm doing it wrong, but my PMs tend to be 1:1 with views. Again, views tend to be unique composites of components and managing their interaction is also unique (ie. after button X is hit, clear this text field and change state of foo to bar). Here's a breakdown of my PM:

    - gets a slice of the model injected
    - expose data for the view to bind to
    - manipulate view component state through bindings
    - expose functions for view to call

    Each function in the PM does nothing more than dispatch an event that a controller picks up.
    So my PM, in addition to my view, tends to be quite dumb. Which makes me question why I need this separation at all.

  • # Posted By Bob Costas | 7/15/11 5:12 PM

    Grr... corporate firewall. Works fine from home. Sorry for the false alarm.

  • # Posted By Brian Kotek | 7/18/11 10:18 AM

    @ash: Yeah, for me, PM's are often associated with more than one view. For example, I may have a UserMasterDetailContainer that has a UserManagementPM injected into it, with properties like userCollection, currentUser, and currentState. The container may have a list state, detail state, and edit state. These can be components, such as UserList (with a grid), UserDetail (details of the selected user), and UserForm (edit form for selected user). The UserManagementPM could be injected into any or all of these to allow easy communication and synchronization of data and state across them all.

    In other words, a PM can be associated with a functional set of related views. Not always, but often.

  • # Posted By Joe Rinehart | 7/18/11 10:43 AM

    I'm entirely -for- use of the PM approach.

    Let's expand on Brian's example: let's say that some permission scheme determines whether or not the user can delete a given contact. Let's also assume that the client now wants a toolbar at the top of the list that presents a delete button. Last, the client wants a "delete" button on the editing form for each contact. The delete buttons are only enabled if the user has permission.

    If we were ditching PM or modeling 1:1 with our views, we'd have to repeat ourselves: either the components (in the case of no PM) or the two separate presentation models would have to maintain the "can this user delete this contact?" semantic. This repetitious code and weaving of what is view behavior logic through each distinct view is what we're trying to move away from, and is easily the mistake I see most often in Flex applications.

    Using -one- presentation model that can be observed by multiple components (and their skins) allows the behavior of the view (it's *model*) to be kept in one place and used by many views. (I was about to type more, but the phrase "standard benefits of decoupling" kinda covers it all.)

  • # Posted By Tom Lee | 7/18/11 4:42 PM

    A few thoughts:

    1) The hostComponent-to-skin relationship is not necessarily 1-to-1. A single hostComponent can be skinned differently in different situations, and the same skin can be used with different hostComponents (if its HostComponent metatag points at an interface instead of a class). In this way, you can get a good deal of re-use out of your hostComponents and skins.

    2) Just because the PM pattern is right for some situations doesn't mean it has to be used everywhere. Use the right pattern for your need. Joe has identified a situation where some state needs to be stored in a shared location - maybe a PM is the right solution for that case, or maybe you just store that one piece of data in a model bean and you don't call it a PM. There isn't one pattern to rule them all (although I don't downplay the importance of keeping your selection of patterns limited and uniform in their application).

    3) The hostComponent has the public API for your (re-usable) component, and should be the way that data gets into your component. The skin is a private member of your component. If you reference a PM from your skin class, you lose the ability to re-use your hostComponent elsewhere without also bringing along the skin and the PM it references. In essence, you are making the skin itself the component, and the hostComponent becomes only an arbitrary hook that is necessary to insert it in the display list.

    4) A good argument can be made that the hostComponent-skin architecture is not really meant for the kinds of composite views that we typically build in Flex (like viewstacks, forms), but rather for more self-contained custom components such as date pickers, etc. You could choose to forego the whole hostComponent-skin thing for larger views, and no one should think the worse of you for it. If that's the way you choose to build your apps, then Presentation Model or one of its brethren is still an essential piece of the design.

  • # Posted By Brian Kotek | 7/18/11 9:46 PM

    Thanks, Tom. Interesting points!

    Regarding 1), I agree that the host component should not be coupled to the skin. And as you point out, you can reuse a skin across different host components by using an interface, just as the skin could be used with different PMs via a PM interface. So the real issue in this case is that without a PM, logic that would have been wrapped up (and reusable) in the PM would be duplicated across different host components.

    For 2 and 4), you touched on uniform application of patterns. I'd re-word that and call it a desire for consistency, and this is something I push for as much as possible. It's certainly true that one could handle things differently in different situations, but the downside there is that the design will vary in ways that are difficult to predict:

    - In the situation Joe described, one might see the the host-skin-PM approach.
    - In other cases, one may see the host-and-skin-only approach, with no PM.
    - And in yet others, one may see components using a PM with no skin at all (which you speak to in your 4th point).

    No one can argue that this wouldn't work, but it's also clearly a variation that an outsider or new developer would have few ways of predicting or of understanding which conditions should result in which approach.

    In contrast, the PM approach is always consistent. Whether one has a component and a skin, or a component with no skin, elements in the view key off bindable properties in the PM, and methods in the PM handle the view-specific logic. It also means that as the design evolves, the PM approach can scale seamlessly from component-only to component-plus-skin, and vice versa.

    On point 3, I'm not sure I agree. Can you clarify what you mean about being prevented from re-using the host component without bringing along the skin and the PM? Since the host has no knowledge of the skin, I'm not sure how the skin would matter. Yes, the component and skin depend on a PM, but it can implement an interface the same way the host can, meaning the PM can be swapped in the same manner. And because the PM can be extended and still maintain type substitution, common logic can be pushed into a superclass. This allows even more potential for re-use, and it can even be done using an abstract type instead of an interface. Conversely, using an interface on the host component is the only option for abstraction from the skin's perspective.

    I'm not sure how a skin that contains "currentState='{pm.currentState}'" means "making the skin the component", but doing "currentState='{hostComponent.currentState}'" does not. What's the difference other than which object hosts the property?

    I don't think that abstracting out the common developer-defined behavior diminishes the role of the component itself. It still brings along all of the inherent SDK behavior for that particular component (component lifecycle, list behavior, layout virtualization, skinability, and all the rest). I'd argue that the SDK itself would benefit greatly from separating and encapsulating component "models". I shudder to think of how much duplication would be eliminated. :-)

    So to sum up, while it is possible to get similar decoupling from skin to host, it seems like PMs bring the additional benefits of a consistent approach with no duplication.

    Whatcha reckon?

  • # Posted By Tom Lee | 7/19/11 9:28 AM

    To clarify on point 3... Let's assume I build a component where the skin gets its data from the PM, and its events are handled in the PM. Also, its state is stored in the PM. I'm using this component in my application and it works so well everyone wants to use it. How can I bundle my component into a library and distribute it? I need to include the hostComponent, obviously - but it no longer has any useful logic, because chances are it's just a SkinnableComponent. Clearly the PM needs to go along with it, because that's what actually contains the business logic of our component now. Also, we can't really expect other people to re-implement our skin just to make use of our logic, pristine though it may be - so we bundle that in as well. So now we have a collection of classes that are not tied together in any way. To fix that, we need to include our dependency injection framework in our library, or mandate that anyone who uses our component must use the dependency injection framework that we used when we wrote the component.

    Also, what is the purpose of even having a hostComponent in this scenario? Since everything important is in the PM, you might as well just do it the Flex 3 way and save yourself the overhead of instantiating an extra UIComponent.

  • # Posted By Brian Kotek | 7/19/11 11:13 AM

    Well in the case of a basic SkinnableComponent some of that may be true, but aren't many (most?) useful components based on something more substantial? List, DataGrid, Form, etc.? In any of those cases, the host component is definitely doing more than just acting as a container. But yes, if the host is really that basic, it may well not require a skin at all. Building the component with no skin might be fine. So the point there is that whether the host is a basic SkinnableComponent or something much more involved, or if there is no skin at all, the approach is the same: bind to PM properties, and invoke PM methods to execute developer-specified view logic.

    On the example of wanting to deploy a component as part of a generic library, that's also totally possible. You don't need to force a DI framework, the component could create it's PM itself, or it could allow one to be set as a public property. This PM can also be set onto the skin, or the skin could grab the PM from the host component. DI would certainly be better, but it doesn't need to be required. In this setup, the user of the library has several options for extension: they can extend the host component, extend the PM (or implement their own), and/or extend or reimplement the skin. In fact, nothing would be stopping someone from building their own skin that doesn't use a PM at all. We could expose public properties on the host component (that may well be bound to the PM internally), and they could take the approach of binding to properties of the host component if they wished. After all, we don't want to force anything on them. They may never know that a PM is in use at all.

    However, we're really talking about an unusual situation. The vast majority of what we do is NOT building libraries for the general public. Doing that requires making a lot of decisions unique to that goal, beyond the question of whether we're using a PM, mainly building the component with the intent that there is no control at all over the environment where it is used. Based on what I touched on above, I still think it can work fine, even though releasing generic public libraries isn't normally what developers are doing.

  • # Posted By ash | 7/22/11 6:05 PM

    @Joe "If we were ditching PM or modeling 1:1 with our views, we'd have to repeat ourselves: either the components (in the case of no PM) or the two separate presentation models would have to maintain the "can this user delete this contact?" semantic."

    Wouldn't this be a prime example of cross-cutting? And if swiz supports AOP, wouldn't the need for a PM be reduced?

  • # Posted By Brian Kotek | 7/25/11 9:43 AM

    For some things, possibly. But my intent with this discussion isn't Swiz-specific. So I wouldn't count AOP as a universal option yet because it would force a specific framework on people.