CREATING SUBCLASSES


Our Person class has become quite functional: We can create instances and give them names and ages, and they all inherit properties from the prototype object. But just as in real life, there can be classes of people within our Person class: doctors, lawyers, Flash developers, and so on each a subclass of people. These subclasses share the characteristics of the Person class (names, ages, legs, a head), but each has unique characteristics as well. If, for example, you created a Doctor class, it could have unique characteristics such as specialty, the "Dr." title, and so on as well as all of the characteristics of the Person class.

  1. Open class2.fla in the Lesson06/Assets folder. With the Actions panel open, select Frame1. Remove the two trace actions at the end of the script, then add the following:

     _global.Doctor = function(almaMater){    this.almaMater = almaMater;  }  Doctor.prototype.title = "Dr."; 

    Here, we've created a constructor function that defines a new class of objects, the Doctor class. Each new instance of this object class is given an almaMater property, whose value is sent to the constructor function when an instance is created. We've also given the Doctor class a prototype property named title , with a value of "Dr.". Because all doctors share this title (that is, it doesn't change from one doctor to the next), placing it on the prototype object of the Doctor class is appropriate.

    graphics/06fig11.gif

    Let's create an instance of our new class.

  2. Place the following actions at the end of the current script:

     frankenstein = new Doctor("Transylvania");  trace(frankenstein.almaMater);  trace(frankenstein.title);  trace(frankenstein.legs); 

    The first action creates a new instance of the Doctor class named frankenstein .

    The trace actions that follow will output the result.

  3. Choose Control > Test Movie to test the project up to this point:

    The Output window will opens and display the following:

     Transylvania  Dr.  undefined 

    You can see that the almaMater property is set (that is, an instance-level property was added at the instance's creation) and that the title property is set (via the Doctor.prototype object); however, the legs property does not exist.

    Our goal is to make the Doctor class a subclass of the Person class, so that all instances of the Doctor class inherit properties such as legs , head, and so forth. This does not happen automatically: Somewhere in our script, we must tell ActionScript that the Doctor class is a subclass of the Person class.

  4. Close the test movie to return to the authoring environment. With the Actions panel open, select Frame 1 and place the following line of script just above Doctor.prototype.title = "Dr." :

     Doctor.prototype = new Person(); 

    This line of script creates a connection between the Doctor and Person classes. It's telling the Doctor prototype object to inherit from the Person prototype object. As a result, all instances of the Doctor class will reflect the properties not only of Doctor.prototype , but Person.prototype as well. The converse is not true; Instances of the Person class will only reflect the properties of Person.prototype . Doctor is now a subclass of Person, and the Person itself is the superclass of the Doctor class.

    There's a reason we've placed the above line of script above the line assigning the title property on the Doctor.prototype object. When the above line is executed initially, it in essence wipes the Doctor.prototype object clean, replacing all existing properties with those in the Person.prototype object. If the title property were attached to the Doctor.prototype object prior to this happening, it would have been lost. Instead, as our script shows, we add the title property to the Doctor.prototype object on the very next line, which solves the problem.

    At the bottom of the script, you should still see the following trace actions, which previously returned (shown in Step 3) the results shown:

     trace(frankenstein.almaMater);// returned "Transylvania"  trace(frankenstein.title);// returned "Dr."  trace(frankenstein.legs);// returned undefined 

    Let's test the results now.

  5. Choose Control > Test Movie to test the project up to this point:

    The Output window will open and display the following:

     Transylvania  Dr.  2 

    You'll see that frankenstein now has two legs (whereas before this property was undefined ). This is the result of Doctor instances inheriting properties from both the Doctor.prototype and the Person.prototype objects, as discussed in the previous step. For this reason, frankenstein also has a head , memories in his head, and so on. If you change a Person.prototype property now, those changes will be reflected not only in instances of the Person class but in instances of the Doctor class as well (since it's a subclass of the Person object). In essence, when Flash looks for the legs property on the frankenstein instance (as an instance level property), it doesn't find it. The next place it looks is the Doctor.prototype object (because frankenstein is an instance of the Doctor class). The property can't be found there either. Finally, Flash looks on the Person.prototype object (because Doctor is a subclass of the Person class) and this is where the legs property is found.

    graphics/06fig12.gif

    Now you may be wondering about the name and age properties of the Person class. Since these properties are not part of the Person.prototype object, do instances of the Doctor class inherit those? Currently, no. To fix this, we need to update the Doctor class definition.

  6. Close the test movie to return to the authoring environment. With the Actions panel open, select Frame 1 and update the Doctor class definition as follows:

     _global.Doctor = function(name, age, almaMater){    this.base = new Person(name, age);    this.name = this.base.name;    this.age = this.base.age;    delete this.base;    this.almaMater = almaMater;  } 

    The first thing you'll notice about the updated Doctor class definition is that it now accepts two new values (in addition to almaMater , which it had before): name and age . All three values are used in the lines that follow to create an instance of the Doctor class.

    The first line uses the name and age values passed to this constructor function to create a new instance of the Person class. Yes, the Doctor constructor function is creating an instance of the Person class. As you can see, this instance is given a name/path of this.base . As soon as ActionScript sees this line, it will create that instance. As a result, this.base is assigned a name and age property (as all instances of the Person class are). The next two lines in the constructor function set the name and age property values of the Doctor instance being created (this.name and this.age ) based on the property values that belong to this.base (the instance of the Person class created in Line 1.) The fourth line in the constructor function deletes the this.base instance, showing that its use was temporary. The fifth line sets the almaMater property for the instance being created (as discussed in Step 1).

    NOTE

    This demonstrates how dynamic constructor functions can be when the need arises: The function can create temporary objects (as shown), do math calculations, perform actions, and more.

    Right about now you may be thinking, Couldn't we have just set up the class definition to read as follows?

     _global.Doctor = function(name, age, almaMater){    this.name = name;    this.age = age;    this.almaMater = almaMater;  } 

    Although the above would suffice under most circumstances, what would happen if our Person class definition was set up to do something dynamic with the name and age values passed to it (such as reversing the value of age or capitalizing all the characters in the name sent to it)? With this latter approach, instances of the Doctor class would not see these affects a limitation not shared by our initial approach.

  7. Update the line that currently reads: frankenstein = new Doctor("Transylvania"); to read as follows:

     frankenstein = new Doctor("Frankenstein", 85, "Transylvania"); 

    This change is necessitated by the addition of the age and name parameters to the Doctor constructor function (as discussed in Step 6). As a result, when the frankenstein instance is created, it is given a name value of "Frankenstein", an age value of 85, and an almaMater value of "Transylvania".

  8. Place the following actions at the end of the current script:

     trace(frankenstein.name);  trace(frankenstein.age); 

    These trace actions will allow us to see the results of our updates. In addition to these, there should be a total of five trace actions at the end of this script (three of which were added in Step 2).

  9. Choose Control > Test Movie to test the project up to this point:

    The Output window will open and display the following:

     Transylvania  Dr.  2  Frankenstein  85 

    The frankenstein instance is now complete.

    Now would be a good time to introduce and examine the __proto__ property. Every prototype object of a class and every instance of a class has a __proto__ property value. This value indicates where an instance or class should look when a property cannot be found within itself. Yes, we already know that the frankenstein instance will look to the Doctor.prototype object in such a case. Thus, frankenstein.__proto__ is Doctor.prototype (instance inherits from a prototype object). Since the Doctor class inherits from the Person class, Doctor.prototype.__proto__ is Person.prototype (one class' prototype inherits from another class' prototype object). This __proto__ property is set automatically when an instance or a subclass is created (as we've described thus far), and it can be changed whenever the need arises. For example, if you had created a Lawyer class and wanted to associate frankenstein with that class (rather than the Doctor class), you would simply use the following line of script:

     frankenstein.__proto__ = Lawyer.prototype; 

    Now frankenstein can now be considered an instance of the Lawyer class, inheriting the prototype characteristics of that class. In the meantime, his Doctor-specific characteristics (those that he inherited from the Doctor.prototype object) have been wiped clean!

    Lastly, let's take a look at two important aspects of Object classes: composition and inheritance. Understanding these will help you better conceptualize properties and inheritance. These two concepts can be summed up in two words: has and is. When you look at the two classes of objects we've created thus far (Person and Doctor), you'll notice the following:

    • Person instance has a name: This is composition.

    • Person instance has a head: This is composition.

    • Person instance has a leg (property): This is composition.

    • Doctor instance is a Person: This is inheritance.

    • Doctor instance has a title: This is composition.

    These concepts have real-world representations as well. For example:

    • A cheeseburger is a sandwich is a food: This is inheritance.

    • A car has a body has a door has a window: This is composition.

    Knowing how to look at objects in this manner can go a long way in helping you develop custom classes of objects in the most efficient manner: Is relationships are good candidates for subclassed objects, while has relationships can cue you to properties that a certain class of objects should have.

    There are several additional aspects of inheritance, but those are beyond the scope of the book. Now that you understand how custom classes of objects are created (and how they resemble real-world objects), the application development process should be a bit easier to conceptualize. At the end of the next section, we'll bring everything together, explaining the significance of what you've learned and demonstrating how you can put it to use in your projects.

  10. Close the test movie to return to the authoring environment. Save your work as class3.fla.

    We'll continue to use this file in the following exercise



Macromedia Flash MX ActionScripting Advanced. Training from the Source
Macromedia Flash MX ActionScripting: Advanced Training from the Source
ISBN: 0201770229
EAN: 2147483647
Year: 2002
Pages: 161

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