2008-10-16 14 views
5

Speriamo di non aver frainteso il significato di "digitazione anatra", ma da quello che ho letto, significa che dovrei scrivere codice basato su come un oggetto risponde ai metodi piuttosto che tipo/classe.Posso migliorare questo metodo con la digitazione anatra?

Ecco il codice:

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 

Quello che voglio è fare in modo che io alla fine con un hash in cui le chiavi sono un numero intero che rappresenta l'ID di un oggetto ActiveRecord. Non mi piace particolarmente dover ripetere le chiavi di hash due volte con all? per determinare se è necessario prelevare l'ID.

Naturalmente, io accetterò altri suggerimenti per migliorare questo codice così :)

+0

mai nemmeno sentito parlare di "duck typing" prima. Dove l'hai incontrato? –

+0

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

risposta

11

Come si scrive questo metodo dovrebbe dipendere dal fatto che ci si aspetta un'eccezione a essere gettato nel corso del normale esecuzione del programma. Se si desidera un messaggio di eccezione leggibile perché un utente finale potrebbe vederlo, quindi generarne uno manualmente ha senso. In caso contrario, mi piacerebbe proprio fare qualcosa di simile:

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

Ciò compiere esattamente la stessa cosa, e ci si può comunque un'eccezione se una chiave di serie non ha un campo id. Ancora meglio, questo usa un po 'più di digitazione anatra perché ora tutto ciò che ha un campo id sarà accettabile, il che è meglio che controllare esplicitamente che qualcosa sia una proprietà. Ciò rende il tuo codice più flessibile, specialmente durante i test unitari.

Abbiamo ancora un controllo esplicito per gli oggetti interi, ma questo tipo di caso speciale occasionale è solitamente accettabile, soprattutto quando si controllano i tipi di dati incorporati.

+1

Ottima risposta descrittiva con un codice molto Rubyish, Eli. Grazie molte per la risposta. –

+1

Il problema è che OGNI oggetto in Ruby ha un metodo #id. È definito su Object e fornisce un riferimento univoco nell'interprete Ruby a quell'oggetto. È deprecato, quindi, mentre riceverai un avviso, non otterrai un'eccezione. – madlep

3

La duck typing è in realtà solo una versione sfumata del polimorfismo. In un linguaggio tipizzato in modo statico come Java, dovresti creare un'interfaccia esplicita che comunica al compilatore tutti i metodi che una determinata variabile può accettare. Con un linguaggio dinamico come Ruby le interfacce esistono ancora in senso astratto, sono solo implicite.

Il problema è che si accettano due diverse strutture di dati in un unico metodo. Il modo per rendere il lavoro di digitazione anatra è quello di richiedere che tutti gli oggetti che vengono passati al tuo metodo obbediscano allo stesso contratto (cioè è sempre un hash di Interi ad oggetti [Foo].) Il processo di conversione di un hash con le chiavi di Proprietà nel la struttura corretta dovrebbe essere il lavoro del codice client. Ciò può essere fatto molto facilmente con una semplice classe wrapper o con una funzione di conversione costituita solo dal corpo della clausola elseif.

In conclusione, è compito del ragazzo chiamare il metodo per assicurarsi che i suoi parametri siano tutti ciarlatani nel modo in cui il metodo si aspetta che facciano i ciarlatani. Se non lo fanno, è quello che ha bisogno di capire come rendere il suo tacchino ciarlatano come un'anatra, non tu.

0

Quello che voglio è assicurarsi di finire con un hash in cui le chiavi sono un numero intero che rappresenta l'ID di un oggetto ActiveRecord.

Probabilmente dovresti verificarlo quando stai creando/inserendo nell'hash. Si potrebbe provare qualcosa di simile:

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

o forse

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