Könyvhöz tartozó online melléklet
Könyv: ActionScript 3.0 a gyakorlatban (Colin Moock)
Melléklet: Olvasson bele!
4. Statikus változók és tagfüggvények
Az
1. fejezetben megtanultuk, miként határozhatjuk meg objektumunk jellemzőit
és viselkedését a példányváltozókkal és -tagfüggvényekkel. Most azzal foglalkozunk,
hogyan kezelhetjük azokat az adatokat és műveleteket, amelyek közvetlenül
az osztályhoz, nem pedig annak példányaihoz kapcsolódnak.
Statikus változók
Az
előző fejezetekben komoly tapasztalatokra tettünk szert a példányváltozók
használata terén, amelyek az osztályok egyes példányaihoz kapcsolódnak. A statikus változók ugyanakkor magához az osztályhoz
tartoznak, nem pedig annak egy példányához – olyan adatokat jelenítenek meg,
amelyek logikailag a teljes osztályhoz kötődnek, tehát nem változnak az egyes
példányok esetében. Így például egy párbeszédablakokat megjelenítő osztály
alkalmazhat statikus változókat a párbeszédablakok új példányainak alapértelmezett
méretezéséhez, egy autóversenyprogramban pedig az autókat megjelenítő osztály
egy statikus változóban rögzítheti az elérhető legnagyobb sebességet,
amely így minden részt vevő autóra érvényes lesz.
A példányváltozókhoz hasonlóan
statikus társaikat is az osztályon belüli változómeghatározással kell létrehoznunk,
de ezúttal fel kell tüntetnünk a static
jellemzőt, az alábbiak szerint:
class
ValamilyenOsztály {
static
var azonosító = érték;
}
A példányváltozókhoz hasonlóan
itt is alkalmazhatunk elérésmódosítókat, ráadásul ugyanaz a négy típus – public, internal, protected és private – áll a rendelkezésünkre. Ha nem
adunk meg semmilyen módosítót, az ActionScript csomagszintű elérést (internal) alkalmaz. A módosítót jellemzően
a static kulcsszó elé helyezik, mint
az az alábbi kódban is látható:
class
ValamilyenOsztály {
private
static var azonosító = érték;
}
A statikus változók eléréséhez nevük
elé az osztály nevét, majd egy pontot kell biggyesztenünk, valahogy így:
ValamilyenOsztály.azonosító = érték;
A
változót meghatározó osztályban az azonosító önmagában is használható (az osztálynév
és a pont nélkül). Így például egy A nevű
osztály v statikus változója
esetében az osztályon belül az A.v és a v kifejezések
azonosak. Mindazonáltal a statikus változók jobb megkülönböztetése érdekében
számos fejlesztő (és könyvünk is) kiírja az osztálynevet, még akkor is, ha
erre nincs feltétlenül szükség.
Egyazon
osztályban létezhet ugyanolyan nevű statikus és példányváltozó. Tekintsük A nevű osztályunkat, és tegyük fel,
hogy létrehoztunk benne egy v nevű statikus, valamint egy azonos nevű példányváltozót.
Ilyenkor a v önmagában a példányváltozóra
hivatkozik, míg a statikus változót az osztálynévvel kiegészített alakkal (A.v) érhetjük el.
Azt mondjuk, hogy a példányváltozó elfedi
a statikus változót.
Hozzunk létre most pár statikus
változót a VirtualPet osztályunkban.
Amint az előzőekben megtanulhattuk, a statikus változók olyan adatokat
követnek nyomon, amelyek logikailag a teljes osztályhoz tartoznak, nem pedig annak
egyes példányaihoz. Ilyen adatunk pedig már kettő is van: a kisállat nevének
maximális hossza, valamint az a kalóriamennyiség, amelyet elfogyaszthat. Ezeket
az adatokat a maxNameLength, valamint a maxCalories nevű új
statikus változókban tároljuk. Az osztályon kívül nem lesz rájuk szükség, így a
private módosítóval határozzuk meg
őket, az alábbiak szerint (a VirtualPet
osztály kódjának többi részét helytakarékosság miatt most elhagyjuk):
package
zoo {
internal
class VirtualPet {
private static var maxNameLength = 20;
private static var maxCalories = 2000;
// A kód
többi részét most elhagyjuk...
}
}
A
két új statikus változóval módosíthatjuk a getHunger(),
az eat() és a setName() tagfüggvényünk kódját. Az eredményt (a VirtualPet osztály teljes kódját) a 4.1.
példában szemlélhetjük meg. Érdemes megjegyeznünk, hogy a statikus változókat a
közmegegyezés szerint a példányváltozók előtt soroltuk fel.
4.1. példa A VirtualPet osztály
package
zoo {
internal
class VirtualPet {
private static var
maxNameLength = 20;
private static var
maxCalories = 2000;
private
var petName;
//
Kisállatainknak a maximális kalóriamennyiség felét adjuk induláskor.
private
var currentCalories = VirtualPet.maxCalories/2;
public
function VirtualPet (name) {
setName(name);
}
public
function eat (numberOfCalories) {
var
newCurrentCalories = currentCalories + numberOfCalories;
if
(newCurrentCalories > VirtualPet.maxCalories) {
currentCalories
= VirtualPet.maxCalories;
}
else {
currentCalories
= newCurrentCalories;
}
}
public
function getHunger ( ) {
return
currentCalories / VirtualPet.maxCalories;
}
public
function setName (newName) {
// Ha
az új név hosszabb maxNameLength karakternél...
if
(newName.length > VirtualPet.maxNameLength) {
//
...csonkoljuk
newName
= newName.substr(0, VirtualPet.maxNameLength);
}
else if (newName == "") {
//
...egyébként, ha az új név üres karakterlánc, a tagfüggvény futása
// véget
ér, és a petName eredeti értéke nem változik
return;
}
//
Rendeljük hozzá az új, ellenőrzött nevet a petName változóhoz
petName
= newName;
}
public
function getName ( ) {
return
petName;
}
}
}
Figyeljük
meg, miként segít a maxNameLength és a maxCalories változó a kód
központosításában. A korábbiakban, ha például a név hosszának korlátját szerettük
volna megváltoztatni, két helyen is bele kellett nyúlnunk a setName() tagfüggvény kódjába – ez a folyamat
pedig időrabló, és egyúttal magában hordozza a hiba lehetőségét. Osztályunk
új kódjában mindehhez csak a maxNameLength értékét kell átírnunk, és a módosítást
mindenütt érvényesítjük.
A
magyarázat nélküli literálértékeket – mint a setName() előző változatában
a 20 – „mágikus értékeknek” szokták hívni, hiszen fontos szerepet töltenek be a
programban, viszont hogy mi is ez a szerep, az korántsem magától értetődő.
Ezért kerüljük az ilyen „mágikus értékek” használatát – a statikus változók hadrendbe
állításával legtöbbször célt érhetünk.
A
statikus változókat gyakran olyan beállítások tárolására használják, amelyek a program
indulása után nem változnak. Ha ezt a korlátozást rögzíteni is szeretnénk, változónkat
állandóként határozhatjuk meg – erről szólunk a következőkben.
Állandók
Az állandók olyan statikus, helyi vagy példányváltozók,
amelyek értéke a kezdeti értékadást követően nem változik. Létrehozásukhoz
a változók meghatározásának hagyományos alakját használjuk, de a var helyett a const kulcsszóval. A hagyomány szerint az állandók nevét csupa nagybetűvel
írjuk. Lássuk most, hogyan is határozhatunk meg egy statikus állandót osztályunk
törzsében:
static
const AZONOSÍTÓ = érték
Ha egy példányváltozót
szeretnénk állandóként meghatározni, az alábbi sort kell elhelyeznünk az osztály
törzsében:
const
AZONOSÍTÓ = érték
Ha pedig egy
helyi változót szeretnénk állandóként alkalmazni, a következőket írhatjuk
egy tagfüggvény vagy függvény törzsébe:
const
AZONOSÍTÓ = érték
Az
előző három példában az AZONOSÍTÓ az állandó nevét,
míg az érték a kezdeti értékét jelöli. A statikus
és a helyi állandók esetében ezt az értéket többé nem lehet megváltoztatni.
A
példányváltozóknál a helyzet kissé bonyolultabb. Ha szigorú módban fordítjuk a programot,
az érték hozzárendelése után az állandók nem módosíthatók. Ha azonban egyszerű
módban fordítjuk a programot, a változó meghatározását tartalmazó osztály
konstruktorában még módosíthatjuk az értékét, másutt azonban nem. (A szigorú és
az egyszerű fordítási mód közti különbségekről a 7. fejezetben olvashatunk.)
Az állandókat leggyakrabban statikus
változók rögzített értékeinek megjelenítésére használják, amelyek egy beállítás
különböző állapotait adják meg. Tegyük fel, hogy egy ébresztőóra-programot
készítünk, ami naponta bekapcsol a megadott időpontban. Három működési
módja van: a vizuális (ilyenkor egy ikon villog a képernyőn), a hangos
(csörgés), valamint az egyszerre vizuális és hangos. Ébresztőóránkat az AlarmClock nevű osztály működteti.
A három ébresztési mód megjelenítésére három állandó statikus változót – MODE_VISUAL, MODE_AUDIO és MODE_BOTH – határozunk
meg. Mindhárom állandóhoz egy-egy számértéket rendelünk: az 1-es felel meg a vizuális
módnak, a 2-es a hangos módnak, a 3-as pedig a vizuális és hangos módnak. Az állandók
meghatározását az alábbiakban láthatjuk:
public
class AlarmClock {
public
static const MODE_VISUAL = 1;
public
static const MODE_AUDIO = 2;
public
static const MODE_BOTH = 3;
}
Az
AlarmClock objektumok aktuális
működési módjának nyomon követésére szolgál a mode nevű példányváltozó
– ehhez rendelhetjük hozzá a megfelelő értékeket (1, 2 vagy 3). Az alábbiakban
új AlarmClock objektumainkat hangos
ébresztésre állítjuk be (2-es mód):
public class AlarmClock {
public
static const MODE_VISUAL = 1;
public
static const MODE_AUDIO = 2;
public
static const MODE_BOTH = 3;
private
var mode = AlarmClock.MODE_AUDIO;
}
Amikor
eljön az ébresztés ideje, az AlarmClock
objektum a működési mód alapján dönti el, hogyan viselkedjen. Az alábbiakban
ezt a döntéshozatali folyamatot láthatjuk a korábban megadott állandókkal:
public
class AlarmClock {
public
static const MODE_VISUAL = 1;
public
static const MODE_AUDIO = 2;
public
static const MODE_BOTH = 3;
private
var mode = AlarmClock.MODE_AUDIO;
private
function signalAlarm ( ) {
if (mode == MODE_VISUAL) {
// A villogó ikon megjelenítése
} else if (mode ==
MODE_AUDIO) {
// A hang lejátszása
} else if (mode ==
MODE_BOTH) {
// Az ikon megjelenítése
és a hang lejátszása
}
}
}
Vegyük észre, hogy ebben a kódban
valójában nincs is szükség a működési módokat jelző állandókra. Ugyanezt
a folyamatot véghez vihettük volna literálokkal, vagyis mágikus értékekkel is.
Az állandók szerepe itt mindössze annyi, hogy érthetőbbé teszik a számértékek
szerepét. Az összehasonlítás kedvéért bemutatjuk az AlarmClock osztály kódját az állandók nélkül. Nyilvánvaló, hogy a megjegyzések
elolvasása nélkül meglehetősen nehéz kitalálni, mit is takarnak az egyes
számértékek.
public
class AlarmClock {
private
var mode = 2;
private
function signalAlarm ( ) {
if
(mode == 1) {
// A villogó
ikon megjelenítése
}
else if (mode == 2) {
// A hang
lejátszása
}
else if (mode == 3) {
// Az
ikon megjelenítése és a hang lejátszása
}
}
}
Most
pedig térjünk át a statikus változók után a statikus tagfüggvények témakörére!
Statikus tagfüggvények
Az
előzőekben megtanultuk, hogy a statikus változók olyan adatokat tárolnak,
amelyek magához az osztályhoz kapcsolódnak. A statikus tagfüggvények szerepe hasonló: olyan műveleteteket
valósítanak meg, amelyek a teljes osztályhoz, nem pedig annak példányaihoz köthetők.
A Flash futásidejű API-jában találhatunk például egy Point nevű osztályt, amely egy Descartes-koordinátarendszerbeli
pontot jelenít meg annak x és y koordinátájával. A Point osztály tartalmaz egy polar()
nevű statikus tagfüggvényt, amely egy Point
objektumot hoz létre megadott polárkoordináták (vagyis egy távolság és egy
szög) alapján. A polárkoordináták átalakítása Descartes-koordinátákká olyan
szolgáltatásnak tekinthető, ami általánosságban a Descartes-koordinátarendszerbeli
pontokhoz tartozik, nem pedig az egyes Point
objektumokhoz – következésképpen statikus tagfüggvényként határozzák meg.
A
példánytagfüggvényekhez hasonlóan a statikus tagfüggvényeket is
függvénymeghatározással hozhatjuk létre az osztály törzsében, itt azonban –
ahogy az alábbi kódban is láthatjuk – fel kell tüntetnünk a static jellemzőt is:
class
ValamilyenOsztály {
static
function tagfüggvényNév (azonosító1 = érték1,
azonosító2 = érték2,
...
azonosítón = értékn) {
}
}
A
példánytagfüggvényekhez hasonlóan a statikus tagfüggvények hozzáférhetőségét
is módosíthatjuk elérésmódosítókkal. Itt is ugyanaz a négy típus – public, internal, protected és private – áll a rendelkezésünkre, és az alapértelmezés
az internal (csomagszintű) elérés.
Ha megadunk valamilyen elérésszabályozót, azt általában a static kulcsszó előtt helyezzük el:
class
ValamilyenOsztály {
public
static function tagfüggvényNév (azonosító1 = érték1,
azonosító2
= érték2,
...
azonosítón
= értékn) {
}
}
A statikus tagfüggvények
meghívásához az alábbi alakot használhatjuk:
ValamilyenOsztály.tagfüggvényNév(érték1, érték2,...értékn)
A
fenti kódban a ValamilyenOsztály jelöli azt az osztályt, amelyikben
a statikus tagfüggvényünket meghatároztuk, a tagfüggvényNév adja a tagfüggvény
nevét, az érték1, érték2, ... értékn pedig az átadott
paraméterek listáját. A tagfüggvényt meghatározó osztályon belül a tagfüggvényNév önmagában
használható (az osztálynév és a pont feltüntetése nélkül). Vegyünk például egy A nevű osztályt, amelyben egy m statikus tagfüggvényt határozunk
meg. Itt az A.m() kifejezés megegyezik az m()-mel.
Mindazonáltal számos fejlesztő (és könyvünk is) használja az osztálynevet,
hogy megkülönböztessék a statikus tagfüggvényeket a példánytagfüggvényektől.
Egyes
osztályok kizárólag statikus tagfüggvényeik okán léteznek. Szerepük, hogy összefogják
az összetartozó műveleteket, de példányok sosem jönnek létre belőlük.
Ilyen például a Mouse osztály, amelynek
a feladata kizárólag a show() és a hide() statikus tagfüggvények meghatározása,
amelyek megjelenítik, illetve eltüntetik az egérmutatót. Elérésük közvetlenül a
Mouse osztályon keresztül történik
(például Mouse.hide()), nem pedig annak
egy példányával. Ez olyannyira igaz, hogy a Mouse
osztályból sosem jön létre objektum.
A statikus tagfüggvények
rendelkeznek két korlátozással, amelyek a példánytagfüggvényekre nem vonatkoznak.
Először is, a statikus tagfüggvények nem alkalmazhatják a this kulcsszót. Másodszor,
a statikus tagfüggvények nem érhetik el saját osztályuk példányváltozóit és példánytagfüggvényeit
(fordítva ez nem igaz, hiszen a példánytagfüggvények elérhetik a statikus változókat
és tagfüggvényeket éppúgy, mint a példányváltozókat és egyéb
példánytagfüggvényeket).
Általánosságban elmondható,
hogy a statikus tagfüggvényeket ritkábban használják a statikus változóknál. Virtuális
állatkertünkben például egyáltalán nem találhatunk statikus tagfüggvényt. Használatuk
bemutatására ezért visszatérünk a 2. fejezetben bemutatott programocskánkhoz,
amellyel az elektronikus levélcímeket ellenőriztük. Tételezzük fel, hogy
alkalmazásunk olyan nagyra nőtt, hogy érdemes egy saját
karakterlánckezelő osztályt létrehoznunk – legyen ez a StringUtils. Ebből az osztályból
nem akarunk példányokat létrehozni, mindössze statikus tagfüggvényeket szeretnénk
benne összegyűjteni. Példaképpen bemutatjuk egyikük, a contains() meghatározását, amely egy Boolean visszatérési értékkel jelzi,
hogy a megadott karakterlánc tartalmaz-e egy bizonyos karaktert:
public
class StringUtils {
public
function contains (string, character) {
for
(var i:int = 0; i <= string.length; i++) {
if
(string.charAt(i) == character) {
return
true;
}
}
return
false;
}
}
A következő
kódsorban bemutatjuk, miként használhatjuk ezt a tagfüggvényt annak vizsgálatára,
hogy egy elektronikus levélcím tartalmazza-e a @ karaktert:
StringUtils.contains("me@moock.org",
"@");
Természetesen egy valódi alkalmazásban
a címet a felhasználó adja meg, és a contains()
segítségével dönthetünk arról, hogy elküldjük-e az űrlap tartalmát a kiszolgálónak.
Lássunk most egy életszerűbb helyzetet:
if
(StringUtils.contains(userEmail, "@")) {
//
Kód az űrlap adatainak elküldésére
}
else {
//
"Érvénytelen adat" üzenet megjelenítése
}
Saját
statikus tagfüggvényeink mellett az ActionScript is létrehoz egyet. Ez a tagfüggvény,
amely minden osztályban megjelenik, az osztálybeállító tagfüggvény névre hallgat.
Az osztálybeállító tagfüggvény
Amikor
az ActionScript futásidőben meghatároz egy osztályt, automatikusan létrehoz
és végre is hajt egy különleges tagfüggvényt – ez az osztálybeállító tagfüggvény. Itt kapnak helyet el az osztály statikus
változóinak kezdeti értékadásai, és minden olyan osztályszintű kód, amely
nem tagfüggvényt vagy változót határoz meg.
Az osztálybeállító
tagfüggvény lehetőséget ad arra, hogy az osztály létrehozásakor egyszeri
műveleteket hajtsunk végre, esetleg az osztályon kívüli változók vagy tagfüggvények
használatával. Tegyük fel, hogy egy olyan alkalmazást szeretnénk készíteni,
amellyel a felhasználóink elolvashatják az elektronikus leveleiket – méghozzá
olyan környezetben, amely megfelel az operációs rendszer felületi megjelenésének.
Központi osztályunk, a MailReader
ezért az osztálybeállító tagfüggvényében megvizsgálja, hogy milyen grafikus témát
alkalmaz az operációs rendszer, és az eredményt a theme nevű statikus változóban tárolja
– ez határozza majd meg a teljes alkalmazás grafikai megjelenését. Az alábbi
kódban bemutatjuk a MailReader osztálybeállító
tagfüggvényét. Az operációs rendszer vizsgálatára a flash.system.Capabilities beépített osztály os statikus változóját
használjuk.
package
{
import
flash.system.*;
public
class MailReader {
static
var theme;
if
(Capabilities.os == "MacOS") {
theme
= "MAC";
}
else if (Capabilities.os == "Linux") {
theme
= "LINUX";
}
else {
theme
= "WINDOWS";
}
}
}
Az
osztálybeállító tagfüggvény kódja értelmezett módban fut, tehát a JIT fordító
nem dolgozza fel. Ezt azért érdemes tudnunk, mivel a JIT-fordítású kód jelentősen
gyorsabban fut, mint az értelmezett kód, így a számításigényesebb kódrészleteket
érdemes máshova helyeznünk, ha a sebesség lényeges szempont.
Class objektumok
A korábbiakban
megtanultuk, hogy a statikus változókat és tagfüggvényeket az őket meghatározó
osztályokon keresztül érhetjük el. Így ha például a maxCalories nevű statikus
változót szeretnénk elérni, amelyet a VirtualPet
osztályban határoztunk meg, a következőt írhatjuk:
VirtualPet.maxCalories
A
fenti kódban a VirtualPet név használata
nem egyszerű programnyelvi fordulat, mint ahogy az előzőek alapján
hihetnénk – itt valóban egy objektumra hivatkozunk, méghozzá a beépített Class osztály egy példányára.
Futásidőben ugyanis az ActionScript
minden osztályát a Class osztály egy
objektuma jeleníti meg. A programozó szempontjából a Class objektumok elsősorban a statikus változók és tagfüggvények
elérésére használatosak. Mindazonáltal, más objektumokhoz hasonlóan a Class osztály példányai is értékek, amelyeket
változókhoz rendelhetünk, tagfüggvényeknek és függvényeknek adhatunk át, vagy
éppen kaphatunk vissza tőlük. Ezt használjuk ki VirtualZoo osztályunk alábbi, módosított változatában, ahol a VirtualPet osztályt megadó Class objektumot egy vp nevű változóhoz
rendeljük, majd ennek a változónak a segítségével hozunk létre egy új VirtualPet objektumot.
package
zoo {
public
class VirtualZoo {
private
var pet;
public
function VirtualZoo ( ) {
var
vp = VirtualPet;
pet =
new vp("Stan");
}
}
}
Ezt
a módszert használhatjuk akkor is, ha egy .swf
fájlból egy másik .swf fájl osztályait
szeretnénk elérni, vagy amikor külső erőforrásokat (például képeket
vagy betűtípusokat) szeretnénk beágyazni egy .swf fájlból. Mindezeket részletesebben könyvünk II. részében tárgyaljuk.
Ezzel
a statikus változók és tagfüggvények témakörének végére értünk. Mielőtt
azonban továbblépnénk a következő fejezetre, érdemes megállnunk egy percre,
és összevetnünk néhány, az eddigiekben megismert kifejezést a Java, valamint a C++
nyelvekben használt megfelelőikkel.
A C++ és a Java szóhasználata
A
példányváltozók és -tagfüggvények, valamint a statikus változók és tagfüggvények
fogalma jelen van a legtöbb objektumközpontú programozási nyelvben. Nem kivétel
ez alól a C++ és a Java sem, amelyeknek a megfelelő kifejezéseit a 4.1.
táblázatban vethetjük össze az ActionScriptben megismertekkel.
4.1. táblázat Elnevezések a különböző programozási
nyelvekben
ActionScript Java C++
példányváltozó mező vagy példányváltozó adattag
példánytagfüggvény tagfüggvény tagfüggvény
statikus változó osztályváltozó statikus adattag
statikus tagfüggvény osztálytagfüggvény statikus tagfüggvény
Irány a függvények világa!
Megtanultuk,
hogy a példánytagfüggvények az objektumok, a statikus tagfüggvények pedig az osztályok
viselkedését határozzák meg. A következőkben megismerkedünk a függvényekkel,
amelyek önmagukban álló – nem osztályokhoz vagy objektumokhoz rendelt – műveleteket
írnak le.