Die Programmiersprache Ruby

Blog| Forum| Wiki  

Eine Klasse ist im Grunde nichts anderes als ein Behälter für Variablen und Methoden, die auf diese Variablen zugreifen oder sie verändern. Klassen vereinfachen Code enorm, anstatt einen Berg an Variablen zu erzeugen, kann man einfach ein Objekt dieser Klasse erzeugen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#Erzeugung eines zweidimensionalen Arrays
dimension_1 = []
dimension_2 = []
zweidimensionales_array = [dimension_1, dimension_2]
#Oder kurz: 
zweidimensionales_array = [[nil, nil, nil],[nil, nil, nil]] #Gr����e ist 3x3. 
#Zuweisen eines Wertes an das Array: 
zweidimensionales_array[1][2] = 5
#Abrufen des Wertes
puts zweidimensionales_array[1][2] #=> 5
#----------------------------------------------------------------------------------------
#Oder gar ein dreidimensionales Array: 
dreidimensionales_array = [[[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]], [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]], [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]] #Gr����e ist 3x3x3. 
dreidimensionales_array[0][1][0] = 7
puts dreidimensionales_array[0][1][0] #=> 7
#----------------------------------------------------------------------------------------
#Mit einer Klasse l��sst sich das einfacher l��sen: 
class DreiD_Array
  attr_reader :breite, :hoehe, :tiefe #Gettermethoden
  def initialize(breite, hoehe, tiefe)
    #Instanzvariablen zuweisen
    @drei_d = []
    @breite = breite
    @hoehe = hoehe
    @tiefe = tiefe
    #Leeres, verschachteltes Array erstellen
    breite.times do |br|
      @drei_d << []
      hoehe.times do |ho|
        @drei_d[br] << []
        tiefe.times do |ti|
          @drei_d[br][ho] << nil
        end
      end
    end
  end
  #Eleganten Zugriff auf das Array erlauben
  def [](x, y, z)
    return @drei_d[x][y][z]
  end
  def []=(x, y, z, wert)
    @drei_d[x][y][z] = wert
    return wert
  end
  def first
    return self.[](0, 0, 0)
  end
  def size
    "#{@breite}x#{@hoehe}x#{@tiefe} = #{@breite * @hoehe * @tiefe}."
  end
  def inspect #Spezialmethode, die immer aufgerufen wird, wenn ein Objekt mit p ausgegeben wird
    "Gr����e:\n#{size}\nInhalt:\n#{@drei_d.inspect}"
  end
end
#Jetzt kann man, sooft man will, 3D-Arrays erstellen, ohne noch einmal die aufw��ndige Erstellprozedur durchzumachen. 
drei_d = DreiD_Array.new(3, 3, 3)
drei_d[0, 1, 0] = 7
puts drei_d[0, 1, 0] #=> 7
p drei_d
#=> Gr����e: 
#=> 3x3x3 = 27
#=> Inhalt: 
#=> [...]

drei_d2 = DreiD_Array.new(10, 10, 10)
drei_d2[7, 4, 3] = 99
drei_d2[9, 9, 9] = "Test"
puts drei_d2[7, 4, 3] #=> 99
puts drei_d2.first #=> nil


Inhaltsverzeichnis

Klassendefinition

Zuerst wird eine Klasse definiert. Dazu wird das Schlüsselwort class benutzt. Klassen beginnen per Konvention in Ruby immer mit einem Großbuchstaben, deshalb halten auch wir uns daran und nennen die Klasse DreiD_Array.

Initialize-Methode

Darauf folgt (wenn nötig) mit def die Definition einer Methode. initialize ist eine spezielle Methode: Sie wird jedes Mal aufgerufen, wenn eine Instanz der Klasse (mit new) erzeugt wird.

Instanzvariablen

Das mit @ bezeichnete Wort ist eine Instanzvariable. Diese Instanzvariablen haben einen speziellen Gültigkeitsbereich, nämlich lediglich ihre eigene Klasse. Sie können in den Methoden der Klasse abgerufen werden, aber nicht von außerhalb. Subklassen können allerdings die Instanzvariablen ihrer Superklassen verwenden. Instanzvariablen sind für jede Instanz einer Klasse anders. Eine Instanzvariable muss nicht definiert werden; ruft man eine bisher nicht definierte Instanzvariable ab, so erhält man nil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
  def set_a(wert)
    @a = wert
  end
  def get_a
    return @a
  end
end
x = A.new
x.set_a(50)
x.get_a #=> 50
y = A.new
y.get_a #=> nil
#---
@a = 5
puts @a #=> 5
puts @b #=> nil

Instanzmethoden

Wie so gut wie jede andere Klasse besitzt auch unsere DreiD_Array-Klasse „normale“ Methoden, Instanzmethoden. Ähnlich wie die Instanzvariablen besitzen auch sie nur einen eingeschränkten Gültigkeitsbereich, die Klasse. Diese Methoden können von innerhalb der Klasse aufgerufen werden, wie in der Methode first[1] oder aber von einer Instanz der Klasse, wie im obigen Beispiel die Zeile
drei_d[0, 1, 0] = 7

.

Klassenmethoden

Neben den Instanzmethoden gibt es noch Klassenmethoden. Sie werden weniger häufig verwendet, können aber durchaus auch nützlich sein. Eine Klassenmethode kann man auf vier Arten definieren:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A
  def A.methode
    puts "Klassenmethode!"
  end
