Chapter 6. Proxy Pattern

<feature><title>In This Chapter</title> </feature>

The Proxy pattern gives us a solution to a very common programming task. A proxy is a class that stands in for and provides access to another object. This other object isn’t always an ActionScript object. It could be an image file, an XML file, a Flash Remoting service, or a Web service.

There are many reasons for wanting to use a proxy object, and each reason has its own type of proxy. One reason you might want to control access to an object is because it is a remote resource; in such a case, the proxy object can manage the communication to and from that object. This is called a Remote Proxy, and we’ll discuss it later in this chapter. Another reason to use a proxy is to defer the full cost of the object’s creation. This could be because the object takes a long time to create or because the object uses a lot of resources once its been created. This type of proxy is knows as a Virtual Proxy, and we’ll discuss it in the next section.

There are many types of Proxy classes; however, the Remote and Virtual proxies are the two most common, and the two we will cover in this chapter.

The Proxy pattern is often confused with a couple other patterns that are very similar in functionality. Those patterns are the Adapter and Façade patterns. The main distinction of a Proxy pattern is that is has the exact same API or interface as the object it is standing in for while both the Façade and Adapter patterns modify the API. We’ll briefly touch on these two related patterns towards the end of the chapter.

Virtual Proxy

The Virtual Proxy is used to proxy objects that are expensive to create or that aren’t available for use right away. The Virtual Proxy can defer the creation or initialization of its subject until it is needed. Before and while the subject is being created, the Virtual Proxy stands in its place. After the creation is complete, it delegates requests directly to the subject.

Image Loader Example

One common example of a Virtual Proxy is an image loader. An image loader is an object that stands in for an external image while it’s being loaded. It’s important that this proxy object have the same API as the image object itself. This enables us to set the image’s position and add effects to the image before it has completely loaded.

Flash Player 9 has a great example of an image loader Virtual Proxy built right into the player. It’s called Loader, and it’s found in the flash.display package. This class extends DisplayObjectContainer, so it has all the properties and methods necessary to add it to a display list, change its position, and even add effects.

In the following example, we’ll use the Loader class to load an external image.

First create a new ActionScript project called ImageProxyExample.

Loading the Image

Inside the main class for this project, we will create and load the image. In the constructor of the ImageProxyExample class, we create the Loader object and load the remote image. Without waiting for the image to load, we add the Loader instance to the display list. We’re able to do this because the Loader class is acting as a proxy to the real image.

package {

   import flash.display.Sprite;
   import flash.display.Loader;
   import flash.net.URLRequest;

   [SWF(backgroundColor="#FFFFFF", width=640, height=480)]
   public class ImageProxyExample extends Sprite {
      
      public function ImageProxyExample () {
         var image:Loader = new Loader();
         image.load(new URLRequest("http://www.communitymx.com/blog/images/dannyp.gif"));
         addChild(image);
      }

   }

}

Note

The SWF metatag allows us to set the background color, width, and height or the SWF in an ActionScript project. I’m using that in this example to better see the image placed on the display list.

Modifying the Image Before It’s Loaded

If the Loader class weren’t a proxy, we would have had to wait for the image to load before we could add it to the display list because only display objects can be added to the display list. In the next example, we modify the ImageProxyExample class by changing the image’s position and adding effects, all before the real image ever loads:

package {

   import flash.display.Sprite;
   import flash.display.Loader;
   import flash.net.URLRequest;
   import flash.filters.GlowFilter;
   import flash.filters.BlurFilter;

   [SWF(backgroundColor="#FFFFFF", width=640, height=480)]
   public class ImageProxyExample extends Sprite {

      public function ImageProxyImp() {
         var image:Loader = new Loader();
         image.load(new URLRequest("http://www.communitymx.com/blog/images/dannyp.gif"));
         addChild(image);
         var glow:GlowFilter = new GlowFilter(0xff99ff, 2, 6, 6, 2, 1);
         var blur:BlurFilter = new BlurFilter(4, 4, 1);
         var filters:Array = new Array();
         filters.push(glow);
         filters.push(blur);
         image.filters = filters;
         image.x = 10;
         image.y = 10;
      }

   }

}

