Zum Inhalt springen
Ruby Anfรคnger 35 min

Klassen, Blocks & Enumerable

Objektorientierung in Ruby: Klassen, attr_accessor, Vererbung, Module als Mixins und das maechtige Enumerable-Modul.

Aktualisiert:
Inhaltsverzeichnis

Klassen, Blocks & Enumerable

Ruby ist durch und durch objektorientiert - und hat mit Modulen als Mixins einen eleganten Umgang mit Mehrfach-โ€Vererbungโ€. Dazu kommt das beruehmte Enumerable-Modul.

Eine einfache Klasse

class Person
  def initialize(name, alter)
    @name = name
    @alter = alter
  end

  def gruessen
    "Hallo, ich bin #{@name}!"
  end
end

anna = Person.new("Anna", 28)
puts anna.gruessen    # "Hallo, ich bin Anna!"
  • class Name ... end mit PascalCase-Namen
  • initialize ist der Konstruktor, aufgerufen via new
  • @variable ist eine Instanzvariable

Getter und Setter

Direkt auf @name von aussen zuzugreifen geht nicht - Ruby kapselt:

anna.name             # FEHLER: undefined method `name'

Du musst Accessor-Methoden anbieten:

class Person
  def name
    @name
  end

  def name=(neu)
    @name = neu
  end
end

Kurzform: attr_accessor

class Person
  attr_accessor :name, :alter

  def initialize(name, alter)
    @name = name
    @alter = alter
  end
end

anna = Person.new("Anna", 28)
puts anna.name         # "Anna"
anna.alter = 29

Varianten:

  • attr_reader :name - nur Getter
  • attr_writer :name - nur Setter
  • attr_accessor :name - beides

Weitere Beispiel-Methoden

class Rechteck
  attr_reader :breite, :hoehe

  def initialize(breite, hoehe)
    @breite = breite
    @hoehe = hoehe
  end

  def flaeche
    @breite * @hoehe
  end

  def umfang
    2 * (@breite + @hoehe)
  end

  def skalieren(faktor)
    @breite *= faktor
    @hoehe *= faktor
    self              # fuer Methoden-Chaining
  end

  def to_s
    "Rechteck #{@breite}x#{@hoehe}"
  end
end

r = Rechteck.new(3, 4)
puts r.flaeche         # 12
puts r.skalieren(2).flaeche    # 48
puts r                 # "Rechteck 6x8" - to_s wird automatisch genutzt

Die to_s-Methode wird von puts und String-Interpolation automatisch genutzt.

Sichtbarkeit

class Konto
  def initialize(start)
    @saldo = start
  end

  def einzahlen(betrag)
    pruefen(betrag)
    @saldo += betrag
  end

  private

  def pruefen(betrag)
    raise ArgumentError if betrag <= 0
  end
end

Alles unter private ist nur von innen aufrufbar. Zusaetzlich gibt es protected.

Klassen-Methoden

Methoden an der Klasse selbst (nicht an Instanzen):

class Person
  def self.anonym
    new("Anonym", 0)
  end
end

anonym = Person.anonym

self innerhalb der Klassendeklaration zeigt auf die Klasse.

Vererbung

class Tier
  def initialize(name)
    @name = name
  end

  def geraeusch
    "..."
  end
end

class Hund < Tier
  def geraeusch
    "Wuff!"
  end
end

class Katze < Tier
  def geraeusch
    "Miau!"
  end
end

tiere = [Hund.new("Bello"), Katze.new("Lilli")]
tiere.each { |t| puts "#{t.instance_variable_get(:@name)}: #{t.geraeusch}" }
  • < markiert Vererbung
  • Methoden werden automatisch geerbt, koennen ueberschrieben werden

super - Basis-Methode aufrufen

class Hund < Tier
  def initialize(name, rasse)
    super(name)            # ruft Tier#initialize
    @rasse = rasse
  end
end

Module als Mixins

Module sind keine Klassen - aber sie liefern Methoden, die in Klassen eingemischt werden koennen:

module Fliegend
  def fliegen
    puts "Flatter flatter"
  end
end

