Sunday, January 17, 2010
Anemic domain model in Flex frameworks
The biggest of these concerns was the fact that both frameworks in Flex promote what Martin Fowler calls the 'Anemic model'.
http://martinfowler.com/bliki/AnemicDomainModel.html
I do know that most frameworks in Flex are designed for enterprise applications where the backend has the domain logic to manipulate the data and the frontend merely displays the data returned. When you look at the big picture, Flex applications are only the view of the entire application with the controller and model on the server. A separate domain model on the client would only make things more complicated.
On the client MVC, model is used exclusively for storing client state. The client is merely determining which backend API to call in response to a user action thus the logic simple enough to reside on the controller as a transactional script while the more complex, UI logic is solved by using the presentation model pattern. Sometimes user interface elements can be have very complex logic which is then encapsulated in components.
Now that was the typical case of Flex being used for enterprise applications. If you need a self-contained AIR application with SQLite or don't have the liberty of adding/changing the backend services, everything starts to fall apart. A lot of domain logic would end up in the controller you either end up with a copy&paste mayhem or have many "helper" objects containing the logic that really belongs in the domain model. These are essentially procedural scripts manipulating the model that run in response to an event and go againt the whole idea of OOP.
For example, to update a Person model using a CakeVO using anemic model.
EatCakeCommand.as
...
if(person.foodVolumeInStomach + cake.volume <= Person.MAX_STOMACH_VOLUME)
{
person.foodVolumeInStomach += cake.volume;
person.foodInStomach.addItem(cake);
if(person.foodVolumeInStomach > Person.MAX_STOMACH_VOLUME * 0.7)
person.hungry = false;
if(cake.isOff)
person.sick = true;
}
...
Instead of encapsulating the logic associated with eating inside the person and simply doing:
EatCakeCommand.as
...
person.eat(cake);
...
In these circumstances I feel that Swiz does not provide a good solution so I either have to ignore the Swiz's guideline or to use PureMVC and give up easy binding and/or dependency injection available in Swiz (I use PureMVC in Flash projects).
Maybe it's time I consider using a third generation frameworks like Robotlegs.
Friday, June 5, 2009
Creating wordwrapping LegendItems for Charts
I saw an interesting forum post in Adobe Forums today
ttp://forums.adobe.com/post!reply.jspa?message=2012407
It was about a person wanting to wrap the text but the standard Legends not allowing you to do so. I imagined the solution was something easy so I tried out myself and... NO, it was not as easy as I first thought. I extended LegendItem and managed to get the text wrap and the LegendItem measure itself somewhat correctly but I had a problem with it setting itself to the largest item there is.
Out of my own interest, I took on the problem personally. After couple of hours later I think I fixed it!!?!?!....
View source is enabled so you can see exactly what I have done
I finally discovered that problem was actually in the Legend Class will ALWAYS space out the LegendItems evenly every time!!! So natually I tried to extend Legend class but there were so much private variables it was impossible. So in order to overcome this issue I had to make a copy of the Legend class to src/mx/charts/Legend.as and everything that it references to replicate its behavious exactly. This way, flex compiler will take priority over the actual Legend Class in the framework.
Then I modified some of the code so it will handle the case when wrapping items were required.
The percentageHeight of each item were always being set to 100
Legend.as
private function addLegendItem(legendData:Object):void
{
var c:Class = legendItemClass;
var newItem:LegendItem = new c();
newItem.marker = legendData.marker;
if (legendData.label != "")
newItem.label = legendData.label;
if (legendData.element)
newItem.element = legendData.element;
if ("fill" in legendData)
newItem.setStyle("fill",legendData["fill"]);
newItem.markerAspectRatio = legendData.aspectRatio;
newItem.legendData = legendData;
newItem.percentWidth = 100;
newItem.percentHeight = 100;
addChild(newItem);
newItem.setStyle("backgroundColor", 0xEEEEFF);
}
so I eliminated this implicit perventHeight assignment in the WrapLegendItem class with a setter
// overriding to ALWAYS SET TO percentHeight to ZERO
override public function set percentHeight(value:Number):void
{
super.percentHeight = 0;
}
Now, I fixed the layoutVertical() function further down in the Legend class.
The lines with --- are the additional lines I put into the Legend class. I wanted to keep the modification to minimum so it wouldn't affect its use for non-wrapping LegendItems.
--- // I add flag a for a wrapping LegendItem
--- var wrapTextItem:Boolean;
if (child.percentHeight > 0)
{
childHeight = Math.min(cellHeight,
cellHeight * child.percentHeight / 100);
}
else
{
--- // This condition is only true if it is a wrapping text LegendItem
childHeight = child.getExplicitOrMeasuredHeight();
--- wrapTextItem = true;
}
child.setActualSize(childWidth, childHeight);
// Align the child in the cell.
var xOffset:Number =
Math.floor(calcHorizontalOffset(child.width, horizontalAlign));
var yOffset:Number =
Math.floor(calcVerticalOffset(child.height, verticalAlign));
child.move(xPos + xOffset, yPos + yOffset);
if ((i % rowCount) == rowCount - 1)
{
yPos = vm.top;
xPos += (colWidth + horizontalGap);
}
else
{
--- // this is where the problem occurs
--- if(wrapTextItem)
--- //increment height by the child's height
--- yPos += (childHeight + verticalGap);
--- else
// this is normal behaviour
yPos += (cellHeight + verticalGap);
--- }
It's not an elegant "fix", and more of a monkey patch, but it works for now and doesn't seem to break anything else in Legend Class. Anyway, I think it was a great personal exercise and maybe help someone in the process.
Wednesday, June 3, 2009
Creating Vista's Flip 3D effect component in Flex
Lately I have come to really like the flip3D window switcher in vista/7, even more than expose in OS-X. I was wondering if i could reproduce the same effect using the new native 3D transformations introduced in Flash 10 using Flex without papervision3d.
To my delight, I found out that a simple compiler argument "-target-player=10.0.0" will open up the world of native 3D for Flex SDK 3.3. Although, obviously, it creates a SWF targeted for Flash Player 10 therefore it crashed if opened in Flash Player 9 or lower.
I started by wanting to create an MXML component that I can just use just like a Canvas, using addChild() and removeChild() to add and remove windows. I liked the idea so much, I ended up extending mx.containers.Canvas :)
For 3D Matrix tweeing, I found out that there was a bug in Flex SDK 3.3's Matrix3D.interpolate() that ignores scaling so I used an excellent alternative code I found at:
http://wonderfl.kayac.com/code/5587dec25ecd87aff4b7670f42352c61dcbb11c5
This is what I have ended up with - Desktop.as (view source is enabled):
The code is very rough there are some bugs I haven't squashed yet, but I am happy with it for now =) I have a feeling this code will be quickly outdated with the next Flex version already in beta, but it was a fun personal exercise. I intend to do more of these in the future and share it here. If you like the what this component does, you are welcome to extend/fix it for your use.
Saturday, May 30, 2009
Making a draggable titlewindow without PopupManager
I wanted to make a application with a Windows-like interface with multiple draggable windows. TitleWindow was the obvious choice for the windows but they are only draggable only when created as a popup by the PopupManager.
I didn't want to use TitleWindow+PopupManager because I wanted to reserve PopupManager to display real popups that will always be on top of other components. Basically I wanted the windows to not be children of the systemManager but of a particular Canvas. This also means that I could have multiple "desktops" in the future.
To do this, first I created an extension of TitleWindow to make it draggable without being a popup.DragWindow.as
package
{
import flash.events.MouseEvent;
import mx.containers.TitleWindow;
public class DragWindow extends TitleWindow
{
public function DragWindow()
{
super();
}
override protected function createChildren():void
{
super.createChildren();
this.titleBar.addEventListener( MouseEvent.MOUSE_DOWN, startDragging );
}
}
}
Then I created a WindowManager that replicates the behaviour of the PopupManager.
WindowManager.as
package
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.MouseEvent;
import mx.containers.Panel;
import mx.containers.TitleWindow;
import mx.core.IFlexDisplayObject;
import mx.events.CloseEvent;
public class WindowManager
{
// array of all windows in any container
public static var windowList:Array = [];
private static var instance:WindowManager;
public function WindowManager():void
{
}
public static function getInstance():WindowManager
{
if(instance == null)
{
WindowManager.instance = new WindowManager;
}
return WindowManager.instance;
}
public static function addWindow(window:IFlexDisplayObject, parent:DisplayObjectContainer):void
{
if(window is TitleWindow)
{
TitleWindow(window).showCloseButton = true;
window.addEventListener(CloseEvent.CLOSE, WindowManager.closeWindow);
}
window.addEventListener(MouseEvent.MOUSE_DOWN, WindowManager.focusWindow);
parent.addChild(DisplayObject(window));
windowList.push(window);
}
// Brings the window to the top of displaylist of the parent
public static function bringToFront(window:DisplayObject):void
{
var parent:DisplayObjectContainer = DisplayObjectContainer(window.parent);
parent.setChildIndex( window, parent.numChildren - 1);
}
public static function createWindow(parent:DisplayObjectContainer, className:Class):IFlexDisplayObject
{
var window:IFlexDisplayObject = new className();
WindowManager.addWindow(window, parent);
return window;
}
public static function removeWindow(window:IFlexDisplayObject):void
{
var parent:DisplayObjectContainer = DisplayObjectContainer(window.parent);
// Remove from display list
parent.removeChild(DisplayObject(window));
// Remove from window list
windowList.splice(windowList.indexOf(window),1);
}
// Brings the window to the top of displaylist of the parent
private static function focusWindow(event:MouseEvent):void
{
var window:DisplayObjectContainer = DisplayObjectContainer(event.currentTarget);
WindowManager.bringToFront(window);
}
// Removes the window from the displaylist
private static function closeWindow(event:CloseEvent):void
{
var window:IFlexDisplayObject = IFlexDisplayObject(event.currentTarget);
WindowManager.removeWindow(window);
}
}
}
asdasd
Saturday, May 16, 2009
Sending and receiving VOs in AMFPHP #1
Last week, I have worked to integrate sending Value Objects via AMFPHP instead of simple arrays in our current project and I would like share my knowledge for people who may have trouble getting started as I have.
I will detail the steps over three posts:
- Creating VOs on PHP and AS3
- Sending and Receiving VOs through AMFPHP
- Sending nested objects in AMFPHP
The first thing you need to do is edit globals.php on the server and edit the $voPath to the correct path for amfphp to find the vo files it receives to map against:
globals.php
$voPath = "path/to/your/vo/";
Then create the actual VO class in that path. Let's say the file is in "path/to/your/vo/users/UserVO.php". Because the vo path is set to "path/to/your/vo", the $_explicitType of the class should be "users.UserVO" because the file is located in "users/UserVO.php" relative to the vo path. This is important because this is the path the server will check for the corresponding VO file to map when it receives this VO from the Flex client.
UserVO.php<?php
class UserVO
{
public $firstName;
public $lastName;
public $email;
public $_explicitType="users.UserVO";
}
?>
We need to create an AS3 equivalent of the UserVO class on the Flex side, but this time each property is strongly typed to the expected object types:
UserVO.aspackage com.flexbreakpoints.vos
{
[RemoteClass(alias="users.UserVO")]
[Bindable]
public class UserVO
{
public var firstName:String;
public var lastName:String;
public var email:String;
}
}The RemoteClass metatag is the key to identifying the object Flex receives from AMFPHP. The alia string must be identical to the $_explicitType in the PHP class it mirrors. This string is what Flex uses to map the received object to the correct AS3 VO. If no match is found, the object will default to ObjectProxy.
Remember, both the $_explicitType and RemoteClass strings should always be the PHP VO's path relative to the $voPath and not the AS3 VO's package. When you are only receiving the VO the strings only need to MATCH, and not necessarily the PHP VO's path, but it is always good idea to in case you need to send it back to the server.
Monday, March 9, 2009
Mate framework for Flex
Coming from Flash development, MXML was the biggest and the best change in Flex and personally, I would like to maximize the use of MXML tags rather than AS3. This is what the Mate framework seems to excel in. It seems to have much less code overhead than other frameworks like Cairngorm and PureMVC.
I will have to work with Mate more to find out how it does in real-life projects compared to Cairngorm and PureMVC in terms of performance, development speed, learning curve, code reusability and reliability.