ActionScript 3.0 Clone: A Prelude to the Prototype Design Pattern

Where’s the Clone()?

The next design pattern I plan to tackle in ActionScript 3.0 is the Prototype Design Pattern. Getting started I ran into the clone() method in other languages like C#. As usual, that wasn’t much help. To my surprise, I found that several classes in ActionScript 3.0 have a clone() method such as the Rectangle class. However, while perfectly functional, cloning a rectangle with origins in the flash.geom namespace isn’t exactly what I had hoped for. (There’s no clone() method in the Shape class where I could make lots of clones of rectangle shapes I could put on the stage.) What I wanted was a way to clone objects.

Borrowing from Java

Buried in the Adobe ActionScript 3.0 Live Docs is a little note about cloning arrays. (see http://livedocs.adobe.com/flex/3/html/10_Lists_of_data_6.html) Using a method commonly found in Java programs, the note indicates that a deep copy clone is possible using this method. One can use concat() or slice() with no arguments to make a shallow copy. In most cases, GoF note that shallow copies will work fine, but for more complex structures a deep copy is required.

The docs list the key method as the following:

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
import flash.utils.ByteArray;
 
function clone(source:Object):*
{
    var myBA:ByteArray = new ByteArray();
    myBA.writeObject(source);
    myBA.position = 0;
    return(myBA.readObject());
}

That’s a nice little piece of code and simple to understand. The clone() method creates a ByteArray that writes the object (an array) and then returns it by reading the object it just wrote. Moreover, it’s a deep copy and as such a true clone in that all of the parts are delivered and not just pointers.

A Cloning Class

Cloning an array is fine, and I even added a note in Live Docs first asking for an example and then provided one. (I’m not trying to befuddle those who screen comments on Live Docs—it’s just an added bonus.) What I really want to do is to clone an object, and since an array is an object, it seemed to be an easy enough task. So I took the basic algorithm and made it the key method of a Clone class. Here’s the code:

Clone.as

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package
{
	import flash.utils.ByteArray;
	import flash.display.Sprite;
 
	public class Clone extends Sprite
	{
		private var cloneWork:ByteArray;
 
		public function Clone()
		{
			//Constructor
		}
 
		public function doClone(source:Object):*
		{
			cloneWork=new ByteArray();
			cloneWork.writeObject(source);
			cloneWork.position = 0;
			return (cloneWork.readObject());
		}
	}
}

Next, I created an object with lots of properties of different types—string, number and Boolean. Then, I tried cloning it, and it seemed to work. The following listing shows the client class that used the clone method—doClone() from the Clone class:

ObjectClone.as

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package
{
	import flash.display.Sprite;
 
	public class ObjectClone extends Sprite
	{
		private var clone:Clone;
		private var webLanguages:Object;
		private var moreWeb:Object;
 
		public function ObjectClone()
		{
			webLanguages={};
			webLanguages.flex="ActionScript 3.0";
			webLanguages.dotNet="C#";
			webLanguages.php="PHP";
			webLanguages.inHtml="JavaScript";
			webLanguages.bandWidth=2500000;
			webLanguages.usesFMS=true;
 
			for (var w:* in webLanguages)
			{
				trace("Property "+w+"="+webLanguages[w]);
			}
 
			trace("------------------------");
 
			moreWeb=new Object();
			clone=new Clone();
			moreWeb = clone.doClone(webLanguages);
 
			for (var k:* in moreWeb)
			{
				trace("Property "+k+"="+moreWeb[k]);
			}
		}
	}
}

The following output shows that the cloned object has all of the properties and values of the original:

Property flex=ActionScript 3.0
Property dotNet=C#
Property usesFMS=true
Property php=PHP
Property inHtml=JavaScript
Property bandWidth=2500000
------------------------
Property flex=ActionScript 3.0
Property dotNet=C#
Property bandWidth=2500000
Property usesFMS=true
Property php=PHP
Property inHtml=JavaScript

The mystery is why the output is ordered the way it is. (If anyone knows, please send a comment.) All of the pieces to the objects are there in the clone, and they reflect the correct values of the original. The clone may seem a bit Frankenstein-ish, since the parts are in a different order, but the original seemed to move the parts around as well; so I’m not too concerned.

Next Stop: Prototype Design Pattern

Now that I have a working clone method for cloning objects, the next step will be working it into the Prototype pattern. The issue of serialization has not been broached here, but it is a topic I hope to take up in discussing the Prototype. Also, there’s not a lot of difference between an Object as used in this example and an associative array (ActionScript’s hash table equivalent). So, I may make it look more like an associative array rather than a plain vanilla object even though I like the idea of an object better. Also, I believe that using the Prototype Design Pattern beyond a simple example may require more work in the clone department to clone more sophisticated objects like MovieClips and Components.

Comments encouraged!!

  • Share/Bookmark

Related posts:

  1. ActionScript 3.0 Prototype Design Pattern: A Minimalist Example
  2. An ActionScript 3.0 Recursion Excursion
  3. The ActionScript 3.0 Flyweight Saga: Part II Extrinsic States

23 Responses to “ActionScript 3.0 Clone: A Prelude to the Prototype Design Pattern”


  • When you iterate over properties in an object using for-in, and for-each, the order of those properties is explicitly not guaranteed. It’s part of the language spec, and I know I’ve seen it hidden in the docs somewhere.

    I assume this keeps things simple for implementers to optimize memory and performance. People often expect that the properties will be accessed in the order they are added to the object, but it might be much faster to access them in a different order. Again, only guessing.

  • Hi Josh,

    Your guess is spot on. Here it is right in the for..in statement AS 3.0 docs:

    Iterates over the dynamic properties of an object or elements in an array and executes statement for each property or element. Object properties are not kept in any particular order, so properties may appear in a seemingly random order.

    It’s all there then. The for..in loop just doesn’t return the properties in any particular order. So, it now makes perfect sense why both the original and clone had different orders from the order in which the properties were placed in the object.

    Many thanks,
    Bill

  • hi there, your website is just very nice, few people write about dp in AS3 …so your website is a great ressource for us.
    Not a bad idea to use the ByteArray class when of course a clone() method doesn’t exeist already..
    Do you advise the use of byteArray event though a clone() method already exist?

  • Hi Gropapa,

    I don’t know if you know Jesse Warden, but when I was trying to decide whether to try and use the ObjectUtility.copy() method–in Flex– or use the clone() method by developing the Java-type code in the docs, he suggested that using the clone() method was better. It may have been the difference between a shallow and deep copy. Using clone() with the ByteArray class I was able to get a deep copy. I’m not sure whether that would be the case using copy() or an array method.

    I’d really like to hear from anyone who’s used the ObjectUtility.copy() method to get a more in-depth idea.

    Thanks,
    Bill

  • What am I missing? (cool site BTW!)

    > The problem with a shallow copy is that any changes to one will make changes in the other. That is, the property values of the copy cannot be changed without making the same changes in the original.

    code:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    var ar:Array = new Array("1", "2", "3", "4", "5");
    trace(ar);
    var nar:Array = new Array();
    nar = ar.slice();
    trace (nar);
    trace("**********");
    nar[1] = "new";
    trace(ar);
    trace(nar);

    output:

    1,2,3,4,5
    1,2,3,4,5
    **********
    1,2,3,4,5
    1,new,3,4,5

  • JCG,

    You’re quite right. I think I found where I may have tripped up in getting shallow copies right. On page 121 of GoF, they discuss a C++ member-wise copy and I took that to be a more general discussion of a shallow copies in the same section. In fact, GoF point out,

    A shallow copy is simple and often sufficient….”

    in talking about how SmallTalk generates a shallow copy by default. However, they go on to note that “cloning prototypes with complex structures usually requires a deep copy.”

    In October I’ll be making a presentation at the OOPSLA Annual Conference, and while I’m there, I’ll talk to some people about the differences between deep and shallow copies in general. In the meantime I’d like to try and find out exactly how shallow and deep copies are made in ActionScript and how they affect cloning in the Prototype design pattern.

    I made changes in the original short article on cloning to reflect the fact that shallow copies can be used for creating a Prototype.

    Thanks again,
    Bill

  • Serialization of AMF types really varies. AMF has a way of determining things and it may be a big-endian or little-endian scenario of how things are written and read. Oh, and if you really want to serialize a class in depth, use describeType to view the complete layout of a class instance, and call a corresponding IExternalizable method, such as writeUTF(), writeBoolean(), writeObject() etc on each member of the class and even to members of each member and so on. That would be a brute-force way to do it, but it would guarantee deep-copying.

  • Hi TK,

    I looked at the Flex docs on IExternalizable, and as you say, it is certainly the interface to go with for a deep-deep copy. Also, I believe that in order to copy some of the more complex classes/UIs, etc., that’s what I’ll have to do. To get started, though, I’m going to use the simpler deep copy as described in the article. This would be the minimalist example so that we can see how everything works in the Prototype.

    In the meantime, if you’d be interested in whipping up a little clone using your suggested method, we’d be giddy with gratitude.

    Thanks a million,
    Bill

    P.S. With luck, I’ll be able to get a little time this week or next to put together a simple working example of a Prototype DP. (Work keeps getting in the way of fun.)

  • for the Prototype Pattern you may want to look at this blog post
    http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html

    now for AS3, because the language as kept his dynamic roots (dynamic and prototype statements), you can say that we do have the Prototype Pattern built in.

    About cloning objects you also want to explore “is-like-a” and “has-like-a” relationships (instead of “is-a”/”has-a”)

    something like that (sorry for the boring code)
    —-

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    
    var ProgrammingLanguage:Function = function() {};
    ProgrammingLanguage.prototype.hasClass = false;
    ProgrammingLanguage.prototype.hasprototype = false;
     
    var ClassBased:Function = function() {};
    ClassBased.prototype = new ProgrammingLanguage();
    ClassBased.prototype.hasClass = true;
     
    var PrototypeBased:Function = function() {};
    PrototypeBased.prototype = new ProgrammingLanguage();
    PrototypeBased.prototype.hasprototype = true;
     
    var pl:* = new ProgrammingLanguage();
    var cb:* = new ClassBased();
    var pb:* = new PrototypeBased();
     
    var ActionScript3:Function = function() {};
    ActionScript3.prototype = cb;
    ActionScript3.prototype.hasprototype = true;
    ActionScript3.prototype.isHybrid = true;
     
    var as3:* = new ActionScript3();
     
    var iterate:Function = function( name:String, o:Object ):void
    {
    	trace( "----" );
    	trace( name );
    	for( var p:String in o )
    	{
    		trace( "|_ "+p +"="+o[p] );
    	}
    	trace( "----" );
    }
     
     
     
    iterate( "pl", pl );
    trace( "is ProgrammingLanguage: " + (pl instanceof ProgrammingLanguage) );
    trace( "is ClassBased: " + (pl instanceof ClassBased) );
    trace( "is PrototypeBased: " + (pl instanceof PrototypeBased) );
    trace("");
     
    iterate( "cb", cb );
    trace( "is ProgrammingLanguage: " + (cb instanceof ProgrammingLanguage) );
    trace( "is ClassBased: " + (cb instanceof ClassBased) );
    trace( "is PrototypeBased: " + (cb instanceof PrototypeBased) );
    trace("");
     
    iterate( "pb", pb );
    trace( "is ProgrammingLanguage: " + (pb instanceof ProgrammingLanguage) );
    trace( "is ClassBased: " + (pb instanceof ClassBased) );
    trace( "is PrototypeBased: " + (pb instanceof PrototypeBased) );
    trace("");
     
    iterate( "as3", as3 );
    trace( "is ProgrammingLanguage: " + (as3 instanceof ProgrammingLanguage) );
    trace( "is ClassBased: " + (as3 instanceof ClassBased) );
    trace( "is PrototypeBased: " + (as3 instanceof PrototypeBased) );
    trace("");

    —-

    so if you read a bit the post from Stege Yegge you will see that we have directly in the language this system of Properties Pattern aka Prototype Pattern

    you can even inject an instance in the prototype to inherit its properties either at the object level or the prototype level

  • Hi Bill

    First of all, congrats for this website, is a great resource. Second, if I understood right you are looking for a clone method that does a deep copy of a custom-sophisticated class. I guess this should work:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    //this is a method of the cloned Class
     
    public function clone():* {
    			registerClassAlias("testClass",getDefinitionByName(getQualifiedClassName(this)) as Class)
    	var ba:ByteArray = new ByteArray();
    	ba.writeObject(this);
    	ba.position = 0;
    	return ba.readObject();
    }

    Of course the registerClassAlias method should be called only once, not every time you clone the class…but I was to lazy to write the entire class :D

    Hope it helps you!
    Cheers!

  • Hi Zw,

    Wow! That was some comment. Thank you. I took a quick look at Steve’s blog and your listing (not boring to me!!) and I really appreciate it.

    I’m still working on a Prototype ‘out in the open’ rather than the built-in one. However, that doesn’t mean I’m not planning on using the built-in Prototype; just waiting until I can build one from the ground up. In that way I feel I can fully understand what’s going on and use it whether it’s built-in or of my own making. However, your post will be used one way or the other!

    Thanks,
    Bill

  • Hi Radu,

    Ok, now you’ve got me interested. The clone method I used is almost identical to yours, but the most important part, registerClassAlias() is not in the listing and it is a big difference. Using it, there’s no need for a parameter in the clone() method itself, but I’m not sure what you do to point to the object you want to clone.

    Could you share with us the registerClassAlias() and how it is used with the clone()? That would be very interesting.

    Kindest regards,
    Bill

  • Hello Bill,

    I’ve made a new example. The registerClassAlias allows you to recover the class after you are reading the ByteArray. So, in your example if you try to clone anything else than an Object you would get an runtime error (Coercion failed). Using registerClassAlias would solve that. Now, as I was writing the example I realized that maybe there is a way to clone the instance properties without itinerate through them (most of the properties like x,y, buttonMode etc of the sprite were preserved in the cloned object). Here is the example:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    package 
    {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.net.registerClassAlias;
    	import flash.utils.ByteArray;
    	import flash.utils.getDefinitionByName;
    	import flash.utils.getQualifiedClassName;
     
    	public class Main extends Sprite 
    	{
    		private var mySprite:Sprite;
    		private var myCopy:Sprite;
     
    		public function Main():void 
    		{
    			if (stage) init();
    			else addEventListener(Event.ADDED_TO_STAGE, init);
    		}
     
    		private function init(e:Event = null):void 
    		{
    			removeEventListener(Event.ADDED_TO_STAGE, init);
     
    			mySprite = new Sprite();
    			//setting some properties for mySprite instance
    			mySprite.x = 300;
    			mySprite.y = 150;
    			mySprite.buttonMode = true;
    			mySprite.rotation = 45;
    			mySprite.graphics.beginFill(0xCCCCCC);
    			mySprite.graphics.drawRect(0, 0, 100, 100);
     
    			addChild(mySprite);
     
    			myCopy = clone(mySprite);
     
    			//some of the properties are the same
    			trace(myCopy.y);//traces 150
    			trace(myCopy.x);//traces 300
    			trace(myCopy.buttonMode)//traces true;
    			trace(myCopy.rotation)//traces 45;
     
    			trace('----------')
    			//and some are not the same (why?);
    			trace(myCopy.width);//traces 0
    			trace(myCopy.height);//traces 0
     
    		}
     
    		private function clone(obj:*):* {
    			registerClassAlias('mySprite', getDefinitionByName(getQualifiedClassName(obj)) as Class);
    			var ba:ByteArray = new ByteArray();
    			ba.writeObject(obj);
    			ba.position = 0;
    			return ba.readObject();
    		}
     
    	}
     
    }
  • Hi Radu,

    Thanks man. I like those methods you added (getDefinitionByName and getQualifiedClassName), and I want to try them out. Very interesting stuff.

    Kindest regards & thanks again,
    Bill

  • import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;

    you find them in the flash.utils package:D

    Regards
    Radu

  • Thank you for the clone method. Exactly what I needed. Thank you again.

  • Hi Bob,

    I’m really glad you have a practical use for it. Oddly, you hit upon an issue that I’d been thinking about–namely real world examples. For some applications, this kind of cloning is just what the doctor ordered for a practical problem they’ve been working on, but others grumble that it’s not real world.

    Well, what is considered real world and what is not depends on the project. So, while the more concrete an example that does something the more it is real world it is, but at the same time it can be so narrow that it’s difficult to see its applicability to other real world problems. You’ll soon see a post on this issue! All because of your good comment!

    Kindest regards,
    Bill

  • Hello I put the code

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public function double_click (event:MouseEvent){
      clone=new Clone();
      var clona:Object=new Object();
      clona=clone.doClone(event.target.parent.parent)
      Global.vars.carton.push(clona)
      Global.vars.carton[Global.vars.carton.length-1].x=Global.vars.carton[Global.vars.carton.length-1].x+12
      Global.vars.carton[Global.vars.carton.length-1].y=Global.vars.carton[Global.vars.carton.length-1].y+12
      //Global.vars.carton[Global.vars.carton.length-1].name="padre_" + String(Global.vars.carton.length+1)
    }

    And say me this

    TypeError: Error #1009: Cannot access a property or method of a null object reference.

  • Hi Israel,

    I’m not sure what you’ve got there or where you came up with the doClone() method or even the clone object. [e.g., clone=new Clone()].

    However, I’ve seen enough grumbling that the clone method that I took from the docs is insufficient for what a lot of people want. (Take a look at the ping-backs too, as they might be of some use.)

    All I wanted when I used the clone() method developed from the Array documentation was any clone() to illustrate elements of the Prototype Design Pattern and really nothing further. The cloning process has nothing inherently in it per se related to either design patterns or OOP principles.

    So in order to help you pull back on the frustration you and others have experienced in using the clone() method from the documentation; it is a method ONLY for cloning an Array. You’ll have to look elsewhere for more materials on ActionScript 3.0 cloning for anything other than an array.

    Good luck with your endeavor,
    Bill

  • Thank you very much for answering so fast and forgive my English pesimo.

    I have an Array of objects.

    Each object is a movieclip which has inside more objects. One is a image.

    I would like to duplicate. I have achieved through Senocular.

    This duplication gives me the impression that all the images reload each time a new instance … .

    Can I create a Sprite, put a image inside and all the new objects pointing to this file with AS3.

    Best reggards

    Code of senocular:

    ?View Code ACTIONSCRIPT
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    package com.senocular.display {
     
        import flash.display.DisplayObject;
        import flash.geom.Rectangle;
     
        /**
         * duplicateDisplayObject
         * creates a duplicate of the DisplayObject passed.
         * similar to duplicateMovieClip in AVM1
         * @param target the display object to duplicate
         * @param autoAdd if true, adds the duplicate to the display list
         * in which target was located
         * @return a duplicate instance of target
         */
        public function duplicateDisplayObject(target:DisplayObject, autoAdd:Boolean = false):DisplayObject {
            // create duplicate
            var targetClass:Class = Object(target).constructor;
            var duplicate:DisplayObject = new targetClass();
     
            // duplicate properties
            duplicate.transform = target.transform;
            duplicate.filters = target.filters;
            duplicate.cacheAsBitmap = target.cacheAsBitmap;
            duplicate.opaqueBackground = target.opaqueBackground;
            if (target.scale9Grid) {
                var rect:Rectangle = target.scale9Grid;
                // WAS Flash 9 bug where returned scale9Grid is 20x larger than assigned
                // rect.x /= 20, rect.y /= 20, rect.width /= 20, rect.height /= 20;
                duplicate.scale9Grid = rect;
            }
     
            // add to target parent's display list
            // if autoAdd was provided as true
            if (autoAdd && target.parent) {
                target.parent.addChild(duplicate);
            }
            return duplicate;
        }
    }
  • Hi Israel,

    Apparently, the clone() method that I got from the documentation only works with strings and numbers but not other objects in Arrays, such as Sprites and Movie Clips. What I meant to say was that the clone() methods with Arrays if all of the elements are either simple Strings or Numbers.

    Many people have been looking for a Clone() method that will handle Movie Clips and Sprites. That might be a problem worth tackling and using in the Prototype Design Pattern.

    Thank you for your patience,
    Bill

  • And always that I put in the stage a new object I must to reload a new image??? .. IT is the same picture to the two objects.

    I can not point to the same image objects from 2

    Thanks for all

  • Hi there,

    Just wonder why your Clone class extends from Sprite? Does that mean you can clone a sprite with graphics, bitmaps in it like “duplicateMovieClip()” function in AS2?

Leave a Reply