1.4.1.7. fejezet, Metódusok

Metódus elnevezés

A metódusok elnevezése általában végig kisbetűs, de ez nem kötelező, csak ajánlott. Ha nagy betűvel kezdenénk egy metódusnevet, akkor az már egy konstans is lehet. Ezért ott egyértelműsíteni kell zárójelekkel, hogy metódushívás történik, például: a.Konst1().

Osztály és objektum metódusok

Korábban említésre került az osztály metódus és a példánymetódus közti különbség.

class MyClass
  def MyClass.classMethod
    puts( "This is a class method" )
  end
 
  def instanceMethod 
    puts( "This is an instance method" )
  end
end

Az osztály metódusok objektum példányosítás nélkül használhatók. Ezek elérik az osztály tulajdonságokat(@@), az osztály-objektum tulajdonságokat (@) az osztálymetódusokon keresztül, viszont nem érik el a példány tulajdonságokat (@). Egy lényeges megállapítás van az előbbiekben, hogy az osztály is egy objektum a ruby-ban. Ezért az osztály metódusokban is elérhető egy osztály-objektum tulajdonsága. Kicsit nyakatekerten hangzik, de ez van, és talán később találunk rá jobb példát.

Osztály és objektum tulajdonságok

Ugyanígy létezik osztály és objektum tulajdonság. Az osztály tulajdonságot @@ előjelekkel, az objektum tulajdonságot egy @ előjellel jelöli a ruby.

class Thing 
  @@num_things = 0
  @name
  @desc 
  def initialize( aName, aDescription ) 
    @@num_things +=1
    @name = aName
    @desc = aDescription 
  end
end
 
t=Thing.new("alma","bagoly")
p(t)

Az initialize metódus ős osztálybeli megfelelőjét csak akkor hívjuk a, ha definiáltuk ott. Az osztály változóknak értéket kell adni. Ez lehet közvetlen vagy közvetett értékadás. Példányszámláló esetén például inicializáláskor növelhetjük a számlálót.

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
  def Obj.write_classSQL(aSQL)
    @classSQL=aSQL
  end
  def Obj.read_classSQL
    return @classSQL
  end
end

Metódus fűzés az osztályhoz egy későbbi helyen.

class << Obj
  def table( aStr )
    return "#{@classSQL}, #{aStr}" # Ez olvashatja a @classSQL osztaly tulajdonsagot
  end
 
  def read_otherSQL
    return "#{@@otherSQL}" # @@otherSQL nincs inicializalva
  end
end

A metódusok hívása:

ob=Obj.new('select * from', "update table")
puts(Obj.classMethod1('classMethod1'))      # statikus metodus osztaly valtozot hasznalhat
puts(ob.get_mySQL("objektum tulajdonsan")) # objektum valtozo hasznalata egy objektumban
Obj.write_classSQL("osztaly tulajdonsag")
puts(Obj.read_classSQL) # osztaly tulajdonsag
puts(Obj.table("osztaly metodus 2"))   # statikus metodus nem eri el az osztalyvaltozot
puts(Obj.read_otherSQL)

Kimenet:

otherSQL = update table classMethod1
select * from objektum tulajdonsan
osztaly tulajdonsag
osztaly tulajdonsag, osztaly metodus 2
 
