2010-08-12 18 views
9

Ok, no estoy tratando de iniciar una guerra de llama aquí, y sé que la discusión entre los lenguajes estáticos y dinámicos se ha solucionado muchas veces, incluso aquí. Pero tengo una pregunta muy práctica que espero que alguien aquí pueda arrojar algo de luz. Perdón por la longitud, pero esta no es una pregunta simple, probablemente no sea una respuesta simple.Refactorización con lenguaje de tipado dinámico

Ruby, PHP y Javascript son lenguajes bastante populares en estos días, y tienen mucha gente que los defiende y argumentan que ser tipeado dinámicamente no detiene al desarrollador. Soy nuevo en estos idiomas y me gustaría empezar a usarlos para proyectos más grandes, pero aquí hay un escenario de refactorización básico que aparece todo el tiempo en el trabajo (trabajo == C#), y me pregunto cuál sería el enfoque en Ruby: elegí a Ruby porque es OO.

Ok, estoy usando Ruby y creo un objeto Cliente. Tiene métodos para cargar/guardar/eliminar de la base de datos. Es bueno y la gente lo usa. Agrego más métodos para otras cosas y la gente lo usa más. Agrego un método para calcular el historial de pedidos basado en algunos parámetros. Por ahora, esta clase se está utilizando en todo el sistema. Entonces, un día decido cambiar los parámetros en el método GetOrderHistory. Así que:

  • añadir los nuevos parámetros al método
  • reescribir el código del método a utilizar los nuevos parámetros
  • cambio el código de cliente que tenía en mente para pasar los nuevos parámetros y utilizar esta modificados método

¿Pero ahora qué? Tengo docenas/cientos/quién sabe cuántos otros lugares en el sistema necesitan ser cambiados. En un lenguaje OO dinámico como Ruby o Javascript, ¿cómo podría hacerlo?

De la parte superior de mi cabeza, sin saber muy bien Rubí, puedo pensar en dos respuestas tontas:

  1. 100% de cobertura de código. Pruebo toda la aplicación y cada vez que se rompe, veo si es ese el método y lo soluciono
  2. Buscar y reemplazar. Uso la búsqueda de texto para buscar ese método. Pero podría tener otros objetos con los mismos nombres de método.

¿Hay una buena respuesta para esto? Parece que el IDE lo pasaría mal. Si tuviera un código como

c = Customer.new 

sería capaz de entenderlo, pero lo que si es

c= SomeFunctionThatProbablyReturnsACustomerButMightReturnOtherThings() 

Entonces, ¿qué enfoque sería expertos que Rubí tomar en este caso?

+0

Estoy bastante seguro de que la refacturación no debe romper los contratos existentes ... – CurtainDog

+0

@CurtainDog ¿qué ocurre con el cambio de nombre de una función privada dentro de una biblioteca? – Toskan

+0

otro buen ejemplo es ruby ​​y python. ¿Puedo recordarte que ni Ruby ni Python se apegan a lo que acabas de decir cuando lanzas nuevas versiones? – Toskan

Respuesta

3

Uno de los argumentos fuertes que escuchará es que usted debe escribir pruebas de antemano. Esto, en teoría, le mostraría exactamente dónde necesita cambiar la aplicación en caso de que algo más cambie.

Pero esto es solo la parte superior del iceberg. Ruby está diseñado con ciertas pautas en mente, como funciones cortas y expresivas, división de responsabilidades en módulos, no repetición de código (DRY), el principio de menor sorpresa, etc .; además de un conjunto de prácticas recomendadas, como probar primero, pasar parámetros como opciones de hash, usar la metaprogramación sabiamente, etc. Estoy seguro de que otros lenguajes dinámicos también lo hacen.

Si c no es un cliente, entonces al menos esperaría actuar como uno. Los IDE podrían buscar el tipado de pato, que es más flexible que buscar una instancia de una clase en particular.

Algunos IDEs (al menos Rubymine) también ven las convenciones. Por ejemplo, en las aplicaciones de Rails, Rubymine va al archivo de esquema y agrega las propiedades del modelo en la base de datos como métodos. También reconoce las asociaciones (has_many, belongs_to, etc.) y agrega dinámicamente los métodos correspondientes, que los Rails generan debajo del capó.

Ahora, esto reduce la necesidad de refactorización, al menos manteniéndola al mínimo. Pero ciertamente no lo resuelve. Y no creo que pueda ser resuelto.

+0

buenas ideas, gracias. La idea del tipado de patos es particularmente interesante, voy a investigar más sobre eso. Además, en otro post aquí, alguien mencionó Flow Analysis, que creo que es una cosa de precompilación más inteligente que puede determinar qué tipo de tiempo de ejecución será. Suena complicado sin embargo ... – LoveMeSomeCode

+1

también, no ser discutidor, pero la idea de las pruebas me molesta. No las pruebas en sí, porque creo que todo buen código debería tener pruebas unitarias exhaustivas, pero la idea de verbosidad. Mucha gente argumentará que los lenguajes dinámicos son menos detallados porque no necesitas descriptores de tipo, pero cuando las personas hablan de seguridad de tipo mencionarán la necesidad de pruebas adicionales, lo que cancela el código conciso que escribieron . solo una observación general ... – LoveMeSomeCode

+0

bueno, lo siento ... pero ¿no podría, y lo digo en serio, argumentar que estos conceptos deberían aplicarse en todos los lenguajes de programación? No poder refactorizar realmente es un gran problema. – Toskan

1

Probablemente esta no sea la mejor respuesta a su pregunta, pero me suele gustar el diseño de métodos para aceptar un hash para cubrir futuros cambios.

Ejemplo:

def my_method(options = {}) 
    if options[:name] 
    ... 
    end 
end 

creo que mucha de la gente más avanzados de Ruby se vería a la aplicación de algún tipo de patrón de Metaprogramación.

Otras opciones pueden incluir anular el método en una subclase para realizar la funcionalidad que desee.

O, ¿qué ...

def get_order_history(required_param, options = []) 

    @required_param = required_param 

    if options[:do_something_else] 
    result = other_method(options[:do_something_else]) 
    else 
    result = ... 
    end 

    result  

end 

def other_method(something_else) 
    ... 
end 
+0

Subclases es un buen punto. Esa es una de las opciones que consideré después de publicar, pero luego pensé, '¿Qué pasa si el método realmente está cambiando, como debido a un cambio en el esquema db, y no necesita el código anterior, y todo el mundo necesita usar el nuevo método? ' – LoveMeSomeCode

Cuestiones relacionadas