ruby-mine

exploring the mine

Repeat Yourself! (hin und wieder)

von skade am 27.05.2009 (16 Uhr)

Aus gegebenem Anlass möchte ich mal auf einen kritischen Blick auf eins der Prinzipien werfen, das bei Ruby so gerne zitiert, aber gerne auch missverstanden wird: DRY.

Trockener Boden

Trocken, aber nicht zu trocken

DRY ist das wohl am häufigsten genannte Prinzip, wenn es um Ruby-Programmierung geht. Allerdings würde ich auch behaupten, dass es auch häufig missverstanden wird. Es geht bei DRY nämlich nicht darum, möglichst wenig zu schreiben, sondern darum, Redundanz zu vermeiden. Konkret soll jedes Stück Wissen des Systems nur an einer Stelle im Code erwähnt werden. Deswegen nannt man die Idee hinter DRY auch Single Point of Truth.

Ein Beispiel:

1
2
3
4
5
6
7
class Address < ActiveRecord::Base
  validates_presence_of :name
  validates_presence_of :street
  validates_presence_of :number
  validates_presence_of :city
  validates_presence_of :postalcode
end

Wiederholt sich häufig, ist aber trotzdem DRY. Sicher, folgende Version ist kürzer und natürlich genauso korrekt:

1
2
3
class Address < ActiveRecord::Base
  validates_presence_of :name, :street, :number, :city, :postalcode
end

Aber sie hat Nachteile bei zweifelhaftem Gewinn: ich kann zum Beispiel keine Validierungen mal schnell auskommentieren, sondern müsste den Namen gleich löschen. Genauso muss ich die Statements gleich wieder aufsplitten, wenn ich unterschiedliche Meldungen für unterschiedliche Felder haben will. Lösung 2 ist shorthand und damit auch gerne mal erlaubt, aber nicht stärker dem DRY-Prinzip folgend als 1.

Um also auf die häufig gestellte Frage zurück zukommen: wie löse ich das DRY? Garnicht! Denn validates_presence_of selbst ist, was diesen Code DRY macht. Um mich zu wiederholen: Jedes Stück Wissen soll nur an einer Stelle vorhanden sein. Unsere Stücke Wissen sind in diesem Fall:

  1. Name muss vorhanden sein
  2. Street muss vorhanden sein
  3. Number muss vorhanden sein
  4. City muss vorhanden sein
  5. Postalcode muss vorhanden sein

Und last but not least:

  1. Wie validiere ich das Vorhandensein eines Feldes?

6. ist einfach beantwortet:

1
2
name = self.name
name.nil? || name.empty?

Was unseren Code jetzt DRY macht, ist der Umstand, dass wir diesen Codeschnipsel nicht immer und überall wieder verwenden, sondern einmal in eine Methode fassen, die diesen Validierungsschritt hübsch gekapselt in eine Validierungsliste legt (validates_presence_of). Damit ist das Wissen, wie man so eine Validierung durchführt an genau einer Stelle im Code vorhanden.

Die Information, dass die 5 Attribute dieser speziellen Klasse validiert werden müssen, befindet sich bereits in beiden Statements oben - und ist DRY.

Ich gehe manchmal sogar soweit und wende das auf requires und verlange zum Beispiel beim laden einer Klasse alle Abhängigkeiten explizit:

1
2
3
4
5
6
7
8
require 'my_module'
require 'my_referenced_class'

class MyClass
  include MyModule

  #hier wird noch irgendwo MyReferencedClass verwendet
end

Das hat den Vorteil, dass ich jede Klasse einzeln laden kann, ohne gleich die komplette Bibliothek zu laden. Das mag auf den ersten Blick etwas nach den hässlichen Java-Imports aussehen und dient mir auch zu einen ähnlichen Zweck: es ist sofort ersichtlich, von welchen Systemelementen diese Klasse essentiell abhängt. Insofern wiederhole ich mich nicht einfach nur, sondern füge dem System eine (schwache) Information hinzu.

Die Essenz ist: Kurz ist nicht zwingend trockener als verbos, wichtig ist der Informationsgehalt.


Kommentar schreiben

Name (notwendig)

Mail (wird nicht veröffentlicht)

Webseite


Kommentare

  1. janfri schrieb am 27.05.2009 (16 Uhr)

    Sehr schön auf den Punkt gebracht.

  2. Stefan Klöckner schrieb am 12.06.2009 (23 Uhr)

    Man kann das ganze natürlich auch wie folgt schreiben: validates_presence_of :name :street, :number, :city, :postalcode Da bleibt dann immer noch die Möglichkeit, einzelne Zeilen auszukommentieren.

  3. Stefan Klöckner schrieb am 12.06.2009 (23 Uhr)

    Leider hat die Blog-Software meine Umbrüche in meinem vorherigen Kommentar entfernt. Eingegeben hatte ich im Code-Schnipsel Umbrüche nach jedem Komma.

  4. Skade schrieb am 15.06.2009 (21 Uhr)

    Selbstverständlich ;). Aber ich hatte das auch mehr als plakative Illustrierung gedacht. Eventuell wäre ein etwas durchdachteres Beispiel angebrachter gewesen.

  5. janfri schrieb am 07.08.2009 (11 Uhr)

    Noch ein aktueller Beitrag dazu von Jim Weirich: "Nitpicking perhaps, but I feel DRY is often used for justifying stuff that it was never meant to cover. (Consider this a DRY best practice :)."