viernes, 31 de agosto de 2007

¿Cuándo verificar si un objeto es válido?

Otra de las preguntas que quedó pendiente del post donde recomiendo crear objetos válidos desde el principio es cuándo conviene verificar si el objeto es válido.
Lo primero que debemos determinar es de quién es la responsabilidad de verificar si es válido el objeto. Se cae de maduro que los que no pueden realizar esa verificación son los objetos que lo usan, por lo tanto quedan dos opciones: 1) La clase de la cual será instancia el nuevo objeto 2) El objeto mismo a ser validado.
Veamos un ejemplo práctico. Un precio es un ente de la realidad que dice cuanto cuesta algo[1]. Por ejemplo el precio de 1 Kilo de Bananas es 10$. Para representar "precios" podemos crear una clase, Precio, la cual sabrá responder al mensaje #para:es:. Por lo tanto, una colaboración para crear precios sería:

Precio para: 1*Kilo*Banana es: 10*Peso

(Este ejemplo supone que están usando Aconcagua o algún paquete de medidas similar y por lo tanto puede crear medidas multiplicando cantidades por unidades, por eso 10*Peso es equivalente a lo que comúnmente denominamos "diez pesos". Por supuesto este ejemplo también asume que Kilo, Banana y Peso son objetos accesible en el contexto de evaluación y representan unidades).
Lo que tiene de particular los precios es que los mismos existen solo para cantidades distintas de 0. O sea, no tiene sentido hablar de el precio de 0 kilos de banana o de 0 autos. El motivo por el cual no tiene sentido se puede ver si representamos el precio como una cuenta matemática (en definitiva lo es). Un precio es la división del valor por la medida a la cual se le asigna el precio. Por lo tanto, si el precio para un kilo de bananas es 10 pesos, matemáticamente (y en Smalltalk) se representa como:

(10*Peso) / (1*Kilo*Banana)

Como pueden ver, no tiene sentido que el divisor de esta división sea 0. Es esta condición por lo tanto, la que se debe verificar cada vez que se crea un precio.
Veamos como se implementaría la opción 2). Esta implica que primero se crea el objeto y luego se valida. A nivel código la implementación sería:

Precio class>>para: unaMedida es: unValor

^self new initializePara: unaMedida es: unValor

Precio>>initializePara: unaMedida es: unValor

unaMedida isZero ifTrue: [ self error: 'No se puede crear un precio con cantidades en 0' ].
medida := unaMedida.
valor := unValor.

La implementación de la opción 1) sería:

Precio class>>para: unaMedida es: unValor


unaMedida isZero ifTrue: [ self error: 'No se puede crear un precio con cantidades en 0' ].
^self new initializePara: unaMedida es: unValor

Precio>>initializePara: unaMedida es: unValor

medida := unaMedida.
valor := unValor.

Dadas estas dos implementaciones, ¿cuál les parece más correcta?.
¿Lo pensaron?.... tomensé unos minutos... no lean la solución que propongo directamente!
[... pensando ... ]
Bien, asumo que ya lo pensaron. La opción correcta es la 1. ¿Por qué? Simplemente porque en la opción 2) el objeto ya fue creado, el precio ya existe pero es inválido! por lo tanto esta rompiendo la premisa que comenté en el post anterior, que solo pueden existir objetos válidos!. Es este el motivo principal por el cual la opción correcta el la 1. En este caso el objeto solo se crea si la validación es correcta.
Otra ventaja que tiene la opción 1 es que se crearan menos objetos en el caso de tener muchos casos inválidos... dependiendo del comportamiento de la aplicación esto puede llegar a impactar en performance puesto que la creación de objetos está directamente relacionada con la del garbage collector.

Bueno, espero que les haya gustado este tipo de heurísticas de las que vine hablando. Estaría bueno que me comenten que les parece... siempre es bueno un poco de feedback para repensar la cosas y seguir escribiendo.

[1] Un precio en realidad es algo más complejo que únicamente el valor de una cantidad de algo, los precios varían para la misma cantidad de ese algo según el momento en que se toma (el precio de hoy puede ser distinto al de ayer, o el de ahora puede ser distinto al de una hora antes) y el lugar donde se generan (el precio en Carrefour de las bananas no es el mismo que el de Jumbo)

¿Tiene sentido usar nil?

En el último post, una de las preguntas que dejé pendiente era si bajo las condiciones de modelado comentadas tenía sentido utilizar "nil" (o "null" en otros lenguajes... aunque "nil" y "null" no son lo mismo[1]), si los objetos debían o no tener variables de instancia referenciando a "nil".
La realidad es que si aplicamos los principios que comenté en el post pasado, ningún objeto debería tener variables de instancia con "nil" (salvo por cuestiones implementativas como las que comento más abajo). Si al momento de crear objetos, a estos se le indica cuales son todos sus colaboradores internos, no debería existir ninguno de ellos referenciando a "nil".
Imaginense las ventajas que esto tiene! El objeto "nil" es uno de los más "dañinos" desde el punto de vista de "programación correcta o segura", puesto que su existencia implica que en algún momento se le deberá preguntar si es él (el famoso mensaje isNil o ==null en otros lenguajes), lo cual deriva en un ifTrue: (o if...) y por lo tanto en un posible foco de error y conflicto (error por olvidarse de hacer dicha pregunta, conflicto en el caso de la evolución del modelo, etc.)
Con esto no quiero decir que el objeto "nil" no debería existir o que no se lo debería utilizar, "nil" debe existir por lo que representa, "la nada", pero cuanto menos utilizado sea, mejor.
Siguiendo los consejos que publiqué en el post anterior, el uso de "nil" por lo tanto se disminuye al máximo y por ende también los errores que el uso del mismo induce.
El único motivo que encontré hasta ahora para utilizar "nil" como variable de instancia en un objeto, es para aquellas variables que "cachean" algún objeto y por lo tanto se pueden inicializar de manera lazy. Pero justamente por ser una cache o inicializarse de manera lazy, son colaboradores que solo debe conocer el objeto que los referencia y por lo tanto nunca otro objeto podrá pasarlo en el momento de la creación.
Por otro lado, les comento que en ambientes de objetos transaccionales como GemStone, tener variables que se inicialicen de manera lazy no es tan trivial puesto que al momento de comitiar una transacción se pueden generar conflictos de write-write. Por ejemplo, si desde dos transacciones distintas se le envía el mensaje que inicializa dicha variable al mismo objeto, aquella que commitee segunda tendrá el conflicto que comenté puesto que la primera ya habrá "escrito" dicha variable de instancia.

[1] "nil" en Smalltalk es un objeto, instancia de UndefinedObject y por lo tanto puede recibir mensajes y también se lo puede modificar. null en Java o .Net no es un objeto, es una construcción sintáctica y por lo tanto no posee la misma riqueza lingüística que "nil". ¿Qué significa "mayor riqueza lingüística?, les dejo un ejemplo para que lo deduzcan por su cuenta: (Este ejemplo fue una gran idea de Luciano!)
En un framework que usamos es necesario crear un diccionario cuyos valores pueden ser "nil" o no. Por ejemplo:
ClassXXX>>insertValuesFrom: aCuenta

^Dictionary new
at: 'sucursal' put: (aCuenta sucursal isNil ifTrue: [ nil ] ifFalse: [ aCuenta sucursal objectID ]);
at: 'calificacionCuenta'put: (aCuenta calificacion isNil ifTrue: [ nil ] ifFalse: [ aCuenta calificacion codigo ]);
...
yourself.
Como pueden ver, este framework no sigue los consejos que di en el post anterior y por ello es necesario verificar si 'sucursal' es "nil" para enviarle el mensaje #objectID en caso de no serlo. Lo mismo sucede con 'calificacion'.
Como podrán darse cuenta, escribir código de esta manera es error prone (propenso a error), tedioso, molesto, cansador, poco inspirador y poco divertido (se lleva todas las malas). La gran idea de Luciano fue darse cuenta que "nil" es un objeto y por lo tanto puede saber responder los mensaje #objectID y #codigo de tal manera que devuelvan self!. Por lo tanto implementando los siguientes métodos en UndefinedObject:
UndefinedObject>>objectID
^self

UndefinedObject>>codigo
^self
no es más necesario hacer los chequeos anteriores!. Transcribo lo que Luciano escribió:
"De esa manera el código del ejemplo anterior se puede escribir así, que es mucho más claro y se eliminan todos los isNil ifTrue:[] ifFalse:[] que entorpecen la lectura:"
ClassXXX>>insertValuesFrom: aCuenta

^Dictionary new
at: 'sucursal' put: aCuenta sucursal objectID;
at: 'calificacionCuenta' put: aCuenta calificacion codigo;
...
yourself
¿Se ve a simple vista no?... Esto es riqueza lingüística, es la posibilidad de modificar el lenguaje para cumplir con objetivos que no estaban pensados en primera instancia, es poder hacer evolucionar un lenguaje de la misma manera que evoluciona un lenguaje natural. De más está decir que con Java, .Net o C++, etc, esta solución no es factible puesto que "null" no es un objeto.
Se puede discutir si desde el punto de vista conceptual es correcto implementar estos mensajes en UndefinedObject... seguramente nuestra conclusión será que no, pero que ayuda, no perjudica y es muy práctico, no quedan dudas.
Así que, me saco el sombreo: Grande Lucho!

sábado, 25 de agosto de 2007

Creación, validación e inmutabilidad de objetos

"Un objeto es una representación en nuestro modelo computacional de un ente de la realidad."
Esta definición, es según mi entender, una de las más importantes del paradigma. La cantidad de conclusiones y prácticas que se pueden derivar de ellas son múltiples, pero hoy me voy a concentrar en una que nos ha dado muy buen resultado, y tiene que ver con el momento en que se crea un objeto, el momento en que el objeto empieza a existir.
Es muy común ver modelos en donde se crean objetos y a los que se les va "pasando" sus colaboradores internos (variables de instancia) en distintos mensajes. Por ejemplo, para el caso de un fecha, es muy común ver cosas así:

unaFecha := Date new.
unaFecha year: 2007.

unaFecha monthNumber: 2.

unaFecha dayNumber: 1.

Cuando los objetos tienen protocolo de este estilo, generalmente aparecen los problemas que comento a continuación:
1) Se puede crear el objeto "unaFecha" pero nunca indicarles cuales son su año, mes y/o día. O sea, puede existir un objeto que debería representar una fecha... ¡¡pero no lo hace!!, y no lo hace porque no existe una fecha que no esté compuesta por su año, mes y día. ¿Qué problemas prácticos puede ocasionar?. Varios, pero el principal es que antes de que un objeto colabore con "unaFecha" deberá asegurarse que realmente representa una fecha, que es un objeto "completo". Todos los objetos que colaboren con "unaFecha" para saber el año por ejemplo, deberán asegurarse que el mismo no sea "nil". (¿No les parece raro? Desde el punto de vista conceptual, ¿puede un año ser "nil"?... humm, vayan pensándolo)
2) Es muy dificil que "unaFecha" se asegure que realmente representa una fecha correcta. ¿Cuándo lo hace? ¿Cuándo le indican el día? ¿y si le indican el día antes que el año o mes?. Algo es seguro, "unaFecha" no debería permitir representar algo que no es una fecha válida. Por ejemplo, los días 29, 30 y 31 son inválidos para "unaFecha" puesto que su mes es Febrero del 2007. ¿Qué soluciones se utilizan para este problema?. Una solución que se ve muy seguido es crear un mensaje llamado "validate" por ejemplo, que todas las fechas sepan responder indicando si son válidas o no. De esta manera, el objeto que está colaborando con "unaFecha" una vez que le indica todos sus colaboradores le puede enviar el mensaje "validate". ¿Qué problemas puede traer esta solución?. Un problema muy común es por ejemplo que los objetos se olviden de enviar el mensaje "validate". Y digo muy común porque ¿alguna vez ustedes le preguntaron a una fecha si es válida?. Si una fecha existe es porque es válida!!, sino no existiría, no sería una fecha (deberían empezar a ver hacia donde voy...). O sea, yo puedo hablar del 30 de Febrero de 2007, pero por más que tenga "forma" de fecha no lo es y cualquier ser humano me lo haría notar. Otro problema más "computacional" se produce si "unaFecha" es compartido desde distintos threads de ejecución, ¿cómo hacer que mientras uno lo está modificando el otro no vea que es inválido?
3) Estos objetos tienen protocolo que hacen que las fechas sean mutables, o sea que un objeto que en un instante esta representado al "01/01/2007", en otro puede representar al "30/07/2008", nada impide en este modelo que eso suceda. En el punto anterior comenté uno de los problemas que puede tener que los objetos sean mutables y tiene que ver con la concurrencia, pero más importante que este problema que reside en el plano computacional es entenderlo en el plano conceptual. Y lo importante es ver que una fecha NUNCA CAMBIA. El 25 de Diciembre del 2007 es siempre el 25 de Diciembre del 2007 no importa que le sume 5 días, le reste 4 o haga lo que haga. Una fecha no cambia de la misma manera que un número no cambia. El 2 es siempre el 2, no importa lo que hagamos con él. Si un objeto representa el ente 25 de Diciembre del 2007, una vez que lo hace no puede pasar en ningún momento a representar otra cosa. ¿Qué problema práctico traería que esto suceda?. Bueno, básicamente si ese objeto es referenciado desde varios otros objetos, solo aquel que lo modifica sabría que ahora representa otra fecha, el resto no se enteraría.
En conclusión, de estos tres puntos podemos inferir que:
1) Cuando se crea un objeto el mismo debe estar "completo" de entrada, debe representar el ente de la realidad desde el momento de su existencia, de no hacerlo habrá un tiempo (que puede ser indefinido) en el cual no representará nada y por definición no será un "buen" objeto o objeto "at all".
2) Los objetos deben asegurarse lo antes posible que son "válidos", que realmente están representando el ente de la realidad para lo cual fueron especificados. No deberían existir objetos que no representan entes de la realidad.
3) Una vez que un objeto representa un ente de la realidad no puede pasar a representar otro. ¡Solo puede cambiar si el ente que representa cambia! y aunque parezca mentira es mucho más común que los entes de la realidad no cambien a que lo hagan. Utilizando términos computacionales, cuantos más objetos inmutables existan, mejor.
Si repasamos la definición del principio, podemos ver que todas estas conclusiones se pueden inferir directamente. Si un objeto representa un ente de la realidad, apenas el objeto exista debe hacerlo, solo debe representar entes que existan y no puede cambiar si el ente de la realidad no cambia. ¡Así de simple!, ¡pero no se imaginan las derivaciones que tiene! Los resultados de seguir estas "reglas" son muy provechosas, se los puedo asegurar no solo por las conclusiones que sacamos conceptualmente, sino por la experiencia de haberlas seguido y utilizado.
Por lo tanto, el ejemplo que mostré al principio de como crear una instancia de una fecha, debería ser:
unaFecha := Date yearNumber: 2007 monthNumber: 2 dayNumber:1.
¿Simple no?. El método que implementa el mensaje #yearNumber:monthNumber:dayNumber: debería "asertar" que dicha combinación forma una fecha válida y por último, no debería existir menajes como #yearNumber: que permitan modificar una fecha.
Si pueden, traten de usar estos consejos en los modelos que hacen, les aseguro que no se van a arrepentir. Cualquier duda, avisenme!, con gusto los ayudaré.
Les dejo mientras tanto, dos preguntas:
1) Bajo estas condiciones de modelado, ¿tiene sentido utilizar "nil" para algo?, ¿deberían objetos tener variables de instancia con "nil"?
2) ¿Cuándo deben los objetos verificar si son válidos? ¿Qué deben hacer en caso de no serlo?

¡Qué tengan un día objetoso! jaja.

viernes, 17 de agosto de 2007

Relativismo linguistico III

Esta semana fue complicadísima! ¿Vieron esas semanas donde parece que se juntan todos los problemas, se crean todos los malos entendidos posibles? bueno, esta semana fue así para mi.
Por suerte ya es viernes a las 17:50 y luego de haber respondido el stack de mails que tenía pendiente aproveché para escribir estas lineas.
Por lo menos si hay algo bueno en tener que viajar una hora para venir al trabajo es que, por más lios que tenga alrededor, puedo leer; y continué con la lectura de Whorf y los capítulos que leí fueron uno más interesantes que el otro. Aquí les mando un pequeño resumen:
Science & Linguistic: En este capítulo es donde formula y explica el relativismo lingüístico. Básicamente dice: "Estamos frente a un nuevo principio de relatividad, que dice que no todos los observadores son guiados por las mismas evidencias físicas a la misma visión del universo, a menos que compartan bases lingüísticas similares, o que pueden de alguna manera ser calibradas".
Linguistics as an Exact Science: Dice que los grandes cambios producidos en la ciencia desde el 1890 no son por nuevos hechos sino por nuevas maneras de pensar sobre los hechos. Realza la importancia del habla sobre el pensamiento con la frase "Las bestias pueden pensar, pero no hablan. Hablar tiene que ser una palabra más noble y digna que Pensar". Según Leonard Bloomfield, la investigación científica empieza con un conjunto de frases que apuntan a ciertas observaciones y experimentos, cuyos resultados no se transforman en científicos hasta que han sido convertidos en lenguaje.
Languages and Logic: Este fue muy interesante. Dice por ejemplo, si se pregunta a alguien la similitudes entre la frase en Inglés "I pull the branch aside" y "I have an extra toe on my foot", seguramente encontrará pocas, o solo que son del mismo tiempo verbal, o sea, presente. Pero en el idioma Shawnee se pronuncia "ni-l'oawa-'ko-n-a" y "hi-l'oawa-'ko-oite", o sea que son frases muy similares!. Su pregunta es ¿cómo puede ser que el mismo concepto o evento en un idioma no tenga ninguna similitud gramatical y en otro sean casi lo mismo?. Transcribo una frase que me pareció muy interesante: "... the English sentences, "The boat is gounded on the beach" and "The boat is manned by picked men", seem to us to be rather similar. Each is about a boat; each tells the relation of the boat to other objects... The linguist would point out the paralelism in grammatical pattern thus: "The boat is xed preposition y". The logician might turn the linguist's analysis into "A is in the state x in relation to y" and then perhaps into fA = xRy. Such symbolic methods lead to fruitful theniques or rational ordering, stimulate our thinking, and bring valuable insight. Yet we should realize that the similarities and contrasts in the original sentences, subsumed under the foregoing forumula, are dependent on the choice of mother tongue and that the properties ot the tongue are eventually reflected as peculiarities of structure in the fabric of logic or mathematics wich we rear"... pero de las misma fraces en Nootka no tienen sujeto y predicado! y por lo tanto no se pueden inferir el mismo tipo de relaciones. Termina dándole con un palo a los Griegos y en especial a Aristóteles cuando se metieron en Lingüística por que según él mostraron un camino equivocado, que los hindúes estaban más avanzados...
En fin, es mucho más interesante leerlo de él que estos pobres resúmenes, pero espero sirva para darles una idea de su manera de pensar.
La semana que viene vuelvo a los objetos!!!

martes, 14 de agosto de 2007

Scripting en Smalltalk

Les paso un link a un Smalltalk para hacer scripting... es muy nuevo pero parece interesante.

http://code.google.com/p/syx/

viernes, 10 de agosto de 2007

GemStone

Recién vi un comentario de Martín (desconozco su apellido) que me preguntaba como se comparaba GemStone con Erlang en escalabilidad. Se lo conteste como comentario pero me parece que no a todos les llegan estos tipos de updates sobre los blogs... me queda la duda.
Así que si tienen ganas lean el comentario sobre el post anterior donde puse un link interesante a algo que está haciendo GemStone y que copio acá: http://www.infoq.com/news/2007/08/gemstone-ruby

Ralph Jonhson on Erlang y OO

Ralph Johnson es profesor de Objetos en la Universidad de Illinois y uno de los principales organizadores de OOPSLA, es más, es uno de las 5 personas que siempre participo de OOPSLA desde que esta conferencia comenzó (las otras son Brian Foote, Rebecca Wirfs-Brock, Allen Wirfs-Brook -el esposo- y Edwared Gehringer).

Ralph acaba de escribir un comentario interesante sobre ErLang, un lenguaje que le están dando mucha manija ahora. Una de las cosas que hace es clasificar la gente de OO en tres categorías, la escandinava, la mistica y la de ingeniería de software... según esta clasificación me da gusto ser un ingeniero de software escandinavo y místico!!! jaja (no veo porque las tres categorías que hacen deberían ser disjuntas...)

jueves, 9 de agosto de 2007

Relativismo linguistico II

Cuando hoy por la mañana llevaba a mis hijas al colegio, una de ellas me comentó que "cuando saltan a la soga, empiezan contando los saltos alto pero terminan contando bajo", una frase completamente entendible en castellano.
Ahora, lo que es el inconciente, porque en ese momento me vino a la cabeza lo que estaba leyendo en el capítulo más interesante hasta ahora del libro de Whorf que comenté en el otro post que se llama "The relation of habitual thought and behavior to language", donde entre otras cosas pone como tesis que en los lenguajes Indo-Europeos (castellano, inglés, latín, etc.) "objetivisamos" elementos que no son tanguibles. Según lo que entendí, se refiere a que entre otras cosas, utilizamos adjetivos "de cosas" para sustantivos que no se refieren a cosas. Eso es lo que hizo mi hija al decir que "cuentan alto" o "cuentan bajo"...
Los adjetivos "alto" y "bajo" son utilizados generalmente como medida de "altura" de una cosa, sin embargo en este caso mi hija lo uso para referirse al tono de voz con que contaban cuantos saltos daban, al "volumen" de voz que usaban. Y es muy común en castellano decir "el volumen está alto", cuando en realidad nos referimos a una unidad de medición que nada tiene que ver con la altura.
En el capítulo que comenté hay muchos más y mejores ejemplos de esta "objetivasión" que tenemos en los idiomas indo-europeos y que no existen en otros idiomas, como el Hopi o el Maya. Esta objetivación, según Whorf, nos afecta en la manera de entender el tiempo al cual lo terminamos viendo (otra objetivasion más! el tiempo no se ve!) como "points on a line"!!. Cuando leí esa frase no lo podía creer!, es la metáfora que usamos para desarrollar el modelo de Tiempo que llamamos Chalten (y es open source by the way). En Hopi esto no sucede y otra diferencia interesante es que no poseen tres tiempos verbales como nosotros, sino dos.
Otro tema que Whorf se encarga de destacar es que no se pueden usar las mismas construcciones sintácticas y semánticas de los idiomas indo-europeos en otros idiomas. No todo lo que nosotros entendemos por "verbo" o "adverbio" es aplicable a Hopi o Maya, tienen construcciones distintas!
Según Whorf, debido a que "objetivamos" el tiempo, es que pensamos en "puntos" del pasado y del futuro y debido a ello mantenemos registro de lo que pasó o manejamos agendas sobre lo que puede pasar en esos "puntos" del futuro, entre otras cosas.
La verdad, un capítulo muy interesante. Por lo menos me hizo ver como "objetivisamos" en nuestro idioma las cosas y porque el paradigma de objetos es el mejor para desarrollar.... porque objetivisamos todo!! jaja

miércoles, 8 de agosto de 2007

El último de la terna... de Steve Ballmer

Prometo no ensuciar más el blog con estos videos!!! (jaja, veré cuanto resisto!!)
http://www.youtube.com/watch?v=bhUAr-P_39U
Es bueno tener gente que defienda a los desarrolladores como este tipo! jaja.

Fin de Cuatrimestre en POO

En realidad el cuatrimestre terminó hace algunas semanas... tenía ganas de ir posteando lo que fui aprendiendo/notando de interesante durante este cuatrimestre pero no tuve tiempo, así que voy a hacer un resumen de lo que me acuerdo y me parece importante. Por ejemplo, hay ciertos errores que me cuesta mucho que los alumnos dejen de cometer, ellos son:
1) No poner el tipo de los colaboradores (parámetros) en los nombre de mensajes. Por ejemplo, crean mensajes como "alterContect: aContext withEvent: anEvent" en vez de "alter: aContect with: anEvent". Esto se debe, me da la impresión, a la experiencia que tienen en utilizar lenguajes con sintaxis tipo C donde es necesario escribirlos así puesto que los nombres de los "parámetros" están completamente desasociados del nombre del mensaje. Por ejemplo, en Java escribirían "alterContextWithEvent (aContext, anEvent)".
2) Les cuesta muchísimo no romper el encapsulamiento de los objetos. Es muy común que a un objeto le pidan un colaborador interno y luego interactúen con el en vez de delegarle esa responsabilidad al objeto que posee dicho colaborador. Por ejemplo al objeto "aGameContext" le piden su "history" y luego a dicho objeto le envían el mensaje "add: anEvent" en vez de que directamente el objeto colabore con "aGameContext" de la siguiente manera: "aGameContext addToHistory: anEvent" y sea "aGameContext" quién termine colaborando con "history". Esto se debe, en mi opinión, a que aún siguen viendo a los objetos como estructuras de datos
3) Otro problema que veo constantemente es que escriben métodos enormes por más que insistimos en no hacerlo y en utilizar el refactoring "extract method".

Sin embargo también aprendí algunas cosas interesantes este cuatrimestre. Uno que me acuerdo tiene que ver con modificar "Interval" para que trabaje con "Medidas" y no solo con números. El problema tenía que ver con que en algún lugar Interval hace "(stop - start) / by = 0" y esto no es factible resolverlo si no se implementó el "polimorphic zero". Por lo tanto la solución que propuso un grupo fue hacer "(stop - start) / by + start = start"... muy piola.