The Object-Oriented Programming Language RubyDe Nederlandse Ruby Homepage




7. Methodes



7.1 Hoe bepaald Ruby welke methode moet worden uitgevoerd?

Alle messages aan methodes worden in Ruby dynamisch gebonden. Het zoekt eerst naar singleton methodes in de ontvanger,daarna naar methodes die gedefineerd zijn in de klasse van de ontvanger en tenslotte naar methodes die gedefinieerd zijn in de superklassen van de ontvanger (inclusief in eventuele mix-in modules). Je kan de zoekvolgorde zichtbaar maken met Classname.ancestors, die de superklassen en en modules van ClassName toont.

Word er ggen methode gevonden dan probeerd Ruby de methode  method_missing te vinden. Deze methode maakt het mogelijk om messages aan onbekende methodes af te handelen en word vaak gebruikt om klassen een dynamische interface te geven.

   
module Indexed
def [](n)
to_a[n]
end
end
class String
include Indexed
end
String.ancestors # -> [String, Indexed, Enumerable, Comparable, Object, Kernel]
"abcde".gsub!(/./, "\\&\n")[1] # -> 10

Dit programma geeft niet, zoals je zou verwachten "b\n" terug, maar 10. Wanneer naar de methode [] word gezocht word ze gevonden in de klasse String, voordat Indexed word doorzocht. Je moet [] rechtstreeks in de klasse String herdefinieeren.

7.2 Zijn +, -, * ... operators?

+, -, etc. zijn geen operators maar methode aanroepen. Het is dus mogelijk om ze te overloaden met nieuwe definities.

   
class MyString < String
def -(other) # New method
self[0...other.size] # self truncated to other's size
end
end

Onderstaande zaken echter, zijn ingebouwde control structuren en geen methodes. Het is dus miet mogelijk om ze te overloaden.

   
=, .., ..., !, not, ||, &&, and, or, ::

Om unaire operators te overloaden of te definieeren moet je +@ en -@ als methode namen gebruiken.

= word gebruikt om een methode te definieeren die aan een attribuut van een object een waarde toekent:

   
class Test
def attribute=(val)
@attribute = val
end
end
t = Test.new
t.attribute = 1

Als operators zoals + en - zijn gedefinieerd regelt Ruby automatisch de self assignment vormen (+=,-= etc.).

7.3 Waar zijn ++ and --?

Ruby heeft geen autoincrement and autodecrement operators. Gebruik in plaats daarvan += 1 of -= 1.

7.4 Al die objecten zijn prima, maar heeft Ruby ook gewone functies?

Ja en nee. Ruby heeft methodes die lijken op functies in talen zoals C of Perl:

   
def writeln(str)
print(str, "\n")
end

writeln("Hello, World!")

Produces:

Hello, World!

Dit zijn echter methode aanroepen zonder ontvanger. In dat geval gaat Ruby ervan uit dat de ontvanger self is.

Dus, writeln lijkt op een functie maar is in werkelijkheid een methode van de klasse Object en die gestuurd wordt naar de ontvanger self. Ruby is een pure object-georienteerde taal.

Natuurlijk kan je zulke methodes gebruiken als of het functies zijn.

7.5 Waar komen al die op functies lijkende methodes vandaan?

Alle klassen in ruby hebben Object als superklasse. De definitie van de klasse Object bevat een mix-in van de methodes die gedefinieerd zijn in de Kernel module. Deze methodes zijn daarom beschikbaar binnen elk object in het systeem.

Zelfs als je een simpel ruby programma schrijft zonder klassen werk je in werkelijkheid binnen de klasse Object.

7.6 Kan ik toegang krijgen tot de instance variabelen van een object?

De instance variabelen (de variabelen wiens naam met @ begint) van een object zijn niet direct toegankelijk buiten het object. Dit bevorderd goede encapsulation. Ruby maakt het gemakkelijk om accessors  voor deze variabelen te definieren zodat ze te gebruiken zijn als of het attributen betreft. Het enige dat je hoeft te doen is attr_reader,attr_writer, or attr_accessor te gebruiken.

   
class Person
attr :name # read only
attr_accessor :wearing_a_hat # read/write
def initialize(name)
@name = name
end
end

p = Person.new("Dave")
p.name # -> "Dave"
p.wearing_a_hat # -> nil
p.wearing_a_hat = true
p.wearing_a_hat # -> true

Je kan ook je eigen accessor functies definieeren (bijvoorbeeld om validatie of afgeleide attribute te implementeren). De read accessor is gewoon een methode zonder parameters en de toekennings accessor een methode die eindigd op =, die een enkele parameter heeft. Hoewel er geen spatie is toegestaan tussen de methodenaam en de = in de methode definitie is een spatie wel toegestaan bij de methode aanroep zodat ze lijkt op een normake toekenning. Je kan self assignments zoals += en -= gebruiken, als de corresponderende + or - methoeds zijn gedefinieerd.

7.7 Wat is het verschil tussen private en protected?

Het keyword  zordt ervoor dat een methode alleen in functie vorm kan worden aangeroepen. De ontvanger kan dus alleen self zijn. Een private methode is alleen aan te roepen vanuit de klasse waarin ze is gedefineerd of in een van diens subklassen.

   
class Test
def func
return 99
end
def test(other)
p func
p other.func
end
end
t1 = Test.new
t2 = Test.new

