sábado, 28 de julio de 2007

Como incentivar a los desarrolladores de software

Les dejo otra perlita de Steve Ballmer... (mientras me tomo vacaciones esta semana)
http://www.youtube.com/watch?v=wvsboPUjrGc

jueves, 26 de julio de 2007

¿Cómo asegurar la evolución de un sistema?

A medida que se adquiere más experiencia desarrollando sistemas, uno se va dando cuenta lo importante que es la evolución de los mismos y por lo tanto asegurarse de no cometer errores cuando estos evolucionan.
La mejor manera que he encontrado para resolver este problema es, como siempre, delegarle esta responsabilidad a los objetos que forman parte del sistema. Siempre es mejor que la computadora trabaje mucho a que nosotros tengamos que hacerlo puesto que a ella no le cuesta, no protesta por hacerlo y no se equivoca!
Veamos un ejemplo donde es necesario adaptar objetos de una jerarquía de clases de una manera especial. En particular, supongamos que tenemos reportes en nuestro sistema donde hay que mostrar información de eventos (que están en una jerarquía) de una manera muy particular, porque el usuario así lo requiere (siempre caprichosos estos tipos!!).
Una manera de solucionar este problema es adaptar cada evento a lo que el usuario desea ver, de tal manera que cada evento puede generar una o más líneas en el reporte y con información que en principio no es polimórfica entre los eventos. Por lo tanto, se genera una jerarquía de objetos que adaptarán los eventos que están en otra jerarquía. El problema es que ahora los objetos deben decidir que adapter corresponde a cada evento. Una solución es la siguiente: (Los nombres están puestos solo para el ejemplo, no son buenos nombres para un modelo real):
Jerarquía de Eventos:
Event
..EventA
..EventB
..EventC
..EventD

Jerarquía de Adapters de eventos para las líneas del reporte:
ReportXLineAdapter
..LineAdapter1
..LineAdapter2

Luego, cuando hay que crear una adapter se le pide a ReportXLineAdapter el adapter que handlea el evento que se quiere adaptar, enviándole el mensaje #adapterFor: que hace lo siguiente:
ReportXLineAdapter class>>adaptersFor: anEvent

^self allSubclasses select: [ :anAdapterClass | anAdapterClass canHandle: anEvent ].

LineAdapter1 class>>canHandle: anEvent

^anEvent class = EventA

LineAdapter2 class>>canHandle: anEvent

^anEvent class = EventB or: [ anEvent class = EventC ]

Si estuviéramos usando el paradigma estructurado, ¿cómo se haría? ¿Qué están reemplazando estos objetos cuando colaboran entre sí?. Están reemplazando la estructura sintáctica de control de flujo conocida como "case" o "switch". Pero el problema que tiene esta implementación es que no tiene implementado el famoso "default" de dicha estructura de control de flujo.
Este problema se hace perceptible al evolucionar el sistema. Si se agrega un nuevo evento (lo podría hacer cualquier programador) seguramente no sabrá que hay que adaptar ese evento para el reporte, es más, quizá este programador no tenga ni idea que ese reporte fue hecho de esta manera.
Los tecnócratas metodologos dirían que con un buen proceso esto se debe resolver... que si se agrega un nuevo evento hay que utilizar la matriz de trazabilidad que nos indicará qué hay que modificar para que ese reporte funcione, etc. etc. etc., o sea, se nota que nunca hicieron un sistema porque tener un proceso que haga eso es carísimo (sí, CMM cuesta), imposible de mantener y además el programador debe mantener esa matriz de trazabilidad en primera instancia. Pero si puede mantener esa matriz de trazabilidad ¿por qué no modifica el código y listo? ¿para qué dar tanta vuelta?, en fin, un absurdo total.
Lo mejor es hacer que los objetos se "protegan" a sí mismos de la evolución. Por lo tanto, se debería modificar #adaptersFor: para que sepa que eventos él sabe que no serán handleados y si no hay adapter para un evento que debe ser handleado, significa algo anduvo mal.
La modificación sería así:
ReportXLineAdapter class>>adaptersFor: anEvent

| adapters |

adapters := self allSubclasses select: [ :anAdapterClass | anAdapterClass canHandle: anEvent ].
(adapters isEmpty and: [ (notHandledEvents includes: anEvent class) not ]) ifTrue: [
self error: 'Decidir como administrar el event ', anEvent class name ].

^adapters

Por otro lado, se escribiría un test que justamente verificaría que esta decisión de diseño tomada para solucionar este problema se la respete mientras evoluciona el sistema. Para ello se puede utilizar el framework de SUnit, es más, nosotros lo utilizamos para crear varios tipos de tests, no solo unitarios; lo usamos para test de estándares de código, de calidad de código, para test de user story y de arquitectura. No tenemos una categoría para este tipo de test, seguramente caería dentro de los test unitarios del reporte, pero por ahí sería bueno crear una categoría para controlar estas decisiones de micro-arquitectura. En fin, el test sería algo así:

testAllEventsAreHandledByReportXAdapter

| eventClasses |

"Me quedo con los eventos concretos"
eventClasses := Event allSuclasses select: [ :aClass | aClass subclasses notEmpty ] .
eventClasses do: [ :aClass | self shouldnt: [ ReportXLineAdapter xxx: aClass new ] raise: Error ]

De esta manera si se crea un evento y la jerarquía de adapters no se modifica para tenerlo en cuenta, este test fallará y nos enteraremos antes de enviárselo al usuario, que bueno no!. Fijensé que este tipo de problemas no lo soluciona ningún sistema de tipos estático, porque no tiene que ver con tipos sino con minas... eh, lapsus momentaneous, con decisiones de diseño de más alto nivel.
Moraleja: Siempre escribir código pensando en la evolución del sistema, o sea, los objetos deben prevenirse ellos mismos de la evolución y el cambio de sus compañeros. En este caso lo hicimos de dos maneras: la primera constructiva (no se me ocurre mejor nombre) y la segunda preventiva.
La constructiva la logramos modificando el mensaje #adaptersFor: para que construya correctamente los adapters para el evento, en caso contrario genera una excepción.
La preventiva la logramos escribiendo el test que verifica que se cumpla con la decisión de diseño de utilizar este idiom para resolver este problema. Esta opción solo se puede hacer con lenguajes de objetos que permitan trabajar sobre el metamodelo como Smalltalk. Esta opción no se puede realizar con lenguajes que no permitan trabajar sobre el metamodelo o lo hagan de manera limitada (como Java o .Net. ¿Cómo se obtienen la subclasses de una clase en Java o .Net?)

No tuve tiempo de pensar en otra solución para este problema, pero hay algo que me dice que debe haber una mejor opción... En fin, el ejemplo es válido de cualquier manera para pensar en la evolución de un sistema y como asegurarse de no introducir errores.

Asi se vende el software!!

Aprendamos a vender software!!! Miren como se hace:
http://www.youtube.com/watch?v=GL4hyATkQ74

viernes, 20 de julio de 2007

Relativismo linguistico

La semana pasada terminé de leer el libro Lingüistica para Todos. Es un libro sencillo y con ilustraciones que lo hacen muy fácil y rápido de leer. Vale la pena para tener un vistazo general del tema.
Una de los temas que me hizo pensar este libro fue la diferencia sobre el medio de comunicación de un idioma y un lenguaje de programación. Un idioma tiene medios de comunicación primarios como el habla y secundarios como la escritura. Es interesante ver que los lenguajes de programación solo se comunican a travez de medios secundarios y nunca primarios... no se si ustedes, pero nunca vi hablar a una persona en Smalltalk o Java!, jaja. Quiza cuando lo podamos hacer habremos logrado tener un lenguaje de programación tan bueno como el lenguaje natural.
Hay otros puntos interesantes que nombra este libro que se ven más en detalle en otro libro que estoy leyendo ahora: Language, Thought, and Reality de Benjamin Lee Whorf
El idioma que más utiliza Whorf para demostrar su tesis de "Relativismo Lingüistico" es el Hopi
utilizado por indios americanos que residían en el noreste de lo que ahora es el estado de Arizona de Estados Unidos.
Lo que más me sorprendió hasta ahora de este idioma es la diferencia con la idea de espacio-tiempo que tenemos las culturas occidentales. Nosotros manejamos el espacio y el tiempo como entes separados y no relacionados directamente, mientras que ellos lo manejan como un solo elemento altamente relacionado. Por lo tanto, la noción de "pasado" no solo está relacionado con hace cuanto pasó algo, sino que tan lejos pasó!. Si dos eventos suceden simultaneamente, pero uno sucede "más lejos" que el otro, el que sucedió más lejos para los Hopi podríamos decir que para lo que nosotros entendemos o podemos traducir, perdiendo mucho contenido, sucedió antes!
Otro punto totalmente distinto es como interpretan los Hopi el paso del tiempo. No recuerdo los detalles pero según lo que comenta Whorf está más de acuerdo con el relativismo de Einstein que los lenguajes Occidentales.
En fin, hay un sin fin de casos que ahora no recuerdo con exactitud pero que me hacen pensar cuanto nos estamos perdiendo de entender la realidad por la limitaciones lingüisticas que tenemos por los idiomas que utilizamos, todos ellos con la misma raíz indo-europea. O sea, si lengua y pensamiento van de la mano, aquello que no podemos decir no lo podemos pensar y justamente solo algunos pocos son capaces de "pensar" aquello para lo cual aún no tenemos palabras.
La tesis de Sapir-Whorf no está completamente aceptada por la comunidad científica, pero en definitiva dice que el idioma que usamos influencian nuestra capacidad de pensamiento.
Si aceptamos esta tesis y llevándola al marco del desarrollo de software, ¿afecta el lenguaje de programación como pensamos las soluciones a los problemas que tenemos que resolver? ¿afecta el lenguaje de programación a cómo entendemos la realidad que modelamos?. Yo creo que sí, aunque no solo el lenguaje influye, hay otros temas más, pero definitivamente las soluciones de un javero no son las mismas que un smalltalkero.
Esto no quiere decir que un lenguaje es mejor que otro, simplemente que con algunos podemos ver más fácilmente cosas que con otros...
La lingüistica es muy interesante, cada vez más la relacionan con entender como pensamos, aprendemos, etc. por lo tanto es muy importante que tengamos conocimientos sobre estos temas para poder desarrollar mejor software

Paso V: ¿Conoce usted qué significa desarrollar con Objetos?

Antes de proseguir, quiero dejar claro que en este paradigma si un objeto no tiene cierta responsabilidad es porque la tiene otro, no existe alternativa. Por lo tanto, si le saco la responsabilidad a las llamadas de calcular su costo, se la tengo que dar a otro objeto, no hay alternativa. La pregunta en cuestión es ¿cuál es ese objeto? Nuevamente debemos recurrir a una analogía. Pensemos por un momento como sería una oficina encargada de facturar llamadas telefónicas, que no posea ningún tipo de computadora para automatizar el procedimiento. ¿Qué personas compondrían esta oficina?, ¿cuáles serían sus responsabilidades? Seguramente habría una persona encargada tipificar una llamada para luego determinar su costo en base a una tabla de precios. Estos son los objetos que necesitamos en nuestro modelo, uno para representar a la persona que tipifica y factura (¿o quizá dos?) y otro para representar la tabla de precios.

El primero puede ser instancia de la clase “Facturador”, sabrá responder cuál es el costo de una llamada dada una tabla de precios. La tabla de precios será otro objeto que indicará el costo por pulso de cada tipo de llamada. Juntos, nos permitirán obtener el costo de una llamada y por lo tanto de todas.

Unos párrafos atrás comenté que la solución no estaba “a la vista”. Esto se debe a que los objetos que necesitábamos ni siquiera estaban mencionados en el enunciado del problema; encontrarlos requiere cierto poder de análisis, capacidad de abstracción para crear analogías y porque no, un pizca de imaginación (¿será por esto que algunos ver la programación como un arte?). Lo interesante de este caso es que ustedes pueden, a partir de ahora, hacer analogías similares para resolver los problemas que se les presentan, utilizar la misma técnica y ver que resultados obtienen. Les aseguro que serán muy positivos.

Sin embargo suena un poco raro que un facturador solo responda el costo de una llamada, su nombre parece indicar otra cosa. En realidad lo que estoy esperando de dicha persona (objeto) es una factura para un cliente. Nuevamente nos encontramos con un caso interesante de este problema, empezamos a hablar de factura algo que no había surgido hasta ahora pero que se comenta en el enunciado. Recuerden la máxima de este paradigma: “Todo ente de la realidad que se está modelando debe tener un objeto que lo represente”; por lo tanto si existe una factura en la realidad, una factura debemos tener en nuestro modelo, o sea, en nuestro programa. ¿Se les había ocurrido tener un objeto para representar la factura? De todas las entrevistas que tomé con este problema, nadie, pero nadie modelo una factura.

¿Por qué es importante tener una factura? La respuesta trivial es: ¡porque existe en la realidad! Pero si ustedes son pragmatistas y quieren una respuesta útil, verán que existen varios motivos, como tener que reimprimir una factura sin volver a calcularla, querer consultar cuanto se facturó (sumar todas las facturas), etc. Aún más interesante es que la factura me permita determinar los motivos por los cuales se llegó a los valores que posee, por lo tanto la factura no solo tendría los ítems facturados sino también podría, de alguna manera, permitirnos llegar a entender por qué se calcularon (importante al momento atender reclamos de los clientes, ¿no lo creen así?). En definitiva, necesitamos también una clase factura que será luego la que por pedido del ejercicio, se deberá imprimir en pantalla.

¿Qué sucede con la clase Cliente de la solución original?, ¿es una buena abstracción? Nuevamente debo responder que no. Posee varios errores respecto al ente que debería representar de la realidad. ¿Un cliente en la realidad, conoce todas las llamadas que hizo?, ¿ustedes como usuarios de teléfono, se acuerdan?, claramente no. No es la responsabilidad de un cliente conocer todas las llamadas que hizo. Menos aún porque si fuese así podría mentirle a la telefónica para tener que pagar menos, ¿no les parece? Este último argumento es válido para demostrar que un cliente ¡no puede tener la responsabilidad de imprimir su factura!, ¡sería una locura! Fíjense como el problema que estamos viendo en este caso no tiene que ver con cuestiones computacionales, no es un problema de performance, acoplamiento, encapsulamiento de datos, etc., es un problema de interpretación de la realidad, es un problema semántico, relacionado al significado que le otorgamos a los objetos, al lenguaje que construimos con nuestro modelo. Este último punto es el que marca la diferencia entre saber programar y saber programar con objetos. La diferencia radica en que programar con objetos implica entender el problema que estamos modelando, entender su significado y entender que por cada ente de este problema debe haber un objeto en nuestro modelo. No puede suceder que exista un objeto en nuestro modelo que represente un cliente y que dicho objeto no tenga nada que ver con un cliente real. ¿Se imaginan la reacción de un experto del dominio si le decimos que el encargado de facturar en nuestro programa es un cliente? No se deben crear objetos por cuestiones computacionales o de conveniencia organizativa, se deben crear si existen en la realidad que estamos modelando.

Ya estamos llegando al final, no se desanime. La verdad es que se necesitan unos cuantos objetos más, como el número de teléfono, alguien que se encargue de tipificar si una llamada es local, nacional o internacional a partir del número de origen y destino, etc., pero creo que a esta altura el objetivo del artículo está cumplido, que usted pueda responderse con sinceridad, sin necedad, si sabe o no desarrollar con objetos correctamente. ¡No se culpe sino es así!, es totalmente comprensible puesto que hay muy pocos buenos libros del tema, los cursos que abordan este problema generalmente lo hacen desde una perspectiva tecnológica con el objetivo de enseñar un lenguaje de programación, y sobre todo, es necesario cambiar la manera de pensar respecto de qué es un programa y cómo se debe crear un programa cuando se trabaja con objetos.

Lo importante es que usted recuerde por lo menos estas frases y trate de aplicarlas:

  • Un programa es un modelo de un problema de la realidad
  • Cuando se utiliza el paradigma de objetos para crear dicho modelo, se cuenta únicamente con objetos que representan entes de la realidad y mensajes que se envían entre sí para llevar adelante una tarea.
  • Es muy importante descubrir qué objetos existen en la realidad y otorgar correctamente las responsabilidades a estos. Hacerlo permite reutilizar una solución.
  • No todos los objetos están a la vista, hay que usar la imaginación para descubrir aquellos que existen implícitamente en la realidad. En otras palabras, hacer explicito lo implícito.
  • Muchos de los objetos que no están a la vista, viven en la ambigüedad del lenguaje natural que usamos los seres humanos para comunicarnos y el la información contextual de una conversación. Es muy importante entrenar nuestra capacidad para encontrar estos objetos.
  • La herencia o subclasificación es una herramienta poderosa pero pésimamente usada; en algunos casos dificulta el reuso.

Espero que hayan disfrutado de este artículo, por lo menos yo lo hice escribiéndolo. Si alguna vez quieren ver la solución completa del ejercicio vayan a la materia de Programación Orientada a Objetos que damos en la Facultad de Ciencias Exactas de la UBA o preguntenme!! jaja.

jueves, 19 de julio de 2007

Paso IV: ¿Conoce usted qué significa desarrollar con Objetos?

Debemos resolver ahora el algoritmo de cálculo del costo. Tenemos una sola clase, Llamada, para “codificar” ese algoritmo, pero al hacerlo ¡la llamada tendrá que determinar si es local, nacional o internacional!, ¡estamos más o menos en lo mismo! Humm, ¿será momento de empezar a cuestionarnos todo lo que creemos y buscar alternativas distintas?, ¿será el momento de utilizar el pensamiento lateral?

Si haber hecho este cambio de diseño no mejoró el cálculo del costo, puede significar que: 1) el cambio no es bueno 2) el cambio es correcto pero no hay una mejor manera de calcularlo o 3) no es correcto asignarle la responsabilidad de calcular el costo a la llamada.

