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