Next Previous Contents

4. Metoder, klasser og objekter.

Ruby er et særdeles objekt-orientert språk. Metoder, klasser og objekter er de grunnleggende byggesteinene.

4.1 Metoder

  1| # Husker du denne?
  2| def si_hei
  3|   puts "Hei verden!"
  4| end
  5| 
  6| # Hva er vel en funksjon uten argumenter?
  7| def si_hei_til( hva )
  8|   puts "Hei #{hva}"
  9| end
 10| 
 11| si_hei_til("Lutvann!")      #=> "Hei Lutvann!"
 12| 
 13| # Funksjoner kan ta flere argumenter og de kan ha default verdier
 14| def send_julegave( til, fra="nissen" )
 15|   puts "God jul, #{til}. Hilsen #{fra}."
 16| end
 17| 
 18| send_julegave("Junior" )    #=> "God jul, Junior. Hilsen nissen."
 19| send_julegave("Ola", "far") #=> "God jul, Ola. Hilsen far."

Returverdier

Ruby returnerer normalt den siste verdien i metoden, hvis ikke return kalles eksplisitt.

  1| def legg_sammen( a, b)
  2|   a + b   # det siste uttrykket returneres
  3| end
  4| puts legg_sammen( 9, 6 ) #=> 15
  5| 
  6| # fibonacci
  7| def fib( i )
  8|   if i <= 1
  9|     return 1   # vi kan returnere eksplisitt
 10|   end
 11|   return fib( i-1 ) + fib( i-2 )
 12| end
 13| puts fib( 3 ) #=>  3
 14| puts fib( 5 ) #=>  8

Spesielle argumenter

I tillegg til &-prefikset som brukes for å pakke en blokk gitt til en metode inn i et Proc-objekt, brukes *-prefikset for å samle flere argumenter i en Array.

  1| # * prefikset brukes for å pakke argumentlista inn i en Array
  2| def list_opp( og_frase, *args )
  3|   puts args[0..-2].join(", ").capitalize +
  4|     " " + og_frase + " " + args[-1] + '.'
  5| end
  6| 
  7| list_opp( "og", "epler", "pærer", "bananer" )
  8|   #=> "Epler, pærer og bananer."
  9| 
 10| # eller pakke opp en Array for å bruke elementene som argumenter
 11| a =  [ "and", "apples", "pears", "bananas" ]
 12| list_opp( *a ) #=> "Apples, pears and bananas."

4.2 Klasser

Som ethvert objekt-orientert språk har Ruby klasser.

  1| # En enkel klasse. 
  2| # Klassenavn må begynne med stor bokstav.
  3| class Person
  4|   # Person.new videresender argumentene til initialize
  5|   def initialize( etternavn, fornavn, alder = 0 )
  6|     # attributter prefikses med @
  7|     @etternavn  = etternavn
  8|     @fornavn    = fornavn
  9|     @alder      = alder
 10|   end
 11|   
 12|   # en vanlig instansmetode
 13|   def to_s
 14|     "#{@fornavn} #{@etternavn} er #{@alder} år."
 15|   end
 16| end
 17| 
 18| if __FILE__ == $0 # Kun når vi kjører denne filen:
 19|   p = Person.new("Nordmann", "Ola", 23) 
 20|   puts p #=> "Ola Nordmann er 23 år."
 21| end

Attributter

Ruby lar deg ikke få tak i et objekts attributter (felter, dataverdier, instansvariable) direkte. Alle attributter er "private". Enhver tilgang fra utsiden til objektet går via metodekall, såkalte get/set metoder.

Instansvariable er "private" på en måte som er mer som protected i andre språk; metoder i sub- og superklasser har tilgang. Men andre instanser har ikke tilgang, selv ikke instanser av samme klasse.

  1| # Vi vil bruke Person-klassen videre
  2| require 'klasse1.rb' 
  3| 
  4| class Person   # Klasser er "åpne" skop, og kan enkelt utvides.
  5| 
  6|   #  get-metode
  7|   def alder
  8|     @alder
  9|   end
 10|   # set-metode
 11|   def alder=( ny_alder )
 12|     @alder = ny_alder
 13|   end
 14| 
 15|   # tungvint? Jepp, så Ruby har en snarvei:
 16|   attr_accessor :alder     # definerer metodene over automatisk
 17| 
 18|   # Vi vil også gjerne kunne lese navnene til personen
 19|   attr_reader   :etternavn, :fornavn
 20| 
 21| end
 22| 
 23| if __FILE__ == $0 # Kun når vi kjører denne filen:
 24|   p = Person.new( "Nordmann", "Baby" )
 25|   p.alder = 3     # Vi setter alderen
 26|   puts p.alder    #=> 3
 27|   p.alder += 1    # Øk alderen med et år
 28|   puts p.alder    #=> 4
 29|   puts p.fornavn  #=> "Baby"
 30| end

:alder, :etternavn, :fornavn

Disse er symboler. (instanser av Symbol-klassen) De ligner litt på String, men kan ikke endres, de er "internert" og begrenser seg til lovlige navn på klasser, metoder, variabler o.l.

attr_accessor, attr_reader, attr_writer

