+ All Categories
Home > Technology > Parsley & Flex

Parsley & Flex

Date post: 09-Jul-2015
Category:
Upload: prideconan
View: 1,974 times
Download: 1 times
Share this document with a friend
42
Parsley framework for Flex/Flash/AIR & Alexander Budjakov Flex Developer [email protected]
Transcript
Page 1: Parsley & Flex

Parsley framework for Flex/Flash/AIR

&

Alexander Budjakov Flex Developer [email protected]

Page 2: Parsley & Flex

Parsley is all about decoupling

Page 3: Parsley & Flex

Parsley is Open Source, licensed under the Apache License 2.0 Written by Jens Halm First version 1.0.RC1 (2007-12-09) Last version 3.0.0 (2012-02-06)

Overview

Parsley is an Application Framework for Flex and Flash Applications built upon an IOC Container and Messaging Framework that can be used to create highly decoupled architectures.

> 4 years

Page 4: Parsley & Flex

Features

Flexible IOC Container Dependency Injection Decoupled Bindings Messaging Framework Managed Commands Dynamic View Wiring Advanced Modularity Object Lifecycle Localization Extensibility

Parsley Spicelib

Command Framework XML-Mapper Reflection API Logging

Page 5: Parsley & Flex

Inversion of Control (IoC)

Page 6: Parsley & Flex

Hollywood Principle: Don't call us, we'll call you.

Program logic runs against abstractions such as callbacks

Inversion of Control (IoC)

Page 7: Parsley & Flex

IoC Example

