1. Trang chủ
  2. » Công Nghệ Thông Tin

Học Actionscript 3.0 - p 17 pdf

10 255 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 4,93 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

An interface is nothing more than a list of public methods that must be present in any class that conforms to the interface.. Car class A public method also named useAccessory is added t

Trang 1

The last important concept of object-oriented programming that we want to

discuss is polymorphism Although we’ll expand the explanation as this

sec-tion evolves, you can start by thinking of polymorphism as a design practice

that allows you to use objects of different types in a uniform manner For

example, for our vehicle exercise, you might create classes for land-, water-,

and air-based vehicles and write code to move each type of vehicle In this

scenario, it’s better to use one method name for moving all of these vehicle

types (such as “move”), instead of separate method names (like “drive,” “pilot,”

and “fly,” for moving a car, boat, and plane, respectively) Doing so makes your

code more flexible, more reusable, and easier to read and document

In ActionScript 3.0, polymorphism is commonly used with inheritance and/or

interfaces We’ll work with interfaces in a moment but, for now, think of them

as rulebooks for classes An interface is nothing more than a list of public

methods that must be present in any class that conforms to the interface For

example, you might continue to develop our vehicle exercise and eventually

end up with vehicles that contain public methods that activate (start up), go,

stop, and deactivate (shut down) your vehicles You can create an interface

that includes these method names and requires classes to adhere to that

inter-face This makes certain all of those classes can be controlled consistently

An interface doesn’t restrict your class to those methods, either Classes can

have their own public methods that are not in the interface, without

conse-quence As long as the interface methods are present, everyone will be happy

We’ll discuss the further role that interfaces play in polymorphism a little

bit later For now, let’s extend what you already know and show how to use

polymorphism with inheritance

Polymorphism and inheritance

Employing polymorphism with inheritance allows you to design subclasses

that can use the same method names as their superclasses, but without

creating a conflict For example, a superclass can have a public method

called “turn,” which multiple subclasses use One subclass, however, might

also have a public method called “turn,” that is either entirely different or

enhanced Ordinarily, the fact that a subclass inherits public methods from a

superclass means that the subclass would effectively have two methods called

“turn” and a conflict would exist

However, polymorphism allows the subclass method to replace or augment

the superclass method of the same name by overriding it Overriding a

meth-od tells the compiler that the new version of the methmeth-od (in the subclass)

takes precedence over the previous version of the method (in the superclass)

To demonstrate this process, let’s begin by adding two methods to our

Vehicle class If you want to look at the source files, they are in the

polymor-phism folder of the chapter archive The new methods can be seen in lines

N OT E

Only public and protected methods can

be seen by ActionScript 3.0 subclasses,

so they are the only kinds of methods that can be overridden.

Trang 2

33 through 39 in the following excerpt, and are named useAccessory() and

changeGear() Both of the new methods are available to the Car and Truck

subclasses through inheritance and notice that the functionality of the use-Accessory() method is to turn on a vehicle’s lights

Vehicle class

20 private function onLoop(evt:Event):void {

21 if (_moving) {

22 _fuelAvailable ;

23 _milesTraveled += _gasMileage;

24 if (_fuelAvailable < 1) {

25 this.removeEventListener(Event.ENTER_FRAME,

26 onLoop, false, 0, true);

27 }

28 trace(this, _milesTraveled, _fuelAvailable);

29 this.x = _milesTraveled;

30 }

31 }

32

33 public function changeGear(): void {

34 trace ( this , "changed gear");

35 }

36

37 public function useAccessory(): void {

38 trace ( this , "vehicle lights turned on");

39 }

Next let’s see how to override the useAccessory() method in the Car and

Truck classes so we can customize its functionality without having to change our API

Car class

A public method also named useAccessory() is added to the Car class, seen in lines 17 through 19 of the following excerpt Remember that this would ordi-narily conflict with the method of the same name in the Vehicle superclass, because of inheritance As discussed previously, we avoid this by preceding the method declaration, including its access control modifier, with the over-ride keyword

The functionality of the method is the same in both classes: to use an acces-sory So the useAccessory() method in the Car class can call its existing

openSunroof() method

13 public function openSunroof():void {

14 trace(this, "opened sunroof" );

15 }

16

17 override public function useAccessory(): void {

18 openSunroof();

19 }

The beauty of this arrangement is that you’ve created an API that employs

Trang 3

without having to change the rest of your application For example, you might

have many method calls using the syntax useAccessory() that all open car

sunroofs If you later decide to change the accessory to something else, you

would need to edit only the Car class, not the many existing method calls, to

update your application

Truck class

Now we’ll do the same thing with the Truck class, but with a twist In some

cases when overriding, you may not want to entirely replace the original

behavior that exists in the superclass When needed, you can execute the

cus-tom code in the subclass method and call the same method in the superclass

To do this, add an instruction in the subclass method to explicitly call the

original superclass method, as seen in line 18 of the Truck class In this case,

you can’t simply use the super() statement the way you did earlier, because

that only works in the constructor Within a method, you must reference the

superclass using the super object, and follow it with the superclass method

you want to call The edit is in bold

13 public function lowerTailgate():void {

14 trace(this, "lowered tailgate" );

15 }

16

17 override public function useAccessory(): void {

18 super useAccessory();

19 lowerTailgate();

20 }

Tires class and Document class

No change to the Tires class is required, but we’ll make two changes to the

document class Main to show the outcome of your efforts First, in both Car

and Truck instances (compact and pickup), we’ll call the other method we

added, changeGear() (lines 18 and 25) This will show that the outcome of a

public method called from either car or truck will be the same if

polymor-phism is not in play

Next, we’ll follow the example discussed and change our code from calling

openSunroof() and lowerTailgate(), for compact and pickup respectively, to

both instances calling useAccessory() (lines 19 and 26) This will make our

code a bit more flexible, as we can later change the accessories in one or both

classes and not have to change our FLA to benefit from the adjustment

12 public function Main() {

13

14 compact = new Car(21, 18);

15 compact.x = 20;

16 compact.y = 20;

17 addChild(compact);

18 compact.changeGear();

19 compact.useAccessory();

20

Trang 4

21 pickup = new Truck(16, 23);

22 pickup.x = 20;

23 pickup.y = 100;

24 addChild(pickup);

25 pickup.changeGear();

26 pickup.useAccessory();

27

28 this.addEventListener(Event.ADDED_TO_STAGE,

29 onAddedToStage,

30 false, 0, true);

31 }

An abbreviated output follows As you can see, the car class traced its tires, the compact instance changed gear, and then used its accessory This opened

the sunroof, but nothing more because the Car class override replaced the functionality of the Vehicle useAccessory() method, which turned on the vehicle’s lights The pickup behaved similarly, but in addition to lowering its

tailgate, also turned on its lights This is because the Truck class also called the useAccessory() method in the superclass, rather than just overriding it [object Car] has high-performance radial tires

[object Car] changed gear [object Car] opened sunroof [object Truck] has storm-ready snow tires [object Truck] changed gear

[object Truck] lowered tailgate [object Truck] turned on lights [object Car] 21 17

[object Truck] 16 22 [object Car] 42 16 [object Truck] 32 21

Polymorphism and interfaces

Earlier, we said there’s another way to use polymorphism that doesn’t focus

on inheritance Because it’s not based on method overriding between sub-class and supersub-class, it’s applicable to more situations The general idea is the same, in that your coding is simplified by using the same method names across different object types However, it’s even more useful in that it adds additional flexibility by not requiring that you type your object to a specific class

To help explain this, let’s sideline a bit to revisit two important ActionScript 3.0 topics: compile-time error checking and the display list The benefit of using data typing with your objects is that the ActionScript compiler will warn you if you do something that’s incompatible with your stated data type