As you can see, a Virtual Proxy can make your code much easier to work with. Without the Loader acting as a proxy to the image, we would have to wait for the image to be successfully loaded before we could add it to the display list and add effects.

It’s important to note here that although the Loader class does stand in for the loaded image, its not a pure form of a virtual proxy. The Loader class doesn’t actually proxy modifications to the image, but instead applies the modifications to itself. The image gets those modifications because it is a child of the Loader class. In the next example, we’ll show a true proxy that passes its requests directly to the subject.

Lazy Serialization Example

The other use of a Virtual Proxy is to stand in place of an object that is expensive to create. A great example of an expensive operation is serialization. Consider an object that models an XML element; we have two options for serialization. First, we could pass the data into the constructor of the model object and parse the values right away into the properties of the object.

This approach is known as “eager” serialization and with large, complex objects it can be very expensive. Our second option is called “lazy” serialization, in which we serialize the properties of the object on demand. This second option eliminates unnecessary serialization to unused properties and it spreads the serialization process out instead of doing it all up front. Figure 6.1 illustrates this process.

An example of lazy serialization.

Figure 6.1. An example of lazy serialization.

To get started, create a new ActionScript project called SerializationProxyExample.

Creating the Product Interface

For this example, both our “real” Product class and our “proxy” Product class implement the same interface. This interface defines the methods that both classes need to implement and allows us to treat the two classes the same. The interface is named IProduct and defines the getTitle(), getPrice(), setTitle(), and setPrice() methods:

package com.peachpit.aas3wdp.proxyexample {

   public interface IProduct {

      function getPrice():Number;

      function getTitle():String;

      function setPrice(price:Number):void;

      function setTitle(title:String):void;
   }

}

Creating the Product Class

The Product class is the “real” class behind our proxy. This class simply holds the values for the product’s title and price properties and has methods for getting and setting those values:

package com.peachpit.aas3wdp.proxyexample {

   import com.peachpit.aas3wdp.proxyexample.IProduct;

   public class Product implements IProduct {

      private var _price:Number;
      private var _title:String;

      public function Product() {}

      public function getPrice():Number {
         return _price;
      }

      public function getTitle():String {
         return title;
      }

      public function setPrice(_price:Number):void {
         this._price = _price;
      }

      public function setTitle(_title:String):void {
         this._title = _title;
      }

   }

}

Creating the Product Proxy Class

The XMLProductProxy class stands in for the Product class to manage the serialization on demand. The proxy uses composition to inherit all the methods of the Product class. When a request is made to getPrice(), for example, the proxy first checks to see whether its instance of the Product class has a value for price. If it does, that value is returned; if not, the proxy grabs the data out of the XML object and sets the value on the “real” product, pbject. This is how serialization occurs only when a method is invoked. Then the correct value is returned. By deferring the serialization to the request, we minimize the amount of serialization that happens up front.

package com.peachpit.aas3wdp.proxyexample {

   import com.peachpit.aas3wdp.proxyexample.IProduct;
   import com.peachpit.aas3wdp.proxyexample.Product;

   public class XMLProductProxy implements IProduct {
      private var _data:XML;
      private var _product:Product;

      public function XMLProductProxy(_data:XML) {
         this._data = _data;
         product = new Product();
      }

      public function getPrice():Number {
         if(isNaN(_product.getPrice())) {
            _product.setPrice(Number(_data.price.toString()));
         }
         return _product.getPrice();
      }

      public function getTitle():String {
         if(_product.getTitle() == null) {
            _product.setTitle(_data.title.toString());
         }
         return _product.getTitle();
      }

      public function setPrice(price:Number):void {
         _data.price = price; 
         _product.setPrice(price);
      }

      public function setTitle(title:String):void {
         _data.title = title;
         _product.setTitle(title);
      }

   }
}

Using the Proxy

Using the proxy we just created is simple. First we create a sample XML object structured to work with our proxy class. Then we create a new instance of the XMLProductProxy class and pass the sample XML object to the constructor. Now, when we call the getTitle() or getPrice() method on the proxy, it returns the value from the XML object. Subsequent calls to those same methods will return the values from the product object and no serialization is required.

package {

   import com.peachpit.aas3wdp.proxypattern.IProduct;
   import com.peachpit.aas3wdp.proxypattern.XMLProductProxy;
   import flash.display.Sprite;

   public class SerializationProxyExample extends Sprite {
      public function SerializationProxyExample () {
         var data:XML = <product>
            <title>Widget</title>
            <price>19.95</price>
         </product>;

         var product:IProduct = new XMLProductProxy(data);
         trace(product.getTitle() + " -- $" + product.getPrice());
      }
   }
}

Even though this is a simple example, the advantages of lazy serialization become clear when you introduce a complex data structure with multiple levels of objects. In such a case, lazy serialization can help make your application run smoother by reducing the amount of serialization that happens up front in an application and by eliminating serialization for items that are never requested.

Remote Proxy

The Remote Proxy also stands in for an object, but in this case the subject is remote. This could be a separate SWF file, an XML file, a Flash Remoting service, a SOAP or REST service, or any number of other type of services. The Remote Proxy acts as a local representative to this remote object. It has the same public methods as the remote resource and delegates requests to that resource. It also handles the communication with the remote resource.

Flickr Search Proxy Example

Flickr (www.flickr.com) is a popular online photo-sharing site. In this example, we’re going to write a simple Remote Proxy to Flickr’s search API. The proxy will implement a search method and handle the communication with the Flickr API. It will then broadcast an Event.COMPLETE or an ErrorEvent.ERROR event with the result.

Note

The Flickr examples in this chapter require that you apply for a key to access the Flickr API. In the examples, you’ll need to replace the text <ADD_YOUR_KEY_HERE> with your Flickr API key. You can apply for anAPI key at the following URL: http://www.flickr.com/services/api/misc.api_keys.html.

First, create a new ActionScript project called RemoteProxyExample.

Creating the Search Proxy

The PhotoSearchProxy class takes local requests using its search method and relays them to the remote Flickr API. Flickr offers a few different flavors of its API. For our example, we’re using the REST API. This API is just a simple HTTP GET request that returns an XML result. The parameters are sent in the query string of the request.