De las tres opciones, la más revolucionaria es la 3, y es justamente la correcta. Usted dirá “¿cómo es posible que una llamada no sepa responder su costo?, ¿para qué existe entonces?, ¿quién se encarga de calcularlo?”. La realidad es que una llamada no tiene por qué saber responder su costo.

Pensemos por un momento que podemos hablar con una llamada, que le podemos hacer preguntas y que ella nos responde, ¿qué pasaría si le preguntaría su costo?, ¿qué respondería? La verdad es que no respondería nada porque ¡ella sola no puede saberlo! Para determinar el costo tiene que conocer la tabla de costos, que tipo de llamada es, etc., y nada de esto es esencial a una llamada. Una llamada es un ente que nos podrá responder el número de teléfono origen y destino, la hora en que se realizó, cuanto duró, pero no mucho más.

Un ejemplo que ayuda a entender este concepto es el siguiente: supongamos que estamos en una casa de alquiler de videos y necesitamos saber cuanto cuesta alquilar uno de ellos, ¿cómo resolvemos este problema?, ¿le preguntamos al video cuanto cuesta? Indudablemente no, si lo hiciéramos las personas de alrededor dudarían de nuestra cordura, y con razón. Lo que haríamos sería preguntarle a la persona que atiende la tienda el costo del mismo y él seguramente utilizaría una lista de precios (o se acordaría de ella, que en definitiva es usarla) para respondernos. Lo mismo sucede con la llamada, el problema es análogo y por lo tanto, también la solución. Una llamada nunca podrá responder cuál es su costo, para eso están otros objetos que luego analizaremos, aunque imagino que los irán intuyendo.

Si aún no los convencí con estos argumentos, les doy otro motivo por el cual no es correcto tener tres clases por cada tipo de llamada. Hacerlo implica que al momento de crear la llamada, alguien tiene que decidir de qué clase es. Esto implica que una vez decidida su clase, la llamada no puede cambiar de categoría puesto que un objeto no puede cambiar de clase (esto no es completamente verdad, pero hacerlo implicaría trabajar a un meta nivel distinto). Sin embargo pueden existir momentos donde no se sepa el tipo de una llamada o que una llamada haya sido mal tipificada.

Este error es muy difícil de verlo, más aún porque estamos acostumbrados a escribir código de prueba de la siguiente manera:


llamada1 = new LlamadaLocal (…);
llamada2 = new LlamadaNacional (…);
llamada3 = new LlamadaInternacional (…);
//etc

Es difícil de verlo porque sucede que en este caso es usted, el programador, quien está decidiendo el tipo de la llamada, a que clase pertenece, y el problema es justamente que usted lo decida. En este paradigma los objetos deben decidir qué hacer, no usted el programador. Y esta regla vale realmente para cualquier paradigma de programación que utilice. Uno de los objetivos que tenemos que buscar como programadores, es que la computadora haga más cosas y nosotros, los seres humanos, menos, simplemente porque la computadora “no se equivoca”. Por lo tanto, tenemos que lograr que sean los mismos objetos los que decidan que tipo de llamada es una llamada.

Utilizando el ejemplo de los videos se entiende mejor. Un video podría ser un estreno o un clásico. Esto se determina a partir de que tan nuevo o viejo es, por lo tanto el costo de alquilar uno u otro depende de una característica temporal. Si para resolver esta categorización se crearan las clases “VideoDeEstreno” y “VideoClasico”, no se podría cambiar la categoría de un video, de estreno a clásico por ejemplo, puesto que se debería cambiar la clase del video. Más simple es tener un conjunto de videos de estreno y otro conjunto con videos clásicos y por lo tanto el cambio de categoría de un video es simplemente sacarlo de un conjunto y agregarlo en otro.

Repasemos. De las cuatro clases que teníamos en la solución original para representar las llamadas, bajamos a una y le sacamos la responsabilidad de calcular el costo. Ahora tenemos que decidir a quién se la damos. Este es uno de los puntos que más me gusta de este problema, porque la solución no está “a la vista”.

Ya se hizo muy largo este post... mañana seguiré

miércoles, 18 de julio de 2007

Parte III: ¿Conoce usted qué significa desarrollar con Objetos?

