[Forward]Selected Design Patterns - Mediator

 

Almost any complex screen of a business application consists of a number of containers and components. The era of developers being responsible for both functionality and visuals is coming to an end, and a large portion of the enterprise RIA is created in a collaboration between professional UI designers and developers.

 

Typically, a UI designer gives you a UI wireframe that he puts together using one of the design tools. In the best-case scenario, the UI designer knows how to use Flash Builder in the design mode or even uses Adobe Flash Catalyst to autogenerate MXML for the UI. But even in this case, you, the developer, will need to rip this code apart and decide what components to build to create this view and how they are going to communicate with each other—you need to refactor the code.

 

Let’s see how you can arrange communication between custom Flex components. The diagram in Figure 2-4 consists of a number of nested components and containers that are numbered for easier reference.

For simplicity and better abstraction, this example does not use the actual components, like panels and drop-downs, but you can extrapolate this image onto the wireframe of the actual view you are about to start developing.

 

 

 

 

A simple (but wrong) approach is to just put all these components in one container (number 1 in Figure 2-4), program the business logic and communications among these components, and be done with it. This would produce a monolithic application with tightly coupled components that know about each other and where removal of one component would lead to multiple code changes in the application. Talk about strings attached!

The better approach is to create loosely coupled custom components that are self-contained, do not know about one another’s existence, and can communicate with the “outside world” by sending and receiving events.

Adobe Flex was designed for creating event-driven applications, and it has a good component model, allowing you to create custom components if need be. But after custom components are designed, they need to communicate with each other. This section covers the use of the Mediator design pattern as it applies to UIs created with Flex.

Think of a single Lego from a Lego toy set. Now, some kid (i.e., the mediator) may decide to use that Lego piece to build a house. Tomorrow, the mediator may decide to use that same Lego piece in a boat.

In the diagram from Figure 2-4 , containers play the role of the mediators. The top-level mediator is the container marked as 1, which is responsible for making sure that the components 2, 3, and 6 can communicate if need be. On the other hand, the number 2 is a mediator for 4 and 5. The number 3 is the mediator for 7 and 8.

Being a mediator is a very honorable mission, but it comes with responsibilities. The mediator must listen for events from one of the Lego parts and possibly fire an event on the other one(s).

For example, if you are building an online store, the number 6 can be a component where you select an item to purchase, the number 4 can be the button named Add to Shopping Cart, and the number 5 can be a shopping cart.

Let’s forget about the number 6 for a moment and examine the content of the mediator, number 2. It contains the button 4, which has a specific look and feel and can do just one thing—broadcast a custom event called AddItemClicked. To whom? To whomever’s interested in receiving such an event. So expect to have the line:

dispatchEvent(new Event("AddItemClicked"))

somewhere inside the code of the component 4.

Because mediator number 2 is interested in receiving this event from number 4, it will define an event listener for such an event, which will receive the event and in turn will dispatch another event right on the number 5:

 

addEventListener("AddItemClicked", addItemClickedEventHandler)
...
private function addItemClickedEventHandler ():void {
	Number5.dispatchEvent(new Event("Add2ShoppingCart"));
}

In this pseudocode, the mediator is choreographing the show by defining how its components will communicate.

We’d like to stress that in the previous example, the number 4 is like shooting an event up into the sky—anyone who wants to can listen. On the other hand, the number 5 is just sitting quietly and listening to the incoming event. From whom? It has no idea. This is what loose coupling of components means. The number 4 mediator does not know about the number 5, but they talk anyway through the mediator.

But as a developer of this screen, you have to take care of mediator-to-mediator communications as well. For instance, if the number 6 is a widget where you can select your Sony TV, the mediator 1 will be notified about it and need to talk to the mediator 2, which in turn will arrange the flow between 4 and 5.

Let’s build a concrete example showing how to build these components and establish their communication using the Mediator design pattern. This is an oversimplified trading screen to buy/sell equities at the stock market. This application will have price and order panels. In the real world, the price panel would get an external feed about the prices and deals for all securities that are being traded on the market.

The web designer might give you the two screenshots shown in Figures 2-5 and 2-6 (we hope that your designer has better artistic talent than we do).

 

 

 

 

This is a pretty simple window. You will design it as two components that communicate with each other without having any knowledge about each other. The Flex application will play role of the mediator here. When the user sees the right price to buy or sell IBM shares, she clicks on the bid or ask price; this action will create a custom event with the current data from the price panel bid and ask prices, the stock symbol, and whether this is a request to buy or sell.

