¿Cuáles son algunas de las ventajas de escribir con pato vs.escritura estática?

StackOverflow https://stackoverflow.com/questions/47972

  •  09-06-2019
  •  | 
  •  

Pregunta

Estoy investigando y experimentando más con Groovy y estoy tratando de comprender los pros y los contras de implementar cosas en Groovy que no puedo o no hago en Java.La programación dinámica sigue siendo solo un concepto para mí, ya que he estado profundamente inmerso en lenguajes estáticos y fuertemente tipados.

Groovy me da la capacidad de tipo pato, pero realmente no puedo ver el valor.¿Cómo es que la escritura pato es más productiva que la escritura estática?¿Qué tipo de cosas puedo hacer en mi práctica de código para ayudarme a aprovechar sus beneficios?

Hago esta pregunta teniendo en cuenta a Groovy, pero entiendo que no es necesariamente una pregunta de Groovy, por lo que agradezco las respuestas de todos los campos del código.

¿Fue útil?

Solución

A continuación, cuál es mejor:¿EMACS o vi?Esta es una de las guerras religiosas en curso.

Piénsalo de esta manera:cualquier programa que sea correcto, será correcto si el idioma se escribe estáticamente.Lo que hace la escritura estática es permitir que el compilador tenga suficiente información para detectar discrepancias de tipos en el tiempo de compilación en lugar de en el tiempo de ejecución.Esto puede ser una molestia si estás haciendo algún tipo de programación incremental, aunque (mantengo) si estás pensando claramente en tu programa, no importa mucho;por otro lado, si estás construyendo un programa realmente grande, como un sistema operativo o un conmutador telefónico, con docenas, cientos o miles de personas trabajando en él, o con requisitos de confiabilidad realmente altos, entonces tener el compilador capaz de detecte una gran clase de problemas sin necesidad de un caso de prueba para ejercitar la ruta de código correcta.

