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

[Forward]Selected Design Patterns - Proxy

    博客分类:
  • Flex
阅读更多

 

proxy is an object that represents another object and controls access to it. Think of someone’s spokesperson or a secretary. If someone brings a package to a big shot, the package is taken by the secretary, who would inspect the contents and then either deliver the package to the boss or delegate its further processing to someone else (e.g., security personnel).

In object-oriented programming in general and in ActionScript specifically, you can wrap the class XYZ in mx.util.ObjectProxy, which will be a proxy that controls access to XYZ’s properties.

Let’s think of some concrete Flex examples that illustrate how proxies can control access to object properties by dispatching propertyChange events. As a matter of fact, your Flex programs that use data binding already implement a similar mechanism of event notifications under the hood.

Data binding is a very useful technique that substantially increases the productivity of Flex developers. If you start the declaration of a variable or a class with the meta tag [Bindable], all of a sudden the variable starts emitting events about all changes that can happen to it. The syntax to make this happen is very simple:

	[Bindable]
	var lastName:String;
	

How does this event notification mechanism get engaged by simply adding the magic word [Bindable]? You are all seasoned programmers and don’t believe in the tooth fairy. Someone has to write the code that will dispatch events when the value of the property lastName changes. The compiler does it behind the scenes by creating a wrapper class that implements a getter and setter for the lastName property and then uses that wrapper class. The setter contains the code-dispatching propertyChange event, which carries such useful information as old and new values of the property that’s being modified.

But you don’t always have to depend on the Flex compiler when you need to create an event notification or any other customization or generalization outside of the original class. For that, you create a proxy on your own using the class ObjectProxy as shown in the following examples.

To illustrate the work of ObjectProxy, we have created a small application that changes the values of the properties of the class Person wrapped into an instance of Object Proxy (Example 2-4).

Example 2-4. Class Person


package com.farata{
	public dynamic class Person {
	public var lastName:String="Johnson";
	public var salary:Number=50000;
	} 
} 

The application code illustrating the use of ObjectProxy is shown in Example 2-5.

Example 2-5. PersonProxy.mxml


<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml 
creationComplete="personProxy.addEventListener(PropertyChangeEvent.PROPERT 
Y_CHANGE, changeHandler)" 
layout="absolute"> 

<mx:Script> 
<![CDATA[ 
import mx.events.PropertyChangeEvent; 
import mx.utils.ObjectProxy; 
import com.farata.Person; 

var person:Person = new Person; 
var personProxy:ObjectProxy = new ObjectProxy(person); 

function changeHandler(event:PropertyChangeEvent):void{

log.text+="event.kind: "+ event.kind + " property :" + event.property +" old value:" + event.oldValue + " new value: " + event.newValue +"\n";
}

 

]]> </mx:Script> <mx:Button x="46" y="31" label="Increase Salary by $3K" click="personProxy.salary += 3000;"/>

 

<mx:Button x="211" y="31" label="Change Last Name toMcCartney" click="personProxy.lastName='McCartney'"/> <mx:Button x="428" y="31" label="Directly Change Last Name to Allen" click="person.lastName='Allen';"/> <mx:Label x="47" y="61" text="Change Log" fontWeight="bold" fontSize="14"/> <mx:TextArea id="log" x="46" y="91" width="600" height="250" fontWeight="bold" fontSize="14"/> <mx:Button x="50" y="357" label="Add pension property " click="personProxy.pension='yes'"/> <mx:Button x="216" y="357" label="Delete pension property" click="delete personProxy.pension"/> <mx:Label text="{personProxy.lastName}" x="428" y="359" fontSize="14" fontWeight="bold"/> </mx:Application>

There is one line in PersonProxy.mxml that wraps up the instance of the class Person into an ObjectProxy:

var personProxy:ObjectProxy = new ObjectProxy(person);

