Noticias Weblogs Foros Wiki Código
Sponsors:

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Anunciarse aquí

PlanetaCódigo en inglés

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin

Mapeo objeto-relacional (primera parte)

Agosto 31st, 2005 - [Enlace local]

Hola. Estos días estoy leyendo acerca de Hibernate y con este post voy a empezar una pequeña
serie de “lecciones básicas” que he aprendido sobre el mapeo objeto-relacional. Así
que hablaré de los problemas que surjen en la implementación de la persistencia de POJOs.

Para cualquier aplicación que tenga que lidiar con una base de datos relacional surje un problema.
Tenemos que “casar” el modelo de objetos con el modelo de tablas (modelo relacional). La primera aproximación
es muy sencilla. Por cada clase crearemos una tabla en la que cada fila guardará los datos de un objeto
y cada columna será una propiedad de un objeto.

Veamos un ejemplo. En nuestro modelo de datos tenemos una clase Persona.
Esta es su definición:

import java.util.Date;
import java.io.Serializable;
	
public class Persona implements Serializable {
	
	private int nif;
	private String nombre;
	private String apellidos;
	private Date fechaNacimiento;
	
	/* métodos get y set */
	
}

Siguiendo la aproximación
anterior vamos a crear una tabla “personas”. La crearemos así:

CREATE TABLE personas(nif INTEGER,
	nombre VARCHAR(50),
	apellidos VARCHAR(50),
	fecha_nacimiento DATE);

Esto que puede parecer muy sencillo tiene un gran inconveniente. Cuando programamos objetos
usamos punteros/referencias. Para manipular un objeto determinado tenemos que tener
un puntero/referencia que apunte a él. Sin embargo un puntero/referencia sólo existe
mientras el objeto está en memoria y es diferente en cada instancia de la aplicación.
Sin embargo los datos de una base de datos “siempre están allí” y necesitamos una forma de hacer
referencia a ellos de forma unívoca.

Para esta necesidad las bases de datos relacionales cuentan con las archiconocidas claves primarias.
Una clave primaria es un valor de una columna que sirve para identificar de forma unívoca un registro en una tabla.
No existen por tanto dos filas en una tabla que tengan la misma clave primaria.
Debemos elegir un campo del objeto para que sea clave primaria.
En el ejemplo dos personas pueden tener el mismo
nombre, dos personas también pueden tener los mismos apellidos o pueden haber nacido el mismo
día, así que ninguna de estas propiedades nos sirve como clave primaria. Sin embargo dos personas
no pueden tener el mismo NIF. Tenemos un campo del objeto candidato para ser clave primaria.
Toda persona tiene un NIF y no existen dos personas con el mismo NIF.

Ocurre que frecuentemente hay objetos en los que ningún campo puede ser usado como clave primaria.
En esas ocasiones crearemos una columna adicional cuya única finalidad es contener los valores de las
claves primarias. El valor que adquirirá esa columna ya no es proporcionado por el usuario, sino
que será un valor normalmente generado por la base de datos, por ejemplo a través mediante
los “auto_increment” de mysql. O bien puede ser un UUID (identificador único universal):
valor pseudo-aleatorio, que puede ser, por ejemplo, la hora acutual
concatenada con un número aleatorio y a su vez concatenado con la IP del ordenador.

Hay quién siempre usa columnas creadas exclusivamente para contener claves primarias incluso
cuando el objeto contiene un campo que por su naturaleza puede ser clave primaria. Esto es por el lema
“todo valor introducido por el usuario es susceptible de ser cambiado”.

Tras esto voy a volver a escribir la sentencia de creación de la tabla indicando que NIF es clave primaria,
y por ello también indico que no puede ser NULL.

CREATE TABLE personas(nif INTEGER NOT NULL,
	nombre VARCHAR(50),
	apellidos VARCHAR(50),
	fecha_nacimiento DATE,
	PRIMARY KEY(nif));

Bueno, eso es todo por hoy. No he contado más que perogrulladas pensará alguno.
Cierto, pero esto es sólo el primer post de una mini-serie.
En próximos posts hablaré de las relaciones entre objetos y las claves foráneas. También
hablaré de las diferentes estrategias para mapear jerarquías de herencia. Y finalmente
hablaré de los “objetos de grano fino” (entidades débiles o “fine grained objects”).

Un saludo!

» Leer más, comentarios, etc...

Gimenoblog

Mapeo objeto-relacional (primera parte)

Agosto 31st, 2005 - [Enlace local]

Hola. Estos días estoy leyendo acerca de Hibernate y con este post voy a empezar una pequeña serie de "lecciones básicas" que he aprendido sobre el mapeo objeto-relacional. Así que hablaré de los problemas que surjen en la implementación de la persistencia de POJOs.

Para cualquier aplicación que tenga que lidiar con una base de datos relacional surje un problema. Tenemos que "casar" el modelo de objetos con el modelo de tablas (modelo relacional). La primera aproximación es muy sencilla. Por cada clase crearemos una tabla en la que cada fila guardará los datos de un objeto y cada columna será una propiedad de un objeto.

Veamos un ejemplo. En nuestro modelo de datos tenemos una clase Persona. Esta es su definición:

import java.util.Date;
import java.io.Serializable;

public class Persona implements Serializable {
	
	private int nif;
	private String nombre;
	private String apellidos;
	private Date fechaNacimiento;
	
	/* métodos get y set */

}

Siguiendo la aproximación anterior vamos a crear una tabla "personas". La crearemos así:

CREATE TABLE personas(nif INTEGER, 
	nombre VARCHAR(50), 
	apellidos VARCHAR(50), 
	fecha_nacimiento DATE);

Esto que puede parecer muy sencillo tiene un gran inconveniente. Cuando programamos objetos usamos punteros/referencias. Para manipular un objeto determinado tenemos que tener un puntero/referencia que apunte a él. Sin embargo un puntero/referencia sólo existe mientras el objeto está en memoria y es diferente en cada instancia de la aplicación. Sin embargo los datos de una base de datos "siempre están allí" y necesitamos una forma de hacer referencia a ellos de forma unívoca.