No es que la escritura dinámica sea algo nuevo y diferente:C, por ejemplo, se escribe efectivamente dinámicamente, ya que siempre puedo emitir un foo* a un bar*.Simplemente significa que es mi responsabilidad como programador de C nunca usar código que sea apropiado en un bar* cuando la dirección realmente apunta a un foo*.Pero como resultado de los problemas con los programas grandes, C desarrolló herramientas como lint(1), fortaleció su sistema de tipos con typedef y finalmente desarrolló una variante fuertemente tipada en C++.(Y, por supuesto, C++ a su vez desarrolló formas de evitar la tipificación fuerte, con todas las variedades de moldes y genéricos/plantillas y con RTTI.

Sin embargo, otra cosa: no confunda "programación ágil" con "lenguajes dinámicos". Programación ágil trata sobre la forma en que las personas trabajan juntas en un proyecto:¿Puede el proyecto adaptarse a los requisitos cambiantes para satisfacer las necesidades de los clientes manteniendo al mismo tiempo un entorno humano para los programadores?Se puede hacer con lenguajes escritos dinámicamente, y a menudo lo es, porque pueden ser más productivos (por ejemplo, Ruby, Smalltalk), pero se puede hacer, y se ha hecho con éxito, en C e incluso en ensamblador.De hecho, Desarrollo de rallyes Incluso utiliza métodos ágiles (SCRUM en particular) para realizar marketing y documentación.

Otros consejos

Muchos de los comentarios sobre la escritura de patos realmente no fundamentan las afirmaciones.No "tener que preocuparse" por un tipo no es sostenible para el mantenimiento o para hacer extensible una aplicación.Realmente tuve una buena oportunidad de ver a Grails en acción durante mi último contrato y es realmente divertido verlo.Todo el mundo está contento con los beneficios que supone poder "crear una aplicación" y ponerse en marcha; lamentablemente, todo te afecta al final.

Groovy me parece lo mismo.Seguro que puedes escribir código muy conciso y definitivamente hay algo de azúcar en la forma en que trabajamos con propiedades, colecciones, etc.Pero el costo de no saber qué diablos se está transmitiendo de un lado a otro es cada vez peor.En algún momento te rascas la cabeza preguntándote por qué el proyecto se ha convertido en un 80% de prueba y un 20% de trabajo.La lección aquí es que "más pequeño" no significa que el código sea "más legible".Lo siento amigos, es una lógica simple: cuanto más tienen que saber intuitivamente, más complejo se vuelve el proceso de comprensión de ese código.Es por eso que las GUI han dejado de volverse demasiado icónicas a lo largo de los años; seguro que se ven bonitas, pero lo que está sucediendo no siempre es obvio.

Las personas en ese proyecto parecían tener problemas para "concretar" las lecciones aprendidas, pero cuando tienes métodos que devuelven un solo elemento de tipo T, una matriz de T, un ErrorResult o un valor nulo...se vuelve bastante evidente.

Sin embargo, una cosa que trabajar con Groovy ha hecho por mí: ¡increíbles horas facturables!

No hay nada de malo en escribir estáticamente si estás usando Haskell, que tiene un increíble sistema de tipos estáticos.Sin embargo, si está utilizando lenguajes como Java y C++ que tienen sistemas de tipos terriblemente incapacitantes, la escritura pato es definitivamente una mejora.

Imagínese intentar utilizar algo tan simple como "mapa" en Java (y no, no me refiero la estructura de datos).Incluso los genéricos cuentan con un apoyo bastante deficiente.

La escritura de pato paraliza la verificación estática de la mayoría de los IDE modernos, que pueden señalar errores a medida que escribe.Algunos consideran que esto es una ventaja.Quiero que el IDE/compilador me diga que he cometido un estúpido truco de programador lo antes posible.

Mi argumento favorito más reciente contra La escritura de pato proviene de un proyecto DTO de Grails:

class SimpleResults {
    def results
    def total
    def categories
}

dónde results resulta ser algo como Map<String, List<ComplexType>>, que sólo se puede descubrir siguiendo un rastro de llamadas a métodos en diferentes clases hasta encontrar dónde se creó.Para los curiosos terminales, total es la suma de los tamaños de List<ComplexType>arena categories es el tamaño del Map

Puede que haya sido claro para el desarrollador original, pero el encargado de mantenimiento deficiente (ME) perdió mucho cabello al rastrear este.

Es un poco difícil ver el valor de escribir con pato hasta que lo hayas usado por un tiempo.Una vez que te acostumbres, te darás cuenta de lo mucho que te quitas la mente de no tener que lidiar con interfaces o preocuparte por exactamente qué tipo es algo.

En mi humilde opinión, la ventaja de escribir pato se magnifica cuando se cumplen algunas convenciones, como nombrar variables y métodos de manera consistente.Tomando el ejemplo de Ken G., creo que se leería mejor:

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

Supongamos que define un contrato sobre alguna operación denominada 'calcularRating(A,B)' donde A y B se adhieren a otro contrato.En pseudocódigo se leería:

Long calculateRating(A someObj, B, otherObj) {

   //some fake algorithm here:
   if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating());
   else return otherObj.calcRating();

}

Si desea implementar esto en Java, tanto A como B deben implementar algún tipo de interfaz que diga algo como esto:

public interface MyService {
    public int doStuff(String input);
}

Además, si desea generalizar su contrato para calcular calificaciones (digamos que tiene otro algoritmo para calcular calificaciones), también debe crear una interfaz:

public long calculateRating(MyService A, MyServiceB);

Con la escritura pato, puedes deshacerte de tu interfaces y simplemente confíe en que en tiempo de ejecución, tanto A como B responderán correctamente a su doStuff() llamadas.No es necesaria una definición de contrato específica.Esto puede funcionar a tu favor pero también puede funcionar en tu contra.

La desventaja es que debe tener mucho cuidado para garantizar que su código no se rompa cuando otras personas lo cambien (es decir, la otra persona debe conocer el contrato implícito en el nombre del método y los argumentos).

Tenga en cuenta que esto se agrava especialmente en Java, donde la sintaxis no es tan concisa como podría ser (en comparación con escala Por ejemplo).Un contraejemplo de esto es el Estructura de elevación, donde dicen que el recuento de SLOC del marco es similar a Rieles, pero el código de prueba tiene menos líneas porque no necesitan implementar comprobaciones de tipo dentro de las pruebas.

Aquí hay un escenario en el que escribir con pato ahorra trabajo.