This is all it takes to ensure that all changes to PersonProxy will be announced—the PropertyChangeEvent will be triggered, and as you’ve added an event listener to the instance of the Person class, notifications are being sent about every little change that happens to that instance.

Figure 2-2 shows the output generated by this event handler after six sequential clicks: top buttons one, two, three, two, followed by the clicks on the two buttons at the bottom.

After the first click, the salary is increased by $3K, and the ObjectProxy notification conveniently offers the old and the new values of the property salary. The click on the second button changes the last name from Johnson to McCartney. The click on the third button quietly changes the last name from McCartney to Allen, because you applied this change not to the personProxy instance, but directly to the Person. To make sure that the value has been changed, you click button two again, which goes through the ObjectProxy and properly reports that the name has been changed from Allen to McCartney.

The two buttons at the bottom just illustrate that because the class Person has been declared as dynamic, you can add and remove properties on the fly and the person Proxy will properly report on these events, too.

Notice the addition of property change notifiers to the class Person without changing a single line of this code. This technique may also become handy when you don’t have the source code of a class but need to enable property change notifications. In other words, you can enable data binding on a class that you did not create. If you’ve had a chance to deal with aspect-oriented programming, this may sound familiar— you add the functionality to the application without changing the application objects.

To give this example more business context, create a custom class MyPersonProxy by subclassing ObjectProxyand adding some application logic to it. If the salary of a person increases over $55K, say, that employee becomes entitled to the pension in the amount of 2 percent of the salary. You want to add this functionality without touching the code of the class Person.

When you create a subclass of ObjectProxy, you’ll be overriding at least two methods: getProperty() and setProperty() from the namespace flash_proxy, the reason being that if you write MyPersonProxy.lastName="McCartney", this object will call its own method setProperty("lastName", "McCartney") and if you want to intercept this call and add some additional processing to it, you just add it to the overridden method setProperty(). The method getProperty() is being called when you are trying to read a property of a Proxyobject. The Proxyclass defines a number of other useful functions, but discussing them is out of the scope of this book.

Our class MyPersonProxy(see Example 2-6) is derived from ObjectProxy. Its constructor receives and stores the instance of the Person class, and its setProperty() method is overridden to add a new property pension as soon as the salary of the person goes over $55K. Obviously, you can use any business logic to intercept the moment when some “important” properties are being changed in your application and react accordingly.

Example 2-6. MyPersonProxy.as


package com.farata { 
import mx.utils.ObjectProxy; import flash.utils.*; 
use namespace flash_proxy;
	public dynamic class MyPersonProxy extends ObjectProxy {
	    // The object to wrap up
	    private var person:Person;
	    public function MyPersonProxy(item:Person) {
	        super(item);
	        person=item;
	    }
	    flash_proxy override function setProperty(name:*, value:*):void { 
	    if ( name == 'salary'&& value>55000) {
	        // add a new property to this instance of the
                // class Person, which can be used in the calculations
                // of the total compensation
	        setProperty("pension", 0.02); 
	    } 
	    super.setProperty(name, value); 
	    } 
	} 
} 

In Example 2-7 , the program CustomProxy illustrates the use of the MyPersonProxy class.

Example 2-7. CustomProxy.mxml

 

<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 

creationComplete="personProxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, changeHandler)"> <mx:Script> <![CDATA[ import mx.events.PropertyChangeEvent;
//import mx.utils.ObjectProxy; 
import com.farata.MyPersonProxy; import com.farata.Person;
var person:Person = new Person; var personProxy:MyPersonProxy = new MyPersonProxy(person);
function changeHandler(event:PropertyChangeEvent):void { 
log.text+="event.kind: "+ event.kind + " property :" + event.property + " old value:" + event.oldValue + " new value: " + event.newValue +"\n";
}
]]>
</mx:Script>
<mx:Button x="46" y="31" label="Increase Salary by $3K"