Para esta necesidad las bases de datos relacionales cuentan con las archiconocidas claves primarias. Una clave primaria es un valor de una columna que sirve para identificar de forma unívoca un registro en una tabla. No existen por tanto dos filas en una tabla que tengan la misma clave primaria. Debemos elegir un campo del objeto para que sea clave primaria. En el ejemplo dos personas pueden tener el mismo nombre, dos personas también pueden tener los mismos apellidos o pueden haber nacido el mismo día, así que ninguna de estas propiedades nos sirve como clave primaria. Sin embargo dos personas no pueden tener el mismo NIF. Tenemos un campo del objeto candidato para ser clave primaria. Toda persona tiene un NIF y no existen dos personas con el mismo NIF.

Ocurre que frecuentemente hay objetos en los que ningún campo puede ser usado como clave primaria. En esas ocasiones crearemos una columna adicional cuya única finalidad es contener los valores de las claves primarias. El valor que adquirirá esa columna ya no es proporcionado por el usuario, sino que será un valor normalmente generado por la base de datos, por ejemplo a través mediante los "auto_increment" de mysql. O bien puede ser un UUID (identificador único universal): valor pseudo-aleatorio, que puede ser, por ejemplo, la hora acutual concatenada con un número aleatorio y a su vez concatenado con la IP del ordenador.

Hay quién siempre usa columnas creadas exclusivamente para contener claves primarias incluso cuando el objeto contiene un campo que por su naturaleza puede ser clave primaria. Esto es por el lema "todo valor introducido por el usuario es susceptible de ser cambiado".

Tras esto voy a volver a escribir la sentencia de creación de la tabla indicando que NIF es clave primaria, y por ello también indico que no puede ser NULL.

CREATE TABLE personas(nif INTEGER NOT NULL, 
	nombre VARCHAR(50), 
	apellidos VARCHAR(50), 
	fecha_nacimiento DATE,
	PRIMARY KEY(nif));

Bueno, eso es todo por hoy. No he contado más que perogrulladas pensará alguno. Cierto, pero esto es sólo el primer post de una mini-serie. En próximos posts hablaré de las relaciones entre objetos y las claves foráneas. También hablaré de las diferentes estrategias para mapear jerarquías de herencia. Y finalmente hablaré de los "objetos de grano fino" (entidades débiles o "fine grained objects").

Un saludo!

» Leer más, comentarios, etc...

niko's mini factory

DWR y Spring

Agosto 30th, 2005 - [Enlace local]

DWR (Direct Web Remoting) o "Easy AJAX for Java" es una biblioteca para facilitar la utilización de AJAX. Y con AJAX (Asynchronous JavaScript And XML for Web GUI processing) podemos actualizar una página web sin necesidad de gets y posts, eliminando el correspondiente "roundtrip" al servidor y refrescos de páginas, logrando una interfaz similar a cualquier aplicación desktop.
DWR expone métodos de una clase Java utilizando Javascript, permitiendo asi su acceso desde cualquier página web (HTML/JSP/etc):

interaction diagram

¿Pero cómo se configura DWR y cómo logramos que esa clase Java sea un bean de Spring?

Luego de copiar dwr.jar al servidor, configuraremos DWR en el WEB-INF/web.xml de la aplicación:

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<description>Direct Web Remoter Servlet</description>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

Luego crearemos un fichero WEB-INF/dwr.xml donde especificaremos qué métodos de qué beans queremos "exponer" mediante dwr, y que beans van a servir de transporte de datos y por lo tanto dwr debe convertir.
Por ejemplo, si queremos llenar una lista desplegable con categorías (paises, por ejemplo) y otra dependiente de esta (ciudades), necesitamos dos métodos de un bean Java (ciudadesManager), y un DTO para transferencia de datos (ListBean).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 0.4//EN"
"http://www.getahead.ltd.uk/dwr/dwr.dtd">

<dwr>
<allow>
<create creator="spring" javascript="ciudadesManager" beanName="ciudadesManager">
<include method="getPaises"/>
<include method="getCiudades"/>
</create>
<convert converter="bean" match="com.package.ListBean" />
</allow>
</dwr>

Consecuentemente, en nuestro beans.xml estará el bean ciudadesManager:

<bean id="ciudadesManager" class="com.package.ciudadesManager"> 
<property name="paisesDAO" ref="paisesDAO"/>
<property name="ciudadesDAO" ref="ciudadesDAO"/>
</bean>

Llegados a este punto, lo que queda es desarrollar la página de nuestra aplicación, y para ello deberemos incluir el javascript necesario para acceder al ciudadesManager (recordando actualizar el [appfolder]:

<script type='text/javascript' src='/[appfolder]/dwr/interface/ciudadesManager.js'></script>
<script type='text/javascript' src='/[appfolder]/dwr/engine.js'></script>
<script type='text/javascript' src='/[appfolder]/dwr/util.js'></script>

El primer include corresponde al javascript que DWR generará automaticamente para nuestro bean ciudadesManager, el segundo es el core de DWR, y el tercero una serie de utilidades para manipular controles html, como gets y sets de propiedades.

Las funciones javascript necesarias para acceder remotamente a los métodos del bean se dividen en dos: uno que hace la llamada y otro de callback. Asi, para llenar la lista de paises crearemos las funciones execPaises y fillPaises, y para llenar la lista de ciudades execCiudades y fillCiudades:

<script>
function execPaises() {
ciudadesManager.getPaises(fillPaises);
}

function fillPaises(data) {
DWRUtil.fillList('paises', data, 'key', 'value');
}

function execCiudades() {
DWRUtil.removeAllOptions('ciudades');
ciudadesManager.getCiudades(fillCiudades, DWRUtil.getValue('paises'));
}

function fillCiudades(data) {
DWRUtil.fillList('ciudades', data, 'key', 'value');
}

</script>

La primer funcion, execPaises, la llamaremos al inicio de la página, dentro del onLoad del body. Al ejecutarse, llamará al método getPaises de ciudadesManager y el resultado lo tendremos en la función que le pasemos como parámetro, en este caso fillPaises.
Dentro de fillPaises utilizaremos la función DWRUtil.fillList para rellenar la lista desplegable cuyo id es paises con la lista de datos recibidos, diciéndole cuál propiedad corresponde al código y cuál a la descripción.
Análogamente, la función execCuidades estará en el onChange de la lista paises, y cuando se ejecute (cada vez que cambiemos de pais) borrará todas las opciones de la lista ciudades y ejecutará el método getCiudades, pasando como parámetro el value de la lista paises (el pais elegido).
El código de las listas quedará asi:

<select id="paises" onChange="execCiudades();"></select>
<select id="ciudades"></select>

Solo nos queda codificar el bean ciudadesManager y el DTO. El manager obtiene la lista de paises o ciudades utilizando un DAO y las trasnfiera al DTO. Cabe señalar que podríamos utilizar directamente los beans Pais y Ciudad y transferirlos a la página, pero estas clases suelen tener muchas mas propiedades que no utilizaríamos y penalizaría la performance. De cualquier modo dependerá de vuestra propia aplicación como hacerlo, esta es solamente una forma mas.

public class CiudadesManager {

private DAO paisesDAO = null;
private DAO ciudadesDAO = null;

public Object[] getPaises() {
List objects = getPaisesDAO().findByNamedQuery("allPaises");
List list = new ArrayList();
for (Iterator it=objects.iterator(); it.hasNext(); ) {
Pais pais = (Pais)it.next();
ListBean bean = new ListBean(pais.getCodigo(), pais.getNombre());
list.add(bean);
}
return list.toArray();
}

public Object[] getCiudades(String idPais) throws BusinessException {
List objects = getCiudadesDAO().findByNamedQuery("byPais", new Object[]{idPais});
List list = new ArrayList();
for (Iterator it=objects.iterator(); it.hasNext(); ) {
Ciudad ciudad = (Ciudad)it.next();
ListBean bean = new ListBean(ciudad.getCodigo(), ciudad.getNombre());
list.add(bean);
}
return list.toArray();
}

public DAO getPaisesDAO() {return paisesDAO;}
public void setPaisesDAO(DAO paisesDAO) {this.paisesDAO = paisesDAO;}
public DAO getCiudadesDAO() {return ciudadesDAO;}
public void setCiudadesDAO(DAO ciudadesDAO) {this.ciudadesDAO = ciudadesDAO;}

}

public class ListBean {

private String key = null;
private String value = null;

public ListBean (String key, String value) {
this.key = key;
this.value = value;
}

public String getKey() {return key;}
public String getValue() {return value;}
public void setKey(String key) {this.key = key;}
public void setValue(String value) {this.value = value;}
}

Si todo ha sido configurado correctamente, el resultado será una lista desplegable de paises que se llena al cargar la página:

y una lista desplegable de ciudades que se refresca cada vez que cambiamos la primera:

Y todo sin que se refresque la página.

En el sitio de DWR hay numerosos ejemplos de como utilizarlo para diversas tareas, como editar tablas y formularios o validar datos, solo hace falta animarse.

» Leer más, comentarios, etc...

Pointer to (void)

Estoy de vuelta!

Agosto 29th, 2005 - [Enlace local]

Enseñad a vuestros hijos lo que nosotros hemos enseñado a nuestros hijos: la tierra es nuestra madre. Lo que afecte a la tierra, afectará también a los hijos de la tierra. Si los hombres blancos escupen a la tierra, se escupen a sí mismos. Porque nosotros sabemos esto: la tierra no pertenece al hombre, sino el hombre a la tierra. Todo está relacionado, del mismo modo que la sangre une a una familia.

Extracto de la carta enviada por el Gran Jefe Seattle de la tribu Dewamish, a Franklin Pierce, presidente de los Estados Unidos de América, para negociar la venta de sus tierras.

» Leer más, comentarios, etc...

Pointer to (void)

Estoy de vuelta!

Agosto 29th, 2005 - [Enlace local]

Enseñad a vuestros hijos lo que nosotros hemos enseñado a nuestros hijos: la tierra es nuestra madre. Lo que afecte a la tierra, afectará también a los hijos de la tierra. Si los hombres blancos escupen a la tierra, se escupen a sí mismos. Porque nosotros sabemos esto: la tierra no pertenece al hombre, sino el hombre a la tierra. Todo está relacionado, del mismo modo que la sangre une a una familia.

Extracto de la carta enviada por el Gran Jefe Seattle de la tribu Dewamish, a Franklin Pierce, presidente de los Estados Unidos de América, para negociar la venta de sus tierras.

» Leer más, comentarios, etc...

… buenas prácticas

Agosto 26th, 2005 - [Enlace local]

Interesante post de Ted Neward respecto a la necesidad de tener en cuenta el contexto en el que nos encontramos a la hora de tomar decisiones tecnológicas. Bueno, tecnológicas y de cualquier otro tipo.

No es que no lo sepamos, simplemente a veces se nos olvida.

» Leer más, comentarios, etc...

Y si hablamos de...

… buenas prácticas

Agosto 26th, 2005 - [Enlace local]

Interesante post de Ted Neward respecto a la necesidad de tener en cuenta el contexto en el que nos encontramos a la hora de tomar decisiones tecnológicas. Bueno, tecnológicas y de cualquier otro tipo.

No es que no lo sepamos, simplemente a veces se nos olvida.

» Leer más, comentarios, etc...

La magia del Smalltalk: Capítulo 10 - ¡No me encuentro!

Agosto 24th, 2005 - [Enlace local]

Los programadores de Smalltalk solemos decir que el ambiente (de Smalltalk) está «vivo» (gracias Dario). La tarea de desarrollo en un ambiente de Smalltalk es muy diferente a la programación en un IDE tradicional o en el vi/emacs.




Es muy, pero muy, raro encontrar «documentación» fuera del ambiente y la documentación que está dentro del ambiente no está en formato de archivos de ayuda, ni html, ni siquiera en texto.




La mejor documentación de un ambiente Smalltalk es el mismo ambiente funcionando.




El ambiente es el mejor ejemplo posible de uso de las clases y de los objetos ya que los casos de uso de los objetos son casos reales que están funcionando delante de nuestros ojos. La documentación en html, pdfs, archivos hlp, etc es demasiado estática y nadie nos garantiza que el ejemplo de uso de tal o cual función o clase sea correcto.




Quizás lo más importante para comenzar a programar en Smalltalk es aprender a usar el ambiente para buscar información.




Lo primero que tenemos que saber es que, en cualquier lugar donde veamos texto, podemos evaluar allí mismo código Smalltalk y acceder a las herramientas.




Una forma de explorar el ambiente es utilizando las herramientas de
senders
(«remitente» o «quien envía el mensaje») e
implementors
(«implementador» o «quien sabe responder al mensaje»).




Seleccionando un fragmento de texto que tenga el envío de un mensaje, podemos pedirle (vía el menú contextual) a nuestro Smalltalk que nos muestro métodos donde el mensaje en cuestión es enviado («senders») o implementaciones del mensaje («implementors»).




Supongamos que necesitamos saber como se usa el mensaje #findTokens:. Para eso escribimos el nombre del mensaje en cualquier panel de texto (por ejemplo en un Workspace) y pedimos la opción «senders» desde el menú contextual o presionando ALT-n.










Seguimos un procedimiento similar para ver las implementaciones del mensaje («implementors»), en este caso presionamos ALT-m.















Y para terminar, una joyita: El
MethodFinder
.




Esta herramienta puede responder preguntas del tipo: ¿Qué mensajes le puedo enviar a un objeto dado, con unos parámetros dados, para que la respuesta sea la dada?. ¡Qué complicado que quedó escrito!, mejor veamos un ejemplo. Escribimos en el panel de arriba a la izquierda lo siguiente: ‘Smalltalk’. $S y aceptamos.










Escribiendo ‘Smalltalk’. $S le estamos preguntando al
MethodFinder
que mensajes le podemos enviar al objeto ‘Smalltalk’ para que la respuesta sea el objeto $S.




Otro ejemplo sería escribir ‘Smalltalk’. 1. $S, en este caso le estamos preguntando que mensaje le podemos enviar al objeto ‘Smalltalk’, con el parámetro 1, para que la respuesta sea el objeto $S.













Espero que con esto estén un poco menos perdidos.




¡Hasta la próxima!

» Leer más, comentarios, etc...

Web 2.0, llegó La Revolución

Agosto 22nd, 2005 - [Enlace local]

Algunos de ustedes habrán oido hablar de web 2.0, pero seguramente no sepan lo que es realmente. ¿Y si les digo que Gmail o Google Maps usan Web 2.0? Exacto, Web 2.0 es, a grandes rasgos, un sistema que permite actualizar una página web sin necesidad de cargar otra página completa.

Siga leyendo. Explicaré con más detalle qué es Web 2.0, y por qué está revolucionando el concepto de Web.

link

» Leer más, comentarios, etc...

Libro para desarrolladores en Mono

Agosto 21st, 2005 - [Enlace local]

Esta semana he iniciado la idea de Mario Carrion de hacer una versión del Libro “Mono a Developer Notebook” pero en su versión en castellano, en lo personal tengo este libro y me parece genial mis felicitaciones a Edd Dumbill y Niel M. Bornstein por tan excelente trabajo.

Debido a que es un idea como muchas usaremos el wiki beta de monohispano.org en cual agregaremos nuestros aportes y esperamos que muchos se nos unan poco a poco o idealmente masivamente.

Cabe destacar que no deseamos hacer un libro acerca de un lenguaje de programación especifico, estará mas enfocado a la plataforma Mono como tal, tal por se puede ver en la introducción al WikiLibro.

El siguiente WikiLibro pretende ser algo diferente a un libro de programación de C# o Visual Basic, la idea principal es que sea una guía practica de referencia para programadores que desean hacer de Mono su plataforma de desarrollo.

Espero que este libro se desarrolle rápido y el resultado ayude a muchos a utilizar Mono como Plataforma de desarrollo, no me resta que invitar a todos a que se creen una cuenta en el wiki de monohispano para que nos ayuden.

Pido disculpas por la imagen que modifique de la portada del libro de Mono ADN, pero me pareció gracioso, además de que seria genial tener un libro impreso del trabajo final, claro imagino que la sección de autores no cabria en la portada, algo pensaríamos en su momento de llegarse a dar.

Por el momento puedes ver el avance del libro aquí

» Leer más, comentarios, etc...

knocte :: MonoTema

Evolución de mi arquitectura de desarrollo web (de…

Agosto 17th, 2005 - [Enlace local]

Lo primero que utilicé fue ASP a secas. En cuanto el proyecto crecía, era muy difícil su mantenibilidad, por problemas derivados de su rapidez y potencia del lenguaje (VB Script). Más tarde, reescribí el proyecto ASP anterior en PHP, esperando que con software libre obtuviera resultados mejores. Y lo fueron: PHP, además de ser libre, era más potente, disponía de más herramientas para facilitar

» Leer más, comentarios, etc...

Bloggingg

The Pragmatic Programmer

Agosto 16th, 2005 - [Enlace local]


Un libro con muchos consejos sobre desarrollo y gestión de proyectos contados con anécdotas, ejemplos y analogías. Después de leerlo intentas aplicar estas buenas prácticas en todos los proyectos.
The Pragmatic Programmer.
Andrew Hunt.
David Thomas.
Addison Wesley. 1999.

» Leer más, comentarios, etc...

Bloggingg

The Pragmatic Programmer

Agosto 16th, 2005 - [Enlace local]


Un libro con muchos consejos sobre desarrollo y gestión de proyectos contados con anécdotas, ejemplos y analogías. Después de leerlo intentas aplicar estas buenas prácticas en todos los proyectos.
The Pragmatic Programmer.
Andrew Hunt.
David Thomas.
Addison Wesley. 1999.

» Leer más, comentarios, etc...

La magia del Smalltalk: Capítulo 9 - Colecciones

Agosto 16th, 2005 - [Enlace local]

Pocas cosas tienen un impacto tan grande en la productividad en el desarrollo de software como el manejo de colecciones. Cualquier lenguaje/ambiente de programación que se precie de serio incluye algún framework para el manejo de colecciones.

La combinación de los bloques como objeto de pleno derecho por un lado, el late binding extremo que predica el Smalltalk y el hecho de tener más de 30 años de maduración hacen de las colecciones de Smalltalk uno de los frameworks más poderosos en esta área.

Para empezar veamos el mensaje básico para las colecciones: do:. El mensaje do: le pide a una determinada colección que evalúe un bloque dado por cada elemento que contiene.

‘Un string es, obviamente, una colección’
    do: [:each | Transcript show: each; cr]
#(1 2 3 4 5 6 7 8 9 10)
    do: [:each | Transcript show: each; cr]

Veamos algunos mensajes más:

#do:separatedBy:
Evalúa un bloque dado por cada elemento, y evalúa el otro bloque dado entre elementos.

#(1 2 3 4 5 6 7 8 9 10)
    do: [:each | Transcript show: each]
    separatedBy: [Transcript show: ‘,’]

#select:
Devuelve una colección, del mismo tipo que la colección receptora del mensaje, con los elementos para los que el bloque dado devuelve true.

#(1 2 3 4 5 6 7 8 9 10) select: [:each | each odd]
respuesta: #(1 3 5 7 9)

#reject:
Similar a #select:, pero devuelve una colección con los elementos donde el bloque devuelve false.

#(1 2 3 4 5 6 7 8 9 10) reject: [:each | each odd]
respuesta: #(2 4 6 8 10)

#collect:
Devuelve una colección, del mismo tipo que la colección receptora del mensaje, con elementos que son el resultado de la evaluación del bloque por cada elemento del receptor.

#(1 2 3 4 5 6 7 8 9 10) collect: [:each | each * 2]
respuesta: #(2 4 6 8 10 12 14 16 18 20)

¡A coleccionar!

» Leer más, comentarios, etc...

The Evolution of a Programmer

Agosto 14th, 2005 - [Enlace local]

Navegando sin rumbo fijo por la embravecida información contenida en del.icio.us, me topo con un gran documento que, doy por sentado, deleitará al público informático presente.

Se trata de The Evolution of a Programmer, un elocuente manifiesto en clave de humor que refleja el ciclo de vida de todo buen programador que se precie. Desde los más dulces inicios en tiempos de instituto y acné facial, enamorado del Spectrum y BASIC, hasta la decadencia del propio ser en el lado oscuro de la fuerza ;-) Cuando lo leáis, entenderéis mejor de lo qué estoy hablando jaja.

Aunque el texto apareció publicado originalmente en la página web de Iavor S. Diatchki, está firmado de forma anónima, por lo que a priori se desconoce el autor del mismo.

No revelo más, espero que disfruten del genuino (y sano, aunque no lo parezca :P ) humor informático. Con todos ustedes, las etapas del un programador:

High School/Jr.High
10 PRINT \"HELLO WORLD\"
20 END
First year in College
program Hello(input, output)
  begin
    writeln('Hello World')
  end.
Senior year in College
(defun hello
  (print
    (cons 'Hello (list 'World))))
New professional
#include <stdio.h>
void main(void)
{
  char *message[] = {\"Hello \", \"World\"};
  int i;
	
  for(i = 0; i < 2; ++i)
    printf(\"%s\", message[i]);
  printf(\"\n\");
}
Seasoned professional
#include <iostream.h>
#include <string.h>
	
class string
{
private:
  int size;
  char *ptr;
	
  string() : size(0), ptr(new char[1]) { ptr[0] = 0; }
	
  string(const string &s) : size(s.size)
  {
    ptr = new char[size + 1];
    strcpy(ptr, s.ptr);
  }
	
  ~string()
  {
    delete [] ptr;
  }
	
  friend ostream &operator <<(ostream &, const string &);
  string &operator=(const char *);
};
	
ostream &operator<<(ostream &stream, const string &s)
{
  return(stream << s.ptr);
}
	
string &string::operator=(const char *chrs)
{
  if (this != &chrs)
  {
    delete [] ptr;
    size = strlen(chrs);
    ptr = new char[size + 1];
    strcpy(ptr, chrs);
  }
  return(*this);
}
	
int main()
{
  string str;
	
  str = \"Hello World\";
  cout << str << endl;
	
  return(0);
}
Master Programmer
[
uuid(2573F8F4-CFEE-101A-9A9F-00AA00342820)
]
library LHello
{
    // bring in the master library
    importlib(\"actimp.tlb\");
    importlib(\"actexp.tlb\");
	
    // bring in my interfaces
    #include \"pshlo.idl\"
	
    [
    uuid(2573F8F5-CFEE-101A-9A9F-00AA00342820)
    ]
    cotype THello
 {
 interface IHello;
 interface IPersistFile;
 };
};
	
[
exe,
uuid(2573F890-CFEE-101A-9A9F-00AA00342820)
]
module CHelloLib
{
	
    // some code related header files
    importheader(<windows.h>);
    importheader(<ole2.h>);
    importheader(<except.hxx>);
    importheader(\"pshlo.h\");
    importheader(\"shlo.hxx\");
    importheader(\"mycls.hxx\");
	
    // needed typelibs
    importlib(\"actimp.tlb\");
    importlib(\"actexp.tlb\");
    importlib(\"thlo.tlb\");
	
    [
    uuid(2573F891-CFEE-101A-9A9F-00AA00342820),
    aggregatable
    ]
    coclass CHello
 {
 cotype THello;
 };
};
	
#include \"ipfix.hxx\"
	
extern HANDLE hEvent;
	
class CHello : public CHelloBase
{
public:
    IPFIX(CLSID_CHello);
	
    CHello(IUnknown *pUnk);
    ~CHello();
	
    HRESULT  __stdcall PrintSz(LPWSTR pwszString);
	
private:
    static int cObjRef;
};
	
#include <windows.h>
#include <ole2.h>
#include <stdio.h>
#include <stdlib.h>
#include \"thlo.h\"
#include \"pshlo.h\"
#include \"shlo.hxx\"
#include \"mycls.hxx\"
	
int CHello::cObjRef = 0;
	
CHello::CHello(IUnknown *pUnk) : CHelloBase(pUnk)
{
    cObjRef++;
    return;
}
	
HRESULT  __stdcall  CHello::PrintSz(LPWSTR pwszString)
{
    printf(\"%ws\", pwszString);
    return(ResultFromScode(S_OK));
}
	
CHello::~CHello(void)
{
  // when the object count goes to zero, stop the server
  cObjRef--;
  if( cObjRef == 0 )
    PulseEvent(hEvent);
	
  return;
}
	
#include <windows.h>
#include <ole2.h>
#include \"pshlo.h\"
#include \"shlo.hxx\"
#include \"mycls.hxx\"
	
HANDLE hEvent;
	
int _cdecl main(
int argc,
char * argv[]
) {
ULONG ulRef;
DWORD dwRegistration;
CHelloCF *pCF = new CHelloCF();
	
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	
// Initialize the OLE libraries
CoInitializeEx(NULL, COINIT_MULTITHREADED);
	
CoRegisterClassObject(CLSID_CHello, pCF, CLSCTX_LOCAL_SERVER,
    REGCLS_MULTIPLEUSE, &dwRegistration);
	
// wait on an event to stop
WaitForSingleObject(hEvent, INFINITE);
	
// revoke and release the class object
CoRevokeClassObject(dwRegistration);
ulRef = pCF->Release();
	
// Tell OLE we are going away.
CoUninitialize();
	
return(0); }
	
extern CLSID CLSID_CHello;
extern UUID LIBID_CHelloLib;
	
CLSID CLSID_CHello = { /* 2573F891-CFEE-101A-9A9F-00AA00342820 */
    0x2573F891,
    0xCFEE,
    0x101A,
    { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }
};
	
UUID LIBID_CHelloLib = { /* 2573F890-CFEE-101A-9A9F-00AA00342820 */
    0x2573F890,
    0xCFEE,
    0x101A,
    { 0x9A, 0x9F, 0x00, 0xAA, 0x00, 0x34, 0x28, 0x20 }
};
	
#include <windows.h>
#include <ole2.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include \"pshlo.h\"
#include \"shlo.hxx\"
#include \"clsid.h\"
	
int _cdecl main(
int argc,
char * argv[]
) {
HRESULT  hRslt;
IHello        *pHello;
ULONG  ulCnt;
IMoniker * pmk;
WCHAR  wcsT[_MAX_PATH];
WCHAR  wcsPath[2 * _MAX_PATH];
	
// get object path
wcsPath[0] = '\0';
wcsT[0] = '\0';
if( argc > 1) {
    mbstowcs(wcsPath, argv[1], strlen(argv[1]) + 1);
    wcsupr(wcsPath);
    }
else {
    fprintf(stderr, \"Object path must be specified\n\");
    return(1);
    }
	
// get print string
if(argc > 2)
    mbstowcs(wcsT, argv[2], strlen(argv[2]) + 1);
else
    wcscpy(wcsT, L\"Hello World\");
	
printf(\"Linking to object %ws\n\", wcsPath);
printf(\"Text String %ws\n\", wcsT);
	
// Initialize the OLE libraries
hRslt = CoInitializeEx(NULL, COINIT_MULTITHREADED);
	
if(SUCCEEDED(hRslt)) {
  hRslt = CreateFileMoniker(wcsPath, &pmk);
  if(SUCCEEDED(hRslt))
    hRslt = BindMoniker(pmk, 0, IID_IHello, (void **)&pHello);
	
  if(SUCCEEDED(hRslt)) {
    // print a string out
    pHello->PrintSz(wcsT);
	
    Sleep(2000);
    ulCnt = pHello->Release();
  }
  else
    printf(\"Failure to connect, status: %lx\", hRslt);
	
  // Tell OLE we are going away.
  CoUninitialize();
}
	
return(0);
}
Apprentice Hacker
#!/usr/local/bin/perl
$msg=\"Hello, world.\n\";
if ($#ARGV >= 0) {
  while(defined($arg=shift(@ARGV))) {
    $outfilename = $arg;
    open(FILE, \">\" . $outfilename) || die \"Can't write $arg: $!\n\";
    print (FILE $msg);
    close(FILE) || die \"Can't close $arg: $!\n\";
  }
} else {
  print ($msg);
}
1;
Experienced Hacker
#include <stdio.h>
#define S \"Hello, World\n\"
main(){exit(printf(S) == strlen(S) ? 0 : 1);}
Seasoned Hacker
% cc -o a.out ~/src/misc/hw/hw.c
% a.out
Guru Hacker
% echo \"Hello, world.\"
New Manager
10 PRINT \"HELLO WORLD\"
20 END
Middle Manager
mail -s \"Hello, world.\" bob@b12
Bob, could you please write me a program that prints \"Hello, world.\"?
I need it by tomorrow.
^D
Senior Manager
% zmail jim
I need a \"Hello, world.\" program by this afternoon.
Chief Executive
% letter
letter: Command not found.
% mail
To: ^X ^F ^C
% help mail
help: Command not found.
% damn!
!: Event unrecognized
% logout
The Evolution of a Programmer (#)

Impresionante ¿eh? Yo me quedo con la última etapa, es genial :-D Y además de una gran verdad; en el momento que dejas de practicar, comienza tu proceso de oxidación (y, éste, tiene límites insospechados como se puede observar jajaja). Y la cosa no queda aquí, para quien quiera más dosis que no se pierda la sección de jokes, chistes, de Iavor.

Y para terminar, también os dejo con The Evolution of a Haskell Programmer, un enlace a un texto en la misma línea pero enfocado a programadores de Haskell y bastante más trabajado por cierto.

Anotaciones relacionadas:

» Leer más, comentarios, etc...

Introducción CMM - CMMI

Agosto 13th, 2005 - [Enlace local]

De mis experiencias en estos 2 años con el modelo CMM - CMMI voy a intentar explicaros de una forma clara y sencilla en que consiste este modelo de calidad del software.

» Leer más, comentarios, etc...

Yet Another Programming Weblog

Diseño de APIs

Agosto 12th, 2005 - [Enlace local]

Leo en el bliki de Martin Fowler una buena entrada acerca del diseño de interfaces y APIs. Viene a decir que si al usar una API tienes que acordarte siempre de hacer algo no es un buen diseño.

Como ejemplo: si tienes que acordarte de llamar a super (o a la clase padre en C++) es que no es un buen diseño. En este caso particular recurre al patrón Método Plantilla (Template Method) para mejorar el diseño.

Y es que, como dice Scott Meyers en un artículo apuntado en la entrada anterior (The Most Important Design Guideline?), "La responsabilidad de un error en el uso de un interfaz es resposabilidad del diseñador, no del usuario". (Convendría seguramente matizar una opinión tan tajante, pero, como norma general, una API es mejor cuanto menos propensa a fallos es)
La misma entrada en BP

» Leer más, comentarios, etc...

Programación en el entorno GNOME

Agosto 11th, 2005 - [Enlace local]

gnome-64.png
A través de DiarioLinux.com y Barrapunto encuentro un fantástico recurso en castellano sobre desarrollo de aplicaciones GNOME basadas en librerías como GLib o GTK+.

La escasez de una documentación en castellano lo suficientemente sólida tanto para desarrolladores en el entorno GNOME como para principiantes impulsó a varias personas de GNOME Hispano a iniciar la elaboración de un libro que reuniera los conceptos necesarios para enseñar la programación de aplicaciones en este entorno. Los objetivos son ofrecer una documentación accesible para todo el mundo y actualizada, por ello es que el libro está publicado con licencia GFDL y se encuentra en su, recientemente lanzada, versión 0.0.3.

Programación en el entorno GNOME se encuentra disponible en su página web, en tres formatos:

Para mayor información, se recomienda echar un vistazo a la web del libro. Desde aquí felicitar y agradecer el trabajo desinteresado de toda la gente que, de cualquier modo, está detrás de este recurso.

» Leer más, comentarios, etc...

Oferta de empleo para programadores

Agosto 11th, 2005 - [Enlace local]

Después del éxito del anterior post en el que se ofrecía trabajo y con el que se consiguió dar curro a Daniel (no tendrás queja Danielín ;) ), me han pedido que ponga otras ofertas de trabajo en la bitácora por si a alguien le interesa. Que conste que yo de esto no percibo ni las cáscaras y que sólo lo hago para hacer un favor a estas empresas y a mis lectores, por supuesto.

En concreto estamos hablando de 3 puestos de Programador Senior para la empresa Opensistemas ubicada en Madrid y uno o dos puestos para la empresa Adaptia ubicada en Cáceres.

Podéis ver el perfil que se está buscando para los 5 puestos en esta dirección de Infojobs. De todas formas enviad vuestro currículum aunque no cumpláis todos los requisitos que es muy posible que se pongan en contacto con vosotros de todos modos para haceros una entrevista. Básicamente se buscan programadores C/C++ y Java bajo entornos *NIX (GNU/Linux sobre todo).

Podéis hacer llegar vuestros currículums por correo para una atención más rápida, en concreto para los puestos de madrid enviadlos a info(en)opensistemas(punto)com y para los de Cáceres enviadlos a alberto(punto)caso(en)adaptia(punto)es, podéis decir que habéis visto la oferta en esta bitácora para romper el hielo ;)

No obstante si preferís mandármelos a mí porque tenéis más confianza o porque os da menos corte yo se los remitiré a los jefes, mis correos son cesar(punto)gomez(en)adaptia(punto)es o cgomez(en)opensistemas(punto)com.

» Leer más, comentarios, etc...

niko's mini factory

Mule

Agosto 11th, 2005 - [Enlace local]

Con el auge de SOA estuve experimentando con Mule como parte de la estrategia opensource de un cliente.
Resulta admirable que con tan solo (o casi solo) una configuración similar a esta:


<bean id="domainRouterEP" class="org.mule.impl.endpoint.MuleEndpoint">
<property name="endpointURI">
<bean class="org.mule.impl.endpoint.MuleEndpointURI">
<constructor-arg value="axis:http://${broker.host}:${broker.ws.port}/router"/>
</bean>
</property>
<property name="synchronous" value="true"/>
</bean>

<bean id="serviceDomainRouter" class="org.mule.impl.MuleDescriptor">
<property name="inboundEndpoint" ref="domainRouterEP"/>
<property name="outboundRouter">
<bean class="org.mule.routing.outbound.OutboundMessageRouter">
<property name="routers">
<list>
<bean class="org.mule.routing.outbound.StaticRecipientList"/>
</list>
</property>
</bean>
</property>
<property name="implementation" value="serviceDomainRouterImpl"/>
<property name="properties">
<map>
<entry key="axisOptions">
<map>
<entry key="wsdlServiceElement" value="RouterService"/>
<entry key="wsdlPortType" value="RouterPort"/>
</map>
</entry>
<entry key="serviceInterfaces">
<list>
<value>broker.router.ServiceDomainRouter</value>
</list>
</entry>
</map>
</property>
</bean>

<bean id="serviceDomainRouterImpl" class="broker.router.impl.ServiceDomainRouterImpl"
singleton='false'>
<property name="hub" value="${broker.hub}"/>
<property name="directory" ref="directory"/>
<property name="schemaFileName" value="request-schema.xsd"/>
</bean>
se pueda exponer un servicio via webservices, el cual derivará el mensaje a una lista (StaticRecipientList) de destinos que proveerá la implementación (ServiceDomainRouterImpl).
Este mensaje puede ser reenviado mediante diversos medios de transporte, como JMS (en mi caso), archivos, ftp, http, soap, pop/smtp.
Muy útil, como se ve, para "conectar" sistemas muy dispares y "orquestar" el buen funcionamiento entre ellos.

» Leer más, comentarios, etc...

Libro de programación de GNOME, versión 0.0.3

Agosto 11th, 2005 - [Enlace local]

Estoy muy contento de haber contribuido con este libro, aunque no tengo mucho tiempo para dedicarle. El anuncio, en barrapunto.

» Leer más, comentarios, etc...

Pintar sin parpadeo con GDI+ o zxSameGame (II)

Agosto 10th, 2005 - [Enlace local]

Cuando hice la primera versión del programa, me encontré con un gravísimo problema a la hora de sobrecargar el evento OnPaint: no importa lo rápido que pinte, el control muestra un molesto parpadeo, incluso si el pintado se realiza fuera del control y luego se hace una copia BitBlt.

Mi primer pensamiento fue que el GDI+ (recordemos que se trata de la forma de pintar del .NET Framework) no estaba a la altura de las circunstancias, más que nada porque había seguido las directivas según se documenta en el Petzold para C# y en el de Profesional C# de Wrox.

Luego vino la investigación y las recomendaciones de la gente de los foros… Al final resulta que el GDI+ no sólo está a la altura de las circunstancias, sino que las supera, facilitando enormemente el trabajo, pues, si así lo decides, los controles se pueden pintar con técnicas de doble buffer ellos mismos. Como en este proyecto pinto en la ficha directamente, pongo el código siguiente en el constructor y ya no se produce ningún parpadeo:

  • ControlStyles.DoubleBuffer habilita las opciones de doble buffer para el control.
  • ControlStyles.AllPaintingInWmPaint indica que no se debe pintar primero el fondo, sino que el propio método Paint se ocupará de todo.
  • El segundo valor es necesario, porque si no, el GDI+ llama primero a una función interna para que pinte el fondo y luego se llama al método Paint sobrecargado, lo que genera el suficiente retardo como para producir extraños efectos estroboscópicos.

    Al final estoy haciendo el programa en C#, y por ahora se encuentra en un estado bastante avanzado, casi terminado.

    » Leer más, comentarios, etc...

    knocte :: MonoTema

    Segundo intento de hablar sobre IE7 en BarraPunto

    Agosto 10th, 2005 - [Enlace local]

    Creo sinceramente que muchos desarrolladores web de habla hispana se beneficiarían infinitamente al usar las librerías javascript de Dean Edwards, un proyecto denominado, precisamente, IE7. Por eso hago mi segundo intento de publicar información en BarraPunto. Debe ser que los editores están algo empeñados en que cualquier cosa que tenga que ver con IE7 es malo (opinión que respeto y comparto)

    » Leer más, comentarios, etc...

    La magia del Smalltalk: Capítulo 8 - Algo útil con el #become:

    Agosto 9th, 2005 - [Enlace local]

    Ya es hora de desvelar algo del misterio que existe detrás del #become:.

    Para comenzar, veamos que pasó con el “ejemplo” anterior:

    | s1 s2 |

    s1 := ‘Un String’.
    s2 := ‘Un String’.

    s1 become: ‘Otro String’.

    Transcript show: s1; cr.
    Transcript show: s2; cr.


    Lo que ocurre es muy simple: El compilador crea, en la tabla de símbolos del método, un sólo objeto ‘Un String’. Ambas variables (s1 y s2) apuntan al mismo objeto, para probar lo que digo evalúen:

    | s1 s2 |

    s1 := ‘Un String’.
    s2 := ‘Un String’.

    Transcript show: (s1 == s2); cr.


    Al hacer #become: entre el objeto ‘Un String’ y el objeto ‘Otro String’ todas las referencias al objeto ‘Un String’ (en este caso las variables s1 y s2) se actualizan.

    Este ejemplo (creo que) muestra todo lo relativo al become:

    | s1 s2 s3 |

    s1 := Date today.
    s2 := s1.
    s3 := ‘Un String’.

    Transcript show: ‘—– pre-become: —–’; cr.
    Transcript show: s1; cr.
    Transcript show: s2; cr.
    Transcript show: s3; cr.
    Transcript show: s1 == s2; cr.
    Transcript show: s1 == s3; cr.

    s1 become: s3.

    Transcript show: ‘—– post-become: —–’; cr.
    Transcript show: s1; cr.
    Transcript show: s2; cr.
    Transcript show: s3; cr.
    Transcript show: s1 == s2; cr.
    Transcript show: s1 == s3; cr.




    Ahora veamos algunos buenos ejemplos de uso del #become:.

    ¿Recuerdan el proxy que hicimos en el Capítulo 1?

    Los proxies, ya sea que se hagan con #doesNotUnderstand: o de la forma tradicional, son un recurso muy útil pero tienen algunas contras. Una de ellas es la penalidad en performance que implica que cada envío de mensaje pasa a través del proxy generando, al menos, un envío de mensaje más.

    Una solución, muy al estilo de Smalltalk, es hacer un proxy que capture los mensajes con un #doesNotUnderstand: tal cual lo vimos en el Capítulo 1. Con esa implementación hacemos un #become: que reemplace el proxy con el receptor en cuanto podamos. Eso sería algo así:

    Proxy>>doesNotUnderstand: aMessage
       ”load the proxied object, switch the receiver with it and routes the incoming message”

       | proxyThenProxied proxiedThenProxy |

       proxyThenProxied := self.

       ”Lazy load of proxied object”
       proxiedThenProxy := self loadProxied.

       proxyThenProxied become: proxiedThenProxy.

       ^ proxyThenProxied
          perform: aMessage selector
          withArguments: aMessage arguments


    Un proxy como este paga el costo de hacer un #become: con el primer mensaje, después se quita del medio removiendo todo impacto en la performance.

    Otra versión muy interesante es hacer que el proxy cargue el objeto decorado en un thread y que haga el #become: cuando termine el proceso de carga.

    Como verán, ¡horas de sana diversión en familia!

    » Leer más, comentarios, etc...

    Varios (¿muchos?) libros gratuitos sobre Smalltalk

    Agosto 9th, 2005 - [Enlace local]

    Stéphane Ducasse se ha tomado el (mucho) tiempo para pedir a la editoriales y a los autores de libros de Smalltalk que ya no se impriman que permitan la publicación on-line.




    Ya consiguió los permisos para publicar nada menos que 19 libros.




    Pueden bajarlos desde: http://www.iam.unibe.ch/~ducasse/FreeBooks.html





    A lo mejor a alguien se le ocurre la excelente idea de traducir alguno al Castellano. ;-)

    » Leer más, comentarios, etc...