Recientemente leí la publicación del blog Three Big Lies y me está costando mucho justificar la segunda mentira, que se cita aquí:
(MENTIRA # 2) EL CÓDIGO DEBE DISEÑARSE EN UN MODELO DEL MUNDO
No hay ningún valor en que el código sea algún tipo de modelo o mapa de un mundo imaginario. No sé por qué este es tan atractivo para algunos programadores, pero es extremadamente popular. Si hay un cohete en el juego, ten la seguridad de que hay una clase "Rocket" (suponiendo que el código es C ++) que contiene datos para exactamente un cohete y hace cosas de cohetes. Sin tener en cuenta en absoluto qué transformación de datos realmente se está haciendo, o para el diseño de los datos. O para el caso, sin la comprensión básica de que donde hay una cosa, probablemente hay más de una.
Aunque hay muchas penalizaciones de rendimiento para este tipo de diseño, la más importante es que no escala. En absoluto. Cien cohetes cuesta cien veces más que un cohete. ¡Y es extremadamente probable que cueste incluso más que eso! Incluso para un no programador, eso no debería tener ningún sentido. Economía de escala. Si tiene más de algo, debería ser más barato, no más caro. Y la forma de hacerlo es diseñar los datos correctamente y agrupar las cosas por transformaciones similares.
Aquí están mis problemas con esta mentira en particular.
Es valioso que el código sea un modelo / mapa de un mundo imaginario ya que modelar el mundo imaginario ayuda (al menos a mí, personalmente) a visualizar y organizar el código.
Tener una clase "Rocket" es, para mí, una opción perfectamente válida para una clase. Tal vez los "Cohetes" podrían desglosarse en tipos de Cohetes como AGM-114 Hellfire, etc., que contendrían la fuerza de la carga útil, la velocidad máxima, el radio de giro máximo, el tipo de objetivo, etc., pero aún así cada cohete disparado necesitaría una posición y una velocidad
Por supuesto, tener 100 cohetes cuesta más de 1 cohete. Si hay 100 cohetes en la pantalla, debe haber 100 cálculos diferentes para actualizar su posición. El segundo párrafo parece que afirma que si hay 100 cohetes, ¿debería costar menos de 100 cálculos actualizar el estado?
Mi problema aquí es que el autor presenta un modelo de programación "defectuoso" pero no presenta una forma de "corregirlo". Quizás estoy tropezando con la analogía de la clase Rocket, pero realmente me gustaría entender el razonamiento detrás de esta mentira. Cual es la alternativa?
fuente
Respuestas:
En primer lugar, echemos un vistazo a algún contexto: este es un diseñador de juegos que escribe en un blog cuyo tema es obtener la última caída de rendimiento de una CPU Cell BE. En otras palabras: se trata de la programación de juegos de consola, más específicamente, la programación de juegos de consola para PlayStation 3.
Ahora, los programadores de juegos son un grupo curioso, los programadores de juegos de consola aún más, y el Cell BE es una CPU bastante extraña. (¡Hay una razón por la que Sony eligió un diseño más convencional para PlayStation 4!)
Entonces, tenemos que mirar esas declaraciones dentro de este contexto.
También hay algunas simplificaciones en esa publicación de blog. En particular, esta Mentira # 2 está mal presentada.
Yo diría que todo lo que abstrae del mundo real es un modelo en algún sentido. Y dado que el software no es real, sino virtual, siempre es una abstracción y, por lo tanto, siempre un modelo. ¡Pero! Un modelo no tiene que tener un mapeo limpio 1: 1 en el mundo real. Eso es, después de todo, lo que lo convierte en un modelo en primer lugar.
Entonces, en cierto sentido, el autor está claramente equivocado: el software es un modelo. Período. En otro sentido, tiene razón: ese modelo en realidad no tiene que parecerse en absoluto al mundo real.
Daré un ejemplo que ya di en otras respuestas a lo largo de los años, el (in) famoso ejemplo de Introducción a la cuenta bancaria OO 101. Así es como se ve una cuenta bancaria en casi todas las clases de OO:
Por lo tanto: el
balance
es de datos , ytransfer
es una operación .¡Pero! Así es como se ve una cuenta bancaria en casi todos los programas bancarios:
Entonces, ahora
transfer
son datos ybalance
es una operación (un pliegue a la izquierda sobre el registro de transacciones). (También notarán queTransactionSlip
es inmutable,balance
es una función pura,TransactionLog
puede ser una estructura de datos "casi" inmutable de solo agregar ... Estoy seguro de que muchos de ustedes detectaron los flagrantes errores de concurrencia en la primera implementación, que ahora desaparecen mágicamente .)Tenga en cuenta que ambos son modelos. Ambos son igualmente válidos. Ambos son correctos. Ambos modelan lo mismo. Y, sin embargo, son exactamente duales entre sí: todo lo que son datos en un modelo es una operación en el otro modelo, y todo lo que es una operación en un modelo son datos en el otro modelo.
Entonces, la pregunta no es si modelas el "mundo real" en tu código, sino cómo lo modelas.
Como resultado, el segundo modelo es también cómo funciona la banca en el mundo real. Como ya he insinuado anteriormente, este segundo modelo es sobre todo inmutable y puro, e inmune a los errores de concurrencia, lo cual es realmente muy importante si tenemos en cuenta que hubo un tiempo no hace mucho tiempo, en donde
TransactionSlip
s eran reales tiras de papel que fueron enviados alrededor a través de caballos y carruajes.Sin embargo, el hecho de que este segundo modelo realmente coincida tanto con el funcionamiento de la banca en el mundo real como con el funcionamiento del software de la banca en el mundo real, no lo hace automáticamente más "correcto". Porque, en realidad, el primer modelo ("incorrecto") se aproxima bastante a cómo los clientes bancarios ven su banco. Para ellos ,
transfer
es una operación (tienen que completar un formulario) ybalance
es un dato en la parte inferior de su estado de cuenta.Por lo tanto, puede ser cierto que en el código del motor del juego principal de un juego de disparos de PS3 de alto rendimiento, no habrá un
Rocket
tipo, pero aún así, habrá algunos modelos del mundo, incluso si el modelo parece extraño a alguien que no sea experto en el dominio de la programación del motor de física de juegos de consola.fuente
balance
como datos y transacciones como más datos, y las transferencias como operaciones, porque eso es lo que ve el usuario, aunque el back-end puede tratarlo de manera diferente.No estoy de acuerdo con cada "mentira" que propone.
TL; DR El autor de este artículo intentaba ser controvertido para hacer su artículo más interesante, pero las llamadas "mentiras" son aceptadas por los desarrolladores de software por buenas razones.
Mentira # 1: Big O es importante para fines de escala. A nadie le importa si una aplicación pequeña tarda más tiempo, que es lo único que importa, las constantes de tiempo les importa que cuando dupliquen el tamaño de entrada no multipliquen el tiempo de ejecución por un factor de 10.
Mentira n. ° 2: los programas de modelado posteriores al mundo real le permiten a un programador mirar su código 3 años después para comprender fácilmente lo que está haciendo. El código debe ser mantenible o debería pasar horas tratando de entender lo que el programa está tratando de hacer. Otra respuesta sugirió que puede tener clases más genéricas como
LaunchPad
yMassiveDeviceMover
. Estas no son necesariamente malas clases, pero aún así necesitarías laRocket
clase. ¿Cómo se supone que alguien sepa quéMassiveDeviceMover
hace o qué mueve? ¿Está moviendo montañas, naves espaciales o planetas? Esto básicamente significa que agregar clases comoMassiveDeviceMover
hace que su programa sea menos eficiente (pero posiblemente mucho más legible y comprensible).Además, el costo del tiempo del desarrollador comenzó a exceder el costo del hardware hace mucho tiempo. Es una idea horrible comenzar tratando de diseñar con optimización al frente de sus pensamientos. Lo programa de manera fácil y comprensible y luego modifica su programa después de descubrir qué partes de sus programas están tardando mucho en ejecutarse. No olvide: el 20% del programa está utilizando el 80% del tiempo de ejecución.
Mentira # 3 - El código es extremadamente importante. El código bien escrito (y modular) permite la reutilización (ahorrando innumerables horas hombre). También le permite filtrar y reconocer datos incorrectos para que puedan manejarse. Los datos son maravillosos, pero sin el código sería imposible analizarlos y obtener información útil de ellos.
fuente
En un sistema de comercio electrónico, no se trata con "cohetes" a nivel de clase, sino con "productos". Por lo tanto, depende de lo que intente lograr y del nivel deseado de abstracción.
En un juego, se podría argumentar que los cohetes son simplemente uno de los muchos tipos de "objetos en movimiento". Se les aplica la misma física que a todos los demás objetos en movimiento. Por lo menos, "cohete" va a heredar de una clase base más general de "objeto en movimiento".
En cualquier caso, el autor del pasaje que citó parece haber exagerado un poco su caso. Los cohetes aún pueden tener características únicas, como "cantidad de combustible restante" y "empuje", y no necesita cien clases para representar esto para cien cohetes, solo necesita uno. La creación de objetos tiene un costo bastante bajo en la mayoría de los lenguajes de programación decentes, por lo que si necesita rastrear cosas similares a cohetes, la idea de que no debería hacer objetos Rocket porque podría ser demasiado costoso no tiene mucho sentido.
fuente
El problema con el mundo real es toda esa maldita física. Separamos las cosas en objetos físicos en el mundo real porque son más fáciles de mover que los átomos individuales, o una escoria gigante fundida de algo que podría ser un cohete.
Del mismo modo, el mundo real proporciona una serie de características útiles en las que confiamos. Es realmente fácil hacer excepciones Penguin: "todas las aves vuelan, excepto ...". Y es realmente fácil etiquetar las cosas como cohetes, es decir, si llamo a ese pingüino cohete y lo enciendo ... simplemente no funciona.
Entonces, cómo separamos las cosas en el mundo real conceptualmente funciona bajo esas restricciones. Cuando hacemos cosas en código, debemos separar las cosas para que funcionen bien bajo esas restricciones, que son decididamente diferentes.
Piensa en las redes. No modelamos puertos, cables y enrutadores en código. En cambio, abstraemos la comunicación de red en conexiones y protocolos. Hacemos eso porque es una abstracción útil independientemente de la implementación en el mundo real. Y pone restricciones útiles (por ejemplo: no puede comunicarse hasta que se abra la conexión) que solo importan en el código .
Entonces, sí, a veces el código de modelado después del mundo real funciona, pero eso es una coincidencia . Cuando la gente habla de POO, los objetos no son objetos del mundo real. Que las escuelas y los tutoriales digan lo contrario es una tragedia de décadas.
fuente
Rocket
tipo en el código de este tipo, estoy dispuesto a apostar que todavía hay algún modelo de ...La alternativa es modelar las cosas que interesan a sus programas. Incluso si su programa trata con cohetes, es posible que no necesite tener una entidad llamada a
Rocket
. Por ejemplo, puede tener unaLaunchPad
entidad y unaLaunchSchedule
entidad y unaMassiveDeviceMover
entidad. El hecho de que todo esto ayude al lanzamiento de cohetes no significa que usted mismo esté manejando cohetes.fuente
Este es el verdadero problema, pero te daré mi opinión como desarrollador, tal vez eso ayude.
Primero, no llamaría mentiras a ninguno de ellos, como conceptos erróneos comunes. Llamarlo mentira es solo una exageración.
Uno tiene razón, de alguna manera. No pasaré mucho tiempo en esto, porque no es parte de la pregunta. Pero en esencia él tiene razón. Podría repetir esto como "Lo que funciona en un laboratorio puede no funcionar en la vida real". Muchas veces los desarrolladores se adhieren a un diseño que funciona en un "laboratorio" pero falla en aplicaciones del mundo real.
Tres suena un poco jabonoso para mí, pero esencialmente está en lo correcto nuevamente. Pero esto podría reescribirse para "escribir código según sus necesidades, no intente ajustar las necesidades a su código".
Dos De nuevo, aquí está en lo correcto. He visto a los desarrolladores pasar semanas o más desarrollando una clase de "cohete" cuando una clase simple de "vehículo" funcionaría, o una clase aún más simple, "móvil". Si todo lo que necesita hacer su cohete es moverse del lado izquierdo de la pantalla a la derecha y emitir un sonido, entonces puede usar la misma clase que utilizó para automóviles, trenes, barcos y moscas. El argumento 100 debería costar menos de 1 * 100 parece estar en tiempo invertido en el desarrollo, y no tanto en costos de cálculo. Aunque apegarse a un menor número de clases generales para reutilizarlas es "más barato" que muchas clases específicas que no pueden reutilizarse. Esto probablemente podría reescribirse como "las clases generales son mejores que las clases específicas,
En esencia, todo el artículo podría reescribirse, con menos palabras de moda y, en el mejor de los casos, solo sería un párrafo. Dicho esto, es una publicación de blog centrada en un área estrecha de programación. He realizado una programación incrustada, y estoy de acuerdo, con la idea general detrás de estas declaraciones, aunque hay bastante "pelusa" a su alrededor para que sea adecuada para una presentación en GDC.
Una última nota, el artículo fue escrito en 2008 (lo mejor que pude ver). Las cosas cambian rápidamente. Las declaraciones son ciertas hoy en día, pero los sistemas integrados son mucho más comunes hoy que en aquel entonces, y los patrones de desarrollo cambian. Quizás incluso en respuesta a este artículo / charla.
fuente
Me parece interesante que estas mentiras se centren en preocupaciones académicas: la plataforma, la eficiencia del uso de la memoria y los datos. Pero ignora por completo el elemento humano.
El software se trata de satisfacer las necesidades de las personas. Por lo general, esto se cuantifica en términos comerciales: hay clientes que quieren algo y patrocinadores que están dispuestos a pagar para que esto suceda. Si el software se está escribiendo de una manera para satisfacer las necesidades de ambos lados de la ecuación, entonces es un buen software, si no, es un mal software.
Si la plataforma no es importante para el cliente, entonces la plataforma no es importante. Si la eficiencia de la memoria no es importante para el cliente, entonces no es importante. Si los datos no son importantes para el cliente, entonces los datos no son importantes. Si el código funciona, pero no se puede leer o mantener, y el cliente quiere cambios rápidos y confiables a un precio decente, entonces el código mal escrito es algo malo. Si el código funciona, pero no se puede leer o mantener, y al cliente no le importa o está dispuesto a pagar por refactores costosos, entonces un código mal escrito es algo bueno.
La gran mentira es que todo menos el elemento humano importa. ¿Por qué son importantes los datos? Porque hay algún cliente o parte interesada que lo necesita. Esa es la "gran verdad".
fuente
En mi humilde opinión, si el código está "diseñado en torno a un modelo del mundo", es más fácil de entender, tanto para los diseñadores y desarrolladores, como para los encargados del mantenimiento. Pero creo que no soy solo yo, y no solo software. De Wikipedia: El modelado científico es una actividad científica, cuyo objetivo es hacer que una parte o característica particular del mundo sea más fácil de entender, definir, cuantificar, visualizar o simular haciendo referencia al conocimiento existente y generalmente aceptado comúnmente
fuente