2008-10-16 16 views
5

Espero no haber entendido mal el significado de "pato escribiendo", pero por lo que he leído, significa que debería escribir código en función de cómo responde un objeto a los métodos en lugar de qué tipo/clase es.¿Podría mejorar este método con el tipado de pato?

Aquí está el código:

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 

Lo que quiero es para asegurarse de que termino con un hash donde las claves son un número entero que representa el ID de un objeto ActiveRecord. En particular, no me gusta tener que repetir las teclas hash dos veces con all? para determinar si necesito extraer las identificaciones.

Por supuesto, voy a aceptar cualquier otra sugerencia para mejorar este código, así :)

+0

oído nunca hablar de "duck typing" antes. ¿Dónde te encontraste con eso? –

+0

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

Respuesta

11

Cómo se escribe este método debe depender de si espera que una excepción a ser lanzado durante el curso de la ejecución normal del programa. Si desea un mensaje de excepción legible porque un usuario final puede verlo, entonces arrojar uno manualmente tiene sentido. De lo contrario, yo acababa de hacer algo como esto:

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

Esto logrará exactamente lo mismo, y todavía tendrá una excepción si una clave de la matriz no tiene un campo de ID. Mejor aún, esto utiliza un poco más de mecanografía de pato porque ahora cualquier cosa que tenga un campo de identificación será aceptable, lo cual es mejor que verificar explícitamente que algo sea una Propiedad. Esto hace que su código sea más flexible, especialmente cuando se realizan pruebas unitarias.

Todavía tenemos una comprobación explícita para objetos enteros, pero este tipo de casos especiales ocasionales suele ser aceptable, especialmente cuando se comprueban los tipos de datos incorporados.

+1

Excelente respuesta descriptiva con algún código muy Ruby, Eli. Muchas gracias por la respuesta. –

+1

El problema con eso es que CADA objeto en Ruby tiene un método #id. Se define en Object y proporciona una referencia única en el intérprete de Ruby para ese objeto. Sin embargo, está en desuso, por lo tanto, aunque reciba una advertencia, no obtendrá una excepción. – madlep

3

Duck typing es realmente una versión matizada del polimorfismo. En un lenguaje estáticamente tipado como Java, tendría que crear una interfaz explícita que le indicara al compilador todos los métodos que puede aceptar una variable en particular. Con un lenguaje dinámico como Ruby, las interfaces todavía existen en un sentido abstracto, son solo implícitas.

El problema es que acepta dos estructuras de datos diferentes en un método. La forma de hacer tipa de pato es exigir que todos los objetos que pasan a su método obedezcan el mismo contrato (es decir, siempre es un hash de enteros a los objetos [Foo]). El proceso de convertir un hash con las claves de propiedad en el la estructura correcta debería ser el trabajo del código del cliente. Eso se puede hacer muy fácilmente con una clase contenedora simple o una función de conversión que consiste en solo el cuerpo de su cláusula elseif.

En resumidas cuentas depende del tipo que llama al método para asegurarse de que todos sus parámetros son del mismo modo que su método espera que cuaqueen. Si no lo hacen, él es quien necesita resolver cómo hacer que su pavo grazné como un pato, no tú.

0

Lo que quiero es asegurarme de que termine con un hash donde las claves son un número entero que representa el ID de un objeto ActiveRecord.

Probablemente deberías comprobarlo cuando estés creando/insertando en el hash. Usted podría intentar algo como esto:

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

o tal vez

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