Saturday, October 9, 2010

Quick Dive into Parsley (Decoupled Bindings - Part 7)

This is part of a series of articles that should get you up and started using Spicesfactory's Parsley.
This is part 7.
In Part 3, I showed how to set up Parsley and showed a basic injection example.In Part 4, I showed how to send messages using Parsley that do not extend the Event class.
In Part 5, I showed how to send messages using Flex Events.
In Part 6, I showed nested contexts and object lifecycle methods.

This article builds upon what was covered in the previous examples, so if you haven't seen them, I encourage you to start with at the beginning of this series.  This article has two examples of how to use the Decoupled Bindings feature of Parsley.
 
Decoupled Binding

   Decoupled binding is a Parsley Feature that provides binding in a decoupled way.  It is not exactly like Flex bindings, as you can’t use it in an MXML object declaration, but it does allow you to create bindings between objects on different components.  Essentially, if you update one, the other one is immediately updated in a decoupled way.  That means that the binding is identified by the object type.

   Truth be told, it acts more like a regular injected object than regular bindings.  So what is the difference between “Decoupled Binding” and regular injection?  Well there are a few.  First “Decoupled Binding” does not require that the “shared” object be manage by the framework, which means it takes less resources.  Second, “Decoupled Binding” objects can be added to the context at any time and not limited to be added at context creation time.  Now I won’t go into all the details regarding “Decoupled Binding” since there is a whole section on the docs (http://www.spicefactory.org/parsley/docs/2.3/manual/) regarding this, and it’s pretty detailed.  You can go straight to section 5 here (http://www.spicefactory.org/parsley/docs/2.3/manual/bindings.php#intro) .

   One useful application of decouple bindings is to publish a current selected object to be used in a master/detail scenario.

Ok, so how do they work?

Well, you have three basic tags.  [Publish], [PublishSubscribe] and [Subscribe].
The [Publish] tag is to be used when you only have ONE publisher.  Only one component is going to update the value, all others are going to be subscribers (readers).  This is basically one way binding.
The [Subscribe] tag is for readers of the value.  They want to be updated when the Publishers updates the value, similar to read only.  You can have multiple subscribers to any publisher.
The [PublishSuscribe] tag allows you to have multiple publishers, and that means that a publisher can also be updated by some other publisher.  They are basically both, a publisher and a subscriber.  You can’t combine a [Publish] with a [PublishSubscribe] on the same type object.  If you plan to have more than one publisher on the same type, use [PublishSubscribe] on all of them.

You essentially place the [Publish] tag infront of any variable declaration that you wish to be a publisher.  Something like:

[Publish][Bindable]
public var myBroadcastingVar:SharedValueObjectClass;

When used in Flex, [Publish] and [PublishSubscribe] must also use the [Bindable] tag.

Subscribers work similarly:

[Subscribe]
public var myBroadcastedVar:SharedValueObjectClass;

Subscribers are not required to be tagged with [Bindable].

One of the great things about Decoupled Binding in Parsley is that it also can be set to use a local scope context or global (default), somewhat like messaging as explained in the previous example.  There is one difference though.  In messaging, the scope is decided only by the message handler.  With decoupled bindings the scoped is selected by the publisher and the subscriber.  You can read more about it in the docs.  You would use something like:

In the Publisher :
[Publish(scope="window")][Bindable]
public var selectedContact:Contact;

And in the subscriber:
[Subscribe(scope="window")]
public var selectedContact:Contact;


You can ask the framework to manage the published object; by default it is not managed.  To have the framework manage the object for you, you would use [Publish(managed="true")].  You can read more about this in the docs.

In this case I’m going to do two examples for the price of one.  The fist example used One Publisher, and the second example uses two publishers.

Decoupled Binding example with one Publisher

Here is the working example with view source enabled.



Here is this structure of the project.
This project is fairly simple. It has two view components called PublishView and SubscribeView.  There is also a DecoupledVO class which is the object that is going to be published and subscribed. 
Let's look at the code.  First the Value Object that will be published.

Here is DecoupledVO.as:

package VO
{
    /***************************************************************
     * This is a simple class that will hold a String that will be
     * shared among different views.  The reason to encapsulate the
     * string into a VO is that it makes it possible for Parsley to
     * identify the object by type.  You can also use Ids.  Refer
     * to Parsley docs. 
     * http://www.spicefactory.org/parsley/docs/2.3/manual/bindings.php#intro
     * 
     * For more Parsley examples visit:  artinflex.blogspot.com
     * */ 
    
    public class DecoupledVO
    {
        [Bindable]
        public var sharedString:String;
    }
}

Nothing special here.  A very simple class with a bindable string property.  This is the object that will be Published.  Why not publish just the string? Well, then you would only be able to publish one string, or have to use id's to identify each individual string.  Parsley knows how to match Publishers and Subscribers through the data type.

Ok, let's look at the publisher.

Here is PublishView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--  This view will be set to do a decoupled binding and it will Publish the value from 
  a textInput to be able to be read by other components 
  For more examples visit:  artinflex.blogspot.com
-->
 <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="210" height="60" borderStyle="solid" cornerRadius="3" paddingLeft="5" xmlns:parsley="http://www.spicefactory.org/parsley">
    <mx:Label  text="Publish" fontWeight="bold"/>
    <mx:TextInput width="200" id="publishingTextInput" />    
    <!--In Flex 3, for binding to be on output, we must specify it with the binding tag-->
    <mx:Binding source="publishingTextInput.text" destination="decoupledVO.sharedString" />
    
    <!--  Configure is how you tell Parsley that you want this view to become 
    part of the Context and participate in all of Parsley’s features.
    For the Publish / PublishSubscribe / Subscribe tags to be seen by Parsley
    this view must be managed. -->    
    <parsley:Configure />
    
    <mx:Script>
        <![CDATA[
            import VO.DecoupledVO;

            /*********************************************
             *     The object that will be published should have its own type to be
             *     identifiable by Parsley without ambiguity.  Alternatively you 
             *  can use IDs.  On the publishing side, Publish objects MUST also
             *  be tagged with Bindable in Flex apps.
             */ 
             [Publish]
            [Bindable]
            public var decoupledVO:DecoupledVO = new DecoupledVO();
            
        ]]>
    </mx:Script>
    
</mx:VBox>

   This component has a TextInput that is bound to the shared string of our object that we are going to publish.  Note that you must instantiate the object that will be published, most likely with new,  you control the creation, and destruction of the object.  If you wanted to have the framework manage the object you would use [Publish(managed="true")].  Note that having the framework manage the object doesn't relieve you from assigning an instance yourself.  With decoupled bindings the framework will not create the instance for you.  The advantage of having the framework manage the object for you is that the object will be able to participate in other Parsley features, such as injection and messaging.

Ok, now let's look at the subscriber.

Here is SuscribeView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--  This view will be set to do a decoupled binding and it will Subscribe the value
  that it receives from other components and set it to a textInput.  Note that it is
  really decoupled, as it doesn't have any reference to the publishing object/view.
  For more examples visit:  artinflex.blogspot.com
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="210" height="60" borderStyle="solid" cornerRadius="3" paddingLeft="5" xmlns:parsley="http://www.spicefactory.org/parsley">
    <mx:Label  text="Subscribe" fontWeight="bold"/>
    <mx:TextInput width="200" id="subscribeTextInput" text="{decoupledVO.sharedString}" />
    
    <!--  Configure is how you tell Parsley that you want this view to become 
    part of the Context and participate in all of Parsley’s features.
    For the Publish / PublishSubscribe / Subscribe tags to be seen by Parsley
    this view must be managed. -->    
    <parsley:Configure />
    
    <mx:Script>
        <![CDATA[
            import VO.DecoupledVO;

            /*********************************************
             *     The object that will be subscribed should have its own
             *  type to beidentifiable by Parsley without ambiguity.
             *  Alternatively you can use IDs.  Bindable is not required
             *  on the Subscribe, but since I bound it to the TextInput
             *  I must include Bindable.
             */ 
             [Subscribe]
             [Bindable]
            public var decoupledVO:DecoupledVO;
            
        ]]>
    </mx:Script>
    
</mx:VBox>

   SubcribeView has a TextInput bound to the same property as PublishView.  One thing that is important is that you should not assign an instance to the var that is tagged as the subscriber, as the publisher will pass the reference to it from its own var.  But your code MUST be aware that the subscribed var can have the value of null.  There is no guarantee that the value of that var (in this case decoupledVO) will not be null for some period of time.

The main file just has both views.

Here is ParsleyDecoupledBindingsExample1.mxml:

<?xml version="1.0" encoding="utf-8"?>

<!--
This example demonstrates Parsley's Decoupled Bindings.  
For more examples visit:  http://artinflex.blogspot.com
-->

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    xmlns:parsley="http://www.spicefactory.org/parsley" 
    xmlns:views="views.*"
    height="144" width="230">
<!--    
    ContextBuilder is how you tell parsley to manage objects that are not Views. 
    In this case I'm instructing Parsley to create a context.  No config file was defined
    because we are not asking Parsley to manage any particular object.  We just need to
    create at least one context.
-->
    <parsley:ContextBuilder />
    <mx:VBox x="10" y="10" height="100%">
        <views:PublishView>
        </views:PublishView>
        <views:SuscribeView>
        </views:SuscribeView>
    </mx:VBox>
    
</mx:Application>


Now let's look at the example with two publishers.

Decoupled Binding example with two Publishers

   The previous example assumes that you only have one potential publisher, and remember that when you use [Publish] you can only have one publisher per datatype within the same scope.

  So what happens if you wish to have two publishers?  Well you use [PublishSubscribe] and in fact your publisher is also a subscriber.

Let's look at the example running (view source is enabled).



Here is the structure of the project:
   This example has three views. One that acts as a Publish only, one that is a Publisher and Subscriber, and a Subscriber only. The keyword here is "acts", since it does use PublishSubscribe, but through my own code it behaves as it was only a Publisher.  Other than that it is basically the same as the above example.
   Let's look a the code. I won't include the code to the DecoupledVO.as as it identical to the example above.

   So what does the PublishView.msml look like?

<?xml version="1.0" encoding="utf-8"?>
<!--  This view will be set to do a decoupled binding and it will Publish the value from 
  a textInput to be able to be read by other components. 
  For more examples visit:  artinflex.blogspot.com
-->
 <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="210" height="60" borderStyle="solid" cornerRadius="3" paddingLeft="5" xmlns:parsley="http://www.spicefactory.org/parsley">
    <mx:Label  text="Publish" fontWeight="bold"/>
    <mx:TextInput width="200" id="publishingTextInput" />    
    <!--In Flex 3, for binding to be on output, we must specify it with the binding tag-->
    <mx:Binding source="publishingTextInput.text" destination="decoupledVO.sharedString" />
    
    <!--  Configure is how you tell Parsley that you want this view to become 
    part of the Context and participate in all of Parsley’s features.
    For the Publish / PublishSubscribe / Subscribe tags to be seen by Parsley
    this view must be managed. -->    
    <parsley:Configure />
    
    <mx:Script>
        <![CDATA[
            import VO.DecoupledVO;

            /*********************************************
             *     The object that will be published should have its own type to be
             *     identifiable by Parsley without ambiguity.  Alternatively you 
             *  can use IDs.  On the publishing side, Publish objects MUST also
             *  be tagged with Bindable in Flex apps.
             *  In this case, since I wish to have multiple publishers of the same
             *  object type, I can't use Publish.  Only ONE object can be set to
             *  Publish.  Therefore I must use PublishSubscribe.  I limit it to
             *  Publishing only through the binding of the TextInput being one way.
             */ 
             [PublishSubscribe]
            [Bindable]
            public var decoupledVO:DecoupledVO = new DecoupledVO();            
        ]]>
    </mx:Script>    
</mx:VBox>

   Well, not much changed from the example above that had only one publisher.  The only difference (besides the comments) is the [PublishSubscribe] tag that replaced the [Publish] tag.  When you run the example you will notice that the TextInput doesn't get updated in the PublishView.  Technically speaking, when the other PublishSubscribeView updates the DecoupledVO, the local decoupledVO object in this view, does get updated, but the TextInput doesn't, simply because I set the binding from the TextInput to the decoupledVO as a one way road, simple trick (take a look at the <Binding> statement.

  SubscribeView is identical to the previous example, nothing changed there.

  Ok, so now let's look at the PublishSubscribeView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--  This view will be set to do a decoupled binding and it will Publish and
  subscribe the value from a textInput to be able to be read by other components
  and receive its value from another component. 
  For more examples visit:  artinflex.blogspot.com
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="210" height="60" borderStyle="solid" cornerRadius="3" paddingLeft="5" xmlns:parsley="http://www.spicefactory.org/parsley">
    <mx:Label  text="Publish and Subscribe" fontWeight="bold"/>
    <mx:TextInput width="200" id="publishingSubscribeTextInput" />    
    <!--In Flex 3, for binding to be on output, we must specify it with the binding tag-->
    <mx:Binding source="publishingSubscribeTextInput.text" destination="decoupledVO.sharedString" />
    <mx:Binding destination="publishingSubscribeTextInput.text" source="decoupledVO.sharedString" />
    
    <!--  Configure is how you tell Parsley that you want this view to become 
    part of the Context and participate in all of Parsley’s features.
    For the Publish / PublishSubscribe / Subscribe tags to be seen by Parsley
    this view must be managed. -->    
    <parsley:Configure />
    
    <mx:Script>
        <![CDATA[
            import VO.DecoupledVO;

            /*********************************************
             *     The object that will be published and subscribed should have
             *  its own type to be    identifiable by Parsley without ambiguity.
             *  Alternatively you can use IDs.  On the publishing side,
             *  Publishing objects MUST also be tagged with Bindable in Flex apps.
             */ 
             [PublishSubscribe]
             [Bindable]
            public var decoupledVO:DecoupledVO = new DecoupledVO();
            
        ]]>
    </mx:Script>    
</mx:VBox>

   The PublishSubscribe view is nearly identical to the PublishView, besides the properties being renamed, the main difference is that binding is set to be bi-directional.  This makes the TextInput get updated when the decoupledVO get's updated.

  A very important note:  When you have have one publisher it is pretty obvious which view must instantiate the original object that gets passed by reference to all the subscribers.  When you have multiple PublishSubscribe elements on different views, it is not so obvious.  Technically speaking only one needs to create the instance and all others will receive a reference to that instance, but it is not always obvious which one should be the one with that responsibility, as this example demonstrates.  You can technically figure it out, it should be the last one that joins in, as it will publish it's own instance last and hence it will pass it's reference to all others, and replacing what ever they had.  If you want to be on the safe side you can just assign some instance to all the PublisherSubscribe elements as I did in this example.  Eventually the instances not used will get garbage collected anyways, as there will no longer be a reference to them.

  This is a drawback of using decoupled bindings in comparison with regular Parsley managed objects.  Decoupled bindings does not go through the full sanity checks that regular context created objects do.  If you have a view with a PublishSubscribe tag that joins in at a latter time, it will replace whatever value was already being shared.  I have created an example that shows this behavior.  I have source enabled, but I won't go into the details of the example, it's just so you can play with it.

Here is that example with view source enabled:



Well I certainly hope that these examples were useful, more to come soon.

Next in the series is: Parsley's Dynamic Command and Dynamic Objects.

11 comments:

  1. Excellent working examples.

    This is the quickest way to get up and running with Parsley.

    (p.s. the [Subscriber] tag in your post should be [Subscribe]?)

    ReplyDelete
  2. Thanks!
    Yes, it should be [Subscribe]. I've fixed it. Thanks again.

    ReplyDelete
  3. Thanks Brett, that's a pretty cool site you have there. http://separ8.net/

    ReplyDelete
  4. Well, so I guess using this over standart DI can sometime make the code more short and "beautiful", but seems to be a lot harder to use it. I'll stick to DI for now, but anyway the article was usefull, because now I know it exists, and maybe later I'll recognize a case calling for this feature.
    Thanks !

    ReplyDelete
  5. I was wondering how fast is this Decoupled Binding compared to standard Flex binding. So I found a post which compares performance of different binding techniques in Flex:
    http://www.screenshot.at/blog/2009/04/18/databinding-under-the-hood-part-1-performance/

    The test listed in that test was perfect starting point for testing Parsley's binding mechanism. The test was modified so it didn't bind Label.text to variables, but DecoupledVO variable instead (as used in your examples). Only three tests have been modified in order to use DecoupledVO, and one more test was added (results for 50,000 iterations are listed in the parentheses):
    1) direct property setting (227 ms = fastest)
    2) BindingUtility (912 ms = +300%)
    3) event dispatching (472 ms = +100%)
    4) NEW - Decoupled Binding (289 ms = +27%)

    The Decouped binding was only 27% slower than setting the properties directly. Thats really impressing result compared to other "fast" binding methods which were tested!

    ReplyDelete
  6. Great! Thanks for posting your results. It does show how much faster they really are.

    Some things do have to be taken into consideration. For it to take 912 ms using BindingUtility, you needed to dispatch a binding event 50,000 times. While BindingUtility may be 3 times slower than Decoupled Binding, on normal most case use, that difference is usually imperceptible.

    You usally don't need to have 50,000 binding dispatches in less than one second, and if you run into that case, you should reconsider your design, to check if your REALLY need more than 100 binding dispatches in one second. Even if you find a more efficient way to do it, is it really necesary?

    Regardless, Decoupled Bindings are faster, which is good, but my point is that you should not complicate your project unnecesarly for a performance gain that while impressive (300%+) it happens so not often that it will go unnoticed.

    ReplyDelete
  7. You are absolutely correct. This was just a simple test which didn't take all the aspects in the consideration.

    The only problem so far I found in decoupled binding is that it's DECOUPLED :) In some special cases than might not be a good thing.

    Let's consider an example in which one would need want to bind item renderers to corresponding value objects. In such a scenario Parsley binding would require a new context to be created for each item renderer - value object pair. If all the instances were in the same context, one VO's publisher would propagate the value change to all the item renderers.

    Flex Binding on the other hand couples the VO-item rednerer pair, so that changes in VO are propagated only to it's corresponding value object.

    Is there any other way to solve this problem?

    P.S.
    In my project I'm using a three tiers of objects: Value Objects, Presentation Model objects and Item Renderers. PMs will be bound to VOs, while IRs will be dynamically bound by hand to PMs.

    ReplyDelete
  8. I don't think decoupled bindings where meant to replace bindings in the way I think you are thinking.

    What is holding your data? The PM of each item renderer?

    Why not have one model that holds the state of your data, which your item renderers pull data out of. That way the context only holds one object that holds all your data.

    This way the only binding necessary is the "visible" item renderers. When data is updated, you send a message to the visible item renderers that they need to refresh their data, which will reduce your dispatched events.

    Even better, if your updates come in data sets, you don't even have to trigger the binding (refresh) until the dataset has been processed entirely by the model, only dispatching one event. This can also be accomplished with regular flex binding when you used "named" binding.

    ReplyDelete
  9. I can not view source.Thanks

    ReplyDelete
  10. Hi Mark,

    You are probably using Google Chrome. It's a known issue. Please use any other browser and you will be able to.

    ReplyDelete