package com.peachpit.aas3wdp.proxypattern {

   import flash.events.DataEvent;
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import flash.net.URLLoader;
   import flash.net.URLRequest;

   public class PhotoSearchProxy extends EventDispatcher {

      private static const API_KEY:String = "<ADD_YOUR_KEY_HERE>";
      private static const FLICKR_URL:String = " http://api.flickr.com/services/rest/";

      public function PhotoSearchProxy() {}

      private function onComplete(event:Event):void {
         dispatchEvent(new DataEvent(Event.COMPLETE, false, false,
                       XML(event.target.data));
      }

      public function search(userId:String, tags:String):void {
         var loader:URLLoader = new URLLoader();
         var request:URLRequest = new URLRequest(PhotoSearchProxy.FLICKR_URL  +
            "?method=flickr.photos.search&user_id=" + userId + "&tags=" + tags +
            "&api_key=" + PhotoSearchProxy.API_KEY);
         loader.addEventListener(Event.COMPLETE, onComplete);
         loader.load(request);
      }

   }

}

Using the Search Proxy

To test the remote search proxy, we just create a new instance of the proxy (PhotoSearchProxy), register the complete and error events, and make a call to the search() method, like this:

package {

   import com.peachpit.aas3wdp.proxypattern. PhotoSearchProxy;
   import flash.display.Sprite;
   import flash.events.DataEvent;
   import flash.events.Event;
   
   public class RemoteProxyExample extends Sprite {

      public function RemoteProxyEmaple() {
         var flickr:PhotoSearchProxy = new PhotoSearchProxy();
         flickr.addEventListener(Event.COMPLETE, onComplete);
         flickr.search("", "yellow");
      }
      private function onComplete(event:DataEvent):void {
         trace(event.data);
      }
   }
}

When you debug this example in Flex Builder, you’ll see the XML result from the Flickr REST request output to the debug console.

The flash.utils.Proxy

In ActionScript 1 and 2, there was a method in the built-in Object class called __resolve. By overriding this method, you could capture any call made on that object that was undefined, including both properties and methods. The most common implementation of this feature was the Remoting and Web Service frameworks that used __resolve to proxy operations on remote methods.

This feature has grown up a bit in ActionScript 3.0 and is now encapsulated in the flash.utils.Proxy class. This class is never used directly, but is instead extended and its methods overridden. In Chapter 4, “Singleton Pattern,” we used the Proxy class to capture calls to undefined properties and instead return a value from an XML configuration file. This was achieved by overriding the getProperty() method. To capture calls to undefined methods, we need to override the callProperty() method. In the next example, we will capture the calls to undefined methods to proxy those calls to the remote Flickr API. This will allow us to create a more flexible proxy.

Note

This chapter isn’t a reference about the built-in Proxy class. This example illustrates the relationship of the Proxy class and the Proxy pattern, not a comprehensive description of how to use the Proxy class.

We just created a remote proxy example that calls a remote search method in the Flickr API, but what if we want to implement all the photo operations available in the Flickr API? We could systematically add each method to the PhotoSearchProxy class. This would be perfectly acceptable. But if we didn’t know all the remote operations, or Flickr was continuously adding new operations, the PhotoSearchProxy class would fall short The solution is to use the built-in flash.utils.Proxy class.

Creating the Remote Photo Proxy

By extending the built-in Proxy class, we can catch calls to undefined methods and relay them to the remote Flickr API. This is a fairly simple process:

  1. We must make our class dynamic so that a call to an undefined method is allowed.

  2. The class needs to extend flash.utils.Proxy and override the callProperty() method. Each time a call to an undefined method is made on our class, we can catch it in callProperty().

  3. We must implement the IEventDispatcher interface and add the EventDispatcher functionality through composition. In our first Remote Proxy example, we extended EventDispatcher, but we can’t do that this time because we’re extending Proxy and multiple inheritance is not permitted in ActionScript 3.0.

There is one minor catch: The call to Flickr requires us to format the method parameters in a query string with name/value pairs. However, ActionScript doesn’t have named parameters at runtime, so we need to find out what every parameter’s name is. Fortunately, the Flickr API has a reflection method — flickr.reflection.getMethodInfo — that allows us to get all the parameters for a given method in the API. So when a call to an undefined method is made, we can save the parameters and extract the method name. Then we can make a reflection call based on the method name. When the reflection call is returned, we can match the saved parameters with their names from the reflection result to generate the query string needed to make the original operation. Here’s how that logic works:

package com.peachpit.aas3wdp.proxypattern {

   import flash.events.DataEvent;
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import flash.events.IEventDispatcher;
   import flash.net.URLLoader;
   import flash.net.URLRequest;
   import flash.utils.flash_proxy;
   import flash.utils.Proxy;

   dynamic public class PhotoProxy extends Proxy implements IEventDispatcher {

      private static const API_KEY:String = "<ADD_YOUR_KEY_HERE>";
      private static const FLICKR_URL:String = " http://api.flickr.com/services/rest/";
      private var eventDispatcher:EventDispatcher;
      private var pendingArgs:Array;

      public function PhotoProxy() {
         eventDispatcher = new EventDispatcher();
      }

      // The following event handler is called when the
      // reflection call is made to the Flickr API. The results
      // of this call tell us what to name the original request's
      // parameters and allows us to build a query string with
      // name/value pairs
      private function onReflectionComplete(event:Event):void {
         var queryString:String = "";
         var reflection:XML = XML(event.target.data);
         var methodArguments:XMLList = reflection.arguments.argument;
         for(var i:Number = 0; i < pendingArgs.length; i++) {
            if(pendingArgs[i] != null) {
               queryString += "&" + methodArguments[i][email protected]() + "=" +
               pendingArgs[i];
            }
         }
         var loader:URLLoader = new URLLoader();
         var request:URLRequest = new URLRequest(PhotoProxy.FLICKR_URL + "?method=" +
            [email protected]() + queryString);
         loader.addEventListener(Event.COMPLETE, onComplete);
         loader.load(request);
      }

      // This event handler is called when the real result is
      // received from the Flickr API. It simply broadcasts this
      // data as a DataEvent event.
      private function onComplete(event:Event):void {
         dispatchEvent(new DataEvent(Event.COMPLETE, false, false,
         XML(event.target.data)));
      }
   
      // This is the method that captures the request. It is a
      // part of the flash.utils.Proxy class.
      flash_proxy override function callProperty(methodName:*, ...args):* {
          pendingArgs = args;
          pendingArgs.unshift(PhotoProxy.API_KEY);
          var loader:URLLoader = new URLLoader();
          var request:URLRequest = new URLRequest(PhotoProxy.FLICKR_URL +
             "?method=flickr.reflection.getMethodInfo&method_name=flickr.photos." +
             methodName.toString() + "&api_key=" + PhotoProxy.API_KEY);
          loader.addEventListener(Event.COMPLETE, onReflectionComplete);
          loader.load(request);
          return methodName.toString();
      }

      public function addEventListener(type:String, listener:Function,
         useCapture:Boolean = false, priority:int = 0, weakRef:Boolean = false):void {
         eventDispatcher.addEventListener(type, listener, useCapture, priority, weakRef);
      }

      public function dispatchEvent(event:Event):Boolean {
         return eventDispatcher.dispatchEvent(event);
      }

      public function hasEventListener(type:String):Boolean {
         return eventDispatcher.hasEventListener(type);
      }

      public function removeEventListener(type:String, listener:Function,
         useCapture:Boolean = false):void {
         eventDispatcher.removeEventListener(type, listener, useCapture);
      }

      public function willTrigger(type:String):Boolean {
         return eventDispatcher.willTrigger(type);
      }

   }

}

Using the Photo Proxy

To use this photo proxy, we need to slightly modify the main class from the first remote proxy example. The only change is the reference to the PhotoProxy class in place of the PhotoSearchProxy class. Also, to show that all the photo operations are now available through this proxy, we will now call the getRecent() operation.

package {

   import com.peachpit.aas3wdp.proxypattern.FlickrResultEvent;
   import com.peachpit.aas3wdp.proxypattern.PhotoProxy;
   import flash.display.Sprite;
   import flash.events.Event;
   import flash.events.DataEvent;
   
   public class RemoteProxyExample extends Sprite {

      public function RemoteProxyExample() {
         var flickr:PhotoProxy = new PhotoProxy();
         flickr.addEventListener(Event.COMPLETE, onComplete);
         flickr.getRecent();
      }

      private function onComplete(event:DataEvent):void {
         trace(event.data);
      }
   }

}

Adapter and Façade Patterns

The Adapter and Façade patterns are very similar to the Proxy pattern. The main difference is that a Proxy class has the same public methods (usually by implementing the same interface) as the object it represents. The Adapter and Façade patterns don’t necessarily have the same methods as the object (or objects) they represent. Although the Adapter and Façade patterns are almost identical, the difference is that an Adapter’s purpose is to convert an object’s API, whereas the Façade’s purpose is to simplify.

Summary

In this chapter, we examined the Proxy pattern and its many uses. The two flavors of Proxy pattern we looked at are the Virtual and Remote Proxy patterns. We also went through several examples to fully understand the Proxy pattern:

  • We used the built-in flash.display.Loader class to proxy the loading of an image.

  • We created a Virtual Proxy to defer serialization of XML to a model object.

  • We built a simple Remote Proxy for handling Flickr searches.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.37.147