Klassen, Blocks & Enumerable
Objektorientierung in Ruby: Klassen, attr_accessor, Vererbung, Module als Mixins und das maechtige Enumerable-Modul.
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 ... endmit PascalCase-Nameninitializeist der Konstruktor, aufgerufen vianew@variableist 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 Getterattr_writer :name- nur Setterattr_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, Konstruktorinitialize, Instanzvariablen mit@ attr_accessorgeneriert Getter/Setter automatisch- Vererbung mit
<,superruft Basis-Methode - Module als Mixins fuer Code-Sharing ohne Vererbung
Comparable+<=>macht Objekte sortierbarEnumerable+eachgibt 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.