Note: After three Flyweight Saga entries, I think I have all of the parts explained and working like they should. To double-check, the following was presented at the 2007 OOPSLA conference in Montreal. The great comments that the readers of this blog provided were most helpful, and further comments by those in the OOPSLA Killer Examples session all added to what I hope works to clarify using the Flyweight Design Pattern with ActionScript 3.0. Also, everything in this article is based on Design Patterns Elements of Reusable Object-Oriented Software by the Gamma, et al.
Air traffic controllers look at virtual simulations of hundreds of aircraft. The images on a screen give the ATCs the information they need for separating the many planes under their control. The hub airports such as Chicago O’Hare and Dallas-Ft. Worth have to juggle hundreds of simultaneous flights arriving at and departing from their respective airports. To maintain accuracy, images that display position, heading, altitude, level flight, ascent and descent must be updated frequently, accurately and quickly. In looking over the set of design patterns set forth by Gamma, et al, the Flyweight pattern offers the following:
- An application uses a large number of objects
- Storage costs are high because of the sheer quantity of objects
- Most object state can be made extrinsic
- Many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed
- The application doesn’t depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects
That pretty well fills the bill for what is required. We need a large number of objects (airplane images), the sheer quantity has high storage costs, the extrinsic state such as heading, horizontal position and vertical position can be made extrinsic, once extrinsic state is removed, a few objects can be used to represent groups of objects, and the application is not dependent of object identity. This is not to say that one cannot be distinguished from the other, but rather the object identity is relative to its extrinsic characteristics.Figure 1 shows the basic class diagram.
The key elements of the class include the following:
- The Flyweight class, (an abstract class or interface) with the operation with parameters for the extrinsic states.
- A Flyweight Factory that aggregates the Flyweight class. (The ball at the end of the aggregation arrow indicates that multiple aggregations may exist.)
- A Concrete Flyweight contains the extrinsic states and the intrinsic states. This is a shared object
- (Optionally) An unshared concrete Flyweight containing the extrinsic state but cannot be shared.
- A Client participant. The Client class in this design pattern has a responsibility and has acquaintance relations with both the concrete flyweights and the factory classes.
The primary functionality of the Flyweight design pattern is the ability to quickly make multiple copies of an object by creating one instance and sharing it for multiple re-uses. The instances and reference to the instances are generated in a Flyweight Factory that aggregates the Flyweight interface or abstract class. The intrinsic value shared is stored in a concrete Flyweight and then shared for multiple uses by assigning different extrinsic values. A Flyweight shared object is one with a common intrinsic value that is used by all references to the Flyweight. The extrinsic values provide the shared object with contextual values that are not shared.
Aggregation and Acquaintance
A key relationship between the Flyweight and Flyweight Factory is that of aggregation. The factory participant is the aggregator and the flyweight the aggregatee. The nature of the aggregation relationship is unique in that the aggregator holds a dependent reference in the sense that its life is dependent on the life of the aggregatee. The following shows what such a relationship looks like at the point where it occurs in the code:
public function getIntrinsic (key:String,... rest):FlyweightPlane
In this case assigning the return type as FlyweightPlane, the Flyweight interface or abstract class, makes the aggregation. The aggregate can only work as long as the FlyweightPlane is extant, and so stands as an example of aggregation in the sense that that have identical lifetimes.The Client participant is connected to the other participants through acquaintance. It has an acquaintance with the Factory and Concrete Client[s]. For instance, declaring an instance of the Factory in the Client participant establishes an acquaintance:
private var planeFactory; … planeFactory=new PlaneFactory() plane1=planeFactory.getIntrinsic("east");
Likewise, the Client participant establishes an acquaintance with the Concrete Flyweight, but far more indirectly. For example, the line,
plane1.doPlane (xPos,yPos,w,h,curve);
creates an acquaintance through the PlaneFactory aggregation of the Flyweight and its subclass, a Concrete Flyweight. In this case, the acquaintance is through the key Flyweight operation (doPlane) implemented in the Concrete Flyweight.
Intrinsic and Extrinsic States
Another defining characteristic of the Flyweight is the handling of intrinsic and extrinsic states. An intrinsic state is immutable and cannot be changed, and as such can be used by other references to the same instance. The intrinsic state is stored in the Concrete Flyweight. For example, a car has an engine (intrinsic state) however the car has many different contexts (extrinsic state), such as it’s location, whether it is moving or stopped and other states that cannot be shared (like the same parking place).The Flyweight establishes an operation to specify the possible extrinsic states as parameters. The Client participant is responsible for supplying the extrinsic state. For example, the line,
plane1.doPlane (xPos,yPos,w,h,curve);
requires values for five extrinsic states. The horizontal and vertical position of the sprite, its width and height and the curve value of a rounded rectangle are the included extrinsic parameters. The Following line
plane1.doPlane (134,220,60,10,20);
draws a button-shaped rectangle positioned at:
- horizontal position = 134
- vertical position = 220
- rounded rectangle width = 50
- rounded rectangle height = 10
- rounded rectangle curve=20
These values are all extrinsic states of the object provided by the client.An intrinsic state can be anything sharable. For example, in the following examples, the intrinsic value is the color. All of the objects have the same color even though the instance references have different horizontal and vertical positions.
The Flyweight Factory
Creating multiple references to the same instance is possible because of the Flyweight Factory. The basis of the Flyweight Factory is its ability to recognize an extant instance and re-use it by adding it to an associative array (or hash table). This has the effect of re-using the same object’s intrinsic state by adding it to the array. In most discussions of the Flyweight Factory, hash tables are used. Both C# and Java, for example, use hash tables. ActionScript 3.0 has no hash tables so associative arrays used to effect equivalent results. Associative arrays are built using the Object class rather than the Array class. (None of the Array class methods or properties are available when keys are used instead of numeric indices.)
Shared Object
With the creation of a new element of an associative array each key corresponds to a property name. The associated value and name establish a key and value pair. If the key is undefined, the associative array creates a new one. The following script shows a generic example:
public class Factory { //Associative Array protected var storeFlyweight:Object={}; public function Factory() { getFlyweights (); } private function getFlyweights () { storeFlyweight["first"]=new ConcreteFlyweight(intrinsic); storeFlyweight["second"]=new ConcreteFlyweight(intrinsic); } public function getIntrinsic (key:String,... rest):Flyweight { switch (storeFlyweight[key] != undefined) { case true : break; case false : storeFlyweight [key]=rest[0]; break; } return storeFlyweight [key]; } }
Unshared Objects
As can be seen, the Flyweight Factory is pretty much like any other factory class except that it returns multiple copies of a single instance. In other words, it’s a shared object. In looking at Factory Method factories, you will not see this. For instance, the following example by Chandima Cumaranatunge (Sanders and Cumaranatunge, 2007) is for a simple factory and it deals with two different instances of an object:
public class Creator { public static function simpleFactory(product:String) { if (product == "p1") { return new product1( ); } else if (product == "p2") { return new product2( ); } } }
The result is that more objects are created resulting in more elements to be stored and displayed. Thus, rather than one instance, the results are multiple instances.
A Simple Flyweight Example: Making Balls
To get started, it’s best to start with a minimalist example so that all of the participants can be seen. In this set of examples, the focus is on the following participants:
- Flyweight
- Flyweight Factory
- Concrete Flyweight (Shared)
- Client
The Unshared Concrete Flyweight is not included since it is optional and at this level really is unneeded.The example does nothing more than create several instances of a ball object. Four of the balls are created using the Flyweight Factory and one is created by simply instantiating an instance of the ball object (Green). The unshared object is to illustrate that there’s no discernable difference in appearance of shared or unshared objects.
Abstract Flyweight
package { //Abstract class Flyweight import flash.display.Sprite; public class FlyweightBall extends Sprite { function doCircle (xPos:uint,yPos:uint,radius:uint):void {} } }
Flyweight Factory
package { //Flyweight Factory public class BallFactory { protected var ballIntrinsic:Object={}; public function BallFactory () { ballIntrinsic["red"]=new FlyBall(0x990000); ballIntrinsic["yellow"]=new FlyBall(0xffff00); } public function getIntrinsic (key:String,... rest):FlyweightBall { switch (ballIntrinsic[key] != undefined) { case true : break; case false : ballIntrinsic[key]=rest[0]; break; } return ballIntrinsic[key]; } } }
Concrete Flyweight
package { //Concrete Flyweight public class FlyBall extends FlyweightBall { protected var fill: uint; public function FlyBall (fill:uint) { this.fill=fill; } override function doCircle (xPos:uint,yPos:uint,radius:uint):void { graphics.beginFill (fill); graphics.drawCircle (xPos,yPos,radius); graphics.endFill (); } } }
Client
package { //Client class import flash.display.Sprite; public class BallClient extends Sprite { //Client data to provide extrinsic state details private var xPos:uint; private var yPos:uint; private var radius:uint; private var cir1:FlyweightBall; private var cir2:FlyweightBall; private var cir3:FlyweightBall; private var cir4:FlyweightBall; private var cir5:FlyBall; public function BallClient () { var ballFactory:BallFactory=new BallFactory; cir1=ballFactory.getIntrinsic("red"); shuffle (); cir1.doCircle (xPos,yPos,radius); addChild (cir1); cir2=ballFactory.getIntrinsic("red"); shuffle (); cir2.doCircle (xPos,yPos,radius); addChild (cir2); cir3=ballFactory.getIntrinsic("yellow"); shuffle (); cir3.doCircle (xPos,yPos,radius); addChild (cir3); cir4=ballFactory.getIntrinsic("yellow"); shuffle (); cir4.doCircle (xPos,yPos,radius); addChild (cir4); //Unshared--Not a Flyweight instance cir5=new FlyBall(0x009900); shuffle (); cir5.doCircle (xPos,yPos,radius); addChild (cir5); } private function shuffle () { //Generate extrinsic states xPos=Math.round(Math.random() * 500); yPos=Math.round(Math.random() * 300); radius=Math.round(Math.random() * 100); } } }
Figure 2 shows a typical output. (Because the radii and Cartesian coordinates are random, the output is different each time the application launches and what you see may be entirely different.)
The Flyweight’s only intrinsic value is the color, and when a Flyweight is created in the Factory, it is done so providing a color value. The name of the object is stored in the associative array, and when another request is made using the same name, the instance is shared with the new request. The extrinsic value are all provided by the Client using random values.

Figure 2: Four Flyweights and an Unshared Green ObjectShowing Single Instance with Multiple Elements
The problem with the ball example is that it is a take my word for it example. That is, it demonstrates nothing to convince or show that the two red and the two yellow balls are any different from the green one. It is useful insofar as the code shows that the green ball is not created in the same way as the others, but the consequences of that are not too clear.
Beyond Extrinsic: Instance External Properties
The way to show that a single instance contains multiple uses of a single object is to change a property of the instance to have an effect on all instances in the object. The extrinsic values can change the horizontal and vertical positions of the balls as well as the radius of each ball. So, while the two yellow balls in Figure 2 have different positions, they are still part of a single instance of the ball.One property that is not part of the extrinsic parameters is rotation. However, rotating a ball isn’t much help because all rotation angles look the same. So, now would be a good time to start building the air traffic control image representing a single aircraft. Thus, instead of a ball, this next example will use an airplane and different extrinsic values. However, rotation will not be one of these extrinsic values. That will be saved for the entire instance. The following application makes these adjustments.
Abstract Flyweight
package { //Abstract class Flyweight import flash.display.Sprite; public class FlyweightPlane extends Sprite { function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void {} } }
Flyweight Factory
package { //Flyweight Factory public class PlaneFactory { protected var rectIntrinsic:Object={}; public function PlaneFactory () { rectIntrinsic["red"]=new MakePlane(0x990000); rectIntrinsic["green"]=new MakePlane(0x009900); rectIntrinsic["blue"]=new MakePlane(0x000099); } public function getIntrinsic (key:String,... rest):FlyweightPlane { switch (rectIntrinsic[key] != undefined) { case true : break; case false : rectIntrinsic[key]=rest[0]; break; } return rectIntrinsic[key]; } } }
Concrete Flyweight
package { //Concrete Flyweight public class MakePlane extends FlyweightPlane { protected var fill: uint; public function MakePlane (fill:uint) { this.fill=fill; } override function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void { graphics.beginFill(fill); graphics.drawRoundRect(xPos,yPos,w,h,curve) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-18,yPos+17,45,10,9) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-7,yPos+37,25,7,12) graphics.endFill(); } } }
Client
package { //Client class import flash.display.Sprite; public class PlaneClient extends Sprite { //Client data to provide extrinsic state details private var xPos:uint; private var yPos:uint=0; private var w:uint; private var h:uint; private var curve:uint; private var planeFactory:PlaneFactory; private var plane1:FlyweightPlane; private var plane2:FlyweightPlane; private var plane3:FlyweightPlane; private var plane4:FlyweightPlane; private var plane5:FlyweightPlane; private var plane6:FlyweightPlane; public function PlaneClient () { planeFactory=new PlaneFactory ; plane1=planeFactory.getIntrinsic("red"); place (); plane1.doPlane (xPos,yPos,w,h,curve); addChild (plane1); plane1.rotation=15; plane2=planeFactory.getIntrinsic("red"); place (); plane2.doPlane (xPos,yPos,w,h,curve); addChild (plane2); plane3=planeFactory.getIntrinsic("blue"); place (); plane3.doPlane (xPos,yPos,w,h,curve); addChild (plane3); plane3.rotation=0; plane4=planeFactory.getIntrinsic("blue"); place (); plane4.doPlane (xPos,yPos,w,h,curve); addChild (plane4); plane4.rotation=25; plane5=planeFactory.getIntrinsic("green"); place (); plane5.doPlane (xPos,yPos,w,h,curve); addChild (plane5); plane5.rotation=180; plane6=planeFactory.getIntrinsic("green"); place (); plane6.doPlane (xPos,yPos,w,h,100); addChild (plane6); plane6.rotation=345; } private function place () { //Generate extrinsic states xPos=250; yPos +=60; w=10; h=50; curve=20; } } }
The Client supplies the values for the extrinsic parameters, but it can also add values for properties of the entire object beyond the extrinsic. An important element is to point out the different rotation values for the object pairs:
- red: plane 1-rotation= 15 ; plane2-rotation=none;
- blue: plane3 -rotation= 0 ; plane4-rotation= 25;
- green: plane5 -rotation= 180 ; plane6-rotation= 345;
When the overall rotation is viewed in the output, it’s clear that both images take the rotation value of the last airplane in the script for their group where a rotation value is specified. This can be seen in Figure 3.

Figure 3. Three objects with six airplanes
The red airplanes (plane1 and plane2 in the code) at the top of Figure 2 are at a 15° angle (0° is straight up). Because plane1 and plane2 are part of the same “red” instance, their angle is the same. Likewise with the other two pairs, even where one is assigned one rotation value and the other a different one, both images show the same rotation. The x and y values of the airplanes are relative to the instance in which each pair resides. The green airplanes have different noses because they have different values for the curve of the rounded rectangle the makes up the fuselage—made possible because each has an extrinsic value for the curve value of a rounded rectangle. So, while references to the same instance have the same intrinsic value (color in this case), the extrinsic values distinguish one from the other even though they are part of the same flyweight instance. Because rotation is a property of the DisplayObject class, it treats all instances as separate entities, but not the multiple references to the same instance. So when an instance is rotated, all references to that instance are rotated in the same direction.
Multiple Updates
Now that there’s some kind of grasp on what’s going on with flyweights and how they are structured, it’s time to really put them to work. Going back to the original purpose of this Flyweight to place many images on the screen quickly using as little memory as possible and update them regularly, this next application constantly refreshes 50 different images. Each reference to a single instance is stored in an array to quickly add images to the screen in a test of concept. The application employs the following code:
Abstract Flyweight
package { //Abstract class Flyweight import flash.display.Sprite; public class FlyweightPlane extends Sprite { function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void {} } }
Flyweight Factory
package { //Flyweight Factory public class PlaneFactory { //Associative Array protected var planeIntrinsic:Object={}; public function PlaneFactory () { getPlane (); } private function getPlane () { planeIntrinsic["east"]=new MakePlane(0x990000); } public function getIntrinsic (key:String,... rest):FlyweightPlane { switch (planeIntrinsic[key] != undefined) { case true : break; case false : planeIntrinsic[key]=rest[0]; break; } return planeIntrinsic[key]; } } }
Concrete Flyweight
package { //Concrete Flyweight public class MakePlane extends FlyweightPlane { protected var fill: uint; public function MakePlane (fill:uint) { this.fill=fill; } override function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void { graphics.beginFill(fill); graphics.drawRoundRect(xPos,yPos,w,h,curve) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-18,yPos+17,45,10,9) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-7,yPos+37,25,7,12) graphics.endFill(); } } }
Client
package { //Client class import flash.display.Sprite; import flash.utils.setInterval; import flash.utils.Timer; import flash.events.TimerEvent; public class Apprentice extends Sprite { //Client data to provide extrinsic state details private var xPos:uint; private var yPos:uint=50; private var w:uint; private var h:uint; private var heading:uint; private var curve:uint; private var plane:Array; private var sweepTime:uint=1000; private var ee:Boolean=false; private var planeFactory:PlaneFactory; private var sweep:Timer; public function Apprentice () { planeFactory=new PlaneFactory ; plane=new Array(); upDate (); sweep=new Timer(sweepTime,0); sweep.addEventListener("timer",newPos) sweep.start(); } private function makeGroup ():void { yPos=80; heading=90; for (var air:uint=0; air < 50; air++) { plane[air]=planeFactory.getIntrinsic("east"); place (); plane[air].doPlane (xPos,yPos,w,h,curve); addChild (plane[air]); plane[air].rotation=heading; } plane[0].x=700,plane[0].y=150; } private function place ():void { //Generate extrinsic states xPos=Math.round(Math.random()*500); yPos= Math.round(Math.random()*400); w=10; h=50; curve=20; } private function cleanup ():void { for (var air:uint=0; air < 50; air++) { removeChild (plane[air]); plane[air]=null; } } public function newPos (e:TimerEvent):void { upDate() } private function upDate ():void { if (ee) { cleanup (); } makeGroup (); } } }
This flyweight application is very similar to the others except that it uses a timer to refresh the images and an array to hold the different instance references. The idea is to simulate the changing positions of aircraft, but instead of changing the actual x and y positions as the aircraft enter the traffic pattern prior to landing, it uses random positioning values to demonstrating quickly rendering large number of images to the screen in different positions.
The Sorcerer’s Apprentice
The application refreshes the images every second, but what occurs is unexpected. Figures 4-6 show the rapid increase of images on the screen:

Figure 4: The expected number of images appears using a single instance.

Figure 5: After a few seconds, it becomes apparent that the associative array is loading the new references on top of the old ones.

Figure 5: Eventually the images become unrecognizable as more and more images are placed
The results of this application reminded me of Mickey Mouse playing the sorcerer’s apprentice in Disney’s animated classic, Fantasia. Based on a poem by the same name by Johann Wolfgang von Goethe, the following lines showed that Goethe knew we’d be working with sprites sooner or later:
Every step and saying
That he used, I know,
And with sprites obeying
My arts I will show.
In the animated 1940 film Mickey Mouse decides to use the sorcerer’s magic to do his chores—carrying water from the well up to a tank to be filled. He uses magic on some brooms and they begin carrying the water up to the tank, but soon the tank fills and he doesn’t know the magic words to stop them, and they just keep on going very much like this application.
The Wrong Spell
The method used to refresh the sprites removed both the image from the stage and set the elements to null.
private function cleanup ():void { for (var air:uint=0; air < 50; air++) { removeChild (plane[air]); plane[air]=null; } }
However, the results show that the cleaup() method simply does not work. So what’s going on? How is it possible for the array to keep adding to the number of images even though the array and all of the elements are cleared from the stage and set to null? The answer is that the method addresses the wrong array. This shows that the Array class used to generate references to the instance of the Flyweight object did not store those references. Rather, they are stored in the associative array in the Flyweight Factory. Therefore, we need a method in the Flyweight Factory to clear them out with every update. Fortunately, it’s quite simple. All that is required to set a given key in the associative array to null. So, in the above example, the following line will do the trick.
planeIntrinsic["east"]=null;
Because a single instance was generating several references with position information and other extrinsic values, every time the set of 50 images was refreshed, it just added to those stored in the associative array. The Array class worked to generate new references to the associative array, but it never removed any. All that the cleanup script did was to empty the contents of the array that shoved more references into the instance held in the associative array.
Multiple Instances and Many References
The final step is to install a system that deals with multiple instances, with each instance referenced by an updated set of new extrinsic values. To continue with the set used so far, this final application will take four instances with each on a different heading and updated using random values. The updates will be every 3 seconds (or 3000 milliseconds) and each set will display a set of 200 images based on four instances, each represented by a different color. The following script finally pulls all of the parts together:
Flyweight
package { //Abstract class Flyweight import flash.display.Sprite; public class FlyweightPlane extends Sprite { function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void {} } }
Flyweight Factory
package { //Flyweight Factory public class PlaneFactory { //Associative Array protected var planeIntrinsic:Object={}; public function PlaneFactory () { getPlane (); } private function getPlane () { planeIntrinsic["east"]=new MakePlane(0x990000); planeIntrinsic["west"]=new MakePlane(0x009900); planeIntrinsic["north"]=new MakePlane(0x000099); planeIntrinsic["south"]=new MakePlane(0xFF6600); } public function getIntrinsic (key:String,... rest):FlyweightPlane { switch (planeIntrinsic[key] != undefined) { case true : break; case false : planeIntrinsic[key]=rest[0]; break; } return planeIntrinsic[key]; } public function clearPlane () { planeIntrinsic["east"]=null; planeIntrinsic["west"]=null; planeIntrinsic["north"]=null; planeIntrinsic["south"]=null; getPlane (); } } }
Concrete Flyweight
package { //Concrete Flyweight public class MakePlane extends FlyweightPlane { protected var fill: uint; public function MakePlane (fill:uint) { this.fill=fill; } override function doPlane (xPos:uint,yPos:uint,w:uint,h:uint,curve:uint):void { graphics.beginFill(fill); graphics.drawRoundRect(xPos,yPos,w,h,curve) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-18,yPos+17,45,10,9) graphics.endFill(); graphics.beginFill(fill); graphics.drawRoundRect(xPos-7,yPos+37,25,7,12) graphics.endFill(); } } }
Client
package { //Client class import flash.display.Sprite; import flash.utils.setInterval; import flash.utils.Timer; import flash.events.TimerEvent; public class CrowdedSky extends Sprite { //Client data to provide extrinsic state details private var xPos:uint; private var yPos:uint=50; private var w:uint; private var h:uint; private var heading:uint; private var curve:uint; private var planeE:Array; private var planeW:Array; private var planeN:Array; private var planeS:Array; private var sweepTime:uint=3000; private var ee:Boolean=false; private var planeFactory:PlaneFactory; private var sweep:Timer; public function CrowdedSky () { planeFactory=new PlaneFactory ; planeE=new Array(); planeW=new Array(); planeN=new Array(); planeS=new Array(); upDate (); sweep=new Timer(sweepTime,0); sweep.addEventListener("timer",newPos) sweep.start(); } private function makeEast ():void { yPos=80; heading=315; for (var air:uint=0; air < 50; air++) { planeE[air]=planeFactory.getIntrinsic("east"); place (); planeE[air].doPlane (xPos,yPos,w,h,curve); addChild (planeE[air]); planeE[air].rotation=heading; } ee=true; planeE[0].x=350,planeE[0].y=700; } private function makeWest ():void { yPos=80; heading=225; for (var air:uint=0; air < 50; air++) { planeW[air]=planeFactory.getIntrinsic("west"); place (); planeW[air].doPlane (xPos,yPos,w,h,curve); addChild (planeW[air]); planeW[air].rotation=heading; } planeW[0].x=700,planeW[0].y=500; } private function makeNorth ():void { yPos=80; heading=45; for (var air:uint=0; air < 50; air++) { planeN[air]=planeFactory.getIntrinsic("north"); place (); planeN[air].doPlane (xPos,yPos,w,h,curve); addChild (planeN[air]); planeN[air].rotation=heading; } planeN[0].x=250,planeN[0].y=200; } private function makeSouth ():void { yPos=0; heading=135; for (var air:uint=0; air < 50; air++) { planeS[air]=planeFactory.getIntrinsic("south"); place (); planeS[air].doPlane (xPos,yPos,w,h,curve); addChild (planeS[air]); planeS[air].rotation=heading; } planeS[0].x=650,planeS[0].y=150; } private function place ():void { //Generate extrinsic states xPos=Math.round(Math.random()*500); yPos= Math.round(Math.random()*400); w=10; h=50; curve=20; } private function cleanup ():void { removeChild (planeE[0]); removeChild (planeW[0]); removeChild (planeN[0]); removeChild (planeS[0]); planeE[0]=planeFactory.clearPlane(); } public function newPos (e:TimerEvent):void { upDate() } private function upDate ():void { if (ee) { cleanup (); } makeEast (); makeWest (); makeNorth (); makeSouth (); } } }
Figure 7 shows the final results. The underlying radar sweep is simply a movie clip running beneath the display.

Figure 7: Flyweight generating 200 images.Flyweight Design Pattern Reconsidered
In working through this pattern, which can be exhausting, it has a number of characteristics that are still elusive. The role of the unshared concrete flyweight is not clear, and some the examples I’ve seen may have missed the mark altogether. The use of the unshared concrete flyweight for assigning values for extrinsic states and how these are used in concert with a client with references to a shared object is not clear in most examples of the flyweight. However, it is a great pattern for understanding class relations and the nature of objects.
All in all, the Flyweight design pattern holds valuable lessons about OOP, especially some of the subtler features and certainly participant relations.With the speed of processors developing at high rates and storage now measured in gigabytes (or terabytes), whether the speed increase gained is worth the added effort is not as obvious as it once was. However, if the asymptotic performance is optimal, I see nothing wrong with looking for ways to speed up the display of large numbers of elements on a stage. I doubt that the 200 images (or even 1000) would be enough to warrant spending too much time on the Flyweight, but when the numbers being reaching 10,000 or even higher, an extra byte or two might come in handy.

looks like figure 1 has gone off the radar…
Sorry about that. It’s only a fraction of the materials. I hope to get the rest up today.
Bill
I’ve never seen anyone switch a Boolean. That’s not what switch was made for. Also, your getIntrinsic() method is not a good use of …rest because you’re only ever passing one possible argument.
Your entire getIntrinsic() method need only be one line.
public function getIntrinsic (key:String, value:* = undefined):Flyweight
{
return store.hasOwnProperty(key) ? store[key] : store[key] = value;
}
Cheers!
Well, you need to cast that actually so you don’t get compiler errors.
return Flyweight(store.hasOwnProperty(key) ? store[key : store[key] = value)
Hi Steven,
I think that’s the first time I used a switch with a Boolean, and I’m trying to figure out why. At the last OOPSLA meeting in Montreal, we were bombarded with taking out conditional statements for better structure. (The State DP was mentioned as a model for ridding one of conditional statements.) Of course the switch statement is a conditional but didn’t look as much like one, and maybe that was floating around in my head. (Honestly I’m just not sure!)
I really like your idea about the use of the …rest parameter, and I’ll give it a try. As for the conditional using the ternary operator, that too is interesting. In looking at the code, did you mean to use the *= operator or the != operator? If you did mean *=, that I do not understand its use in context and would love to hear how it is employed.
While the content of this article is great, you should really cite when you quote from a book. Large chunks were used from “Design Patterns Elements of Reusable Object-Oriented Software”
Asa,
Thank you for your comments. Of course you are absolutely correct, and I just added such a reference at the very beginning of the article. In the three previous entries in the Flyweight Saga, I mentioned GoF and their book several times, but here it was somehow overlooked. I just assumed that everyone would know that it is based on the GoF book. That was certainly a rude oversight on my part.
However, thanks to your comments, that has been fixed and right at the beginning I make the overlooked reference!
“In looking at the code, did you mean to use the *= operator or the != operator? If you did mean *=”
… getIntrinsic (key:String, value:* = undefined) …
I think “value:* = undefined” as in “optional parameter named ‘value’ of any type, with default value as ‘undefined’”.
Anyway, very interesting read. Thank you.
Hi Lee,
It only took me 6 months to reply, but about the time you posted your comment I was recovering from a broken leg and missed a lot of things!
As to using !=, I did mean to use that operator and not *=. Basically, it queries whether the value is undefined or not to generate a Boolean value used in a Switch statement. It’s an unusual way to use Switch as Steven Sacks noted, but it’s a nice clear way to illustrate the structure of the design pattern.
Thanks for your comment,
Bill