t1.test(t2)

# Now make 'func' private

class Test
private :func
end

t1.test(t2)

Produces:

99
99
99
prog.rb:7:in `test': private method `func' called for #<Test:0x401b4900> (NameError)
from prog.rb:21

Protected methodes zijn ook alleen maar aante roepen vanuit de klasse waarin ze is gedefineerd of in een van diens subklassen, maar ze kunnen met een ontvanger worden aangeroepn. Bijvoorbeeld:

   
def <=>(other)
age <=> other.age
end

Dit compileert als age een protected methode is, maar niet wanneer ze private is.

Dit alles helpt je om het binnenwerk van je klassen af te schermen.

7.8 Hoe kan ik de zichtbaarheid van een methode veranderen?

De zichtbaarheid van methodes kan worden veranderd doorde keywords private,protected of public te gebruiken. Wanneer ze zonder parameters gebruikt worden veranderen ze de zichtbaarheid van de na het keyword gedefinieerde methodes. Gebruikt met parameters veranderen ze de zichtbaarheid van de genoemde methodes.

   
class Foo
def test
print "hello\n"
end
private :test
end

foo = Foo.new
foo.test

Produces:

prog.rb:9: private method `test' called for #<Foo:0x401b4a2c> (NameError)

e kan een klasse methode private maken met de private_class_methode.

   
class Foo
def Foo.test
print "hello\n"
end
private_class_method :test
end

Foo.test

Produces:

prog.rb:8: private method `test' called for Foo:Class (NameError)

De standaard zichtbaarheid voor methodes is public. De einige uitzondering is de instance initializatie methode, initialize.

Methodes die buiten een klasse zijn gedefinieerd zijn ook standaard public.

7.9 Kan een identifier die begint met een hoofdletter de naam van een methode zijn?


Ja, maar denk er goed over na voordat je dit doet.Als Ruby een naam met een hoofdletter ziet gevolgd door een spatie zal ze er in het algemeen van uitgaan dat het gaat om een constante i.p.v. om een mothode. Gebuik je methodenamen met hoofdletters dan moet je er aan denken om altijd de parameters tussen haakjes te zetten en het openingshaakje direct na de methodenaam te plaatsen (dit laatste is zo wie zo een goed idee).

7.10 Het aanroepen van super geeft eenArgumentError!

Het aanroepen van super zonder parameters in een methode geeft alle parameters van die methode door aan een methode met die zelfde naam in een superklasse. Als het aantal paramters van de oorspronkelijke methode afwijkt van die uit de hoger liggende klasse word een ArgumentError veroorzaakt. Om dit te voorkomen moet je super aanroepen met het juiste aantal parameters.

7.11 Hoe roep ik een methode aan met dezelfde naam maar twee niveau's hoger? 

super roept een methode met de zelfde naam op die een niveau hoger ligt. Als je een methode overload die meer dan een niveau hoger ligt moet je alias gebruiken om die een nieuwe naam te geven voordat je die maskeerd met je methode definitie. De oude methode is dan aan te roepen via de alias.

7.12 Hoe roep ik een ingebouwde methode aan nadat ik haar geherdefinieerd heb?

Binnen de methode definitie kan je super gebruiken. Je kan ook alias gebruiken om haar een alternatieve naam te geven. Tenslotte kan je de oorspronkelijke methode aanroepen als een singleton methode vanKernel.

7.13 Wat is een destructive methode?

Een destructieve methode is een methode die de staat van een object wijzigd. String, Array, Hash en andere klassen hebben zulke methodes. Vaak zijn er twee versies van een methode, een met een normale naam, en een met dezelfde naam gevolgd door een !. De normale versie maakt een kopie van de ontvanger, voert de verandering uit en geeft de kopie terug. De versie met het ! veranderd de ontvaanger zelf.

Let er op dat er een behoorlijk aantal destructieve methodes is die geen ! hebben, bijvoorbeeld: toekennings operators (name=), array toekenning ([]=), en methodes zoals Array.delete.

7.1Waarom kunnen destructieve methodes gevaarlijk zijn?}

Bedenk dat toekenning over het algemeen kopieen van object referenties maakt en dat het doorgeven van parameters equivalent is met toekenning. Dit betekend dat je meerdere variabelen kan krijgen die verwijzen naar het zelfde object. Als een van die variabelen gebruikt wordt in een destructieve methode zal het object waarnaar de andere variabelen verwijzen dus wijzigen.

   
def foo(str)
str.sub!(/foo/, "baz")
end

obj = "foo"
foo(obj) # -> "baz"
obj # -> "baz"

In dit geval is de actuele parameter gewijzigd.

7.15 Kan een methode meerdere waardes teruggeven?

Ja en nee.

   
def m1
return 1, 2, 3
end
def m2
return [1, 2, 3]
end
m1 # -> [1, 2, 3]
m2 # -> [1, 2, 3]

Dus, er word maar een  iets teruggegeven, maar dat iets kan een complex object zijn. In het geval van arrays kan je meervoudige toekenningen gebruiken om het effect van meervoudige return waardes te krijgen. Bijvoorbeeld:

   
def foo
return 20, 4, 17
end

a, b, c = foo
a # -> 20
b # -> 4
c # -> 17