XI. A JavaBeanek programozása

Bevezetés

Tanfolyamunk utolsó leckéjében azt mutatjuk be, hogy hogyan lehet NetREXX-ben JavaBeaneket (Java babszemeket?) készíteni. A JavaBeanek olyan különálló objektumok, amelyekbôl a Java fejlesztôkörnyezetek (pl. VisualAge for Java) építkeznek. A JavaBean tehát egyfajta építôelemnek is tekinthetô. Tanfolyamunk során csak az egyszerû beanekkel fogunk foglalkozni és nem fogunk belemerülni a JavaBeanek készítésének minden apró részletébe.

JavaBean alapok

Egy JavaBean nem más, mint egy Java osztály, amely rendelkezik a következô összetevôkkel:

Tulajdonságok (Properties)Az osztály állapotát leíró állapotváltozók, vagy más néven tulajdonságok. Háromféle fajtájuk létezik. Az egyszerû állapotváltozók értékének megváltozása nem jár semmilyen esemény bekövetkezésével. A kapcsolt (bound) változók értékének módosulása viszont mindig maga után vonja a PropertyChange esemény bekövetkezését. Amennyiben az állapotváltozó értékének megváltozásában érdekelt osztályok regisztrálják magukat a PropertyChangeListener listára történô feliratkozással, akkor minden esetben értesülnek a változásról. A harmadik típusú, feltételes állapotváltozók olyan változók, amelyeknek értékének módosulását más osztályok ellenôrzik és szükség esetén meg is akadályozhatják.
MetódusokPublikus, vagyis más osztályok által meghívható metódusok.
Események (Events)A beanen belül bekövetkezô események, amelyek más, az esemény bekövetkezésének értesítési listájára feliratkozott osztályokban folyamatokat indít el. A PropertyChange esemény például egy ilyen esemény, amely a kapcsolt és feltételes tulajdonságok megváltozásakor generálódik.

A JavaBeanekhez a legtöbb esetben tartozik egy úgynevezett bean információs osztály is, amelyben definiálják az állapotváltozók, metódusok és események nevét. Mivel az állapotváltozók és metódusok nevét magából a beanbôl is ki lehet nyerni, ezért ezek definiálása opcionális. Az események nevének megadása ugyanakkor kötelezô.

Egy egyszerû JavaBean

Példaként alkossunk meg egy JavaBeant, amely egy alkalmazottat reprezentál. A bean három állapotváltozóból, két metódusból és egy eseménybôl fog állni. Az állapotváltozók az alkalmazott száma, neve és fizetése lesznek. A fizetést az increaseSalary és decreaseSalary metódusok segítségével lehet növelni és csökkenteni. Amennyiben a fizetés meghalad egy bizonyos értéket, akkor egy figyelmeztetô eseményt fog a bean generálni. Egy bean megírása minden esetben a JavaBeans package importálásával és a class utasítással kezdôdik:

import java.beans.
class lecke11a public binary

A második lépés a privátként definiált állapotváltozók felsorolása:

properties private
 fieldNumber = int 0
 fieldName = String null
 fieldSalary = float 0

Esetünkben mindegyik állapotváltozó rendelkezik egy get metódussal, amellyel lekérdezhetô az adott változó értéke:

method getNumber() public returns int
 return fieldNumber

method getName() public returns String
 if fieldName == null then fieldName = String ""
 return fieldName

method getSalary() public returns float
 return fieldSalary

A get metódusok mellett meg kell adnunk a set metódusokat is, amelyekkel az állapotváltozók értékét lehet megváltoztatni. Sajnos ezen metódusok megalkotása kicsit körülményesebb, mivel kapcsolt változókat használunk, és ezek értékének módosulásakor generálnunk kell egy PropertyChange eseményt is. Ehhez nem kevesebb mint három metódusra van szükség:

properties inheritable
 propertyChange = PropertyChangeSupport(this)

method addPropertyChangeListener(listener=PropertyChangeListener) public protect
 propertyChange.addPropertyChangeListener(listener)