c:\wamp\bin\apache\Apache2.2.11\cgi-bin>ruby singleton.rb
otherSQL = update table classMethod1
select * from objektum tulajdonsan
osztaly tulajdonsag
osztaly tulajdonsag, osztaly metodus 2
singleton.rb:29: warning: class variable access from toplevel
singleton.rb:29:in `read_otherSQL': uninitialized class variable @@otherSQL in 
Object (NameError)
        from singleton.rb:39:in '<main>'

A fenti példában a classSQL osztálymetódust és az osztály-objektum tulajdonságot érdemes megfigyelni.

class O2 < Obj
  def initialize(mySQL,otherSQL)
    super
  end
end

Leszármazott osztály sem éri el az osztály-objektum tulajdonságot.

ob2=O2.new("insert into classes", "create table new")
puts(O2.read_classSQL)
puts(O2.classMethod1('O2.classMethod1'))

Kimenete:

# ures sor, nincs classSQL ertek inicializalva
otherSQL = create table new O2.classMethod1

Miért is készítenénk osztály metódusokat? Nézzük meg erre válaszként a File osztályt. Tele van osztály metódussal.

fn = 'file_methods.rb' 
if File.exist?(fn) then 
  puts(File.expand_path(fn))
  puts(File.basename(fn)) 
  puts(File.dirname(fn)) 
  puts(File.extname(fn)) 
  puts(File.mtime(fn)) 
  puts("#{File.size(fn)} bytes") 
else
  puts( "Can't find file!") 
end

new metódus, vagy initialize

A new metódus az osztály konstruktora. Ez egy osztálymetódus, és miután elkészült az objektum, meghívja a példány initialize metódusát, ha van ilyen. Kerüljük a new metódus felüldefiniálását. Emlékezzünk csak arra, hogy minden metódus visszatérési értéke az utoljára végrehajtott kifejezés. Vagyis az alábbi kód eredménye egy String objektum "HELLO WORLD" tartalommal, a MyClass objektum helyett

class MyClass
  def initialize( aStr )
    @avar = aStr 
  end 
 
  def MyClass.new( aStr )
    super
    @anewvar = aStr.swapcase
  end 
end
 
ob = MyClass.new( "hello world" )
puts( ob )
puts( ob.class )

Objektum egyedi metódusok

Statikus metódusnál osztálynevet használtunk előtétként, singleton metódusnál objektum változót használunk.

class Creature 
  def initialize( aSpeech ) 
    @speech = aSpeech 
  end 
 
  def talk 
    puts( @speech ) 
  end 
end
 
cat = Creature.new( "miau" )
farkas = Creature.new( "vau" )

Mondjuk, hogy egy farkas teliholdnál ugatás helyett vonyít. Ezt az objektum metódussal érhetjük el.

  def farkas.vonyitas
    if TELIHOLD then
      puts( "How-oo-oo-oo-oo!" )
    else
      talk 
    end
 end

Egy újabb kutya objektumnál nem találunk vonyitas metódust.

Ha kíváncsiak vagyunk a singleton metódusok listájára, rákérdezhetünk pl. az IO osztályban találhatókra

p( IO.singleton_methods )

vagy a Creature osztályból készített farkas objektumunkra:

p( farkas.singleton_methods )

Minden ruby osztályból készített példány az Object osztály közvetett leszármazottja (ebben van a singleton_methods tömb, aminek az include? metódusával megtudhatjuk, tartalmazza-e a nekünk szükségest), ezen pedig magába foglalja a Class osztályt is. Ellenőrizhetünk egy metódus létezését szimbólum használatával is, az Object.respond_to? metódusával:

if item.respond_to?( :congratulate ) then
  item.congratulate
end

Egyedi osztályok

Tegyük fel, hogy több metódussal, tulajdonsággal szeretnénk bővíteni az objektumunkat. Az egyedi objektum metódus létrehozás helyett bővíthető az objektum egy egész osztályrészlettel.

starprize = MyClass.new( "Star Prize" )
class << starprize
  def congratulate 
    puts( "You've won a fabulous holiday in Grimsby!" )
  end 
 
  def turnup
    puts( "Turned up")
  end
end

A létrehozott starprize objektum kibővült két metódussal. Le is kérdezhetjük a p(starprizer.singleton_methods) többel.

Kicsit más felfogása van az egyke osztályhoz képest a singleton class fejezetnek. Tervezési mintákban (design patters) az egyke osztály azt jelenti, hogy a belőle létrehozott példányból egyetlen egy létezik az egész program futása alatt. Az objektumot egy rejtett osztály változóban tárolja, és statikus konstruktor metódus ennek az értékét adja vissza. Tertmészetesen ellenőrzi, hogy üres objektumot ne adjon ki.

Override

Metódus felülírás a leszármazott osztályba lehetséges. A metódus nevével definiált újabb metódus felülírja az eredetit. A felülírt metódus a super.<metódusnév> formában hívható. Az aktuális objektumban lévőre a self.<metódusnév> formában.

A láthatóságról esett már szó. A private, protected, public kulcsszavaról. Metódusokra ezek is érvényesek, hozzá teszem még, hogy ezek a Module osztály metódusai, és nem kulcsszavak, mint megszoktuk.

Egy kibúvót azért enged a ruby a privát metódusok hívására. Ez a send metódus. Első paramétere a metódusnév szimbóluma, a többi az átadandó paraméter. Ha sűrűn használjuk, jobb a metódus láthatóságán változtatni.

ob.send( :privmeth, "hello" )

Egyedi metódus generálást már láttunk, osztályon kívülről. Most ugyan ez történik, osztályon belül:

class MyClass
  def MyClass.methodA
    puts("a") 
  end 
 
  class << self
    def classMethodB
      puts("b")
    end
 
    def classMethodC
      puts("c")
    end
  end
 
end

Most megnézzük hogyan ágyazhatunk egymásba metódusokat.

class X
  def x 
    print( "x:" ) 
 
    def y
      print("y:")
    end
 
    def z
      print( "z:" )
      y
    end
  end
end

Egymásba ágyazott metódusok akkor is elérhetők kívülről - nem csak a metóduson belülről, ahol definiáltuk -, ha elötte lefuttatjuk a definiáló metódust.

ob = X.new
ob.x #=> x: Ha ezt elhagyjuk, hibaüzenetként megkapjuk, hogy y és z nem definiált.
puts
ob.y #=> y: 
puts 
ob.z #=> z:y: