New location

May 12, 2007

This Blog has moved to http://www.thomas-holl.com/wordpress. New articles will only be posted at the new location.


The Humble MXML

April 29, 2007

UPDATE: A new version of this article/blog

While thinking about how to separate logic and user interface code on my current Flex project, I rerun into Michael Feathers great article about the “Humble Dialog Box”. I used this approach a couple of years before on a C++ project, was happy with it and I am now implementing it again. This article is about how to realize this separation in a Flex/MXML environment.

The basic idea of separating logic and ui code is propably as old as the first graphical user interfaces itself. Even older is the tendency to “just add the cool new feature” then “applying a fix here” and before your realize it, you’re stuck with the ui class handling everything. To cite from the article: “It is easy to just override an event from a component and drop your interaction logic right there in the dialog box class.”

And the Flex/MXML ecosystem is no exception: As easy as Flex makes it to chum out MXML code that looks and work like a charm, it is also really easy to be led into a design which puts everything into the MXML file itself. The examples by Adobe are leading you exactly there. To be fair, their goal is coming to the point of showing off what the Flex Framework can do and not educating you about proper practices.

Example

I’ll describe the concepts with my component ImageManager as an example. First a screenshot of the component:

ImageManager component

ImageManager will provide the user a list of tags (the list on the left hand side) and (depending on which tag was chosen by the user) display a TileList of Images according to the tag. Search tags can also be entered manually by the user via the TextInput field. The real search is done on a Ruby on Rails server in the background (but that’s another topic). If a user clicks on a tag, the TextInput field will be updated with the search tags (could be words more than just the tag from the list).

Concept and Realisation

The Humble Dialog Box (in this case The Humble MXML) suggests to refactor the .MXML file into 3 entities which each have exactly one reason to exist (and only one reason to change):

  1. ImageManager.mxml: The user interface itself, the MXML class. This class should contain mostly and almost no code in the tag
  2. ImageManagerLogic.as: The class handling the logic and only the logic. It communicates with the interface via the 3rd element, the interface
  3. IImageManager.as: The protocol to transfer information from the logic to the user interface

Taking the logic out of the MXML and putting it into a separate class is pretty obvious. The ImageManager.mxml uses the ImageManagerLogic directly via a member-instance.

The interesting point in “The Humble MXML” is how to do the communication from the logic to the mxml. We don’t want the logic to depend on the user interface, so we obviously can’t reference the MXML file from the ImageManagerLogic. This is where the interface comes in: Put all callbacks from the logic into the IImageManager interface. In this example, when a user clicks the tags the TextInput has to be filled with the tags calculated by ImageManagerLogic. The IImageManager then looks like this:


interface IImageManager
{
	function set searchTags(t:String):void;
}

The ImageManagerLogic uses the IImageManager by calling _view.searchTags = …:


class ImageManagerLogic
{
	private var _view : IImageManager;

	public function set view(v:IImageManager):void
	{
		_view = v;
	}

	public function selectTag(index:int):void
	{
		_view.searchTags = tags[index].label;
		// then go on and trigger the search
	}

	// various private methods hidden

	public function searchForTags(tags:String):void
	{
		// do the search
	}
}

In the implementation of ImageManager.mxml the following happens:

  • The ImageManager.mxml implements the IImageManager interface (implements=”IImageManager”) and tells the logic to use it (logic.view = this)
  • The implementation of the IImageInterface just puts the tags into the TextInput field
  • All event handlers just call the logic directly (e.g. logic.searchForTags(…))

 

   1:  <mx:Module implements="IImageManager" initialize="logic.view = this">
   2:      <mx:Script>
   3:          <![CDATA[
   4:              ImageManagerLogic logic;
   5:  
   6:              // implements IImageManager
   7:              public function set searchTags(tags:String):void
   8:              {
   9:                  inputSearchTags.text = tags;
  10:              }
  11:          ]]>
  12:      </mx:Script>
  13:  
  14:      <mx:HDividedBox width="100%" height="100%">
  15:          <mx:List width="200" height="100%" id="list"
  16:                  dataProvider="{logic.tags}"
  17:                  click="logic.selectTag(list.selectedIndex)" />
  18:          <mx:VBox width="100%" height="100%">
  19:              <mx:HBox>
  20:                  <mx:TextInput id="inputSearchTags"
  21:                          enter="logic.searchForTags(inputSearchTags.text)"/>
  22:                  <mx:Button label="search"
  23:                          click="logic.searchForTags(inputSearchTags.text)"/>
  24:              </mx:HBox>
  25:              <mx:TileList dataProvider="{logic.images}" />
  26:          </mx:VBox>
  27:      </mx:HDividedBox>
  28:  </mx:Module>

So, what do you get?

  • Clear design: It is pretty obvious where to put stuff and where to look for it. If it is how something should look like (layout, formatting), it belongs into the .mxml file. If it processes data or calculates stuff, it belongs to the logic
  • Reusability: It is easy to e.g. create an advanced ui/dialog by just wiring a new MXML class with the existing logic code. Or use the logic somewhere else, perhaps reusing it in another, more complex logic block
  • Testability of the logic (by using a mock object for the view): Because the MXML just contains call to the logic if something happens, it is very probably the bug is in the logic – there you can have automated tests and need not to worry about the gui

Some things to consider

  • The only code allowed in the markup part of the .MXML file are calls to the logic layer. If you need complex calculations of input data (e.g. combining strings) for the logic layer this could be a sign that this functionality belongs into the logic. If it really doesn’t, put it in the part
  • If there are no callbacks from the logic layer to the gui, you don’t need the interface IImageManager

What does it cost?

  • You end up with 3 files instead of having just one. For small projects this may look like overkill, but even small projects can grow up real fast
  • Having to abstract the communication between the logic and the ui via an interface. For more complex patterns, this can be a real pain, but it is better to think about the protocol before instead of trying to seperate logic and ui at a later time when things got messed up

Opinions?

  • Do you consider this overkill?
  • Which unit testing frameworks do you use? Experiences?

Follow

Get every new post delivered to your Inbox.