click="personProxy.salary += 3000;"/>
<mx:Label x="47" y="61" text="Change Log" fontWeight="bold" fontSize="14"/>
<mx:TextArea id="log" x="46" y="91" width="600" height="250" fontWeight="bold" fontSize="14"/>
</mx:Application>
Run this program and you’ll see the output in Figure 2-3 after making three clicks on the Increase Salary button. The second click properly reports the addition of the pension property as well as the salary change. The third click doesn’t report the change—the pension property was being assigned the same value on the third click; the proxy did not dispatch a PropertyChangeEvent regarding the pension.

 

 

Here’s another example, RemoteObject:

 

<mx:RemoteObject id="ro" destination=quotMyEmployees" />

 

 

What exactly happens when you call a method on a remote destination that goes by the nickname MyEmployees?

MyEmployees.getEmployees();

Flex is client software that does not need to know what powers the MyEmployeesfunction has on the server side. Is there a ColdFusion or a Java object that has the function getEmployees() implemented? Flex doesn’t need to know or care.

In the Java world, if you want to implement client/server communication using Remote Method Invocation between objects located in different Virtual Machines (VMs), you’d have to explicitly define a stub class on the client (a proxy) that represents its peer skeletonclass on the server on the remote VM.

Flex spares you from creating stubs, automatically wraps these remote calls into proxy objects, and internally uses the invoke() method call to pass the name of the remote method and its parameters.

Flex’s ability to declaratively define a reaction to the changes in the data or components state greatly simplifies programming and reduces errors related to low-level coding.

In order for binding to work, you need to make sure that the Flex framework knows when the data changes. Unlike most dynamic language implementations, ActionScript

3.0 is built for speed and heavily utilizes direct access to properties and methods. In this situation, the only way for data to notify the world about the changes is to embed the code to fire change events.

The Flex compiler helps in a big way by introducing [Bindable] and [Managed] tags. If you prefix your variable with the [Bindable] tag, the compiler does the following:

  • Inspects every public property and setter of your variables class and generates wrapper getters/setters that add event notification
  • References these getters/setters instead of original properties every time a “bindable” property is being used

Having a wrapper with a setter and a getter is technically the same as creating a proxy; that is, the setter can include and execute additional code every time the value of this specific property changes. Obviously, it does not work with untyped data coming from the server. Such data is converted to a dynamic Object type. The problem is alleviated a bit by the fact that the Flex framework would automatically wrap the Object in the ObjectProxy if the default property of the RemoteObject makeObjectBindable=true were not modified.

However, Flex will wrap only the top level and not the individual array members, making changes to those undetectable. For example, say you are passing a set of the objects from a remote Java assembler class that sends data transfer objects (DTOs) that may include an array property. These DTOs will eventually become rows in a DataGrid. The changes to these array elements are not going to dispatch change events unless you explicitly wrap each array element in the ObjectProxy, for example:

 

private function onResult(r:ResultEvent) : void {
	var quotes:ArrayCollection = r.result.quotes;
	var wrappedQuotes = new ArrayCollection();
	for each (var quote in quotes)
	wrappedQuotes.addItem(new ObjectProxy(quote))
	view.dataProvider = wrappedQuotes;
} 

 

ObjectProxy can make the code development process more productive, but keep in mind that you are going to pay a high price for this as it introduces additional processing during the runtime—dynamic objects are much slower than strongly typed ones. Even more important, because of automatic wrapping the code might dispatch an event on each data change. Data binding is great, but if you need to process larger data sets and really need to use data binding, consider strongly typed classes that will support [Bindable] on the class members level and even optimize dispatching of the events. If you are doing massive updates of data, using ObjectProxy or any other form of data binding can substantially affect performance and the ability to trace your applications.

 

 

The bottom line is this: implement the proxy design pattern whenever you need to monitor the changes that are happening to a particular object. Yet another advantage of using proxies is that you can modify the behavior of an object without the need to modify its code.

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics