EMZETT.
Login

Kurz: @@anzahl = 0 # Klassenvariable ZINS = 0.02 # Konstante

Teil des Kurses Ruby

Kapitel 6 von 12 im Kurs Ruby (Abschnitt „Objektorientierung“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Klassen

class Konto
  attr_reader :inhaber, :stand            # Getter
  attr_accessor :notiz                    # Getter + Setter
 
  @@anzahl = 0                            # Klassenvariable
  ZINS = 0.02                             # Konstante
 
  def initialize(inhaber, stand = 0)      # Konstruktor
    @inhaber = inhaber                    # Instanzvariablen beginnen mit @
    @stand = stand
    @@anzahl += 1
  end
 
  def einzahlen(betrag)
    raise ArgumentError, "Betrag muss positiv sein" unless betrag.positive?
    @stand += betrag
    self                                  # für Verkettung
  end
 
  def reich? = @stand > 1000
 
  def to_s = "#{@inhaber}: #{format('%.2f', @stand)}"
 
  def self.anzahl = @@anzahl             # Klassenmethode
 
  private
 
  def geheim = "intern"
end
 
k = Konto.new("Mia", 100)
k.einzahlen(50).einzahlen(25.5)
puts k
puts k.reich?
k.notiz = "VIP"
puts k.notiz
puts Konto.anzahl
puts Konto::ZINS
begin
  k.einzahlen(-5)
rescue ArgumentError => e
  puts "Fehler: #{e.message}"
end
begin
  k.geheim
rescue NoMethodError => e
  puts "NoMethodError: #{e.message[0, 40]}"
end
puts k.inspect.sub(/0x[0-9a-f]+/, "0x...")
puts k.instance_variables.inspect
puts k.respond_to?(:einzahlen), k.is_a?(Konto), Konto.instance_methods(false).sort.first(3).inspect

Ausgabe:

Mia: 175.50
false
VIP
1
0.02
Fehler: Betrag muss positiv sein
NoMethodError: private method `geheim' called for an in
#<Konto:0x... @inhaber="Mia", @stand=175.5, @notiz="VIP">
[:@inhaber, :@stand, :@notiz]
true
true
[:einzahlen, :inhaber, :notiz]

Sichtbarkeit: public (Standard), protected (nur Objekte derselben Klasse), private (nur ohne expliziten Empfänger aufrufbar).

Vererbung

class Tier
  attr_reader :name
  def initialize(name) = @name = name
  def laut = "..."
  def vorstellen = "#{name} sagt #{laut}"
end
 
class Hund < Tier
  def laut = "Wuff"
end
 
class Katze < Tier
  def initialize(name, leben = 9)
    super(name)                  # Elternkonstruktor
    @leben = leben
  end
  def laut = "Miau"
  def vorstellen = super + " (#{@leben} Leben)"
end
 
[Hund.new("Rex"), Katze.new("Mimi")].each { |t| puts t.vorstellen }
puts Hund.superclass, Hund.ancestors.first(3).inspect
puts Katze.new("x").is_a?(Tier), Hund.instance_method(:laut).owner

Ausgabe:

Rex sagt Wuff
Mimi sagt Miau (9 Leben)
Tier
[Hund, Tier, Object]
true
Hund

Ruby kennt nur Einfachvererbung, ergänzt durch Module (nächstes Kapitel).

Operatoren, Vergleich und Gleichheit

class Vektor
  include Comparable
  attr_reader :x, :y
  def initialize(x, y) = (@x, @y = x, y)
 
  def +(o) = Vektor.new(x + o.x, y + o.y)
  def *(k) = Vektor.new(x * k, y * k)
  def -@ = Vektor.new(-x, -y)
  def [](i) = i.zero? ? x : y
  def <=>(o) = laenge <=> o.laenge
  def laenge = Math.sqrt(x**2 + y**2)
  def ==(o) = o.is_a?(Vektor) && x == o.x && y == o.y
  def to_s = "(#{x}, #{y})"
  def inspect = "#<Vektor #{self}>"
  def coerce(n) = [Vektor.new(n, n), self]
end
 
a = Vektor.new(1, 2)
b = Vektor.new(3, 4)
puts a + b, a * 3, -a, a[1]
puts a < b, [b, a].min, a == Vektor.new(1, 2), a.equal?(a)
puts a.between?(a, b), a.clamp(a, a)
p a

Ausgabe:

(4, 6)
(3, 6)
(-1, -2)
2
true
(1, 2)
true
true
true
(1, 2)
#<Vektor (1, 2)>

Struct, Data und OpenStruct

Für einfache Datenträger gibt es Kurzformen:

Punkt = Struct.new(:x, :y) do
  def abstand = Math.sqrt(x**2 + y**2)
end
p1 = Punkt.new(3, 4)
p p1, p1.abstand, p1.to_a, p1 == Punkt.new(3, 4)
p1.x = 10
puts p1.x
 
Koord = Data.define(:lat, :lon) do        # unveränderlich (Ruby 3.2)
  def to_s = "#{lat}/#{lon}"
end
k = Koord.new(lat: 52.5, lon: 13.4)
puts k, k.with(lat: 0).inspect
begin
  k.instance_variable_set(:@lat, 1)
rescue FrozenError => e
  puts "FrozenError"
end
case k
in { lat:, lon: } then puts "Muster: #{lat}"
end

Ausgabe:

#<struct Punkt x=3, y=4>
5.0
[3, 4]
true
10
52.5/13.4
#<data Koord lat=0, lon=13.4>
FrozenError
Muster: 52.5

Merke

  • class Name ... end, initialize als Konstruktor, @variablen
  • attr_reader/writer/accessor erzeugen Zugriffsmethoden
  • private/protected, Klassenmethoden self.name
  • Vererbung class B < A, super; Comparable für Vergleiche
  • Struct und Data für kleine Datenobjekte

Übungsaufgabe

Baue eine Klasse Rechteck mit flaeche, umfang und Vergleich über die Fläche (Comparable).

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Methoden, Blöcke, Procs und Lambdas

Weiter: Module, Mixins und Enumerable

Alle Kapitel: Ruby im Überblick