Antipatrones en arquitecturas de varios niveles Enero 25, 2010
Posted by ravelus in Informática.add a comment
Cuando se trabaja con arquitecturas de varios niveles, hay ciertas consideraciones a tener en cuenta que tienen que ver con la consistencia de los datos, el uso de objetos distribuidos y la concurrencia, que de no estudiar apropiadamente pueden dar más de un quebradero de cabeza durante el mantenimiento de la aplicación.
Lo primero a tener en cuenta es la diferencia entre capa y nivel. Habitualmente una aplicación bien diseñada tendrá varias capas con dependencias cuidadosamente definidas. Dichas capas pueden estar ubicadas en un sólo nivel o estar diseminadas en varios niveles. Digamos que una capa es un concepto organizacional de la aplicación, mientras que un nivel denota una separación de componentes que pueden estar (o de hecho están) separados en distintos soportes físicos.
Toda aplicación que interactúa con una base de datos tiene más de una capa, a no ser que la base de datos esté embebida en el propio proceso de la aplicación. Otro ejemplo de arquitectura multinivel sería una aplicación web que involucra a una base de datos, un servidor y un navegador.
Sin embargo, no nos referiremos a estos ejemplos tan simples como arquitecturas multinivel. El primero de los ejemplos es un caso muy simple y común, y no se suele considerar a efectos prácticos un caso multinivel por estar presente casi siempre. El segundo caso plantea una situación también muy común, además a todos los efectos el servidor web y el cliente pueden ser entendidos como un nivel de cliente.
¿A qué casos nos referimos, pues? A casos donde participan servicios web u otro tipo de servicios que se proveen entre distintos módulos. Típicamente, una arquitectura multinivel constará de, como mínimo, un nivel de base de datos, un nivel intermedio que expone un servicio y un nivel de cliente.
Planteados estos conceptos, el primer consejo sobre la distribución de objetos en arquitecturas multinivel viene de la mano de Martin Fowler en su libro “Patterns of Enterprise Application Architecture” (Addison-Wesley, 2002): ¡No distribuyas tus objetos!. Bien; si tomamos esta afirmación como cierta cerramos el artículo y nos vamos a tomar un café.
No, no nos vamos a tomar un café, y no es que vengamos a afirmar que Martin Fowler está equivocado, sino que simplemente hay situaciones donde esta ley debe quedar a un lado. Sí es cierto que es muy recomendable considerar y controlar los objetos que se distribuyen entre los distintos niveles y reducirlos en cantidad y tipología en la medida de lo posible, pero reducirlos a cero lo dejaremos como una utopía que no es viable en muchas situaciones. Vamos, pues, con los antipatrones:
Fuerte acoplamiento
Todos sabemos que el acoplamiento entre subsistemas, paquetes y en general módulos de nuestros sistemas debe ser el mínimo posible y lo llevamos a la práctica, ¿verdad? De acuerdo. Sin embargo, aunque seamos tan buenos arquitectos software hemos de reconocer que mantener un acoplamiento bajo conduce a soluciones complejas y en ocasiones poco eficientes. ¿Por qué introducir una dependencia mediante una interfaz cuando podemos simplemente crear una instancia del objeto de la clase directamente? Sin embargo, los beneficios de un acoplamiento bajo vienen a la larga, cuando nos movemos en los términos de mantenibilidad y extensibilidad.
Cuando trasladamos el problema del acoplamiento a sistemas multinivel, el problema se hace más grande. Imaginemos que tenemos que cambiar un nivel, pongamos el nivel medio. ¿Qué ocurre si el acoplamiento de nuestro sistema afecta al nivel de cliente? Habremos de cambiar todos los clientes… El truco aquí está en identificar grupos de módulos que cambian con una frecuencia similar; es imposible evitar que haya dependencia entre niveles, pero si agrupamos correctamente los niveles, podremos mantener una afectación entre niveles mínima.
Asumir requisitos estáticos
Bueno, ya conocemos que los requisitos son cambiantes, pero tampoco podemos vivir con fobia a los requisitos. ¿Qué es susceptible de cambiar?
Según el autor, hay dos aspectos fundamentales que suelen hacerse mal cuando se considera la estabilidad de los requisitos: pensar que el cliente es fiable e inamovible por un lado; por otro, hacer que el servicio que provee el nivel intermedio asuma que el cliente será implementado utilizando una determinada tecnología.
El primer problema se hace patente cuando, por ejemplo, se validan datos únicamente en el cliente, asumiendo el nivel intermedio que lo que le llega es pescado fresco del día, procesándolo y enviándolo a la base de datos. Esta consideración acarrea más problemas de los que se pueda pensar, más aún cuando el servicio que ofrece el nivel intermedio puede ser utilizado por más clientes de los cuales no teníamos constancia previamente.
En cuanto al segundo caso, sabemos que la tecnología siempre cambia. Protege tu arquitectura para que si el día de mañana el nivel de cliente deja de ser un navegador web para ser una aplicación para teléfono móvil o una aplicación Silverlight no trastoque todo tu sistema.
Concurrencia no manejada
Un error en la concurrencia es el típico problema que sólo se detecta cuando el sistema ya está en producción. De los divertidos. Y si el problema se manifiesta como un fallo con luces de colores y pantallazos con mensajes de error, vamos listos. Lo mejor es cuando estos fallos producen corrupción en los datos y el problema perdura en el tiempo hasta que finalmente es detectado.
La gestión de la concurrencia suele hacerse de forma optimista: en el supuesto caso de que varios clientes tuvieran que acceder a la base de datos simultáneamente, el número de veces que tienen que modificar la misma entidad surgiendo conflictos de consistencia es muy pequeño.
El Entity Framework da un soporte de concurrencia bastante optimista; mediante el seguimiento del valor original del valor a modificar cuando dicho valor es consultado y buscando conflictos antes de realizar un update. El problema en una arquitectura multinivel estriba en que este proceso es transparente siempre y cuando se utilice una misma instancia de ObjectContext desde el cual se consulta el valor hasta que éste es almacenado con SaveChanges. Si se serializan entidades de un nivel a otro lo recomendable es pasar el contexto entre niveles mientras dure la llamada al servicio que provee el nivel intermedio. De esto se desprende que debería crearse un nuevo contexto para cada llamada al servicio.
El problema viene porque mientras se trabaja en este modo “en-desconexión” (desconectado en el sentido de que las entidades están desconectadas de su contexto tras la consulta, enviadas a otra capa y después reconectadas cuando hay que modificar los datos mediante un update) ocurre lo siguiente:
1.- Se consulta la entidad y se serializa al nivel de cliente.
2.- El cliente recibe la entidad, realiza los cambios que tenga que hacer y envía de vuelta la versión modificada a la capa intermedia.
3.- Ya que nadie mantiene una traza de la concurrencia o de las propiedades que han sido modificadas, el servicio vuelve a consultar a la base de datos para conocer el estado actual de la entidad, creando un nuevo contexto para dicha consulta.
Después se comparan los valores de la entidad guardada y la que llega del cliente.
4.- El servicio llama a SaveChanges, que verifica si ha de hacer persistentes los cambios o no.
De este flujo surgen dos problemas fundamentales:
El primero es que cada vez que se modifica una entidad, hay que leer de la base de datos dos veces su valor, con el coste que eso supone.
El segundo y más importante es que el valor “original” utilizado por el Entity Framework para verificar si la entidad ha sido modificada en la base de datos viene de una segunda consulta en lugar de la primera consulta. ¿Qué ocurre si entre los pasos 1.- y 3.- alguien modifica el valor?
El sistema no detectará esta situación, detectará un cambio en el valor leído y tratará de almacenar el nuevo valor.
La solución para ambos problemas pasa por realizar una copia del valor leído en el cliente y enviarlo de vuelta junto con el valor modificado al nivel intermedio. Cuando dicho nivel recibe ambos valores, compara y decide.
Servicios con estado
Del antipatrón anterior se deduce que una buena idea para solucionar algunos problemas de concurrencia es pasar el contexto entre las distintas capas.
Lo que ocurre es que esta solución puede convertirse en una pesadilla rápidamente cuando se traslada a la práctica. Colisiones, manejo de múltiples contextos… Cada vez que se realiza una llamada al servicio, el nivel intermedio debe procesar la llamada, solicitar los recursos necesarios, y después liberar dichos recursos. Si podemos hacer que dicha información se mantenga en el cliente en lugar de en el nivel intermedio, de modo que no haya problemas con el manejo de múltiples sesiones (de múltiples llamadas) el cliente se encarga de liberar los recursos y gestionar sus propias llamadas, y además no se consumen recursos del servidor para dar servicio a un cliente.
Dos capas que pretenden ser tres
Simplificar el proceso: Si creamos un ObjectContext en el nivel de cliente, ejecutamos una consulta para cargar las entidades en dicho contexto, modificamos dichas entidades y después las guardamos mediante SaveChanges (lo que provocará un update), ¿para qué tener el nivel intermedio?
Ten siempre en cuenta la ley de Fowler. La única razón para tener una arquitectura multinivel es porque realmente es necesario: bien porque necesitas más seguridad o la posibilidad de escalar en múltiples servidores, o lo que sea. Si no, no utilices arquitecturas multinivel.
Puedes estar perdiendo todo el tiempo que quieras modelando una arquitectura meticulosamente diseñada, con múltiples niveles, totalmente independientes y todo lo que se te ocurra. Pierdes todo el tiempo creando una infraestructura superior y te das cuenta de que no has dedicado un segundo en trasladar un valor realmente útil a los usuarios.
Céntrate en tus objetivos y considera si es necesario una arquitectura multinivel. En ocasiones una arquitectura de dos niveles es suficiente.
Referencias
“Anti-Patterns To Avoid In N-Tier Applications”
Danny Simmons
MSDN Magazine
Junio 2009
Solución al problema de los filósofos comensales con la biblioteca Asynchronous Agents Enero 13, 2010
Posted by ravelus in Informática.add a comment
La librería Asynchronous Agents de Visual C++ 2010 permite dar solución al problema de los filósofos comensales sin utilizar semáforos, monitores, bloqueos ni nada de esto directamente, sino simplemente utilizando un sistema de paso de mensajes síncronos y asíncronos. Esto facilita mucho la implementación puesto que podemos centrarnos en las abstracciones del problema y despreocuparnos bastante de los problemas de concurrencia y bloqueo mutuo.
Los filósofos comensales
Empecemos repasando el problema de los filósofos comensales. Recordemos que este problema planteado por Djikstra planteaba una mesa a la cual se sienta un determinado número de filósofos que se pasan el tiempo pensando o comiendo, de forma alternativa, de un gran bol de arroz que se encuentra situado en el centro de la mesa. Para ello, los filósofos necesitan tomar un par de palillos, uno que se encuentra a su izquierda y otro a su derecha. El problema es que no hay palillos para todos, de manera que a cada lado de cada filósofo se encuentra solamente un palillo. Hasta que un filósofo no tiene los dos palillos no puede empezar a comer. Cuando un filósofo desea pensar, deja cada palillo a cada lado, y deja de comer. El problema del bloqueo mutuo en este planteamiento viene si todos los filósofos cogen uno de los palillos, puesto que jamás podrán coger el otro palillo, que está ocupado por otro filósofo, produciéndose una espera indefinida. ¿Cómo coordinamos a los filósofos para que puedan comer y pensar sin problema?
Cómo resolver el problema con Asynchronous Agents
En este problema, los filósofos serían hilos independientes que tratan de acceder a un recurso compartido, representado por los palillos. Cada filósofo solamente tiene constancia de los dos palillos que hay a ambos lados de su plato, siendo el resto invisibles para él.
El problema así puede ser resuelto con cuatro entidades:
- Una clase Chopstick, que representa a cada uno de los palillos situados en la mesa.
- Una clase ChopstickProvider, que se encarga de mantener los palillos en la mesa y asignárselos a los filósofos.
- Una clase Philosopher que es responsable de pensar y comer y es consciente solamente de dos ChopstickProviders.
- Una clase Table que representa a la mesa y que contiene un conjunto de Philosopher, otro de Chopstick y otro de ChopstickProvider.
Es posible que a la hora de la implementación nos demos cuenta de que alguna de estas clases puede ser colapsada en una estructura de datos o algo más sencillo, dependiendo de la funcionalidad que pueda proveer.
Evitaremos de aquí en adelante todo detalle de implementación, limitándonos simplemente a decir cómo podemos utilizar la biblioteca Asynchronous Agents para resolver el problema de los filósofos comensales.
Envío de mensajes
Los ChopstickProviders pueden ser implementados utilizando colas de llamadas:
- Concurrency::send para enviar un mensaje a una cola o buffer de mensajes; se espera un ACK del envío.
- Concurrency::receive para recibir un mensaje; se bloquea hasta que recibe un mensaje.
- Concurrency::asend, como send pero asíncrono.
- Concurrency::try_receive, como receive pero no se bloquea hasta que haya un mensaje en la cola.
Los filósofos
Pueden ser implementados como agentes, derivar de una clase abstracta llamada Concurrency::agent, e implementar el método run(). Durante el ciclo de Comer() y Pensar() se puede realizar una espera aleatoria o cualquier otra cosa.
La sincronización
Viene provista de la llamada Concurrency::join, que puede soportar distintos tipos de fuentes de mensajes o solamente el mismo tipo de fuente; además puede realizar un uso glotón de los mensajes que llegan (consumiéndolos a medida que llegan) o no (esperando a que todos los mensajes requeridos lleguen). Es fácil ver que para esta solución necesitamos utilizar la alternativa de un simple tipo de fuente y no glotona, precisamente para evitar el problema del bloqueo mutuo.
Los detalles de la implementación de esta solución se encuentran en el artículo que se referencia debajo.
Referencias:
“Solving the Dining Philosophers Problem With Asynchronous Agents”
Rick Molloy
MSDN Magazine
June 2009
Moby Dick Enero 5, 2010
Posted by ravelus in Cultura.3 comments
Por fin terminé esta novela de Herman Melville que me ha llevado meses terminar.
Lamentablemente tengo que decir que la excusa de semejante tardanza es lo tremendamente aburrida que me ha parecido. Y es que a veces los clásicos a veces son un tostón, por muy trascendentales que hayan sido después en la historia de la literatura. De este libro lo único que destaca, bajo mi ignorante punto de vista, es su enorme influencia en obras posteriores, destacando sobre todas ellas la de Ernest Hemingway, El viejo y el mar, una obrita que no va de policías y ladrones, códigos y da Vincis, sino de sentimientos, anhelos y metas que uno se impone en la vida; cómo se lucha por llegar a dichas metas, y cómo a veces todo se acaba yendo a la mierda.
Comencé precisamente a leer esta obra por el enorme poder que ha tenido sobre obras posteriores, el omnipresente personaje de Ahab (¿quizá un juego de palabras con el rey Acab, uno de los muchos pérfidos reyes hebreos que surgieron tras la época dorada del rey David y su hijo, Salomón?) como el del viejo chiflado, pero un chiflado peligroso, pues arrastra al resto de la tripulación del Pequod a su trágico destino, del cual un marinero poco más que grumete, Ismael, reflejo del propio autor, se salva. La intención es buena, pero el libro se convierte en un tostón de casi quinientas páginas y cientoveinticinco capítulos en los cuales se explica las diferencias entre la ballena y el cachalote, su caza, su fisonomía, su comportamiento y hasta la forma que tiene el chorro de agua que emite de su válvula dorsal; encuentros con otros barcos balleneros, tradiciones balleneras en otros países y demás eventos sin la mayor relevancia.
Si algo positivo de ello saco es que he aprendido cómo se cazaban ballenas en la época; probablemente no me vaya nunca a sacar de un apuro, y tampoco te importe un comino, amable lector, pero te vas a joder y te lo voy a contar: resulta que no se lanzaban arponazos desde el propio barco ballenero, como pensaba yo, sino que cuando se avistaba una ballena se bajaban unos botes empujados por un grupo de remeros, liderados por un piloto que dirigía el remo de timón y exhortaba al resto, y uno o dos arponeros. Los arpones, por tanto, se lanzaban desde los propios botes, e iban atados con una cuerda cuyo otro extremo iba a parar al propio bote, de manera que una vez el arpón alcanzaba al animal, éste remolcaba al bote durante unos cuantos cientos de metros hasta que finalmente moría. Tras su muerte el animal flotaba, y la tripulación esperaba al barco ballenero para poder remolcar e izar el enorme bicho al barco, como buenamente podían, para evitar lo que de otro modo sería un festín de tiburones.
Como se puede imaginar, cada fase de la caza de la ballena era tremendamente complicada y arriesgada, desde remar a más no poder para alcanzar al titánido marino, sin olvidar el peligro de remar entre tiburones que solían circundar por tales lares, mordisqueando los remos en ocasiones, como ofrecer una buena posición desde el bote para que el arponero pueda atacar a su presa, sin olvidar los posibles coletazos del animal que en innumerables ocasiones acababa con más de un marinero nadando entre tiburones o con el bote partido en dos. Después había que descuartizarlo en cubierta, con la bestia levantada por una especie de grúa, mientras un marinero experimentado desollaba al animal sujeto en vilo por una cuerda enrollada alrededor de su cuerpo que era sujetada por otro marinero en cubierta, el cual, a su vez tenía la cuerda enrollada a su cintura. Si la polea fallaba, los dos acababan en el mar, desdedonde tendrían seguramente el placer de nadar entre tiburones, siempre presentes ante la sangre y la carroña. De este modo se aseguraban que cada uno hiciera su cometido con la mayor presteza posible y no se le ocurriera hacer el gracioso.
Sin embargo, a pesar de que el hilo argumental es lento a más no poder, hay otra cosa a destacar del libro, aparte de lo que he aprendido sobre el mundo marino: los tres últimos capítulos: “La caza, primer día“, “La caza, segundo día” y “La caza, tercer día“, donde por fin la acción se desencadena cual vehículo de carreras sin frenos, sucediéndose todo el meollo de la novela vertiginosamente, magistralmente, en un cúmulo de descripciones que nos transporta mágicamente al lugar, a sentir lo que los personajes sienten, arrepentimientos, miedos, osadías, valentías, respeto y muerte. Para un lector que desee conocer la obra, realmente no tiene más que leer estos tres últimos capítulos. No conocerá a los personajes, pero creo que no será un inconveniente demasiado importante. Merece la pena realmente leer estos tres últimos capítulos porque vienen a significar, realmente, por qué Moby Dick está entre los grandes clásicos.
Por cierto, una nota para la gente de Edicomunicación S.A.: revisen mínimamente sus publicaciones, hablen con la traductora responsable o con quien sea; es rotundamente inaceptable que un libro de menos de 500 páginas tenga más de 3000 erratas, algunas sin sentido en absoluto. Lamentable.
Monitorizar el uso de memoria de nuestras aplicaciones .NET Diciembre 29, 2009
Posted by ravelus in Informática.add a comment
En aquellas aplicaciones de cierto tamaño, donde el consumo de memoria se convierte en un tema a tener en cuenta, es importante que se considere el consumo de memoria desde las primeras fases de diseño de la aplicación, o las consecuencias después serán peores y más difíciles de arreglar, como veremos a lo largo del presente artículo. Típicamente, las aplicaciones se pueden clasificar en pequeñas (<20Mb e consumo de memoria RAM), medianas entre 20Mb y 50Mb) y grandes (>50Mb). Cuanto mayor es el consumo de memroria de una aplicación, más mportante es tenerlo en cuenta desde las primeras fases de diseño de dicha aplicación.
¿Cómo afecta el uso de memoria al desempeño?
Recordemos que un computador normal tiene al menos 3 niveles de memoria: L1 y L2 que son típicamente cachés on-chip, de baja capacidad y muy rápido acceso, una posible L2 on-board y la propia memoria RAM. Después está el disco, pero si estamos contando con él como sistema de memoria estamos perdidos, pues su velocidad de acceso es 0.000 veces más lento que el de una RAM.
Existen tres tipos de cuellos de botella a tener en cuenta para toda aplicación:
- Rutas de acceso a datos que se utilizan continuamente, y que deberán ser traídos continuamente como operandos a las cachés. Si estos conjuntos de datos son muy grandes, no podrán ser ubicados en las cachés de una forma más o menos permanente y útil para el desempeño.
- Primer arranque de una aplicación. Lo que se conoce como “arranque frío”. El problema de este arranque es que todos los datos del programa deben ser traídos de disco. El desempeño se ve afectado aquí por los datos e instrucciones que utiliza el propio programa, no la memoria que va a utilizar, el montículo o la pila de llamadas, detalles que son gestionados por el sistema operativo.
- Cambios de aplicación. En un entorno típico multitarea donde conviven varias aplicaciones en memoria y se dan los fenómenos de concurrencia y planificador en la CPU, cada vez que se cambia de aplicación por parte del usuario para trabajar en dicha aplicación se produce un robo de memoria entre aplicaciones, lo cual se traduce en un intercambio a disco de las páginas que deben ser desalojadas para que sean utilizadas por la nueva aplicación que tiene el “foco”. Este consumo afecta no solamente a los datos del programa e instrucciones como ocurría en el caso anterior, sino a toda la memoria que utiliza un programa, pues las páginas que se trasladan contienen datos, pila, etc.
¿Qué puedo hacer para mejorar el consumo de memoria de mis aplicaciones?
Existen tres modos en los que se puede pellizcar un poco el uso de memoria. Todos ellos se ven afectados por el diseño de los propios algoritmos, que son los que dictan cómo se accede a memoria.
- Ejecutar menos código al inicio, lo cual favorece el arranque frío.
- Tocar menos datos, lo cual favorece el primer factor que explicamos antes.
- Modificar las estructuras de datos para utilizar otras más eficientes, que también favorece el primer factor.
La mayor parte de las veces lo que se suele hacer es optar por la tercera solución, lo cual implica retocar sensiblemente el código, con los costes que ello conlleva, no sólo de rediseño, y desarrollo sino que también de testing. Es por esto por lo que es importante pensar en la eficiencia de la memoria desde las primeras fases de desarrollo si preveemos un consumo de memoria importante, porque luego pueden venir los problemas.
Monitorizando procesos
Para monitorizar procesos, se puede utilizar el Task Manager como primera aproximación. Esta herramienta nos aportará valiosa información de cada proceso, como PID, memoria compartida y memoria privada.
La memoria compartida de un proceso se refiere generalmente a las instrucciones, que son de sólo lectura. La memoria privada, por otro lado, está constituida principalmente por los datos locales de cada proceso, que son datos de lectura y escritura. Sin embargo, hay un tipo de memoria que no aparece en el Task Manager y que hay que tener en cuenta, precisamente porque es el mayor cuello de botella de todo proceso: el sistema de ficheros. Cada vez que se accede al sistema de ficheros se incurre en una penalización notable en el rendimiento del proceso que en muchos casos es inevitable.
Otra herramienta más avanzada para la monitorización del uso de memoria es VADump, que aporta información hasta el nivel de DLL’s.
En el artículo que se referencia más abajo encontrará más información acerca de esta herramienta.
El recolector de basura de .NET.
El recolector de basura elimina regiones de memoria que no son utilizadas más por un proceso. Existen algunas posibilidades de manipulación del recolector que permite mejorar el consumo de memoria. Para ello hay que entender cómo se organiza dicho recolector en .NET.
Una optimización muy importante es que el GC no busca en todo el montículo de objetos cada vez que es ejecutado, sino que divide el montículo en tres “generaciones”:
- GC0: la más pequeña y rápida, solamente busca candidatos a limpiar entre las nuevas localizaciones ocupadas de memoria tras la última pasada del GC. Idealmente, el tamaño de esta generación es menor que la L2.
- GC1: selecciona las localizaciones que sobrevivieron a la última pasada del GC. Es más lento de ejecutar que la GC0.
- GC2: recorre todos los objetos. La más lenta, por tanto, de hecho el tiempo que tarda en pasar es significativo. Este tiempo que tarda en recorrer todos los objetos crece de una forma más o menos lineal con el tamaño del montículo. El coste real depende de la cantidad de memoria que sigue estando ocupada por el proceso, el número de punteros del GC a dicha memoria y cómo de fragmentado esté el montículo.
Una herramienta que permite monitorizar con precisión el uso del GC es PerfMon. Para más información acerca de dicha herramienta, léase el artículo que se referencia al final.
Referencias:
“Memory Usage Auditing for .NET Applications”
Subramanian Ramaswamy & Vance Morrison
MSDN Magazine
June 2009
Antipatrones: el blob Diciembre 20, 2009
Posted by ravelus in Informática.add a comment
Sigo leyendo e informándome acerca de patrones. El libro que estoy leyendo, cuyo título aparece referenciado al final del artículo, es de los mejores que he leído de informática y gestión de proyectos, estoy encontrando cosas muy interesantes y creo que se le puede sacar bastante partido. Publicaré todos los antipatrones que pueda, al menos los que encuentre más ilustrativos y curiosos.
El Blob (*)
Yo lo llamo el “monstruo” o el “bicho”, porque viene a ser precisamente eso, una cosa gigantesca y poco manejable. Una evidencia de que estamos ante la presencia de un Blob es cuando alguien dice “esta es la clase que representa el núcleo de la arquitectura“. Malo. Desconfiar de ello.
El blob se encuentra en sistemas donde una clase monopoliza el procesamiento y el resto de las clases básicamente se limitan a encapsular datos. El problema es que la mayoría de las responsabilidades están contenidas en una sola clase. Esto provoca que modificar el blob implique modificar otras muchas clases.
Al final, el blob se convierte en una clase procedural disfrazada de orientación a objetos. Puede ser resultado de una localización inapropiada de los requisitos. Suelen aparecer como clases controladores o manejadoras / gestionadoras.
También puede aparecer en prototipos evolutivos, al crecer éstos. No se reparten responsabilidades y un módulo va asumiendo todo. Además, debido al mantenimiento, se va acumulando código útil mezclado con código que ya no sirve para nada. Esto mismo se da en sistemas incrementales, por la misma razón.
Síntomas
- Clases con muchos atributos y/o métodos.
- Dispar colección de atributos / métodos sin aparente relación entre sí (falta de cohesión).
- Una única clase controladora asociada con clases simples de datos.
- Ausencia de diseño orientado al objeto. El blob parece más bien un Main().
- Un diseño legado que no ha sido correctametne refactorizado en una arquitectura orientada a objetos.
Consecuencias
- El blob es típicamente demasiado complejo como para ser reutilizado y probado.
- Puede afectar gravemente al rendimiento al cargar un objeto tan grande en memoria probablemente utilizar un gran número de recursos.
Causas
- Falta de arquitectura orientada al objeto, o de una arquitectura consistente y estable en general.
- Intervención limitada: cambios en el código tienden a añadir sobre lo que ya hay, en lugar de crear nuevas clases y rediseñar / refactorizar.
- Especificación inadecuada: captura de requisitos incompleta o no adecuada.
No es una mala práctica porque…
Es aceptable cuando se adaptan sistemas legados no orientados a objetos. No se requiere refactorizar el código, sino hacerlo más accesible al sistema legado.
Solución refactorizada
Mover código fuera del blob. Identificar y categorizar atributos y operaciones, según la cohesión. Buscar alojamiento para dichos atributos y métodos en clases apropiadas. Eliminar redundancia en las asociaciones que ya no son necesarias y reducir el acoplamiento. Extraer herencia y abstracción allí donde sea necesario. Estudiar las comunicaciones entre clases, paso de parámetros, etc.
Variaciones
- Transformar el blob en una clase coordinadora, que gestiona cómo hacen el trabajo los demás, delegando dicho trabajo en las clases de este modo coordinadas. De manera que el blob no es un ejecutor, sino un coordinador.
- El blob puede presentarse también como un centro de almacén de datos compartido por muchas clases, aunque no realice procesamiento alguno con dichos datos; algo así como un repositorio de datos en memoria, una sección de memoria compartida gigantesca e inapropiada.
(*)No confundir con el tipo de datos para el almacén de objetos binarios en una base de datos; no tiene nada que ver.
Referencias:
Anti Patterns – Refactoring Software, Architectures and Projects in Crisis, varios autores.
Antipatrones Software Noviembre 22, 2009
Posted by ravelus in Informática.add a comment
Un patrón software es una solución común, adaptable y probada para un problema conocido. Algo así como una plantilla de encaje que se ajusta a según qué condiciones (fuerzas y contexto) de manera que aporta una solución (diseño) implementable y ejecutable (software).
Un antipatrón es precisamente lo contrario. Dicho así de pronto parece claro que es algo así como un “lo que no hay que hacer” y que por tanto lo que voy a tratar aquí es un compendio de malas prácticas. Pero un antipatrón no es exactamente esto. Existen muchos libros que tratan de buenas y malas prácticas: en análisis, en diseño, en implementación, en pruebas… Un antipatrón no es una mala práctica, aunque su consecuencia es un mal diseño y por tanto puede abocar al fracaso de un producto software.
Un antipatrón es la aplicación de un patrón software bajo unas precondiciones erróneas, es decir: la aplicación de un patrón software en un contexto equivocado. Es algo así como decir: “tu intención era buena peeero no estudiaste bien las fuerzas y el contexto que intervenían en tu situación particular, y por ello tu solución es inadecuada”.
Bueno, esto parece diferente a lo que tradicionalmente considerábamos como mala praxis en el desarrollo software, y parece tener algo de atractivo. En estos momentos estoy leyendo un librito que trata este tema, y a medida que encuentre temas interesantes que pueda publicar lo haré. Veamos si no es un palabro más inventado para escribir libros y ganar dinero.
Lo que expongo a continuación es un breve resumen de patrones y antipatrones extraídos de la fuente que anexo al final del artículo. Para ir abriendo boca.
Patrones:
Cultura de reutilización: trata precisamente de establecer una política de reutilización de software y de que todo el equipo desarrolle pensando siempre en que sus artefactos son susceptibles de ser reutilizados.
Artefacto robusto: un elemento bien documentado, construído para alcanzar los objetivos y necesidades generales en lugar de limitarse simplemente a los objetivos específicos.
Generalización automotivada: Tratar de pensar en la generalización a la hora de desarrollar, en facilitar elementos que se puedan utilizar as-is.
Ingeniero senior de reutilización: es aquel que pone en práctica los patrones anteriores.
Antipatrones:
Artefacto no reutilizable: No sólo es aquel que no es reutilizable porque no se pensó en ello, sino que también es aquél que se pretendió que fuera reutilizable pero que en realidad no lo es (y esto último es más importante y más grave).
Reutilización orientada a repositorios: La creencia de que simplemente por crear una especie de almacén de piezas software que se pueden reutilizar, la reutilización viene de forma automática.
Síndrome del No-Está-Hecho-Aquí: Los desarrolladores desconfían del trabajo hecho por otros y tienden a desarrollar por sí mismos artefactos software que ya están hechos.
Reusabilidad declarada: La creencia de que algo es reutilizable simplemente porque lo digo yo o lo dijo alguien…
Reusabilidad orientada a incentivos: Una vez más, ofrecer incentivos para que los desarrolladores hagan software de mayor calidad (en este caso, reutilizable) no es el camino, pequeño saltamontes…
Producción antes de consumición: Algo así como que la reutilización vendrá por sí sola en cuanto empieces a pensar en desarrollar artefactos reutilizables. La realidad es que es necesario invertir fuertemente para que la reutilización sea un hecho probado: dedicar recursos y soporte.
Reutilización solamente de código: Creer que lo único susceptible de ser reutilizado es el código.
Reutilización orientada a proyectos: Limitar la generalización que lleva a construir artefactos reutilizables solamente como decisión que incumbe al ámbito del proyecto para el cual se desarrolla dicho artefacto.
Referencias:
http://www.sdmagazine.com/uml/thinking/s0002to.shtml
Revista Dr Dobb’s, actualmente la fuente no está disponible. El artículo lo encontré por ahí… lamento no poder facilitar más las cosas.
HowTo: Connect to a VPN in Linux Noviembre 10, 2009
Posted by ravelus in Informática.2 comments
Foreword
The following steps works fine in Ubuntu 8.04 Hardy. You should not have any problem in other versions.
Install required packets
First of all, we need to know which is the VPN infrastructure to connect. Depending on it, we’ll distinguish between 3 different cases:
A) – openVPN.
B) – Windows VPN
C) – Cisco VPN.
Once we certainly have this information, we can download the appropiate package:
A) – sudo apt-get install network-manager-openvpn
B) – sudo apt-get install network-manager-pptp
C) – sudo apt-get install network-manager-vpnc
If your distribution is not Ubuntu, try to search those packets in Synaptics, YaST or whatever.
After installing them, you should see a net icon in the status bar. Click on it and create a VPN connection. Introduce all the information provided to connect to your server and proceed as you would do in Windows.
When you have finished, click again in the net icon in the status bar and select your connection. The system will ask for your credentials. Type them and enter. Quite simple.
Useful links
http://www.cs.umn.edu/help/offsite/vpn.php#ubuntu_config
This is a useful link with a very simple tutorial including snapshots. In the examples it explains how to connect to a Cisco VPN, but the other cases are quite similar.
Here you can find information about packages to install and things like that.
Lanzarote, fuego por dentro Octubre 18, 2009
Posted by ravelus in Cultura, Experiencias.add a comment
Hace poco menos de un mes estuve con mi novia de vacaciones en Lanzarote, una de las islas que, como todos saben, conforma el archipiélago canario. Disfrutamos de un tiempo espléndido, visitamos lugares muy interesantes y cenamos a cuerpo de rey. Quería compartir aquí mi experiencia desde un punto de vista más o menos objetivo, para que futuros visitantes de la isla puedan tener un punto de referencia acerca de qué ver, qué comer y dónde hospedarse.
Hospedaje
Nosotros elegimos un apartotel por la privacidad de tener nuestra propia casita y el precio de este tipo de hospedajes. Encontramos una buena oferta en Sun Park en régimen de alojamiento y desayuno, por 16€ cada persona. Un precio muy asequible teniendo en cuenta que las habitaciones incluyen una pequeña cocina con todos los utensilios necesarios para preparar comida. Además el complejo hotelero tiene varias piscinas, salas de reunión, parques para los críos y demás. Situado en Playa Blanca, al sur de la isla, en dirección a Playa Papagayo.
Qué comer
Algunos días cenamos en los numerosos restaurantes que se ubican en el paseo marítimo de Playa Blanca, con vistas al mar y todo el pastel. Alguna vez elegimos pescado del día, como no podía ser de otra manera, y no nos decepcionó en absoluto. Cherne (como allí llaman al mero) fino, suave al paladar, aunque es más típica la Vieja. Los precios varían entre restaurantes levemente, pero en general se come barato y bien.
Qué ver
Montañas de fuego.

