¿Qué quiso decir Rich Hickey cuando dijo: "Toda esa especificidad [de interfaces / clases / tipos] mata su reutilización!"

41

En la sugerente conferencia magistral de Rich Hickey " El valor de los valores ", a los 29 minutos, habla sobre la sobrecarga de un lenguaje como Java y hace una declaración como: "Todas esas interfaces matan su reutilización". ¿Qué quiere decir? ¿Es eso cierto?

En mi búsqueda de respuestas, me he encontrado con:

  • El Principio de Menos Conocimiento, también conocido como La Ley de Demeter, que fomenta las interfaces API herméticas. Wikipedia también enumera algunas desventajas.

  • La crisis de la ropa imperial de Kevlin Henney, que argumenta que el uso, no la reutilización, es el objetivo apropiado.

  • Charla de Jack Diederich " Stop Writing Classes " que argumenta en contra de la sobre ingeniería en general.

Claramente, cualquier cosa escrita lo suficientemente mal será inútil. Pero, ¿cómo impediría la interfaz de una API bien escrita que se use ese código? Hay ejemplos a lo largo de la historia de que algo hecho para un propósito se usa más para otra cosa . Pero en el mundo del software, si usa algo para un propósito para el que no fue diseñado, generalmente se rompe.

Estoy buscando un buen ejemplo de una buena interfaz que evite el uso legítimo pero no intencionado de algún código. ¿Existe eso? No puedo imaginarlo.

GlenPeterson
fuente
1
No he visto / leído las cosas (he agregado "Dejar de escribir clases" a mi lista de observación :)), pero ¿tal vez están discutiendo desde un ángulo de escritura dinámico vs estático? ...¿otra vez?
Andres F.
oO Interfaces de interfaz de programación de aplicaciones
Thomas Eding
Gracias por los enlaces! No encontré la charla de Jack Diederich particularmente esclarecedora (mira cómo no responde a las preguntas genuinas de la audiencia de manera convincente ... "uh, sí, tal vez en ese caso ...". Me gusta que parece estar discutiendo por la Programación Funcional sin incluso notándolo;)), pero la "Crisis de la ropa imperial" es muy buena y perspicaz.
Andres F.
1
MPO es que las personas que no creen en la reutilización no dividen las cosas en unidades lo suficientemente pequeñas. Una gran cosa construida para un propósito específico no se puede reutilizar. Sin embargo, las cosas pequeñas generalmente tienen un propósito lo suficientemente pequeño como para que el propósito pequeño sea útil en más de un contexto.
Amy Blankenship
1
@AmyBlankenship Encontré la "Crisis de la ropa imperial" vinculada anteriormente para ser perspicaz. El autor considera que "reutilizar" es un ídolo falso (algo que no se ha demostrado útil en la práctica, y además, la mayoría de las personas ni siquiera lo entienden a pesar de que usan la palabra). Tampoco considera que las bibliotecas sean "reutilizadas"; que utiliza una biblioteca, no se vuelve a utilizar la misma. También considera diseñar algo para reutilizar "una espada de doble filo"; algo que la gente suele considerar una situación de ganar-ganar, pero que realmente no lo es: cuando diseñas algo para reutilizarlo, siempre es un compromiso (por ejemplo, puedes perder en simplicidad)
Andres F.

Respuestas:

32

No he visto la presentación completa de Rich Hickey, pero si lo entiendo correctamente, y a juzgar por lo que dice sobre la marca de 29 minutos, parece estar discutiendo sobre los tipos que matan la reutilización. Él está usando el término "interfaz" libremente como sinónimo de "tipo con nombre", lo cual tiene sentido.

Si tiene dos entidades { "name":"John" }de tipo Person, y { "name": "Rover" }de tipo Dog, en Java-land, probablemente no puedan interactuar a menos que compartan una interfaz o ancestro común (como Mammal, lo que significa escribir más código). Así las interfaces / tipos aquí son "matar a su reutilización": a pesar de que Persony Dogtienen el mismo aspecto, no se puede utilizar indistintamente con el otro, a menos que escribir código adicional para apoyar eso. Nota: Hickey también bromea sobre proyectos en Java que necesitan muchas clases ("¿Quién ha escrito una aplicación Java usando solo 20 clases?"), Lo que parece una consecuencia de lo anterior.

Sin embargo, en los lenguajes "orientados al valor", no asignará tipos a esas estructuras; son solo valores que comparten la misma estructura (en mi ejemplo, ambos tienen un namecampo con un valor de cadena) y, por lo tanto, pueden interoperar fácilmente, por ejemplo, se pueden agregar a la misma colección, pasar a los mismos métodos, etc.

En resumen, todo esto parece ser algo sobre igualdad estructural versus igualdad explícita de tipo / interfaz . A menos que me haya perdido algo de las partes del video que aún no he visto :)

Andres F.
fuente
2
Por cierto, la charla de Jack Diederich "Detener las clases de escritura" parece no estar relacionada con este tema, y ​​trata más sobre YAGNI y "no escriba código hasta que lo necesite, y luego solo escriba código simple".
Andres F.
99
ERROR: Object doesn't have a property called "name"a menudo es el resultado de value-orientedidiomas, y el otro problema es cuando ya no desea llamar a esa propiedad name. Buena suerte refactorizando porque probablemente hay cientos de objetos con una propiedad namepero no todos son Persono Dog.
Reactgular
2
@MathewFoscarini Sí, no estoy necesariamente de acuerdo con eso, es solo mi interpretación de lo que creo que Hickey decía :) Me gustan los tipos y la escritura estática; Estoy empezando a no gustarme Java. Y mi disgusto no está relacionado con interfaces, sino con el desorden que es el típico proyecto de Java.
Andrés F.
1
Java es el lenguaje de programación para aquellos que prefieren pensar demasiado. Es uno de los pocos idiomas que permite a un desarrollador ocultar fácilmente sus intentos de diseñar un proyecto en exceso.
Reactgular
"En los lenguajes 'orientados al valor' no asignarás tipos a esas estructuras". Creo que debes decir "En el dinámico 'orientado al valor' ..." Haskell y Scala están orientados al valor, pero su sistema de tipos estático les da el problema exacto que está describiendo. Creo que la solución a este problema no son los valores sino el uso de mapas para pasar parámetros a las funciones. Usar mapas inmutables (valores) es simplemente más seguro.
GlenPeterson
28

Es probable que se refiera al hecho básico de que una interfaz no puede ser instanciada. No se puede reuseuna interfaz. Solo puede implementar código que lo admita, y cuando escribe código para una interfaz no hay reutilización.

Java tiene un historial de proporcionar marcos de muchas API que toman una interfaz como argumentos, pero el equipo que desarrolló la API nunca implementa una amplia gama de clases para que pueda reutilizar con esas interfaces.

Es algo así como un marco GUI que tiene una IWindowinterfaz para un cuadro de diálogo, y luego puede agregar IButtoninterfaces para los controles. Excepto, nunca te dieron una buena Buttonclase que implemente IButton. Entonces te quedas creando el tuyo.

Los marcos abstraídos que tienen una amplia gama de clases base que proporcionan funcionalidades centrales son más reutilizables, y eso funciona mejor cuando esas clases abstractas son accesibles para aquellos que usan el marco.

Los desarrolladores de Java comenzaron a hacer esto donde sus capas API solo estaban expuestas interfaces. Podría implementar esas interfaces, pero nunca podría reutilizar las clases del desarrollador que implementó esas interfaces. Es como un estilo de capa y daga de desarrollo de API.

Reactgular
fuente
44
Gracias por esta respuesta Ahora siento que entiendo la pregunta y la respuesta :)
MetaFight
2
+1 Realmente aprecio tu respuesta y agrega una capa fascinante de información interesante a esta pregunta. Pero creo que la respuesta de Andreas F. probablemente esté más cerca del corazón de lo que quería decir el Sr. Hickey, así que acepté la suya.
GlenPeterson
@GlenPeterson no hay problema, creo que él también podría estar en la marca.
Reactgular
1
Bueno, esto y la respuesta aceptada destacan dos interpretaciones ligeramente diferentes, pero igualmente interesantes. Tengo curiosidad por saber cuál tenía el Sr. Hickey en mente al hablar de esto ...
David Cowden
No puede reutilizar una interfaz, pero puede extenderla (y proporcionar un número de versión valioso) sin cambiar las clases antiguas. También puede heredar de muchas interfaces para agregar un nuevo trabajo para nuevas clases, o agregar una nueva herencia en las antiguas clases compiladas. También puede extender la clase que implementa esta interfaz para un nuevo trabajo.
cl-r
14

Creo que la diapositiva 13 en su presentación ( El valor de los valores ) ayuda a comprender esto:

http://i.stack.imgur.com/LVMne.png


Valores

  • No necesito metodos
    • Puedo enviarte valores sin código
      y estás bien

Según tengo entendido, Hickey sugiere que si necesito, por ejemplo, duplicar el valor que me enviaste, simplemente escribo un código como

    MyValue = Double(YourValue)

Verá, el código anterior es el mismo, sin importar el tipo de valor que envió, una especie de reutilización perfecta .

Ahora, ¿cómo se vería esto en el lenguaje que tiene objetos e interfaces?

    Doublable MyValue = YourValue.Double()

¡Oh espera! ¿Qué YourValuepasa si no se implementa Doublable? no es que no se pueda duplicar, puede ser perfectamente, pero ... ¿y si simplemente no hay un método Double ? (¿Qué pasa si hay un método llamado say TwiceAsMuch?)

Uh, oh, tenemos un problema. YourValue.Doubleno funcionará, ya no se puede reutilizar . Según mi lectura de la diapositiva anterior, esto es sobre lo que Hickey quiso decir cuando dijo: "¡Todas esas interfaces matan su reutilización!"

Verá, las interfaces suponen que los objetos se pasan "junto con sus métodos", junto con el código que opera en estos. Para usar objetos, uno necesita entender cómo invocar ese código, a qué método llamar.

Cuando falta el método esperado , hay un problema, aunque semánticamente , la operación deseada tiene mucho sentido para un objeto. Como se indicó en la presentación, los valores no necesitan métodos ("Puedo enviarte valores sin código y estás bien"), lo que permite escribir código que trata con ellos de manera genérica.


Nota al margen: la noción de pasar valores sin código de alguna manera me recuerda a un patrón Flyweight en OOP.

un objeto que minimiza el uso de memoria al compartir la mayor cantidad de datos posible con otros objetos similares; es una forma de usar objetos en grandes cantidades cuando una simple representación repetida usaría una cantidad inaceptable de memoria ... Los objetos de peso mosca son, por definición , objetos de valor . La identidad de la instancia del objeto no tiene ninguna consecuencia, por lo tanto, dos instancias de Flyweight del mismo valor se consideran iguales ...

Los usos de peso mosca que he visto generalmente siguen el mismo enfoque de quitar el código (métodos, interfaces) de los objetos y pasar cosas como valores sin código , esperando que recibir código tenga los medios necesarios para operar en estos.

Esto se siente más o menos como en la diapositiva, "los valores no necesitan métodos. Puedo enviarle valores sin código y está bien".

mosquito
fuente
55
Los genéricos se encargarían de ese problema. Duplicar tiene sentido en algunos objetos, pero no en otros. En el lenguaje Go, hay una implementación de interfaz implícita (una forma de escribir pato), por lo que no tiene que preocuparse por todas esas interfaces. Por otro lado, debes saber qué objeto va a ser alcanzado por la firma de tu método; de lo contrario, puede obtener resultados inesperados. Siempre hay compensaciones.
Robert Harvey
1
Una toma interesante. ¡Buena respuesta!
maple_shaft
2
El patrón de peso mosca no es de lo que habla Rich. Como dice la segunda oración del artículo, el propósito del patrón de peso mosca es conservar la memoria. El enfoque de Rich no busca hacer eso.
55
MyValue = Double(YourValue)no tiene sentido si YourValue es una cadena, una dirección, un usuario, una función o una base de datos. De lo contrario, su argumento de método perdido es fuerte. OTOH, los métodos de acceso le permiten imponer varias restricciones para que sus valores sean válidos y que solo se usen operaciones sensatas para producir nuevos valores. Si luego decide separar la Dirección de su Usuario y Empresa, los métodos de acceso significan que no romperá a todos los clientes de su código. Por lo tanto, pueden ayudar a reutilizar a largo plazo, incluso si a veces lo obstaculizan a corto plazo.
GlenPeterson
44
(Por otro lado, estoy de acuerdo en que en Java-land, la explosión de clases, interfaces y frameworks es una pesadilla. La solución "empresarial" más simple en Java es un desastre de código. Por lo tanto, tomo una valiosa lección de esto preguntas y respuestas, sin estar necesariamente de acuerdo con el material de escritura dinámica)
Andres F.
2

En un (es decir, mi) clase mundial e interfaces ideales siempre describirían el comportamiento, pero el hecho es que con demasiada frecuencia realmente terminan describiendo datos. Ayer vi el video de alguien construyendo una supuesta BankAccountclase que no era más que una glorificada int(de hecho, era mucho menos útil que una int, por lo tanto, "matar" la reutilización que hubiera tenido si simplemente la hubiera dejado como int). todo en nombre del "buen" diseño. La cantidad de código, sudor y lágrimas desperdiciados en reinventar continuamente representaciones retorcidas de datos es asombrosa; Si no está utilizando los datos de manera significativa, simplemente déjelo.

Ahora, esta es la etapa en la que Rich Hickey se contenta con tirar al bebé con el agua del baño y decir que todos deberíamos vivir en la tierra de los valores (un vecino del reino de los sustantivos). Creo, por otro lado, que la POO puede y promueve la reutilización (y lo que es más importante la capacidad de descubrimiento, que me parece que falta en la programación funcional) cuando se emplea con criterio. Si está buscando un principio OOP que mejor capture esta tensión, creo que podría ser http://c2.com/cgi/wiki?TellDontAsk (que por supuesto es un primo cercano de Demeter)

CortinaPerro
fuente
¿Qué quieres decir con capacidad de descubrimiento? ¿Es similar a esto ?
1
Sí, creo que toca muchos de los puntos principales. Es un punto sutil, pero la capacidad de descubrimiento es un acto de equilibrio, por lo que hacer las cosas demasiado transparentes también es indeseable porque obtendrás una mala relación señal / ruido.
CurtainDog