2008-10-16 6 views
5

Hoffentlich habe ich nicht die Bedeutung von "Ente tippen" missverstanden, aber von dem, was ich gelesen habe, bedeutet es, dass ich Code basierend darauf, wie ein Objekt auf Methoden und nicht auf welchen Typ/Klasse es ist, schreiben sollte.Kann ich diese Methode mit der Eingabe von Enten verbessern?

Hier ist der Code:

def convert_hash(hash) 
    if hash.keys.all? { |k| k.is_a?(Integer) } 
    return hash 
    elsif hash.keys.all? { |k| k.is_a?(Property) } 
    new_hash = {} 
    hash.each_pair {|k,v| new_hash[k.id] = v} 
    return new_hash 
    else 
    raise "Custom attribute keys should be ID's or Property objects" 
    end 
end 

Was ich will, ist sicher zu stellen, dass ich mit einem Hash am Ende, wo die Tasten sind eine ganze Zahl die ID eines Active Objekt darstellt. Es macht mir nicht besonders viel Spaß, die Hash-Schlüssel zweimal mit all? durchlaufen zu müssen, um festzustellen, ob ich die IDs rausholen muss.

Natürlich, ich werde andere Vorschläge akzeptieren auch diesen Code zu verbessern :)

+0

noch nie von "Duck Typing" schon einmal gehört. Wo bist du auf das gestoßen? –

+0

@Brian, http://en.wikipedia.org/wiki/Duck_typing –

Antwort

11

Wie Sie diese Methode schreiben sollte davon abhängen, ob Sie erwarten eine Ausnahme im Verlauf der normalen Programmausführung geworfen werden. Wenn Sie eine lesbare Ausnahmebedingungsnachricht erhalten möchten, weil ein Endbenutzer sie möglicherweise sehen kann, ist es sinnvoll, sie manuell auszulösen. Ansonsten würde ich nur so etwas wie diese:

def convert(hash) 
    new_hash = {} 
    hash.each_pair { |k,v| new_hash[ k.is_a?(Integer) ? k : k.id ] = v } 
    return new_hash 
end 

Dies erreicht genau die gleiche Sache, und Sie werden immer noch eine Ausnahme, wenn ein Array-Schlüssel nicht ein ID-Feld hat. Noch besser, dies verwendet ein wenig mehr Tippeingabe, da jetzt alles, was ein ID-Feld hat, akzeptabel ist, was besser ist, als explizit nach etwas zu suchen, das eine Eigenschaft ist. Dies macht Ihren Code flexibler, insbesondere beim Komponententest.

Wir haben immer noch eine explizite Prüfung auf Integer-Objekte, aber diese Art von gelegentlichem Spezialfall ist normalerweise akzeptabel, besonders wenn man nach eingebauten Datentypen sucht.

+1

Große, beschreibende Antwort mit etwas sehr Ruby Code, Eli. Vielen Dank für die Antwort. –

+1

Das Problem dabei ist, dass jedes Objekt in Ruby eine #id-Methode hat. Es ist für Object definiert und gibt eine eindeutige Referenz im Ruby-Interpreter für dieses Objekt. Es ist veraltet, obwohl Sie eine Warnung erhalten, erhalten Sie keine Ausnahme. – madlep

3

Ente tippen ist wirklich nur eine nuancierte Version des Polymorphismus. In einer statisch typisierten Sprache wie Java müssten Sie eine explizite Schnittstelle erstellen, die dem Compiler alle Methoden mitteilt, die eine bestimmte Variable akzeptieren kann. Mit einer dynamischen Sprache wie Ruby existieren die Interfaces immer noch in einem abstrakten Sinn, sie sind nur implizit.

Das Problem ist die Tatsache, dass Sie zwei verschiedene Datenstrukturen in einer Methode akzeptieren. Die Art und Weise, wie Ducky tippt, setzt voraus, dass alle Objekte, die an deine Methode übergeben werden, demselben Vertrag folgen (dh es ist immer ein Hash von Integers to [Foo] -Objekten) Die korrekte Struktur sollte die Aufgabe des Client-Codes sein. Dies kann sehr einfach mit einer einfachen Wrapper-Klasse oder einer Konvertierungsfunktion erfolgen, die nur aus dem Rumpf Ihrer elseif-Klausel besteht.

Fazit: Es liegt an dem Typen, der die Methode aufruft, um sicherzustellen, dass seine Parameter alle so quatschen, wie Ihre Methode von ihnen erwartet. Wenn sie es nicht tun, ist er derjenige, der herausfinden muss, wie man seinen Truthahn quakt wie eine Ente macht, nicht Sie.

0

Ich möchte sicherstellen, dass ich am Ende mit einem Hash, wo die Schlüssel eine ganze Zahl sind, die die ID eines ActiveRecord-Objekts darstellt.

Sie sollten wahrscheinlich überprüfen, wenn Sie in den Hash erstellen/einfügen. Man könnte so etwas wie dies versucht:

 
h = {} 
def h.put obj 
    self[obj.id]=obj 
end 

oder vielleicht

 
h = {} 
def h.[]= key, value 
    raise "hell" unless key == value.id 
    super 
end