end
A.methode #=> Klassenmethode!
#---
class A
  def self.methode
    puts "Klassenmethode!"
  end
end
A.methode #=> Klassenmethode!
#---
def A.methode
  puts "Klassenmethode!"
end
A.methode #=> Klassenmethode!
#---
class A
  class << self
    def methode
      puts "Klassenmethode!"
    end
  end
end
A.methode #=> Klassenmethode!

Die letzten beiden sind dabei besonders, sie nutzen aus, dass in Ruby jedes Objekt (auch Klassen sind Objekte) eine Singletonklasse besitzt, dessen einzige Instanz es ist. Das wird im Artikel für Singletons ausführlicher behandelt.

Klassenvariablen

Genauso wie es Klassenmethoden gibt, gibt es Klassenvariablen, sie werden mit @@ definiert. Diese Variablen sind komplett auf ihre Klasse ausgelegt, nicht auf die Instanzen. Eine Klassenvariable ist für alle Instanzen die Gleiche:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A
  @@anzahl_instanzen = 0
  @@klassenvariable = 0
  def initialize
    @@anzahl_instanzen += 1
  end
  def self.anzahl_instanzen
    @@anzahl_instanzen
  end
  def +(zahl)
    @@klassenvariable += 1
  end
  def wie_viel?
    @@klassenvariable
  end
end
A.new
A.new
A.new
puts A.anzahl_instanzen #=> 3

x = A.new
y = A.new
x + 99
puts y.wie_viel? #=> 99

Getter- und Settermethoden

Als Getter- bzw. Setter-Methode bezeichnet man Methoden, die Instanzvariablen einer Klasse les- bzw. schreibbar machen. Eine Getter-Methode, die die Breite unseres dreidimensionalen Arrays lesbar macht, wäre zum Beispiel folgende:

1
2
3
4
5
class DreiD_Array
  def width
    @breite
  end
end

Für all diese Instanzvariablen in einer Klasse aber Getter- und Settermethoden anzulegen, wäre eine leidige Arbeit, die den Code unnötig verlängern würde, deshalb gibt es bereits vordefinierte, so genannte »Decorator-Methoden«, die diese Aufgabe erledigen. Diese Methoden sind attr_reader, attr_writer und attr_accessor. Sie erstellen anhand der Symbole, die ihnen mitgegeben werden, Getter- und/oder Settermethoden für die durch diese Symbole beschriebenen Instanzvariablen.

  • attr_reader(*syms): Erzeugt eine Gettermethode für alle durch die mitgegebenen Symbole beschriebenen Instanzvariablen.
  • attr_writer(*syms): Erzeugt eine Settermethode für alle durch die mitgegebenen Symbole beschriebenen Instanzvariablen. Wird selten verwendet.
  • attr_accessor(*syms): Erzeugt sowohl eine Getter- als auch eine Settermethode für alle durch die mitgegebenen Symbole beschriebenen Instanzvariablen.

Diese Methoden funktionieren nicht für Klassenvariablen!

1
2
3
4
5
6
7
8
9
class A
  attr_accessor :b
  def initialize
    @b = 1
  end
end
x = A.new
x.b = 5
x.b #=> 5

Methodenzugriff

Wie viele andere Programmiersprachen, besitzt auch Ruby Möglichkeiten, die Zugriffsrechte auf Methoden zu verändern. Die Methoden – ja, Methoden – dazu heißen public, private und protected. Standardmäßig sind alle in einer Klasse definierten Methoden public.

Methode Zugriff von außen Vererbbar Zugriff innerhalb der Klasse
public Ja Ja Ja
private Nein Ja Ja
protected Nein Nein Ja
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class A
  def do_b_and_d
    b
    d
  end
  private
  def b
    puts "b - private!"
  end
  public
  def c
    puts "c - public!"
  end
  protected
  def d
    puts "d - protected!"
  end
end
class X < A
end
x = A.new
x.b #=> NoMethodError
x.c #=> c - public!
x.d #=> NoMethodError
x.do_b_and_d
#=> b - private!
#=> d - protected!

y = X.new
y.b #=> NoMethodError
y.c #=> c - public!
y.d #=> NoMethodError
y.do_b_and_d
#=> b - private
#=> NoMethodError


Es ist auch über einen Trick möglich, geschützte oder private Methoden von außen über Object#send aufzurufen. Das sollte man aber nur mit gutem Grund tun.

1
2
3
4
5
6
7
8
9
class A
  def b
    puts "b - private!"
  end
  private :b # -> so kann man auch eine methode ��ndern
end
x = A.new
x.b #=> NoMethodError
x.send(:b) #=> b - private


Offene Klassen

Rubys Klassen sind offen. Das heißt, man kann sie nach ihrer Definition wieder öffnen, Methoden überschreiben, hinzufügen, entfernen etc.

1
2
3
4
5
6
class Array
  def shuffle
    sort_by{rand}
  end
end
[1, 2, 3].shuffle #=> [2, 3, 1]

Fußnoten

  • [1]In der Methode first wird der Methodenaufruf mit self aufgerufen – das ist aber in der Regel nicht notwendig, es reicht, wenn man den Methodennamen schreibt. Hier ist es nur erforderlich, weil [] vom Interpreter sofort als Arraydefinition aufgefasst wird.

Siehe auch