Analicemos ahora por qué no es una buena solución. La verdad es que hay varios motivos. El primer error grave es crear una clase por cada uno de los tipos de llamadas. ¿Por qué?, porque hacerlo implica realizar una categorización sobre las llamadas que no es “esencial” a la llamada en si, sino accidental a la tipificación utilizada para facturar su costo. Crear estos conjuntos de clases implica asumir que las llamadas en todo contexto, deben saber responder su costo, cuando quizá no sea importante para otro problema. Usted seguramente dirá: “el ejercicio habla sobre obtener el costo de las llamadas, ¿por qué debería preocuparme sobre como serán utilizadas las llamadas en otro problema o contexto?”. Justamente una de las características de trabajar correctamente con objetos es saber interpretar la esencia de los mismos, para que luego puedan ser reutilizados en distintos contextos. Es por este motivo que trabajar con objetos correctamente permite “reutilizar código”, esa famosa característica que se vendió del paradigma y que fue pésimamente relacionada con la “herencia”. Acá estamos viendo un ejemplo claro donde la “herencia” va en contra del reuso, puesto que esta solución no serviría para un sistema de marketing de llamadas, para nombrar un caso.

¿Qué es, entonces, la esencia de un objeto? Responder esta pregunta es meternos en un tema más bien filosófico que computacional, pero créanme que programar tiene mucho más de filosófico de lo que creemos. Para dar una respuesta más o menos rápida, debemos entender que un objeto es una “representación” de un “ente” de la realidad. En otras palabras, un objeto en mi programa debe existir solo si existe un “ente” en la realidad que sea representado por dicho objeto. Ustedes dirán, “pero si tenemos llamadas locales, nacionales e internacionales, ¿cuál es el problema en tener esas tres clases?”. El problema es que estamos confundiendo la finalidad de la herramienta denominada “herencia” o “subclasificación” con la categorización de objetos. Este error se repite constantemente porque la subclasificación implica categorización, sin embargo la inversa no es verdad y es muy común ver que los programadores utilicen “herencia” para resolver todo problema de clasificación, como en este caso, donde es necesario clasificar las llamadas en locales, nacionales e internacionales.

Repasemos un poco lo dicho hasta ahora. Vimos que no está bien tener una clase por cada tipo de llamada porque el motivo de esta clasificación no corresponde a las llamadas en sí, sino al problema de facturarlas. Hacerlo impide su reutilización en otros contextos. Veamos ahora un punto de vista distinto. Qué sucedería si tuviera una sola clase, por ejemplo Llamada, y tres conjuntos (no importa si están implementados con un vector, lista, diccionario, etc.), uno para las llamadas locales, otra para las nacionales y otra para las internacionales, ¿no se solucionaría el problema de la clasificación de llamadas? La realidad es que sí, con algunas ventajas adicionales, como poder mover una llamada de un conjunto a otro fácilmente, o que una llamada pueda estar en más de un conjunto a la vez (seguramente un conjunto de otro problema o contexto, como por ejemplo tipificar llamadas por duración). En definitiva una solución que posee menos “acoplamiento” entre la llamada y su tipificación (¿se acuerda de la frase “máxima cohesión, mínimo acoplamiento”?).

Mañana veremos como solucionar el problema del costo con esta nueva solución.

martes, 17 de julio de 2007

Parte II: ¿Conoce usted qué significa desarrollar con Objetos?

Empecemos ahora a analizar una solución del ejercicio que plantee la semana pasada.

Del enunciado se deduce claramente que es necesario representar llamadas locales, nacionales e internacionales. Como todas ellas son llamadas, lo más lógico es usar “herencia” de tal manera que tengamos una clase abstracta a la que denominaremos “Llamada” y tres subclases, una para representar las llamadas locales (LlamadaLocal) otra para las nacionales (LlamadaNacional) y otra para las internacionales (LlamadaInternacional).

A partir de estas tres clases concretas podemos empezar a resolver el ejercicio, otorgando a dichas abstracciones la responsabilidad de devolver su costo. Para ello, está claro que debemos implementar el método “costo” en estas tres clases (seguramente será un método abstracto en Llamada).

Pensemos ahora como sería la implementación de este método. Para que una llamada pueda devolvernos su costo, es necesario que sepa cuánto duró y en que momento se hizo (hora y día). Indudablemente estos deben ser colaboradores (variables de instancia) de las llamadas, de esta manera pueden ser utilizados en la implementación del algoritmo del costo. Como pueden ver, el ejercicio es muy trivial. Una vez resuelto cuales son las abstracciones principales que debemos tener y la funcionalidad más importante (calcular el costo), el resto no posee casi importancia.

Para terminar de resolverlo, es necesario imprimir por pantalla algo que muestre el abono mensual, el costo de las llamadas y por último un total. ¿Dónde podemos ubicar este código? Luego de pensar un poco queda claro que es necesario tener algún objeto que represente a un cliente, el cual contendrá la colección de llamadas realizadas y la implementación del método “imprimirFactura”. Creo que no hay duda que dicho objeto es el lugar más conveniente para implementar esta funcionalidad.

Una posible implementación de algunos métodos sería (utilizando Java)

Class Cliente
{
private Llamada[] llamadas;
public Cliente (Llamada[] llamadas)
{
this.llamadas = llamadas;
}

public void imprimirFacturas( Date fecha)
{
imprimirAbono (fecha);
imprimirCostoDeLlamadas (fecha);
…. //etc.
}
}

Resumiendo, tenemos clases que modelan las llamadas (una para cada tipo) y tenemos la clase Cliente que permite imprimir las facturas y conocer las llamadas que realizó. Se puede observar que las “estructuras de datos” presentadas son sencillas y las relaciones entre ellas son bastante lógicas.

A esta altura del partido, usted se puede estar preguntado: ¿Por qué utilizo este ejercicio como ejemplo de este artículo y en las entrevistas si es tan sencillo? ¿Acaso un ejercicio tan sencillo puede ocasionar problemas? La solución presentada es directa, con estructuras claras, objetos sencillos, ¿no es acaso una buena solución? ¿No es acaso similar a la solución que usted pensó? ¿No es la solución que usted presentaría si estuviese en la entrevista o haciendo el parcial de la facultad?

La verdad es que este ejercicio tan sencillo demuestra rápidamente el conocimiento sobre el paradigma de objetos que posee una persona. Si usted cree que la solución presentada es una buena solución, le pido disculpa por lo que le voy a decir, pero entonces usted no sabe desarrollar con objetos. Siento decírselo de manera tan ruda y directa, pero la verdad es que esta solución (que hace el 90% de las personas) es una pésima solución, está plagada de errores de interpretación y modelado; es una solución que no aprobaría el parcial de la facultad.

En este momento usted puede estar experimentando varios sentimientos. Puede estar sorprendido por lo que digo e interesado en conocer por qué, o puede estar molesto y pensar que esta leyendo burradas. Le pido un poco de paciencia, que trate de seguir leyendo para ver si concuerda o no con mi punto de vista.

Le comento que encaré este artículo pensando en producir una molestia en el orgullo del lector y así lograr que no se olvide fácilmente de él. Otro objetivo es que usted vea un mal ejemplo, puesto que muchas veces es mejor aprender de los errores.

Mañana posteo como sigue esta historia...

lunes, 16 de julio de 2007

Idea de Dan Ingalls

Dan Ingalls mandó un link a este video... Parece que se esta divirtiendo...

http://www.youtube.com/watch?v=Rj4moeKDGY4

viernes, 13 de julio de 2007

¿Conoce usted qué significa desarrollar con Objetos?

Me dí cuenta que escribir en un blog, y además escribir algo serio no es "moco de pavo", hay que tener tiempo. Por eso decidí tomar una artículo que escribí como hace un año y que nunca publiqué, e ir posteandolo de a poco para ver como resulta.
El artículo es sobre un ejercicio que tomamos en Mercap para determinar el nivel técnico de las personas que se presentan para trabajar con nosotros. Espero lo disfruten.
--------

¿Conoce usted realmente qué significa desarrollar con objetos? Esta es una pregunta que he realizado durante mucho tiempo como gerente de desarrollo, a las personas que he entrevistado para trabajar conmigo y también a los alumnos que he tenido/tengo en la facultad. ¿Por qué realizo una pregunta tan básica? La realizo porque luego de tantos años de vida del paradigma de objetos (recuerden que el primer lenguaje de objetos fue Simula ’67, el 67 se debe a que fue releaseado en 1967!), luego de tantos años donde no se pone más en duda si el paradigma objetos es mejor que el estructurado (quizá no tantos porque esta dicotomía se empieza a dilucidar con el advenimiento de Java), aún se ven ejemplos de diseño y código que presentan revistas, libros, etc., donde la respuesta es claramente no, aún se ven lugares donde se “confunde” a la gente sobre el significado de los objetos, aún me sigo desilusionando con los candidatos de mis entrevistas.

Explicar QUE es el paradigma de objetos es una solución para este problema. Sin embargo ya se hizo tantas veces, ya se escribió tanto al respecto (aunque hay mucha basura dando vuelta) que no es mi intención repetirlo en un artículo (además de ser imposible por cuestiones de espacio). Cansado de dar explicaciones teóricas, voy a concentrarme esta vez en un ejercicio práctico, que le permitirá a usted contestar la pregunta presentada como título de este artículo; usted podrá decir “SI, se desarrollar con objetos”, o “NO, aún debo aprender más”.