Vista del parque nacional de Timanfaya y el Atlántico al fondo
En pleno parque nacional de Timanfaya, en el centro de la isla, existe una ruta en autobús, por 8€ que le mostrará el árido y virgen desierto rojo de la isla, suelo protegido por la Unesco como Reserva de la Biosfera. Es algo fuera de lo común para los que vivimos en la península, y puede merecer la pena por ello; pero van a ver eso mismo: desierto.
La Cueva de los Verdes.
Un paseo de aproximadamente 1km por el interior de cuevas rehabilitadas por César Manrique, artista polifacético y gran defensor de la isla. En su interior no veremos las típicas estalactitas y estalagmitas, sino marcas de los ríos de lava que quedan de las últimas erupciones de la isla, allá por el siglo diecinueve. Todo decorado con luces de colores, da un aspecto bastante curioso. Lo del nombre de los Verdes es algo muy curioso que… no, es mejor que vaya y se lo cuenten. Cuidado con los desfiladeros, en algunos, inexplicablemente, no hay barandilla… Precio: 8€.
El volcán de Caldera blanca.
Ruta de senderismo a través de Timanfaya, por un auténtico pedregal que desemboca en los volcanes de Caldera Pequeña y Caldera Blanca más adelante, los volcanes con mayor altitud de la isla, a unos 500m de altura. Muy recomendable llevar bastón y calzado adecuado, esto es: botas de montañismo, no zapatillas deportivas. Dificultad: media, por la ascensión al cráter y el suelo pedregoso, no apto para tobillos frágiles.

Interior del volcán de Caldera Blanca
Jameos del agua.
Bueno, pues otra bajada a una gruta con un estanque subterráneo donde se aloja una especie endémica de cangrejo canijo y blanco. Los jameos son precisamente las salas subterráneas que la lava fue creando a su paso por debajo de la tierra y que después han quedado al descubierto al derrumbarse los techos de roca. Incluye un pequeño centro de interpretación donde aprenderemos cómo funciona el asunto del volcanismo, la formación de la isla y demás. Precio: 8€.

Jardín de cactus
Jardín de cactus.
Alberga miles de especies de cactus recogidas de todos los rincones del planeta, a cada cual más curioso. Precio: 6€.
Y para terminar…
… puede dar un paseo en camello por las áridas arenas de Timanfaya; el precio es de 12€ el alquiler del camello, en el cual pueden montar dos personas.
