Skip to content

Lesson 07 Dependency Injection

Roland Zwaga edited this page Jul 31, 2013 · 3 revisions

Please Note: There is a new plugin version available which requires an SDK change, please read for more details:.

In this lesson, you will use Randori Guice, a Randori specific port of Google’s dependency injection framework to inject objects into your mediator. You will see how Randori will recursively resolve dependencies on your behalf and understand the ease at which this allows your application to scale.

  1. Ensure you have the SimpleTest project you created in earlier lessons open in IntelliJ.

  2. Right click on the src package and make a new package called messages.

  3. Right click on the messages package and create a new Randori class named Message

  4. Add two public variables to the Message class. The first being title of type String, and the second being message of type String as follows:

    public class Message {
      public var title:String;
      public var message:String;
    
      public function Message() {
      }
    }
  5. Next build modify the constructor for Message to define a title and a message as parameters and assign them to the instance variables.

    public class Message {
      public var title:String;
      public var message:String;
    
      public function Message( title:String, message:String ) {
        this.title = title;
        this.message = message;
      }
    }

    This is going to be a value object used to contain a Message as it is passed around the application.

  6. Save your file and right click on the messages package and create a new Randori class named MessageGenerator. This class will be a simple factory for making Messages.

  7. Inside of the MessageGenerator class, create a new method named newMessage() which accepts no arguments but returns a Message.

    public function newMessage():Message {
    
    }
  8. Inside of the newMessage() method, create a message object with a title and a message. You could make this static or use a date, as I do in the following code to make it clearly dynamic on the screen.

    public function newMessage():Message {
      var dateString:String = new Date().toString();
      var message:String = "My Message " + dateString;
    
      return new Message("My Title", message );
    }
  9. Save the MessageGenerator class and build this code so you can examine it next.

  10. Open the MessageGenerator.js file from the generated/messages folder. Not the folder contains both MessageGenerator.js and Message.js

  11. Examine the newMessage() method.

    You will see that the code instantiates a messages.Message() object and sets the initial properties as your code indicated.

  12. Next, Examine the getClassDependencies() method.

    You will see that messages.Message is pushed into an Array in this method. This code is generated by the Randori compiler. Effectively, it is indicating that the MessageGenerator class requires the Message class to function and therefore Randori must ensure Message is loaded before MessageGenerator.

    The compiler tracks these dependencies and writes the required functions. Randori ensures they are loaded on your behalf as system objects are created.

    While this is an interesting example, it’s slightly wasteful. Value objects are a useful concept at compile time, but here you are instantiating a Message object simply to hold a value. In JavaScript most would likely just use a generic object for this purpose and save some processor cycles. You will deal with that next.

  13. Return to your Message ActionScript class, you will now add a piece of JavaScript metadata above the class

    [JavaScript]
    public class Message {
      public var title:String;
      public var message:String;
    
      public function Message( title:String, message:String ) {
        this.title = title;
        this.message = message;
      }
    }

    Randori treats each class as if it were decorated with this metadata. However, you can also

    customize the attributes of it to instruct the compiler to treat this class in a different way.

  14. Set three attributes in the metadata. export=“false”,name=“Object”,mode=“json” as follows:

    [JavaScript(export="false",name="Object",mode="json")]
    public class Message {

    This metadata instructs the compiler to do things differently here. First, it instructs the compiler not to export this class, meaning that a JavaScript file will not be created for Message. Next, it instructs the compiler that, anywhere someone reference the Message object, it can be treated as a generic object.

    Finally, the mode instructs the compiler that any instantiations of this object should actually just be treated as though the developer had written JSON for the object.

    Effectively, this means you will have a typed object at compile time that can be refactored, passed and used in a typed way, however, at runtime, it incurs no penalty for this as the compiler will simply instantiate, pass and use a generic object instead.

  15. Save the MessageGenerator class. Perform a build and clean of this code so you can examine it next.

  16. Open the MessageGenerator.js file from the generated/messages folder. You may also note that the Message.js file is no longer present in this folder.

  17. Examine the newMessage() method.

    You will see that the code no longer instantiates a messages.Message() object. Instead it uses a generic object for this purpose and sets the initial values as your code indicated.

  18. Next, Examine the getClassDependencies() method.

    You will note that it is no longer dependent upon the message.Message object.

  19. Close the js files and open your IndexMediator.

  20. In the constructor of your IndexMediator, indicate that you require a MessageGenerator instance to instantiate this class.

    public function IndexMediator( messageGenerator:MessageGenerator ) {
    }
    

    As the entirety of class building in Randori is built on dependency injection, you generally specify the

    classes you require rather than instantiate them and allow Randori to provide these classes.

  21. Create a private variable in the class to hold this instance and assign it in the constructor.

    private var messageGenerator:MessageGenerator;
    ...
    public function IndexMediator( messageGenerator:MessageGenerator ) {
      this.messageGenerator = messageGenerator;
    }
  22. Change your on initialize method to use the messageGenerator to create a new message and pass the values to the showMessage() method.

    override public function initialize():void {
      var messageObj:Message = messageGenerator.newMessage();
      messageWindow.showMessage(messageObj.title, messageObj.message );
    }
  23. Save, build and run your code.

    Your Mediator will now use the injected MessageGenerator to display the message, you will be able to tell this is the case, as the browser output will include a date along with the message.. This is the basis for how mediators will interact with any and all business logic in the system.

  24. At this point it is worthwhile to examine your code and the narration of the events occurring below to understand how Randori works.

    1. Your browser loads index.html.

    2. The browser executes the script blocks loading Randori and RandoriGuice.

    3. The script block with the RandoriBootstrap executes.

    4. Randori recurses the DOM from the point you specified (document in this case)

    5. Randori finds the node with stylesheet/randori.

      • Randori loads and parses that stylesheet.

      • Randori ‘releases’ the stylesheet to the DOM.

        • In practice this means you will see two loads of the same stylesheet if you look in your network tab, however, the second one is pulled from the cache.

    6. Randori encounters the body tag and notices the style requiring an IndexMediator

      • Randori loads IndexMediator.js

      • IndexMediator requires a MessageGenerator

        • Randori loads MessageGenerator.js

        • Randori instantiates MessageGenerator

      • Randori then instantiates the IndexMediator, passing it the MessageGenerator instance

      • Randori provides the IndexMediator instance a reference to the body element

      • Randori recurses further into the DOM

      • Randori finds the div requiring the MessageWindow

        • Randori loads MessageWindow.js

        • Randori instantiates the MessageWindow

        • Randori provides the MessageWindow instance a reference to the div element it manages

        • Randori finishes recursing the DOM

        • Randori calls the initialize() method of the MessageWindow instance

      • Randori provides the IndexMediator instance a reference to the MessageWindow that it instantiated

        (remember, the IndexMediator has View metadata asking for the MessageWindow)

      • Randori calls the initialize() method of the IndexMediator instance.

    7. Randori does nothing. It’s not monitoring or in any other way interacting with the system. It is done until it is explicitly asked to do something again.

    By default, the Randori compiler creates singular files for each equivalent class and loads each dynamically. This allows fantastic scalability, especially in situations like large applications where the

    user’s path through an application is unknown and is unlikely to need every file in your project. As you will see later, you can customize this behavior to decide which files should be left dynamic, and which loaded initially.

The next lesson will dive further into injection by covering contexts, which governs how injection rules are applied and the subsequent lesson will explain how to ask Randori to ‘do something’ again by dynamically loading more content.