Vamos por lo tanto a realizar un ejercicio sencillo, que lo utilizo en mis entrevistas y que rápidamente, una vez resuelto por el entrevistado, permite determinar si la persona en cuestión entiende o no de objetos. Este ejercicio es una simplificación de un parcial que varias veces hemos tomado en la materia de “Programación Orientada a Objetos” de la Facultad de Ciencias Exactas de la UBA. Su enunciado es el siguiente:

Realizar un sistema de facturación de llamadas telefónicas teniendo en cuenta los siguientes requerimientos:
1)
La facturación se realiza de manera mensual
2)
La facturación está compuesta por:
a.
Un abono mensual básico
b.
Consumo por llamadas Locales
c.
Consumo por llamadas Nacionales e Internacionales
3)
Las llamadas locales tienen distintos valores según la franja horaria en la que se realizan y el día. Para los días hábiles, de 8 a 20 hrs. el costo es de 0,20 centavos el minuto, mientras en el resto de las horas es de 0,10 centavos el minuto. Los sábados y domingos cuesta 0,10 centavos el minuto
4)
Las llamadas Internacionales tienen un costo distinto según el país al que se llame
5)
Las llamadas Nacionales tienen un costo distinto según la localidad a la que se llame

Consideraciones adicionales:

1)
No es necesario realizar una interfaz de usuario visual.
2)
No es necesario realizar persistencia de los datos (o sea, conexión a base de datos, archivos, etc.). Alcanza con simular los datos creándolos en memoria
3)
Como salida alcanza ver por pantalla como sería una factura (sin preocuparse por darle un formato especial)

De más está decir que este problema es una muy grande simplificación del problema de facturación que tienen las telefónicas, pero alcanza para hacer de este problema uno resoluble en un tiempo considerablemente corto (un par de horas).

Por favor, tómese por lo menos un par de minutos para pensar una solución. Piense qué objetos y clases tendría, cuál sería la responsabilidad de dichos objetos, etc. Nada complicado, solo un par de minutos.

-----------

La semana que viene empiezo a postear la solución! Tiene el fin de semana para pensarlo!! jaja.

viernes, 6 de julio de 2007

Meta Math

Termine de leer el libro Meta Math de Gregory Chaitin
Chaitin es un matemático argentino que terminó estudiando en USA y trabajando en IBM.
Su trabajo se basa en la teoría de la complejidad algoritmica, pero a diferencia utilizar el tiempo como medida de complejidad utiliza el "tamaño" de los programas.
El libro está interesante porque se utiliza su teoría para demostrar el teorema de incompletitud de Gödel y el Halting Problem de Turing aunque se torna un poco pesado todas las "self references" que realiza a su trabajo y persona.
Algunas cosas que aprendí de este libro es la importancia de Leibniz como matemático y filósofo (Leibniz creé el concepto de Monad, tan de moda ahora en lenguajes funcionales como Haskell). Algo que nombra Chaitin es que Leibniz fue más prolifero que Newton y mejor persoan, pero bue, habría que constatarlo un poco mejor.
Algunas conclusiones del libro:
1) Las FAS (Formal Axiomatic System) son un fracazo. No alcanzan para explicar todo, son limitadas, imconpletas o inconsistentes (algo que ya sabiamos por Gödel yTuring)
2) Cree que "Experimentation is the only way to prove that software is correct", algo con lo que estoy de acuerdo.
3) No cree en las demostraciones formales de software, con lo que también concuerdo
4) Que la matemática y la física en definitiva no son tan distintas puesto que la matemática necesita datos empíricos y creatividad, puesto que está demostrado que no se puede hacer matemática formalmente. Siempre algo de "incertidumbre" existe y el ser humano debe tomar una posición.
5) "Even though software is pure mind-sutff, not physical, programmers behave like physicists, not mathematicians, when it comes to debuggin", lo cual concuerda completamente con mi manera de ver el desarrollo de software como una ciencia más empírica que formal
6) Una frase interesante sobre diseño: "In my opinion, what it counts most in design is conceptual integrity, beingfaithful to an idea, not confusing the issue!". Cuanta razón tiene!!! pero que tan difícil es lograrlo en un grupo de desarrolladores!!, por eso es tan importante la cultura de desarrollo de un grupo y no sirve, desde el punto de vista técnico, cambiar la gente que trabaja en los proyectos, cada vez que alguien se va, se pierde "integridad conceptual"

En fin, este libro reatifica algo que venía pensando hace tiempo... hasta la matemática es subjetiva! jaja, una afirmación un poco loca no?