By design, the simplest case means that you can only work with one data type (A look at Chapter 2 will reinforce this idea if you need a quick review.) However, there are times when you may want things to be a bit more flexible For example, you may want to put either a MovieClip or Sprite into a vari-able If you type the variable as MovieClip, only a movie clip will be accepted

Trang 5

from which both MovieClip and Sprite descend (see Chapter 4 for more

information), and the compiler won’t object

The downside to this is that it can be a bit too generic If, for example, you

used a movie clip method on an object that the compiler only understood as

a DisplayObject, an error would occur:

var thing:DisplayObject = new MovieClip();

thing.play();

Why? Because, although play() is a legal movie clip method, the compiler

doesn’t understand that thing is actually a movie clip It might be a sprite

(and that flexibility is the very reason we’re discussing this), and a sprite

doesn’t have a timeline

This can be addressed by casting (also discussed in Chapter 4), but that kind

of defeats the purpose of what we’re doing Instead, what if you could specify

a data type that was flexible enough to work with different kinds of objects,

but also knew which methods those objects supported? That’s where

inter-faces come in

As we explained earlier, an interface is simply a list of public methods that

must be present in a class The following is an example of an interface that

might be used with classes for devices that play music (like a radio or CD

player) All of the code for this discussion can be found in the polymorphism_

interface source code directory The interface is called IAudible and is found

in the IAudible.as source file It’s a very common practice to start the name of

all interfaces with a capital I, to differentiate them from classes

1 package {

2

3 public interface IAudible {

4

5 function turnOn():void;

6 function playSelection(preset:int):void;

7 function turnOff():void;

8

9 }

10 }

As you can see, not even the content of a method is included Only the name,

parameters and data types, and return data type (which are collectively called

the method’s signature) are included Also, any import statements needed to

support included data types are required (In this case, the compiler

auto-matically understands the int data type However, if a data type represents a

class, such as MovieClip or Event, that class must be imported.)

Once you’ve created an interface, you can require a class to adhere to it by

implementing it using the implements keyword in the interface declaration, as

shown in line 3 of the following simple Radio class (Radio.as):

1 package {

2

3 public class Radio implements IAudible {

4

Trang 6

5 public function Radio() {

6 trace( "radio added" );

7 }

8

9 public function turnOn():void {

10 trace( "radio on" );

11 }

12

13 public function playSelection(preset:int):void {

14 trace( "radio selection: channel" , preset);

15 }

16

17 public function turnOff():void {

18 trace( "radio off" );

19 }

20 }

21 } All this class does is trace appropriate diagnostic statements, identifying itself

as “radio” each time It complies with the interface because every method required is present Here is a CDPlayer class (CDPlayer.as) that also

imple-ments, and complies with, the same interface The purpose of the class is similar, but it identifies itself as “cd player” in each trace to demonstrate unique functionality

1 package {

2

3 public class CDPlayer implements IAudible {

4

5 public function CDPlayer() {

6 trace( "cd player added" );

7 }

8

9 public function turnOn():void {

10 trace( "cd player on" );

11 }

12

13 public function playSelection(preset:int):void {

14 trace( "cd player selection: track" , preset);

15 }

16

17 public function turnOff():void {

18 trace( "cd player off" );

19 }

20

21 public function eject():void {

22 trace( "cd player eject" );

23 }

24

25 }

26 } Although the Radio and CDPlayer classes do different things (demonstrated simply by the unique traces), the method names required by the interface are present in both classes This means that you can write a full application using a radio, later swap out the radio with a CD player, but not have to

Trang 7

The CDPlayer class also demonstrates that additional methods, not referenced

by an interface, can appear in classes—as shown by the eject() method in

lines 21 through 23 An interface is only designed to enforce a contract with

a class, making sure the required methods are present It doesn’t restrict the

functionality of a class

Simple example

All that remains is putting this into practice The following basic

implemen-tation is found in the sound_system.fla source file The key step in using

interfaces in this context is typing to the interface If you type to Radio, you

can’t switch to CDPlayer later However, if you type to IAudible, the compiler

will nod approvingly at both Radio and CDPlayer Also, because the interface

rigidly enforces that all public methods are present, you don’t run into

situa-tions where the compiler is unsure if a method is legal This is polymorphism

at its best The following script starts with a radio and then switches to a CD

player, using methods in both cases without error

var soundSystem:IAudible = new Radio();

soundSystem.turnOn();

soundSystem = new CDPlayer();

soundSystem.turnOn();

soundSystem.playSelection(1);

Adding a sound system to your vehicles through composition

Now let’s practice what you’ve learned by composing the sound system

example into the ongoing vehicle exercise This will review encapsulation,

composition, and polymorphism

First, add another private property to the Vehicle class to hold the sound

system, just like we did when we composed Tires into the exercise It’s typed

to the interface to allow a vehicle to have any sound system that implements

IAudible The property can be seen in line 12 of the following excerpt from

the Vehicle.as source file:

8 private var _gasMileage:Number;

9 private var _fuelAvailable:Number;

10 private var _milesTraveled:Number = 0;

11 private var _moving:Boolean;

12 private var _soundSystem:IAudible;

Next, provide public access to this property by adding a getter and setter,

again typed to the IAudible interface The following excerpt, still in the

Vehicle.as source file, shows this addition in lines 64 through 70:

60 public function get milesTraveled():Number {

61 return _milesTraveled;

62 }

63

64 public function get soundSystem():IAudible {

65 return _soundSystem;

66 }

Trang 8

67

68 public function set soundSystem(device:IAudible): void {

69 _soundSystem = device;

70 }

The last class changes involve adding an instance of CDPlayer in the Car class, and a Radio instance in the Truck class—just as we did when adding Tires in the composition example This excerpt from the Car class (Car.as) shows the

change at the end of the constructor:

7 public function Car(mpg:Number, fuel:Number) {

8 super(mpg, fuel);

9 _tires = new Tires( "highperformance" );

10 trace(this, "has" , _tires.type, "tires" );

11 soundSystem = new CDPlayer();

12 } This excerpt from the Truck class (Truck.as) also adds the sound system at

the end of the constructor The edits in both classes appear in bold at line 11:

7 public function Truck(mpg:Number, fuel:Number) {

8 super(mpg, fuel);

9 _tires = new Tires( "snow" );

10 trace(this, "has" , _tires.type, "tires" );

11 soundSystem = new Radio();

12 } Finally, the document class is modified to use the sound system in both the

Car instance (compact) and Truck instance (pickup) when you click the stage

Shown in bold in the Main.as excerpt below, lines 42 through 44 access the

CD player and radio through the soundSystem property This triggers the get-ter method in the respective classes and returns the car’s CD player and the truck’s radio

39 public function onClick(evt:MouseEvent):void {

40 compact.go();

41 pickup.go();

42 compact.soundSystem.turnOn();

43 compact.soundSystem.playSelection(2);

44 pickup.soundSystem.turnOn();

45 } The trace immediately reflects the fact that the car has a CD player and the truck has a radio Once you click the stage (shown by the gap in the output that follows), the sound systems are used and the vehicles drive off into the sunset

[object Car] has high-performance radial tires

cd player added [object Car] changed gear [object Car] opened sunroof [object Truck] has storm-ready snow tires radio added

[object Truck] changed gear [object Truck] lowered tailgate [object Truck] turned on lights

Trang 9

cd player selection: track 2

radio on

[object Car] 21 17

[object Truck] 16 22

Navigation Bar Revisited

Chapter 5 concluded with the start of a simple navigation bar created using

procedural programming techniques We’ll now step through a new exercise

to demonstrate one way to approach the same task using OOP This exercise

combines the use of standalone classes with classes that are linked to movie

clips in the main Flash file, LAS3Lab.fla—found in the nav_bar folder of the

chapter source archive

This exercise is also the start of the navigation system for the cumulative

book/companion website collective project In this chapter, we’ll use a basic

array to create five main buttons Later, in Chapter 14, we’ll add submenus to

this system and load all the content dynamically through the use of XML

The files and directories you create here will continue to be used and

enhanced throughout the remainder of this book, so establishing a logical

directory structure now will be very helpful The FLA and document class

should reside in the top level of a new directory Adjacent to the FLA, you’ll

eventually create two directories for classes In later versions of the exercise,

you’ll create a com folder for general packages that you may use in multiple

projects At this point, you’re ready to create an app folder for classes specific

to this project that you are less likely to reuse As always, adopting naming

conventions and organization recommendations are personal choices that

you can adapt when your comfort increases

The FLA requires two symbols in the library (included in the source):

MenuButtonMain

In our example, this is a movie clip that looks like a tab (Its name was

influenced by the fact that submenus will be introduced to this example,

later in the book.) The symbol’s linkage class is called MenuButtonMain,

too However, we’ll be using a custom class this time, rather than just

rely-ing on the automatic internal class created by Flash Professional for the

sole purpose of birthing the object with ActionScript Therefore, the fully

qualified path name, which includes not only the class name but also

its package, is used as the symbol’s linkage class:

com.learningaction-script3.gui.MenuButtonMain

HLineThick

This is simply a thick line, approximately 8 pixels tall and the width of

your file This serves as the horizontal plane on which the main menu

but-tons reside to form the navigation bar Unlike the button symbol, there’s no

N OT E

Push Yourself: A great way to make sure you understand packages is to reorganize the source files in the poly-morphism_inheritance exercise by put-ting the sound system files in their own package Pick a package name such as

app.las3.soundsystem, or try your own reverse domain path Don’t forget

to revise the package declaration line

in each affected class, and add import statements to the other classes referenc-ing your sound systems An example of this kind of organization can be found

in the polymorphism _packages direc-tory.

Trang 10

external class for this line, as it has no functionality Still, we’ll give it a linkage class that includes a package location anyway: com.learningactionscript3 gui.HLineThick The result will be the same as using a class name without package information; Flash Professional will still create a placeholder class

in the SWF However, the nice thing about preplanning this way is that if you ever want to add functionality to this asset, you can create a class in this location and perhaps avoid additional edits to the FLA

Document class

The entry point to this project is the document class, LAS3Main.as, which

follows Lines 3 and 4 import the MovieClip class and custom NavigationBar

class, which you’ll create in a moment Line 6 declares the class and extends

MovieClip Lines 8 through 14 contain the class constructor

This navigation bar can feature a variable number of buttons, determined

by the contents of an array seen in lines 9 and 10 Lines 11 and 12 creates an instance of the NavigationBar class and passes in the array of labels for the new buttons Finally, line 13 adds the navigation bar to the display list

1 package {

2

3 import flash.display.MovieClip;

4 import com.learningactionscript3.gui.NavigationBar;

5

6 public class LAS3Main extends MovieClip {

7

8 public function LAS3Main() {

9 var menuData:Array = [ "one" , "two" , "three" ,

10 "four" , "five" ];

11 var navBar:NavigationBar =

12 new NavigationBar(menuData);

13 addChild(navBar);

14 }

15 }

16 }

NavigationBar

Next we need to create the NavigationBar class (NavigationBar.as), which will

be the home for our buttons Here we’ll focus on the times that are appreciably different in purpose from the prior class, or are otherwise noteworthy Line

1, for example is the package declaration discussed several times previously

in the book, but is worthy of mention because it reflects the location of the class—in the gui directory, within the com directory, found in the same folder

as the FLA Lines 9 through 12 contain the class constructor, populate the properties with the incoming argument data, and call the build() method:

1 package com.learningactionscript3.gui {

2

3 import flash.display.MovieClip;

4

Ngày đăng: 06/07/2014, 18:20