Dette er metoder i Module-klassen som lager get/set metoder for deg. Som argument tar de symbolene til attributtene du vil lage get/set metoder for.

Arv

  1| require 'klasse2.rb'   # Fortsetter der vi slapp...
  2| 
  3| # Arv - alle studenter er en submengde av alle personer.
  4| class Student < Person
  5|   def initialize( etternavn, fornavn, alder = 0, studiested = "NTNU" )
  6|     # kall super-klassens versjon av metoden
  7|     super( etternavn, fornavn, alder )
  8|     @studiested = studiested
  9|     @karakterer = Array.new # Eventuelt []
 10|   end
 11|   
 12|   # redefinerer Person#to_s metoden.
 13|   def to_s                    
 14|     "#{@etternavn}, #{@fornavn} - studerer ved #{@studiested}."
 15|   end
 16| 
 17|   def ta_eksamen( karakter )
 18|     @karakterer.push karakter
 19|   end
 20| 
 21|   def karaktersnitt
 22|     sum = 0
 23|     @karakterer.each{ |karakter|
 24|       sum += karakter
 25|     }
 26|     sum.to_f / @karakterer.size
 27|   end
 28|   
 29| end
 30| 
 31| if __FILE__ == $0 # Kun når vi kjører denne filen:
 32|   flinkis = Student.new("Einstein", "Al", 128, "Mensa")
 33|   flinkis.ta_eksamen( 1.0 )
 34|   flinkis.ta_eksamen( 2.0 )
 35|   puts flinkis #=> "Einstein, Al - studerer ved Mensa."
 36|   puts flinkis.karaktersnitt #=> 1.5
 37| end

super

et alias for superklassens versjon av den metoden vi er i nå.

Multippel arv

Beklager. Det er ikke lov å la en klasse arve fra mer enn en superklasse i Ruby.

Derimot har Ruby mixin, som kan legge til funksjonalitet fra flere moduler inn i en klasse. Dvs, du kan bare arve fra en klasse, men kan blande inn funksjonalitet i fra flere moduler.

  1| class Familie
  2|   # Vi inkluderer funksjonalitet fra modulen kalt Enumerable.
  3|   include Enumerable
  4| 
  5|   # Enumerable forventer at each-metoden 
  6|   # itereres over innholdet.
  7|   def each
  8|     yield "Far"
  9|     yield "Mor"
 10|     yield "Sønn"
 11|     yield "Datter"
 12|   end
 13| end
 14| 
 15| f = Familie.new
 16| 
 17| # include? og sort metodene er mikset inn fra Enumerable.
 18| puts f.include?("Sønn") #=> true
 19| puts f.sort.join(", ")  #=> "Datter, Far, Mor, Sønn"

I kontrast til Java tilbyr dette multippel arv av funksjonalitet. Java bruker interface til å "etterligne" multippel arv, men tilbyr ikke arv av implementasjon og løser noe som er et ikke-problem når man har dynamisk typing.

Klassevariabler

Klassevariabler er variabler som deles mellom alle instanser av klassen, samt instanser av subklasser. (tilsvarende static variabler i Java)

  1| class Bil
  2|   # En klassevariabel for å telle antall biler i verden.
  3|   @@num_biler = 0
  4|   def initialize
  5|     @@num_biler += 1
  6|   end
  7|   # En klassemetode (eller singleton-metode)
  8|   def Bil.antall
  9|     @@num_biler
 10|   end
 11| end
 12| 
 13| class Lada < Bil
 14| end
 15| 
 16| class Yugo < Bil
 17|   def krasj
 18|     # klassevariabelen er felles for alle instanser av Bil,
 19|     # samt instanser av subklasser av bil
 20|     @@num_biler -= 1
 21|   end
 22| end
 23| 
 24| lada = Lada.new
 25| yugo = Yugo.new
 26| puts Bil.antall #=> 2
 27| yugo.krasj
 28| puts Bil.antall #=> 1

Død og begravelse: Finalize

Objekter fødes, brukes og dør. I Ruby dør objektene når de hentes av søppeltømmeren. (garbage collector) Når det skjer, er usikkert. Ingen referanser til objektet må eksistere og garbage collectoren må startes eksplisitt eller implisitt, f.eks. når det begynner å bli lite ledig minne.

  1| streng = "Hvil i fred."
  2| 
  3| # Vi gir en block som skal kjøres når streng objektet dør.
  4| ObjectSpace.define_finalizer(streng){|id|
  5|   puts "Objektet med ID=#{id} er nå dødt. "
  6|   puts "Rest in peace."
  7| }
  8| 
  9| # Starter søppeltømmeren eksplisitt.
 10| puts "Henter søppel!"
 11| GC.start
 12| # Men ingenting skjer, da det ennå er en referanse til strengen.
 13| 
 14| # Prøver en gang til...
 15| streng = nil
 16| puts "Henter mer søppel!"
 17| GC.start
 18| # finalizer blocken blir kjørt.

Legg merke til at objektet allerede er dødt når finalizer-blokken kalles. Ressurser som må lukkes eksplisitt, holdes via bindingen til blokken. (Bare pass på at blokken ikke også holder en referanse til objektet.)

Flere finalizere kan registreres på et objekt.


Next Previous Contents