package com { public interface IMap { //methods } }

package com { public class GPSNavigator implements INavigator { private var map:IMap; public function GPSNavigator() { this.map = new UkraineMapImpl(); } //other methods } }

Here are a common smells that should lead you to refactor to IoC

Here is the simplest possible IoC component

package com { public class GPSNavigator implements INavigator { private var map:IMap; public function GPSNavigator(map:IMap) { this.map = map; } //other methods } }

It is hard coded Not reusable.

package com { public class GPSNavigator implements INavigator { private static var map:IMap = MapFactory.getMap(); public function GPSNavigator() { } //other methods } }

Some other smells.

Page 8: Parsley & Flex

IOC Container

We may ask for the object from the container and the container creates the object and its dependencies

Parsley is a classic IOC Container. It provides support for Dependency Injection, Object Lifecycle Management and Messaging

EBookReader <interface>

File

PDFFileImpl TXTFileImpl

IoC Container

inject

creates

Page 9: Parsley & Flex

Dependency Injection (DI)

Page 10: Parsley & Flex

Dependency Injection (DI)

How objects obtain references to each other

The core feature of any IOC Container [Inject]

Page 11: Parsley & Flex

Dependency Injection (DI)

You may have these different tools

Page 12: Parsley & Flex

Dependency Injection (DI)

OR just one tool with attachments

You may have these different tools

Page 13: Parsley & Flex

Dependency Injection (DI)

This brings us to the Design Principles - Program to an interface, not an implementation

OR just one tool with attachments

You may have these different tools

Page 14: Parsley & Flex

Dependency Injection (DI)

Three ways of classic DI

Page 15: Parsley & Flex

Dependency Injection (DI)

public class Controller { private var model:Model; public function Controller(model:Model) { this.model = model; } }

Constructor Injection

Page 16: Parsley & Flex

Dependency Injection (DI)

public class Controller { private var model:Model; public function Controller(model:Model) { this.model = model; } }

public class Controller { private var _model:Model; public function set model(value:Model):void { _model = value; } }

Constructor Injection

Setter Injection

Page 17: Parsley & Flex

Dependency Injection (DI)

public class Controller { private var model:Model; public function Controller(model:Model) { this.model = model; } }

public class Controller { private var _model:Model; public function set model(value:Model):void { _model = value; } }

public interface IController { function injectModel(model:Model):void } public class Controller implements IController { private var model:Model; public function injectModel(model:Model):void { this.model = model; } }

Constructor Injection

Setter Injection

Interface Injection

Page 18: Parsley & Flex

public class Controller { private var model:Model; public function Controller() { model = new Model(); } } public class Controller { private var model:Model; public function Controller() { model = ModelFactory.create(Model); } } public class Controller { private var model:Model; public function Controller() { model = Model.getInstance(); } }

NOT Dependency Injection

Page 19: Parsley & Flex

IoC and DI Benefits

• Dependency management • Simplify the reuse of classes or components • Offers configuration flexibility • Simplify unit-testing • The "cleaner" code

Page 20: Parsley & Flex

Configuration and Initialization

Page 21: Parsley & Flex

Configuration and Initialization

1

• Telling the IOC Container which classes it should manage

• MXML, XML, AS

Page 22: Parsley & Flex

<?xml version="1.0" encoding="utf-8"?> <parsley:Objects xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:parsley="http://www.spicefactory.org/parsley"> // *hidden imports <fx:Declarations> <parsley:Object id="userModel" type="{Model}" lazy="true"/> <parsley:DynamicObject type="{Counter}"> <parsley:Property name="maxCount" value="100" /> </parsley:DynamicObject> <parsley:View type="{UsersView}"/> <service:MockUsersService /> <parsley:MapCommand type="{GetUsersCommand}" messageType="{GetUsersMessage}"/> </fx:Declarations> </parsley:Objects>

UserConfig.mxml

Page 23: Parsley & Flex

Configuration and Initialization

1

• Telling the IOC Container which classes it should manage

• MXML, XML, AS.

2

• Configure DI or Messaging for each individual class

• MXML, XML, Metadata tags

Page 24: Parsley & Flex

public class GetUsersCommand { [Inject] public var service:IUsersService; [Inject(id="userModel")] public var model:Model; public function execute():AsyncToken { return service.getUsers(); } public function result(users:ArrayCollection):void { model.users = users; } }

public class UsersView extends SkinnableComponent { [Inject] public var model:Model; [Publish(objectId="selectedUser")] [Bindable] public var seletedUser:User; [MessageHandler(selector="user")] public function externalUserSelection(msg:SelectMessage):void { //do something } }

GetUsersCommand.as

UserView.as

Page 25: Parsley & Flex

Configuration and Initialization

1

• Telling the IOC Container which classes it should manage.

• MXML, XML, AS.

2

• Configure DI or Messaging for each individual class

• MXML, XML, Metadata tags

3 • Initialize IOC Container

Page 26: Parsley & Flex

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:parsley="http://www.spicefactory.org/parsley" <fx:Declarations> <parsley:ContextBuilder config="{UserConfig}"/> </fx:Declarations> </s:Application>

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:parsley="http://www.spicefactory.org/parsley" <fx:Declarations> <parsley:ContextBuilder> <parsley:FlexConfig type="{UserConfig}"/>

<parsley:FlexConfig type="{ServiceConfig}"/>

<parsley:FlexConfig type="{MockServiceConfig}"/>

</parsley:ContextBuilder

</fx:Declarations> </s:Application>

You can use one big configuration file

Or several files with separated configuration

ContextBuilder.newBuilder() .config(FlexConfig.forClass(UserConfig)) .config(FlexConfig.forClass(ServiceConfig)) .config(XmlConfig.forFile("logging.xml")) .build();

Configuration DSL

Page 27: Parsley & Flex

Decoupled Bindings [Publish] [Subscribe] [PublishSubscribe]

• This feature is much more dynamic than Dependency Injection. • It is really more the decoupled equivalent of the typical usage of Flex Bindings. • The published object does not even have to be a container managed object.

Page 28: Parsley & Flex

Decoupled Bindings

[Bindable] [Publish(objectId="selectedUser")] public var selectedUser:User; [Subscribe(scope="window")] public var selectedUser:User; [Bindable] [PublishSubscribe(persistent="true", objectId="selectedUserId")] public var selectedUserId:int;

<parsley:View type="{UsersView}"> <parsley:Publish property="selectedUser“ objectId="selectedUser"/> </parsley:View>

MXML Example

ActionScript Examples

uses Local SharedObjects

Page 29: Parsley & Flex

Messaging [ManagedEvents] [MessageDispatcher] [MessageHandler] [MessageBinding] [MessageError]

• The exchange messages between objects in a fully decoupled manner (sender and the receiver do not have to know each other)

• Messaging Framework is generic, it does not impose a particular usage style

Page 30: Parsley & Flex

[Event(name="userSelected",type="com.xyz.UserSelectedEvent")] [ManagedEvents("userSelected, somethingSelected")] public class UsersView extends SkinnableComponent { private function handleUserSelect(user:User):void { dispatchEvent(new UserSelectedEvent("userSelected", user)); } }

ActionScript Examples

public class UsersView extends SkinnableComponent { [MessageDispatcher] public var dispatcher:Function; private function handleUserSelect(user:User):void { dispatcher(new UserSeletedMessage(user)); } }

UserSelectedEvent.as Event based class

UserSeletedMessage.as Simple class

[MessageHandler(type="com.xyz.UserSelectedEvent", messageProperties="user, role“)] public function handleUserSelect(user:User, role:String):void { }

[MessageBinding(messageProperty="user", type="com.xyz.UserSelectedEvent")] public var user:User;

Page 31: Parsley & Flex

Object Lifecycle [Init] [Destroy]

preConfigure preInit postInit preDestroy postDestroy

Lifecycle phases

Page 32: Parsley & Flex

Object Lifecycle [Init] [Destroy]

[Init] public function init():void { // }

[Destroy] public function destroy():void { // }

The methods marked with [Init] get invoked after the object has been instantiated and all injections have been processed.

The methods marked with [Destroy] get invoked after the Context instance they belong to has been destroyed with Context.destroy() or when the object was removed from the Context.

Lifecycle phases

preConfigure preInit postInit preDestroy postDestroy

Page 33: Parsley & Flex

Managed Commands

• Dynamically add a command to a Context only for the time it executes • Declarative way to configure Commands • Grouping commands (parallel, sequence, flow) • Map command to message

Page 34: Parsley & Flex

Managed Commands <parsley:MapCommand type="{GetUserProfileCommand}"/>

public class GetUserProfileCommand { [Inject("userService")] public var service:RemoteObject; public function execute(msg:GetUserMessage):AsyncToken { return service.getUserProfile(msg.userId); } public function result(profile:Profile):void { // } }

[CommandResult] public function profileResult(profile:Profile, message:GetUserMessage):void { [CommandError] public function handleResult(fault:FaultEvent, trigger:GetUserMessage):void {

[CommandResult] [CommandError] [CommandComplete] [CommandStatus]

[Bindable] [CommandStatus(type="com.xyz.GetUserMessage")] public var isGettingProfile:Boolean;

<s:Button label="GetProfile" enabled="{isGettingProfile}" click="..." />

Page 35: Parsley & Flex

Managed Commands

<parsley:MapCommand messageType="{FindUserMessage}"> <parsley:CommandSequence> <parsley:Command type="{GetUsersCommand}"/> <parsley:Command type="{FindUserCommand}"/> </parsley:CommandSequence> </parsley:MapCommand>

Declaring Groups in MXML

<parsley:MapCommand messageType="{FindUserMessage}"> <parsley:ParallelCommands> <parsley:Command type="{FindUserCommand}"/> <parsley:Command type="{OutputDataComand}"/> </parsley:ParallelCommands> </parsley:MapCommand>

Page 36: Parsley & Flex

Building MVC Architectures

Page 37: Parsley & Flex

Custom Scopes

Page 38: Parsley & Flex

Custom Scopes

[ManagedEvents("add, remove", scope=“global")] [MessageDispatcher(scope="local")] public var dispatcher:Function; [MessageHandler(selector="add", scope="local")] public function save (event:UdpateUser):void { [Subscribe(objectId="selectedUser", scope="window")] public var selectedUser:User;

<parsley:ContextBuilder> <parsley:FlexConfig type="{UserConfig}"/> <parsley:FlexConfig type="{ServiceConfig}"/> <parsley:Scope name="window" inherited="true"/> <parsley:MessageSettings defaultReceiverScope="local"/> </parsley:ContextBuilder>

Default scope is global

Add new “window” scope

Change default scope to local.

Actionscript Examples

Page 39: Parsley & Flex

Localization

[ResourceBinding(bundle="resources", key="user.profile")] public var message:String;

<s:Label text="{resourceManager.getString('resources', ‘user.profile')}"/>

Usual using with flex binding

It is useful for properties of objects managed by the IOC Container

<parsley:View type="{ProfileView}"> <parsley:ResourceBinding property="header" bundle="resources" key="user.profile" /> </parsley:View>

MXML Example

Page 40: Parsley & Flex

Configuration Properties project.properties

dev_url = http://www.dev.xyz.com prod_url = http://www.prod.xyx.com version = 1.0.0

Property File compiled into the Application

<fx:Declarations> <fx:String id="properties" source="project.properties" /> </fx:Declarations> <parsley:ContextBuilder <parsley:PropertiesString source="{properties}"/> </parsley:ContextBuilder>

External Property File <parsley:ContextBuilder <parsley:PropertiesFile file="project.properties" /> </parsley:ContextBuilder>

<parsley:Object type="{Config}"> <parsley:Property name="dev" value="{properties.dev_url}" /> <parsley:Property name="prod" value="{properties.prod_url}" /> <parsley:Property name="version" value="{properties.version}" /> </parsley:Object>

Using Properties in MXML Configuration

Page 41: Parsley & Flex

Conclusion

Page 42: Parsley & Flex

Questions?

Information

Understanding


Recommended