method removePropertyChangeListener(listener=PropertyChangeListener) public protect
 propertyChange.removePropertyChangeListener(listener)

method firePropertyChange(propertyName=String, oldValue=Object, newValue=Object) public
 propertyChange.firePropertyChange(propertyName, oldValue, newValue)

Mindazon osztályok, amelyek értesülni akarnak egy adott tulajdonság értékének megváltozásáról, fel kell hogy iratkozzanak az addPropertyChangeListener metódussal az értesítési listára és implementálniuk kell a PropertyChangeListener interfészt, vagyis rendelkezniük kell egy saját PropertyChange metódussal. A leiratkozás a removePropertyChangeListener metódussal történhet. A bean firePropertyChange metódusa meghívja az értesítési listára feliratkozott osztályok PropertyChange metódusát, hogy azok megkapják a változás hírét és elvégezhessék a számukra ilyenkor elôírt feladatokat.

Ezek után már nincs más hátra, mint hogy megalkossuk a három tulajdonság set metódusát:

method setNumber(number=int) public
 oldValue = int fieldNumber
 fieldNumber = number
 firePropertyChange("number", Integer(oldValue), Integer(number))

method setName(name=String) public
 oldValue = String fieldName
 fieldName = name
 firePropertyChange("name", oldValue, name)

method setSalary(salary=float) private
 oldValue = float fieldSalary
 fieldSalary = salary
 firePropertyChange("salary", Float(oldValue), Float(salary))

Amint látjuk, mindhárom set metódus meghívja a firePropertyChange metódust, hogy figyelmeztesse az értesítési listára feliratkozott osztályokat a kapcsolt állapotváltozók értékének megváltozásáról. Figyeljük meg, hogy a változók régi és új értékét az Object osztály alosztályaként (Integer vagy Float) kell átadni! A setSalary metódust privátnak deklaráltuk, mivel azt akarjuk, hogy az alkalmazott fizetését csak az alábbiakban bemutatandó increaseSalary és decreaseSalary publikus metódusokkal lehessen csökkenteni illetve növelni:

method increaseSalary(amount=float) public
 setSalary( getSalary() + amount )

method decreaseSalary(amount=float) public
 setSalary( getSalary() - amount )

Ezekben a metódusokban már nem hívtuk meg a firePropertyChange metódust, mivel az megtörténik a setSalary privát metódus meghívásakor.

A metódusok után már csak a bean harmadik összetevôjét, az esemény(eke)t kell megalkotnunk. Az események a kapcsolt állapotváltozókat manipuláló metódusokhoz hasonlóan három összetevôbôl állnak. Minden esetben szükség van ugyanis két olyan metódusra, amelyekkel az értesítési listára lehet fel- illetve leiratkozni, valamint egy olyan metódusra, amelyik triggereli az eseményt:

properties inheritable
 aActionListener = Vector null -- értesítési lista

method addActionListener(newListener=ActionListener) public -- feliratkozás
 if aActionListener == null then aActionListener = Vector()
 aActionListener.addElement(newListener)

method removeActionListener(newListener=ActionListener) public -- leiratkozás
 if aActionListener \= null then
 aActionListener.removeElement(newListener)

method fireActionPerformed(e=ActionEvent)
 if aActionListener == null then return
 currentListeners = aActionListener.clone().elements()
 loop while currentListeners.hasMoreElements()
 	(ActionListener currentListeners.nextElement()).actionPerformed(e)
 end

Amint látjuk, az értesítési lista tulajdonképpen nem más, mint egy vektor, amelyben eltároljuk mindazon osztályok referenciáját, amelyek értesítést akarnak kapni az esemény bekövetkezésérôl. Amikor az esemény bekövetkezik, akkor meg kell hívnunk a fireActionPerformed metódust, amely elküldi az összes regisztrált osztálynak az esemény bekövetkezésének hírét. Példaprogramunk esetében egyetlen eseményt kell csak jeleznünk. Azt szeretnénk ugyanis, hogy ha a fizetés átlép egy bizonyos határértéket, akkor figyelmeztessük az ebben érdekelt osztályok:

properties constant
 salaryLimit = float 100 -- 100 egység a limit

method setSalary(salary=float) private
 oldValue = float fieldSalary
 fieldSalary = salary
 if salary > salaryLimit then do
 	salary = salaryLimit
 	fieldSalary = salaryLimit
 	fireActionPerformed( 	ActionEvent( this, -
 				ActionEvent.ACTION_FIRST, -
 				"SALARY-LIMIT" ) )
 end
 firePropertyChange("salary", Float(oldValue), Float(salary))

Ahhoz, hogy meghívhassuk az esemény bekövetkezését jelzô metódust, létre kell hoznunk egy ActionEvent objektumot, amelyik tartalmazza az esemény forrását, az esemény kódját és egy, az eseményt leíró karakterláncot.

Beanleíró osztályok

Mivel példánk egy eseményt is tartalmaz, ezért mindenképpen készítenünk kell a beanhez egy leíró osztályt is. Amennyiben a bean csak tulajdonságokból és metódusokból állna, abban az esetben ez nem lenne kötelezô, mivel a többi osztály meg képes határozni a bean publikus interfészét (lásd Java introspection support a JDK dokumentációban). Példánk esetében a leíró osztály a következôképpen nézhet ki:

import java.beans.
class lecke11b extends SimpleBeanInfo public binary

properties private
 beanClass = Class

method lecke11b() public -- konstruktor
 beanClass = Class.forName("lecke11b")

method getEventSetDescriptors() returns EventSetDescriptor[ ] public -- az esemény definíciója
 eventDesc = EventSetDescriptor[1]
 do
 	eventDesc[0] = EventSetDescriptor( beanClass, "actionPerformed", -
 	ActionListener.class, "actionPerformed" )
 	eventDesc[0].setDisplayName("empEvent" )
 	eventDesc[0].setShortDescription("employee event" )
 	return eventDesc
 catch Throwable
 end
 return null

Az állapotváltozók és metódusok leírására az xxxxxPropertyDescriptor és xxxxxMethodDescriptor metódusokat lehet használni, ahol xxxxx helyébe az állapotváltozó vagy a metódus nevét kell behelyettesíteni.

A beanek használata

Amennyiben elkészültünk a NetREXX-ben megírt beannel, akkor elôször át kell fordítani a kódot Java nyelvre, hogy a bean használható legyen például VisualAge for Java-ban. A Java kódra történô fordítást a NetREXX fordító -keep opciójával lehet elvégezni:

nrc lecke11a -keep
nrc lecke11b -keep

Az eredményül kapott lecke11a.java.keep és lecke11b.java.keep fájlokat nevezzük át *.java-ra! Importáljuk az így kapott Java fájlokat a VisualAge for Java-ba, majd nyissuk meg a lecke11a osztályt és lapozzunk a BeanInfo oldalra. Válasszuk ki mindegyik tulajdonságot és állítsuk a bound attribútumot TRUE-ra! Az eljárás hatására újra fog generálódni a teljes lecke11b osztály. A bean ezek után szabadon felhasználható appletekben vagy alkalmazásokban.


REXX GYÍK:

K1. Miért érdemes NetREXX-ben Java beaneket írni?
V1. Ennek több oka is lehet. NetREXX-ben könnyebben és gyakran tömörebben lehet programozni, mint Java-ban. Esetenként a már klasszikus REXX-ben megírt programrészletek újrafelhasználásának könnyedsége is jó érv lehet a NetREXX mellett.


Gyakorlat:

1. Alakítsa át a példa beant oly módon, hogy az a fizetés egy alsó határértékének átlépésére is eseményt generáljon!

2. Bôvítse ki a példabeant további állapotváltozókkal (pl. alkalmazott neme, lakcíme, beosztása, stb.). Hozza létre az új állapotváltozók értékének manipulálásához szükséges metódusokat is!

3. Készítsen appletet a VisualAge for Java-ban a példabean felhasználásával!

Kádár Zsolt
2000. 08. 24.
[ Elôzô lecke | Tartalom ]