module Schwimmend
  def schwimmen
    puts "Platsch"
  end
end

class Ente
  include Fliegend
  include Schwimmend

  def name
    "Donald"
  end
end

d = Ente.new
d.fliegen       # Flatter flatter
d.schwimmen     # Platsch

Das ist Rubys Antwort auf Mehrfachvererbung - sauberer als in C++, maechtiger als in Java vor Default-Methods.

Das Comparable-Modul

Ruby selbst nutzt Module. Beispiel Comparable:

class Temperatur
  include Comparable

  attr_reader :wert

  def initialize(wert)
    @wert = wert
  end

  def <=>(andere)
    @wert <=> andere.wert
  end
end

a = Temperatur.new(20)
b = Temperatur.new(25)

a < b         # true
a > b         # false
[a, b].min    # Temperatur(20)

Du implementierst nur <=> - und bekommst <, >, <=, >=, ==, between?, clamp, min, max geschenkt.

Das Enumerable-Modul

Wenn du each auf deiner Klasse definierst und Enumerable einbindest, bekommst du 100+ Methoden geschenkt - map, select, reduce, group_by, sort, min, max und viel mehr.

class Truhe
  include Enumerable

  def initialize
    @items = []
  end

  def <<(item)
    @items << item
    self
  end

  def each(&block)
    @items.each(&block)
  end
end

t = Truhe.new
t << "Gold" << "Silber" << "Edelstein"

puts t.count                        # 3
puts t.map(&:upcase).inspect        # ["GOLD", "SILBER", "EDELSTEIN"]
puts t.select { |i| i.length > 4 }.inspect

Das ist Rubyโ€™s Eleganz auf den Punkt gebracht.

Blocks vertieft

Drei Arten von โ€œcallableโ€ Code:

Block

[1, 2, 3].each { |n| puts n }

Block ist immer an einen Methodenaufruf gekoppelt, nicht als Wert gespeichert.

Proc

quadrat = Proc.new { |n| n * n }
puts quadrat.call(5)     # 25
puts quadrat.(5)         # 25 (Kurzform)
puts quadrat[5]          # 25 (noch kuerzer)

Lambda

quadrat = ->(n) { n * n }
puts quadrat.call(5)     # 25

Lambdas sind strikter bei Argument-Zahl und behandeln return anders - fuer den Alltag reicht das ->(...)-Kurzform zu kennen.

Praktisches Beispiel

module Begruessbar
  def gruessen
    puts "Hallo, ich bin #{name}!"
  end
end

class Person
  include Begruessbar

  attr_reader :name, :alter

  def initialize(name:, alter:)
    @name = name
    @alter = alter
  end

  def volljaehrig?
    @alter >= 18
  end

  def to_s
    "#{name} (#{alter})"
  end
end

class Team
  include Enumerable

  def initialize
    @mitglieder = []
  end

  def <<(person)
    @mitglieder << person
    self
  end

  def each(&block)
    @mitglieder.each(&block)
  end
end

team = Team.new
team << Person.new(name: "Anna", alter: 28)
team << Person.new(name: "Leo",  alter: 17)
team << Person.new(name: "Max",  alter: 34)

puts "Alle:"
team.each { |p| puts "  #{p}" }

puts "\nVolljaehrige:"
team
  .select(&:volljaehrig?)
  .sort_by(&:alter)
  .each { |p| p.gruessen }

Zusammenfassung

  • Klassen mit class ... end, Konstruktor initialize, Instanzvariablen mit @
  • attr_accessor generiert Getter/Setter automatisch
  • Vererbung mit <, super ruft Basis-Methode
  • Module als Mixins fuer Code-Sharing ohne Vererbung
  • Comparable + <=> macht Objekte sortierbar
  • Enumerable + each gibt dir 100+ Collection-Methoden
  • Blocks, Procs und Lambdas - drei Arten von โ€œcallableโ€

Damit hast du die wichtigen Ruby-Konzepte im Griff. Im weiteren Kurs vertiefen wir Metaprogramming, Exception-Handling, Rake und den Einstieg in Rails.

Zurรผck zum Ruby Kurs