`
hehailin1986_163.com
  • 浏览: 153884 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

[Forward]Selected Design Patterns - Data Transfer Object

    博客分类:
  • Flex
阅读更多

Data transfer objects are also known as value objects (VOs) and are used for data exchanges between various application components, which can be either colocated in the same process or on remote computers. These DTOs can even be written in different programming languages, for example, Java and ActionScript.

First, modify the application from the previous section and encapsulate the order details in a simple OrderDTO that will be placed in the event object and will happily travel between price and order panels. When this is done, you will spend some time with more advanced DTOs that you may want to use in Flex remoting.

Example 2-12 is a simple OrderDTO.as that will be passed between the price and order panels.

Example 2-12. OrderDTO.as


package com.farata.dto {
	// [RemoteClass] meta tag goes here if this DTO
	// is used in Flex Remotin
	[Bindable]
	public class OrderDTO {
		public var symbol:String;
		public var bid:String;
		public var ask:String;
		public var buy:Boolean; 
		//a buy/sell flag
		public function OrderDTO(symbol:String, bid:String, ask:String,
		buy:Boolean=false) {
			this.symbol=symbol;
			this.bid=bid;
			this.ask=ask;
			this.buy=buy;		
		}
	} 
}

In Example 2-13 ’s second version of the price panel, add a function startDataFeed(), emulating the real data feed that may be bringing the market data to the pricing panel. Please note that the PricePanel now displays the data from this “external” feed by binding the UI controls to the properties of the currentData object “received” from a remote server.

Example 2-13. PricePanel2.mxml


<?xml version="1.0" encoding="utf-8"?> 
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="230" height="100" 
backgroundColor="#D4E5D9">

<mx:TextInput x="0" y="-1" width="228" backgroundColor="#0DF113"

text="{currentData.symbol}" fontWeight="bold" fontSize="19" 

textAlign="center"/> <mx:Label x="39" y="31" text="Bid" fontWeight="bold" fontSize="14"/> <mx:TextArea x="1" y="49" width="109" height="47" backgroundColor="#EBF4A2"
text="{currentData.bid}" fontSize="22" fontStyle="normal" fontWeight="bold" click="placeOrder(true)" editable="false" creationComplete="startDataFeed()"/>
<mx:Label x="154" y="31" text="Ask" fontWeight="bold" fontSize="14"/>

 

<mx:TextArea x="118" y="49" width="109" height="47" backgroundColor="#A2BFF4" text="{currentData.ask}" fontSize="22" fontStyle="normal" fontWeight="bold" click="placeOrder(false)" editable="false"/> <mx:Script> <![CDATA[ import com.farata.dto.OrderDTO; import com.farata.events.OrderEvent2; [Bindable] private var currentData:OrderDTO; private function startDataFeed():void { // the code for getting the real data feed goes here currentData = new OrderDTO("ADBE","40.47", "40.51"); } // Create the OrderEvent and place the DTO there // Dispatch the event to be picked by a mediator private function placeOrder(buy:Boolean):void { currentData.buy=buy; // set the flag to buy or sell dispatchEvent(new OrderEvent2(OrderEvent2.PREPARE_ORDER_EVENT,currentData)); } ]]> </mx:Script> </mx:Canvas>

In Example 2-14 , the function placeOrder()dispatches the OrderEvent2with a packaged DTO inside. There is no need to declare multiple variables, as this was done in Example 2-9 .

Example 2-14. OrderEvent2.as

package com.farata.events{
import com.farata.dto.OrderDTO; import flash.events.Event;
	public class OrderEvent2 extends Event {
	public var orderInfo: OrderDTO; public var eventType:String;
	public static const PREPARE_ORDER_EVENT:String ="OrderEvent"; public static const PLACE_ORDER_EVENT:String ="PlaceOrderEvent";
		public function OrderEvent2(eventType:String, order:OrderDTO ) { 
		super(eventType,true, true); // let it bubble this.orderInfo=order; // store the orderDTO
		this.eventType=eventType;
		} 
		
		override public function clone():Event { 
		return new OrderEvent2(eventType,orderInfo); 
		} 
	} 
}

The new version of your driving application, Trading2.mxml (Example 2-15), does not assign the symbol, bid, and ask values to the price panel, as this was done for simplicity in Example 2-11 . Now the PricePanel is being populated by its own data feed.

Example 2-15. The driving application, Trading2.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:comp="com.farata.components.*" backgroundColor="white" applicationComplete="this.addEventListener(
OrderEvent2.PREPARE_ORDER_EVENT,orderEventHandler)" >
<mx:Label text="Price Panel" y="4" height="23" x="69" fontSize="16" 

fontWeight="bold"/> 
<mx:Label text="Order Panel" y="4" height="23" x="290" fontSize="16" 

fontWeight="bold"/> 
<comp:PricePanel2 y="31" x="7"/> 
<comp:OrderPanel2 id="ordPanel" x="245" y="30"/> 
<mx:Script>

<![CDATA[
import mx.controls.Alert;
import com.farata.events.OrderEvent2;

private function orderEventHandler(evt:OrderEvent2):void{
 // The mediator decides what to do with the received event 
// In this case it forwards the order received 
// from PricePanel to OrderPanel
var orderEvt: OrderEvent2= new OrderEvent2(OrderEvent2.PLACE_ORDER_EVENT,evt.orderInfo); 
ordPanel.dispatchEvent(orderEvt);
}
]]> 
</mx:Script> 
</mx:Application>

Even though you haven’t yet seen the code of the OrderPanel2, you can still use it, as long as its API is known—in this case, you know that it listens to the OrderEvent2. As a matter of fact, in many cases you’ll be using components without having any knowledge about how they operate inside.

But to go easy on you, Example 2-16 shows you the source code of OrderPanel2.mxml, which receives the OrderEvent2, extracts the OrderDTO, and populates its UI controls.

Example 2-16. OrderPanel2.mxml

<?xml version="1.0" encoding="utf-8"?> 
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="230" height="100" 
backgroundColor="#4CF3D2" creationComplete="this.addEventListener(OrderEvent2.PLACE_ORDER_EVENT,orderEventHandler)">  

<mx:Text id="sym" x="0" y="10" width="61" fontWeight="bold" fontSize="19"/>
<mx:Text id="operation" x="81" y="10" fontSize="19"/>
<mx:Text id="price" x="48" y="37" width="91" fontWeight="bold" fontSize="16"/>
<mx:Label x="5" y="65" text="Qty:" fontSize="19" fontWeight="bold"/>
<mx:TextInput id="qty" x="70" y="69" width="71" text="100" fontSize="16" selectionBeginIndex="0" selectionEndIndex="5"/>

 

<mx:Button id="go" x="147" y="7" label="GO!" height="60" width="74" fontSize="22" click="placeOrder()" enabled="false"/> <mx:Button x="148" y="75" label="Cancel" width="72" click="cancelOrder()"/> <mx:Script> <![CDATA[ import com.farata.dto.OrderDTO; import mx.controls.Alert; import com.farata.events.OrderEvent2; private var orderInfo:OrderDTO; // the order packaged in the DTO private function orderEventHandler(evt:OrderEvent2){ go.enabled=true; orderInfo=evt.orderInfo; // extract the DTO from the event object sym.text=orderInfo.symbol; operation.text=orderInfo.buy?"Buy":"Sell"; price.text=operation.text=="Buy"?orderInfo.bid:orderInfo.ask; qty.setFocus(); }

 

private function placeOrder():void{ Alert.show(operation.text + " " + qty.text + " shares of " + sym.text +" at" + price.text + " per share", "Placing order"); // call a remote service to place this order }

 

private function cancelOrder():void{ sym.text=""; operation.text=""; price.text=""; go.enabled=false; }

 

]]> </mx:Script> </mx:Canvas>

Examples 2-12 through 2-16 illustrated an application that used a DTO as a sort of exchangeable currency in the interaction between colocated Flex components.

But DTOs also play an important role during the exchange of data with the server-side application using Flex remoting or Data Management Services. In such enterprise applications, the server-side team provides a DTO coded in one of the programming languages (this example uses Java), and the Flex team has to provide a similar Action-Script DTO.

Flex RemoteObjector DataServiceclasses will serialize/deserialize these DTOs into each other, regardless of which direction they travel.

If you don’t define DTOs on the Flex side, the data will be wrapped into ObjectProxy instances, which has a negative effect on performance. If you do, annotate Flex DTOs with the [RemoteClass...] meta tag or via the registerClassAlias() function call.

We highly recommend using strongly typed data transfer objects, as opposed to dynamic objects or XML for data exchange between the client and server tiers. If you are working with a Java Server, make your Java (methods) accept/return custom classes and not generic map objects.

The following list gives you some generic recommendations about creating DTOs that are meant for communication with a remote subsystem, and then offers a solution that can automate the process of creating ActionScript DTOs from their Java peers.

  • Define similar classes in Java and ActionScript languages.
  • If you envision dynamic updates to the data on the client (e.g., the data feed of new stock prices constantly updating the data), declare these classes with the meta tag [Bindable]. Use collections of these bindable instances as data providers for Flex List-based controls like DataGrid, and Flex will ensure that all changes to the data will be reflected by the visual control. Remember, the [Bindable] meta tag results in autogeneration of the code dispatching events on every property change. 

    Use an ArrayCollectionof such bindable DTOs as a dataProviderin your DataGrid, List, and similar components. Imagine a collection of objects with complex structure, with class variables of non-primitive data types—for example, a collection of Employee objects in which each object has a variable of type WorkHistory, which is a class with its own properties. If a variable declared in the WorkHistory class gets modified, the collection of Employee objects won’t know about this change unless you explicitly dispatch the propertyChange event.
  • Make sure that both server-side and client-side DTOs provide a unique property uuid. Flex uses this property to uniquely identify the data elements of the List-based controls. You will find numerous uses for this property, too. 

    For instance, instead of sorting orders by the DTO’s property symbol, you’d sort by symbol and uuid. In this case, the autogenerated hash value of each DTO will be unique for each record, which will result in better performance.
  • Don’t try to intercept the changed values on the visual controls (a.k.a. View). This task belongs to the data layer (a.k.a. Model).
  • Consider replacing each public property with the getter and setter. This will allow you to have more control over the modifications of these properties. You can add code to these setters/getters that will intercept the action of data modification and perform additional processing based on what’s being changed. Then, the setter can dispatch the event PropertyChange as illustrated in this code snippet:
    [Bindable(event="propertyChange")]

     

    public dynamic class OrderDTO extends EventDispatcher {

     

    private var _bid:Number;

     

    public function set bid( value : Number):void { var oldValue:Object = _bid; if (oldValue !== value) { lastPrice = value; dispatchUpdateEvent("bid", oldValue, value); } } public function get bid() : String{ return _bid; } private function dispatchUpdateEvent(propertyName:String, oldValue:Object, value:Object):void { dispatchEvent( PropertyChangeEvent.createUpdateEvent(this, propertyName, oldValue, value)); } }

    This is yet another technique (remember wrapping up an object in a proxy?) for customizing the behavior of the objects when the data is being changed. Imagine that you need to create your own version of a data management service and want to maintain a collection of changed objects that remember all modifications in a DataGrid that uses a collection of OrderDTO objects as a data provider. You can maintain a collection of changed objects that remember all old and new values.

    There’s a difference between the [Bindable(event="propertyChange")] and [Bindable]meta tags. The former syntax instructs the Flex compiler to generate code watching the propertyChange events. The latter syntax forces the Flex compiler to generate the event—it replaces the property with a setter/getter pair in which the setter’s role is to dispatch the event. But if your code has taken care of event dispatching already, you may wind up with events being dispatched twice!
  • Over your project’s life span, you will see many additional uses for DTOs: custom serialization and custom toString() and toXML() methods, for example.
  • Create a basic OrderDTO as in Example 2-12 and subclass it. This way, the superclass OrderDTO maintains its original structure while its subclass allows you to add some new functionality like notifying a third party about properties’ changes or adding new properties like total order amount, which is a result of the multiplication of total shares by price per share:

    
    [Bindable(event="propertyChange"]
    public function get totalOrderAmount():Number {
    	return price*totalShares; 
    } 
    

If you are creating DTOs for the data exchange between Java and ActionScript classes using subclassing, both ActionScript classes will have the meta tag [RemoteClass] pointing to the same Java DTO. This won’t be an issue; Flex is smart enough to use the subclass for serialization.

In the real world, an enterprise project’s Flex and Java developers often belong to different teams and if Java folks change the structure of their DTOs, Flex developers need to ensure that the structure of their classes is updated accordingly. There are different ways of automating this process, as shown in Example 2-17 .

DTO2Fx is a free plug-in that’s available at http://www.myflex.org . It generates ActionScript DTO classes using the subclassing technique described earlier.

Consider the Java DTO in Example 2-17 .

Example 2-17. Annotated OrderDTO2.java


package com.farata.dto; 
import com.farata.dto2fx.annotations.FXClass; 

@FXClass 
publicclass OrderDTO2 { 
public String symbol; 
public String bid; 
public String ask; 
public Boolean buy; 

	public OrderDTO2(String symbol, String bid,String ask, Boolean buy) {
	
	this.symbol=symbol;
	this.bid=bid;
	this.ask=ask;
	this.buy=buy;
	} 
}

The DTO2Fx plug-in uses Java annotations in the process of generating ActionScript classes, and @FXClass is such an annotation. The rest of the process is simple. As soon as you create or modify this class, it automatically regenerates a couple of ActionScript classes: _OrderDTO2.as and OrderDTO2.as. You can find more details about this process in the User Guide of DTO2Fx, but for now just examine the generated code in Example 2-18.

Example 2-18. Superclass _OrderDTO2.as

package com.farata.dto {
	import mx.events.PropertyChangeEvent;
	import flash.events.EventDispatcher; import mx.core.IUID; import mx.utils.UIDUtil;
	/* [ExcludeClass] */ 
	public class _OrderDTO2 extends flash.events.EventDispatcher implements mx.core.IUID {
	/* Constructor */
		public function _OrderDTO2():void {
		 super();
		} 
	
		// implementors of IUID must have a uid property
		private var _uid:String;
		[Transient]
		[Bindable(event="propertyChange")]
		public function get uid():String {
		
			 // If the uid hasn't been assigned a value, just create a new one.
			if (_uid == null) {
			 _uid = mx.utils.UIDUtil.createUID();
			
			}
			return _uid;
		}
		
		public function set uid(value:String):void {
			const previous:String = _uid;
			if (previous != value) {
		
				_uid = value;
				dispatchEvent( mx.events.PropertyChangeEvent.createUpdateEvent( this, "uid", previous, value));
			}
		} 
		
		/* Property "ask" */
		private var _ask:String;
		[Bindable(event="propertyChange")]
		public function get ask():String {
		
			return _ask;
		}
		public function set ask(value:String):void {
		
		const previous:String = this._ask;
			if (previous != value) {
				_ask = value;
				
				const ev:mx.events.PropertyChangeEvent =
				mx.events.PropertyChangeEvent.createUpdateEvent(
 this, "ask", previous, _ask);
				dispatchEvent(ev);
			
			}
		} 
		
		/* Property "bid" */
		private var _bid:String;
		[Bindable(event="propertyChange")]
		public function get bid():String {
			return _bid;
		}
		public function set bid(value:String):void {
		
			const previous:String = this._bid; 
			if (previous != value) { 
				_bid = value; 
				constev:mx.events.PropertyChangeEvent = mx.events.PropertyChangeEvent.createUpdateEvent( this, "bid", previous, _bid); dispatchEvent(ev); 
			} 
		}
		/* Property "buy" */

 

private var _buy:Boolean;

 

[Bindable(event="propertyChange")] public function get buy():Boolean { return _buy; } public function set buy(value:Boolean):void { const previous:Boolean = this._buy; if (previous != value) { _buy = value; constev:mx.events.PropertyChangeEventmx.events.PropertyChangeEvent.createUpdateEvent(this, "buy", previous, _buy );

 

dispatchEvent(ev); } } /* Property "symbol" */ private var _symbol:String; [Bindable(event="propertyChange")] public function get symbol():String { return _symbol; } public function set symbol(value:String):void { const previous:String = this._symbol; if (previous != value) { _symbol = value; const ev:mx.events.PropertyChangeEvent = mx.events.PropertyChangeEvent.createUpdateEvent( this, "symbol", previous, _symbol); dispatchEvent(ev); } } } }

Example 2-18 is a superclass that will always be regenerated by DTO2Fx anytime the Java class changes. This class has a unique object identifier (uid) and includes getters and setters that will dispatch propertyChange events when the time comes.

The code of the class OrderDTO2 is shown in Example 2-19 . This class is generated only once and is a subclass of _OrderDTO2.as. This is a place for an application developer to add application-specific customization, such as the addition of new properties and/ or functions. This class will never be overridden by DTO2Fx, regardless of what was changed in OrderDTO2.java.

Example 2-19. Subclass OrderDTO2.as

package com.farata.dto {
	[RemoteClass(alias="com.farata.dto.OrderDTO2")]
	public class OrderDTO2 extends com.farata.dto._OrderDTO2 {
		/* Constructor */
		public function OrderDTO2():void { 
			super(); 
		} 
	} 
}

We hope that our message to you is clear now: the use of DTOs is a preferred way of designing interobject communications.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics