Magic Numbers ersetzen

Magische Zahlen (Magic Numbers) sind hart kodierte, statische Werte. Das heißt, es sind nicht nur Zahlen sondern können auch zum Beispiel Strings oder andere Objekte sein. Oft werden sie mit einem zusätzlichen Kommentar versehen, der den Wert dann näher beschreibt. Trotzdem sind sie ein code smell.

Magic Numbers sind schlecht

  1. Magic Numbers sind riskant zu ändern (der selbe Wert könnte in einem anderen Kontext verwendet worden sein)
  2. Magic Numbers verringern die Lesbarkeit (selbst, wenn sie kommentiert wurden)
  3. Magic Numbers verleiten zu exzessiven Code (das gilt insbesondere für Strings)

Grundsätzlich gilt für Magic Numbers die selbe Regel, wie für Codewiederholungen: Duplikate müssen extrahiert und referenziert werden.

Named Constants sind gut

Das probate Mittel gegen Magic Numbers sind Benannte Konstanten (Named Constants). Sie machen erklärende Kommentare überflüssig und Änderungen sind nur an einer einzigen Stelle notwendig.
Hier wird die Begrifflichkeit Named Constant verwendet, obwohl in der Literatur oft von Symbolic Constant die Rede ist (z.B. Refactoring, Improving The Design Of Existing Code by Martin Fowler]1). Der Grund dafür ist die mögliche Verwechslung mit dem Konzept des Symbol in Ruby.
Ruby beinhaltet schon einige sprechende Konstanten. Zum Beispiel in der Klasse Math:

Math::PI # => 3.141592653589793

Ein einfaches Beispiel

Die Method color gibt eintweder die gesetzte Farbe oder die Standardfarbe zurück:

module Inkable
  attr_writer :color

  def color
    color || '#178C18'
  end
end

Die Einführung einer Konstante ist allein schon aus Lesbarkeitsgründen ein Gewinn:

module Inkable
  DEFAULT_COLOR_GREEN = '#178C17'
  attr_writer :color

  def color
    color || DEFAULT_COLOR_GREEN
  end
end

Ruby hilft

Außerdem ist Ruby so konzipiert, daß die Verwendung von Zahlen oft vermieden werden kann.
In manchen Sprachen sind solche Konstrukte sicherlich notwendig:

numbers = [1, 2, 3]
language = "Ruby"

# bad
numbers[0] # => 1
numbers.size > 0 # => true
language.length == 0 # => false

in Ruby allerdings nicht:

# good
numbers.first # => 1
numbers.any? # => true
language.zero? # => false

Ein dröhnendes Salut geht an Dave Aronson und Todd Knarr für das Korrektur lesen und diskutieren!