Menu
Coddy logo textTech

Zustand schützen

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 20 von 57.

Ein Objekt sollte Anfragen ablehnen, die seinen Zustand beeinträchtigen würden. raise beendet die Methode, bevor sich irgendetwas ändert. Der Name deiner eigenen Fehlerklasse bezeichnet das Problem genau; < StandardError macht die Klasse zu einer Fehlerart (Vererbung, Kapitel 5):

class InsufficientFunds < StandardError
end

class Account
  attr_reader :balance

  def initialize(balance)
    @balance = balance
  end

  def withdraw(amount)
    raise InsufficientFunds, "only #{balance} available" if amount > balance

    @balance -= amount
  end
end

account = Account.new(50)
begin
  account.withdraw(80)
rescue InsufficientFunds => e
  puts "#{e.class}: #{e.message}"
end
puts account.balance

Ausgabe:

InsufficientFunds: only 50 available
50

Separate Fehlerklassen ermöglichen es dem Aufrufer, auf jedes Problem unterschiedlich zu reagieren, mit einem rescue pro Klasse. Deklariere Fehlerklassen vor den Klassen, die sie auslösen:

class InvalidAmount < StandardError
end

class LimitReached < StandardError
end

class Card
  def charge(amount)
    raise InvalidAmount, "amount must be positive" unless amount.positive?
    raise LimitReached, "limit is 100" if amount > 100

    "charged #{amount}"
  end
end

[40, -5, 500].each do |amount|
  puts Card.new.charge(amount)
rescue InvalidAmount => e
  puts "fix the input: #{e.message}"
rescue LimitReached => e
  puts "try later: #{e.message}"
end

Ausgabe:

charged 40
fix the input: amount must be positive
try later: limit is 100

freeze macht ein Objekt schreibgeschützt: Danach löst jede Änderung an seinen Instanzvariablen FrozenError aus, während das Lesen weiterhin funktioniert. frozen? gibt an, ob ein Objekt eingefroren ist:

class Point
  attr_accessor :x

  def initialize(x)
    @x = x
  end
end

pt = Point.new(1).freeze
puts pt.frozen?
begin
  pt.x = 2
rescue FrozenError => e
  puts e.class
end
puts pt.x

Ausgabe:

true
FrozenError
1

Ein Leser, der ein internes Array zurückgibt, händigt das tatsächliche Array aus, sodass Code außerhalb des Objekts es ändern könnte. Gib eine eingefrorene Kopie mit @items.dup.freeze oder @items.dup zurück, damit das Objekt die Kontrolle über seine eigenen Daten behält:

class Playlist
  def initialize
    @songs = []
  end

  def add(song)
    @songs << song
  end

  def songs
    @songs.dup.freeze
  end
end

list = Playlist.new
list.add("Intro")
begin
  list.songs << "Hack"
rescue FrozenError
  puts "songs are read-only"
end
p list.songs

Ausgabe:

songs are read-only
["Intro"]
challenge icon

Aufgabe

Einfach

Deklariere die Fehlerklassen InvalidAmount und InsufficientFunds, die beide Unterklassen von StandardError sind, und vervollständige Account. deposit(amount) und withdraw(amount) lösen für Beträge unter 1 InvalidAmount mit amount must be positive aus. withdraw löst InsufficientFunds mit balance is only 20 aus, wenn der Betrag größer als das Guthaben ist. Wenn ein Fehler ausgelöst wird, ändert sich nichts.

Der bereitgestellte Code liest Befehle dep n oder wd n ein und gibt nach jedem Befehl balance 70 aus, bei InvalidAmount invalid: amount must be positive und bei InsufficientFunds refused: balance is only 20.

Dein Code gehört in invalid_amount.rb, insufficient_funds.rb und account.rb. main.rb enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

require_relative 'invalid_amount'
require_relative 'insufficient_funds'
require_relative 'account'

# Bereitgestellter Ein-/Ausgabe-Code: belasse ihn unverändert
input = []
while (line = gets)
  input << line.chomp
end
account = Account.new
input.each do |line|
  action, amount = line.split
  begin
    action == "dep" ? account.deposit(amount.to_i) : account.withdraw(amount.to_i)
    puts "balance #{account.balance}"
  rescue InvalidAmount => e
    puts "invalid: #{e.message}"
  rescue InsufficientFunds => e
    puts "refused: #{e.message}"
  end
end
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler