2012-03-22 6 views
21

Como no hay ningún tipo de rubí, ¿cómo se aseguran los programadores de Ruby de que una función reciba los argumentos correctos? En este momento, estoy repitiendo las declaraciones if object.kind_of/instance_of para verificar y aumentar los errores de tiempo de ejecución en todas partes, lo cual es feo. Debe haber una mejor forma de hacer esto.¿Cómo hacen los programadores de Ruby la verificación de tipos?

+0

¿Por qué necesita asegurarse de que la entrada sea de un tipo específico? Las funciones hacen lo que se les dice y en realidad no se las debe forzar a verificar. – Blender

+3

buscar "pato-tipado" –

+0

@Blender Eg. setNextNode (link). En esta función, si no tengo un control de tipo, se puede pasar cualquier cosa ... Estoy haciendo una tarea ahora y me han pedido que revise el tipo y me asegure de que esté en tiempo de ejecución. – kun

Respuesta

18

Ruby es, por supuesto, tipeado dinámicamente.

Por lo tanto, la documentación del método determina el tipo de contrato; la información de tipo se mueve desde el sistema de tipo formal a la documentación del método [tipo de letra informal]. Mezclo generalidades como "actúa como una matriz" y detalles como "es una cadena". La persona que llama solo debe esperar trabajar con los tipos indicados.

Si la persona que llama viola este contrato, puede pasar cualquier cosa. El método no necesita preocuparse: se usó incorrectamente.

A la luz de lo anterior, evitar la comprobación de un tipo específico y evitar tratando de crear sobrecargas con tal comportamiento.

Pruebas unitarias pueden ayudar asegurarse de que el contrato funcione para los datos esperados.

+7

@cjk Si bien este es un buen paradigma a seguir, todavía no responde la pregunta. Sugiero que aceptes la respuesta de Sawa en su lugar. – anthropomorphic

20

Mi manera personal, que no estoy seguro si es una forma recomendada en general, es verificar y hacer otras validaciones una vez que se produce un error. Puse la rutina de verificación de tipo en un bloque de rescate. De esta forma, puedo evitar la pérdida de rendimiento cuando se dan los argumentos correctos, pero todavía devuelvo el mensaje de error correcto cuando ocurre un error.

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 

Supongamos que en el main_routine, se utiliza el método each en arg1arg1 suponiendo que es una matriz. Si resulta que se trata de otra cosa, a la que no se ha definido each, el mensaje de error aparecerá como method each not defined on ..., que, desde la perspectiva del usuario del método foo, podría no ser útil. En ese caso, el mensaje de error original será reemplazado por el mensaje Expecting an array: ..., que es mucho más útil.

+4

El problema con esto, sin embargo, es que el error * real * podría ser enmascarado debido a uno de los otros problemas que se recogen. Por lo tanto, aunque podría * también * ser un problema, podría no estar relacionado con lo que realmente se planteó. Si se va a realizar una comprobación de tipos explícita, hágalo * primero *, no en recuperación. –

+2

¿Por qué usas '# {arg1}' en lugar de '# {arg1.inspect}'? O más específicamente, '" Esperando una cadena: # {arg2} "' en lugar de '" Esperando una cadena: # {arg2.inspect} "'? –

+1

@pst Eso se puede evitar colocando otro 'raise' al final mientras editaba. Si se genera un error interno a 'foo' pero se pasa toda la validación, entonces el caso es que la verificación de validación no fue suficiente o que hay un error interno en' foo'. – sawa

8

Si un método tiene un motivo para existir, se lo llamará.

Si se escriben pruebas razonables, se ejecutará todo.

Y si se llama a todos los métodos, se verificarán todos los métodos.

No pierda el tiempo realizando comprobaciones de tipo que pueden limitar innecesariamente las llamadas y solo duplicará la comprobación de tiempo de ejecución de todos modos. Pasar ese tiempo escribiendo pruebas en su lugar.

1

se recomienda utilizar el aumento en el principio del método para agregar la comprobación manual tipo, simple y eficaz:

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

mejor manera cuando usted no tiene mucho parámetros, también una recomendación es usar palabras clave argumentos disponibles en ruby ​​2+ si tiene múltiples parámetros y observa sus detalles de implementación actuales/futuros, están mejorando la situación, dando al programador una manera de ver si el valor es nulo.

más: se puede utilizar una excepción personalizada

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