Aquí hay una clase muy trivial.

class BookFinder {
    def searchEngine

    def findBookByTitle(String title) {
         return searchEngine.find( [ "Title" : title ] ) 
    }
}

Ahora para la prueba unitaria:

void bookFinderTest() {
    // with Expando we can 'fake' any object at runtime.
    // alternatively you could write a MockSearchEngine class.
    def mockSearchEngine = new Expando()
    mockSearchEngine.find = {
        return new Book("Heart of Darkness","Joseph Conrad")
    }

    def bf = new BookFinder()
    bf.searchEngine = mockSearchEngine
    def book = bf.findBookByTitle("Heart of Darkness")
    assert(book.author == "Joseph Conrad"
}

Pudimos sustituir SearchEngine por un Expando, debido a la ausencia de verificación de tipos estáticos.Con la verificación de tipos estáticos habríamos tenido que asegurarnos de que SearchEngine fuera una interfaz, o al menos una clase abstracta, y crear una implementación simulada completa de la misma.Esto requiere mucha mano de obra o puede utilizar un sofisticado marco de burla de un solo propósito.Pero la tipificación de patos es de uso general y nos ha ayudado.

Debido al tipeo pato, nuestra prueba unitaria puede proporcionar cualquier objeto antiguo en lugar de la dependencia, siempre y cuando implemente los métodos que se llaman.

Para enfatizar, puede hacer esto en un lenguaje escrito estáticamente, con un uso cuidadoso de las interfaces y las jerarquías de clases.Pero con la escritura pato puedes hacerlo pensando menos y pulsando menos teclas.

Esa es una ventaja de escribir pato.Esto no significa que la escritura dinámica sea el paradigma correcto para usar en todas las situaciones.En mis proyectos Groovy, me gusta volver a Java en circunstancias en las que siento que las advertencias del compilador sobre los tipos me ayudarán.

Con, TDD + Cobertura de código 100% + Herramientas IDE para ejecutar mis pruebas constantemente, ya no siento la necesidad de escribir estáticamente.Sin tipos fuertes, mis pruebas unitarias se han vuelto muy fáciles (simplemente use Maps para crear objetos simulados).Especialmente, cuando usas genéricos, puedes ver la diferencia:

//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>>

vs

//Dynamic typing
def someMap = [:]   

No es que la escritura pato sea más productiva que la escritura estática, sino que simplemente es diferente.Con la escritura estática, siempre debe preocuparse de que sus datos sean del tipo correcto y en Java se muestran mediante la conversión al tipo correcto.Con la escritura pato, el tipo no importa siempre y cuando tenga el método correcto, por lo que realmente elimina gran parte de la molestia de convertir y convertir entre tipos.

@Chris Manojo

No es que la escritura estática sea más productiva que la escritura pato, sino que simplemente es diferente.Con la escritura pato siempre tienes que preocuparte de que tus datos tengan el método correcto y en Javascript o Ruby se muestra a través de muchas pruebas de métodos.Con la escritura estática no importa siempre que sea la interfaz correcta, por lo que realmente elimina gran parte de la molestia de las pruebas y conversiones entre tipos.

Lo siento pero tenía que hacerlo...

Mi opinión:

Los lenguajes escritos dinámicamente o escritos con pato son juguetes.No puede obtener Intellisense y pierde tiempo de compilación (o tiempo de edición, cuando usa un IDE REAL como VS, no esa basura que otras personas piensan que son IDE) de validación de código.

Manténgase alejado de todos los idiomas que no estén escritos estáticamente, todo lo demás es simplemente masoquismo.

Para mí, no son muy diferentes si consideramos los lenguajes escritos dinámicamente simplemente como una forma de escritura estática donde todo se hereda de una clase base suficientemente abstracta.

Los problemas surgen cuando, como muchos han señalado, empiezas a ponerte extraño con esto.Alguien señaló una función que devuelve un único objeto, una colección o un valor nulo.Haga que la función devuelva un tipo específico, no varios.Utilice múltiples funciones para colección única o colección.

Todo se reduce a que cualquiera puede escribir código incorrecto.La escritura estática es un gran dispositivo de seguridad, pero a veces el casco se interpone cuando quieres sentir el viento en tu cabello.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top