1.4.1.2. fejezet, Osztályok és objektumok
Egy OOP szemléletű programozási nyelv megismeréséhez első teendőnk megtanulni az osztályok és objektumok használatát. Itt egy rövid példa a Ruby OOP programozására.
class Allat @name def initialize(aName) @name=aName end end class Emlos < Allat @description @@letszam=0 protected def initialize(name,descriptor) super(name) @descriptor=descriptor @@letszam+=1 end public attr_reader :name, :descriptor def get_letszam() return @@letszam end protected def say() return sayOnce() end end; class Kutya < Emlos protected def sayOnce() return @name+" say that: vau-vau" end public def talk() return say() end end class Macska < Emlos protected def sayOnce() return @name+' say that: miau' end public def talk() return say() end end kuty = Kutya.new('Morzsi','foltos-bolyhos kiskutya') macs = Macska.new('Cirmi','kormos-morcos kiscica') borz = Emlos.new('Lutri','dumafranci') puts(kuty.talk) puts(macs.talk) puts("Number of animals: "+borz.get_letszam.to_s);
Az osztálynevek nagy betűvel kezdődnek, a tulajdonság és metódusnevek kis betűsek. A Ruby kisbetű/nagybetű-érzékeny (case sensitive).
A self metódussal hivatkozhatunk az aktuális objektumra, a class metódus adja vissza az objektum osztálynevét. A super metódussal hívhatók az ősosztálybeli metódusok, és a superclass metódussal kérdezhető le egy osztály ősosztálya. Polimorfizmushoz nincs szükség típuskényszerítésre (lásd talk metódus). Nincs absztrakt osztály, absztrakt és virtual metódus. Az initialize metódust ajánlott definiálni, mert ezen keresztül is kaphatnak alapértelmezett értékeket a belső tulajdonságok. Nincs destruktor metódus, ugyanis automatikusan szabadulnak fel a nem használt objektumok által foglalt memóriaterületek (garbage collection, lásd még: ObjectSpace).
Kimeneti értékek:
Morzsi say that: vau-vau
Cirmi say that: miau
Number of animals: 3
Mint látható,a Ruby erősen követi az objektumok tulajdonságainak elrejtésére vonatkozó szabályt. A tulajdonságok @ jellel kezdődnek, és kizárólag metódusokon keresztül érhetőek el. A láthatósági szabályok kizárólag a metódusok elérését szabályozza. A get_tulajdonság metódusokat automatikusan generálhatjuk az attr_reader kifejezéssel. Ugyanez vonatkozik az attr_writer és attr_accessor metódusokra is (ez utóbbi read és write metódusokat generál a tulajdonságokhoz).
Külön megjegyzem, hogy ezek a metódusok felül is írhatók, így írhatunk olyan writer metódust, ami mondjuk egy boltban a körte árát az alma árához viszonyítja, és akármelyik változása esetén újraszámítja. A definiált író és olvasó metódusokon keresztül a tulajdonságot látszólag úgy is kezelhetjük a kódban, mint ha közvetlenül érnénk azt el. Például:
class Bolt @alma @korte def initialize(alma,korte) @alma=alma @korte=korte end attr_accessor :alma, :korte def korte=(value) @korte=value*@alma end def alma=(value) @alma=value @korte=value*@alma end end b = Bolt.new(10,10) puts("Korte ara a boltban: #{b.korte} fitying.") b.korte = 100 puts("A korte uj ara a boltban: #{b.korte} fitying.") b.alma = 5 puts("A korte legujabb ara a boltban: #{b.korte} fitying.")
Kimeneti értékek:
Korte ara a boltban: 10 fitying. A korte uj ara a boltban: 1000 fitying. A korte legujabb ara a boltban: 25 fitying.
Statikus metódusok készítésére külön jelölést alkalmaz:
class Obj @mySQL attr_accessor :mySQL @@otherSQL def initialize(mySQL,otherSQL) @mySQL=mySQL @@otherSQL = otherSQL end def Obj.classMethod1(aStr) return "otherSQL = #{@@otherSQL} #{aStr}" end def get_mySQL(aStr) return "#{@mySQL} #{aStr}" end end class << Obj def table( aStr ) return "#{aStr}" #{@@otherSQL} end end ob=Obj.new('select * from', "update table") puts(Obj.classMethod1('egyszervolt')) # statikus metodus osztaly valtozot hasznalhat puts(Obj.table("valami")) # statikus metodus nem eri el az osztalyvaltozot puts(ob.get_mySQL("holmi")) # objektum valtozo hasznalata egy objektumban
Kimeneti eredménye:
otherSQL = update table egyszervolt
valami
select * from holmi
A classMethod1 egy osztály metódus, vagy másként statikus metódus. Ezzel és az osztály változóval építhetünk egyke (singletone) objektumokat.
A Ruby metódusainak mindig az utoljára végrehajtott kifejezés a visszatérési értéke. Tömb és hash tábla visszatérési értéke is lehet egy metódusnak, valamint alapértelmezett értéket is kaphat. A * jellel megjelölt változó több értéket is fogadhat.
def aMethod( a=10, b=20, c=100, *d ) return a, b, c, d end p( aMethod( 1,2,3,4,6 ) )
Kimeneti értékek:
[1, 2, 3, [4, 6]]
def ret_hash return {'a'=>'hello', 'b'=>'goodbye', 'c'=>'fare thee well'} end
Kimeneti értékek:
{"a"=>"hello", "b"=>"goodbye", "c"=>"fare thee well"}
Tulajdonságnál értékadásra használt = jel működése is felülírható:
def name=( aName ){ }
Egy osztályon belül lehet újabb osztályt definiálni, és egy metóduson belül szintén lehet újabb metódust definiálni. Definiálni lehet még konstansokat, az alábbiak szerint:
class X A = 10 class Y end end X::A
Itt a nagybetűs konstans, az A. Osztályneveket is tárolhatunk ezekben az állandókban, és az osztálynevek is állandók, hiszen nagy betűvel kezdődnek. Ha ilyen értéket teszünk a konstansba, objektumot is generálhatunk:
ob = X::Y.new
Globális konstansok is vannak, ezek a $ előjellel jelöljük.
Részletekben is definiálhatunk osztályokat. Ugyan ez nagy kavarodást eredményezhet a kódban, de azért érdemes vele foglalkozni.
class A def a puts( "a" ) end end class B < A def ba1 puts( "ba1" ) end end class A def b puts( "b" ) end end class B < A def ba2 puts( "ba2" ) end end
Ezek után érvényesek az alábbi metódushívások:
ob = B.new
ob.a
ob.b
ob.ba1
ob.ba2
Így ha egy meglévő osztályt szeretnénk kiegészíteni, megtehetjük az alábbiak szerint:
class Array def gribbit puts( "gribbit" ) end end
A tömb osztály kiegészült egy új metódussal. Meghívása pedig:
[1,2,3].gribbit
Egy érdekesség még az osztálykezelésre:
x=B begin x = x.superclass puts(x) end until x == nil
Ha ezt az előzőleg definiált osztályokhoz tesszük, az alábbi kimenetet kapjuk:
A Object BasicObject
Minden osztály a Class osztály példányosítása. Ez egy érdekes megközelítés. A metódusoknál lesz még szó erről.
Egy metódusnak átadott objektum változatlanul kerül ki a metódusból, ha változatlanul hagyjuk az értékét. Egyébként új objektum születik az új értékkel.
Az integer (Fixnum) objektumok különlegesek abból a szempontból, hogy az object_id tulajdonsága mindnek az értékként meghatározott számmal van összefüggésben. Például:
puts( 10.object_id ) x = 10 puts( x.object_id ) y = 10 puts( y.object_id )
Kimeneti eredménye
21 21 21
Más típusoknál ez nem érvényes.
Metódusok be és kimeneti értékei, érték vagy referencia átadása
def hidden( aStr, anotherStr ) anotherStr = aStr + " " + anotherStr aStr = anotherStr.reverse + " bello " + aStr.reverse return aStr +"\r\n"+ anotherStr.reverse end str1 = "dlrow" str2 = "olleh" str3 = hidden(str1, str2) # str3 receives returned value puts( str1 ) # input argument: original values unchanged puts( str2 ) # input argument: original value unchanged puts( str3 )
Eredménye:
dlrow
olleh
hello world bello world
hello world
Itt érték szerinti paraméter átadás történt.
Objektumok összehasonlítása
s1 = "hello" s2 = "hello" puts( s1==s2 ) # true, mert ertek szerint azonos tartalmuk van #Ket objektum azonossaganak a vizsgalatara az equal? metodust alkalmazzuk puts( s1.equal?(s2) ) # false
- A hozzászóláshoz be kell jelentkezni