In brokerage, bid means the highest price that the trader is willing to pay for the stock or other financial product, and ask is the lowest price the seller is willing to accept.

Example 2-8 shows the PricePanel component. It has three public variables—symbol, bid, and ask. When the trader clicks on one of the numbers in the price panel, the code creates an instance of the custom event of the type OrderEvent.PREPARE_ORDER_EVENT, and all public variables and the name of the requested operation are nicely packaged inside of this event. Then the PricePanel component dispatches this event. To whom? It has no idea.

Example 2-8. PricePanel.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="{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="{bid}" fontSize="22" fontStyle="normal" fontWeight="bold"
click="placeOrder(true)" editable="false" /> 

<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="{ask}" fontSize="22" fontStyle="normal" fontWeight="bold" click="placeOrder(false)" editable="false"/> <mx:Script>
<![CDATA[
	import com.farata.events.OrderEvent;

	public var symbol:String;
	[Bindable]
	public var bid:String;
	[Bindable]
	public var ask:String;

 // Dispatch the OrderEvent to be picked by a Mediator<
private function placeOrder(buy:Boolean):void { 
	dispatchEvent(new OrderEvent(OrderEvent.PREPARE_ORDER_EVENT,symbol,bid,ask,buy)); 
	} 
]]> </mx:Script>
</mx:Canvas>

And Example 2-9 shows the definition of the custom OrderEvent. In this version, it declares several variables for storing the order data, but the section on data transfer objects simplifies this event a little bit.

Please note that this event defines two event types. The OrderEvent of the type PREPARE_ORDER_EVENT is being sent by the PricePanel; the mediator receives it and forwards it to the OrderPanel as PLACE_ORDER_EVENT.

Example 2-9. OrderEvent.as

package com.farata.events{
import flash.events.Event; public class OrderEvent extends Event {
public var symbol:String; public var bid:String; public var ask:String; public var buy:Boolean; public var eventType:String;
	public static const PREPARE_ORDER_EVENT:String ="OrderEvent"; public static const PLACE_ORDER_EVENT:String="PlaceOrderEvent"; 

 

public function OrderEvent(eventType:String, symbol:String, bid:String, ask:String, buy:Boolean ) { super(eventType,true, true); // let it bubble this.symbol=symbol; this.bid=bid; this.ask=ask; this.buy=buy; this.eventType=eventType; } override public function clone():Event { return new OrderEvent(eventType,symbol, bid, ask,buy); } } }

 

The OrderPanel shown in Example 2-10 listens to the event of the OrderEvent.PLACE_ORDER_EVENT type. When this event arrives (this panel has no idea from whom), the OrderPanel populates the fields with the order data extracted from the event object.

Example 2-10. OrderPanel.mxml

<?xml version="1.0" encoding="utf-8"?> 
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" 
width="230" height="100" backgroundColor=quot;#4CF3D2" creationComplete=
"this.addEventListener(OrderEvent.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"

        <mx:Script>
	<![CDATA[
	import mx.controls.Alert;
	import com.farata.events.OrderEvent;

	private function orderEventHandler(evt:OrderEvent) {
		go.enabled=true;
		sym.text=evt.symbol;
	operation.text=evt.buy?"Buy":"Sell";
	price.text=operation.text=="Buy"?evt.bid:evt.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>

Here comes the mediator (Example 2-11), which includes two components— PricePanel and OrderPanel. The mediator listens to the event from the PricePanel and forwards it to the OrderPanel in the function orderEventHandler.

Example 2-11. A test application: Trading1.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(OrderEvent.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:PricePanel symbol="IBM" bid="117.45" ask="117.48" y="31" x="7"/> 
<comp:OrderPanel id="ordPanel" x="245" y="30"/> 

<mx:Script> <![CDATA[ import mx.controls.Alert;
import com.farata.events.OrderEvent;
private function orderEventHandler(evt:OrderEvent):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: OrderEvent= new
OrderEvent(OrderEvent.PLACE_ORDER_EVENT,
evt.symbol, evt.bid, evt.ask, evt.buy);
ordPanel.dispatchEvent(orderEvt);
}
]]> 
</mx:Script> 
</mx:Application>

Once again, components don’t know about one another and can be reused in another context, too.

 

The mediator is one of the most useful patterns for any programming environment that includes components communicating with each other—even more so if you program in an event-driven environment such as Flex. Use this pattern before implementing the UI design. Identify your mediators and custom reusable components and decide what events these components will broadcast or listen to.

After you have made all these decisions, select the format of the data that will travel between the components. This is where the data transfer pattern comes into the picture.

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值