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.

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.




Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net