Signals Best Practices

walpolea's Avatar

walpolea

16 Jul, 2013 04:08 PM

I'm just getting into RL2, and really liking how signals simplifies a very event-heavy framework. However I have seen signals being used in 2 ways and I'm wondering what the difference is and which would be the best practice?

Signal Usage 1:

signalCommandMap.map( StartupSignal ).toCommand( StartupCommand );

use the SignalCommandMap to map a signal to a command.

Signal Usage 2:

injector.map( StartupSignal ).asSingleton();

add the signal class to the injector so everyone can have access to it.

I would assume usage 1 is the best practice and preferred way to do things, however I have also seen prolific use of usage 2 and wanted to know if you would consider it a bad practice or not. The benefit of usage 2 that I see is mediators can directly listen for those signals. Hope someone can shed some light for me. Thanks!

  1. 1 Posted by khaneron_898 on 16 Jul, 2013 07:05 PM

    khaneron_898's Avatar

    I also had a question about signalCommandMap in RL2

    ex:
    using Event system :
     in AppConfig class we can declare something like this
      
    contextView.view.stage.addEventListener(Event.RESIZE ,handleResize);

      function handleResize(ev:Event):void { dispatcher.dispatchEvent(ev);}

    and in some mediator we can add
     eventMap.mapListener(eventDispatcher, Event.RESIZE, handleResize, Event);

    but using signalCommanMap , I don't know how to use it since it's looks like signalCommandMap should map some Signal Class to command

  2. 2 Posted by walpolea on 16 Jul, 2013 08:52 PM

    walpolea's Avatar

    I think I figured this out. Great question, as I kind of knew where to start but was fuzzy on this as well... here is the solution to this that worked for me:

    In AppConfig:

    `signalCommandMap.map( ResizeSignal ).toCommand( ResizeCommand ); contextView.view.stage.addEventListener( Event.RESIZE, handleResize );

    private function handleResize( e:Event ):void {
    //this is the only way I know how to access your Signal instance from the Config injector.getInstance( ResizeSignal ).dispatch( contextView.view.stage ); }`

    Then your signal class looks like this:

    `package { import org.osflash.signals.Signal; import flash.display.Stage;

    public class ResizeSignal extends Signal {
    
        public function ResizeSignal() {
                        //This is the type of class that dispatch passes and is available to inject into your command
            super( Stage );
        }
    }
    

    }`

    Then finally your command:

    `package {

    import robotlegs.bender.bundles.mvcs.Command;
    import flash.display.Stage;
    
    public class ResizeCommand extends Command {
    
        [Inject]
        public var stage:Stage; //you can inject this because of your Signal definition
    
        override public function execute():void {
            trace( "resize command", stage.stageWidth, stage.stageHeight );
    
        }
    }
    

    }`

    Hope this helps, also would like any experts out there to validate this solution.

  3. 3 Posted by khaneron_898 on 17 Jul, 2013 04:03 AM

    khaneron_898's Avatar

    Thanks for the solution but I think I'll use your Signal Usage 2 in this case since I think I can directly Inject the signal to mediator and listen to it without need to declare the command , and btw from example
    https://github.com/lidev/robotlegs2-signals-feathers-flickr-example/blob/master/source/AppConfig.as ,
    I think he use usage 1 for signal that will execute something , and usage 2 for signal that doesn't need to execute command,
    that's make me consider usage 2 is not a bad practice

  4. Support Staff 4 Posted by creynders on 17 Jul, 2013 07:10 AM

    creynders's Avatar

    To clear some things up: it's a good idea to always explicitly map the signal as a singleton with the injector (usage 2) even when you map the signal to a command with the signal command map (usage 1).
    The signal command map actually does (2) behind the scenes when you tell it to do (1), if no mapping of the signal with the injector was found.

    However, not mapping the signal explicitly with the injector can easily lead to bugs.
    Let's say you map a signal to a command with the signal command map. You inject the signal into one mediator, in order to be able to trigger it, but also into another mediator since it needs to listen to it.
    If you no longer need the command and remove the mapping, the compiler will not complain, but you'll get run-time errors when the views of the mediators are added to the stage, since there's no longer no mapping whatsoever of the signal with the injector.

    Also, it's more legible and clear, if you have one central place where all of the signals are mapped to the injector, regardless of whether they're mapped with the signal command map or not.

    That said, all of the above is one of the reasons why I don't like to use signals for system-wide messaging. Personally I always use events system-wide and restrict signals to loose coupled messaging between two parts: e.g. view to mediator and vice versa, or parser to service and vice versa.
    The major advantage of signals IMO is that you can enforce the messaging behaviour through an interface, but this only happens when one class has a direct dependency on the interface of another class, which is never the case when you use signals system-wide.

  5. 5 Posted by walpolea on 17 Jul, 2013 01:35 PM

    walpolea's Avatar

    Thanks for the insight, that helps a lot!

  6. 6 Posted by khaneron_898 on 17 Jul, 2013 04:31 PM

    khaneron_898's Avatar

    Thanks, much more clear now

  7. Ondina D.F. closed this discussion on 23 Jul, 2013 03:43 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac