2012-03-22 12 views
21

Poiché non vi è alcun tipo in ruby, in che modo i programmatori di Ruby si assicurano che una funzione riceva argomenti corretti? In questo momento, sto ripetendo le dichiarazioni if object.kind_of/instance_of per verificare e aumentare gli errori di runtime ovunque, il che è brutto. Ci deve essere un modo migliore per farlo.Come fanno i programmatori Ruby a digitare il controllo?

+0

Perché è necessario assicurarsi che l'input sia di un tipo specifico? Le funzioni fanno quello che viene detto e non dovrebbero essere forzate a digitare check. – Blender

+3

cercare "duck-typing" –

+0

@Blender Eg. setNextNode (link) .In questa funzione se non ho un controllo di tipo tutto può essere passato in ... Sto facendo un incarico ora e mi è stato detto di fare il controllo del tipo e assicurarmi la sicurezza del runtime. – kun

risposta

18

Ruby è, naturalmente, digitato in modo dinamico.

Pertanto la documentazione del metodo determina il tipo di contratto; l'informazione sul tipo viene spostata dal sistema di tipo formale alla [specifica del tipo informale nella documentazione del metodo]. Io mescolo le generalità come "agisce come un array" e specifiche come "è una stringa". Il chiamante dovrebbe aspettarsi di lavorare solo con i tipi dichiarati.

Se il chiamante viola questo contratto, può succedere di tutto. Il metodo non deve preoccupare: è stato usato in modo errato.

Alla luce di quanto precede, evitare controllo per un tipo specifico e evitare cercando di creare sovraccarichi con tale comportamento.

I test unitari possono help assicurarsi che il contratto funzioni per i dati previsti.

+7

@cjk Sebbene questo sia un buon paradigma da seguire, non risponde ancora alla domanda. Suggerisco di accettare invece la risposta di Sawa. – anthropomorphic

20

Il mio modo personale, che non sono sicuro se è un modo consigliato in generale, è di controllare il tipo e fare altre convalide una volta che si è verificato un errore. Ho inserito la routine di controllo del tipo in un blocco di salvataggio. In questo modo, posso evitare la perdita di prestazioni quando vengono forniti gli argomenti corretti, ma comunque restituire il messaggio di errore corretto quando si verifica un errore.

def foo arg1, arg2, arg3 
    ... 
    main_routine 
    ... 
rescue 
    ## check for type and other validations 
    raise "Expecting an array: #{arg1.inspect}" unless arg1.kind_of?(Array) 
    raise "The first argument must be of length 2: #{arg1.inspect}" unless arg1.length == 2 
    raise "Expecting a string: #{arg2.inspect}" unless arg2.kind_of?(String) 
    raise "The second argument must not be empty" if arg2.empty? 
    ... 
    raise "This is `foo''s bug. Something unexpected happened: #{$!.message}" 
end 

Supponiamo nel main_routine, si utilizza il metodo each su arg1 supponendo che arg1 è un array. Se risulta che è qualcos'altro, a cui each non è definito, il messaggio di errore nudo sarà qualcosa come method each not defined on ..., che, dal punto di vista dell'utente del metodo foo, potrebbe non essere utile. In tal caso, il messaggio di errore originale verrà sostituito dal messaggio Expecting an array: ..., che è molto più utile.

+4

Il problema con questo però è che l'errore * real * potrebbe essere mascherato a causa di uno degli altri problemi rilevati. Quindi, mentre potrebbe * anche * essere un problema, potrebbe non essere correlato a ciò che è stato effettivamente sollevato. Se si eseguirà il controllo esplicito del tipo, farlo * prima *, non in fase di ripristino. –

+2

Perché usi '# {arg1}' invece di '# {arg1.inspect}'? O in modo più specifico, '" Aspettando una stringa: # {arg2} "' invece di '" Aspettando una stringa: # {arg2.inspect} "'? –

+1

@pst Ciò può essere evitato mettendo un altro 'raise' alla fine mentre ho modificato. Se viene sollevato un errore interno a 'pippo', ma tutti i passaggi di convalida, allora è il caso che il controllo di validazione non sia stato sufficiente o che ci sia un bug interno a' pippo'. – sawa

8

Se un metodo ha un motivo per esistere, verrà chiamato.

Se vengono scritti test ragionevoli, verrà chiamato tutto.

E se ogni metodo viene chiamato, allora ogni metodo verrà controllato.

Non sprecare tempo inserendo controlli di tipo che potrebbero limitare inutilmente i chiamanti e duplicheranno comunque il controllo di runtime. Spendi quel tempo per scrivere invece i test.

1

vi consiglio di usare rilancio all'inizio del metodo per aggiungere il tipo controllo manuale, semplice ed efficace:

def foo(bar) 
    raise TypeError, "You called foo without the bar:String needed" unless bar.is_a? String 
    bar.upcase 
end 

miglior modo quando non si dispone di molto i parametri, anche una raccomandazione è di usare parole chiave argomenti disponibili su Ruby 2+ se si hanno più parametri e si osservano i dettagli di implementazione attuali/futuri, stanno migliorando la situazione, dando al programmatore un modo per vedere se il valore è zero.

più: è possibile utilizzare un'eccezione personalizzata

class NotStringError < TypeError 
    def message 
    "be creative, use metaprogramming ;)" 
#... 
raise NotStringError 
Problemi correlati