18-smalltalk
-
Upload
harley-ospina -
Category
Documents
-
view
715 -
download
13
Transcript of 18-smalltalk
A todos aquellos que saben que la verdadera revolución
de la información no ocurrirá hasta que seamos
capaces de romper unas cuantas barreras
Disculpen mi falta de originalidad, pero tengo que agradecer especialmente
a mi núcleo familiar por el soporte y comprensión que me han brindado.
Mil gracias a mi compañera de viaje Raquel y a mis hijos Nicolás y Nahuel;
ellos son los que realmente han hecho sacrificios para que este libro llegue a
buen término.
7S
Autor:DIEGO GÓMEZ DECK
Responsable editorial:SORAYA MUÑOZ
Responsable de Marketing y Comunicación:ÁLVARO GARCÍA
Diseño y maquetación:CARLOS MONTES
ISBN: 978-84-934371-3-8Depósito Legal:
Edita:©EDIT LIN EDITORIAL, S.L, 2006Avda. Portugal, 85- local28011 Madrid (España)Tels.:91 577 03 55Fax: 91 577 06 18
LICENCIASe permite la copia y distribución de la totalidad o parte de esta obra sin ánimo de lucro. Toda copia total o parcial deberá citar expresamente el nombre del autor, nombre de la editorial e incluir esta misma licencia, añadiendo, si es copia literal, la mención “copia literal”.
Se autoriza la modificación y traducción de la obra sin ánimo de lucro siempre que se haga constar en la obra resultante de la modificación el nombre de la obra originaria, el autor de la obra originaria y el nombre de la editorial. La obra resultante también será libremente reproducida, distribuida, comunicada al público y transformada en términos similares a los expresados en esta licencia.
Impreso en España (Printed in Spain)
*Este libro ha sido realizado con Software Libre, concretamente con: Squeak, OpenOffice.org, Evolution, Mozilla, GIMP.
PRÓLOGO
8S 9S
prologoIntroducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Enfoque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11Metodología . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Alcance del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Audiencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Licencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Sitio web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Capítulo 1. ¿Qué es Smalltalk? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 Conceptos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Interfaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Encapsulación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Polimorfismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Variables de Instancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Herencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Programar es simular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Historia del Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aportes del Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 ¿Qué es Squeak? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Capítulo 2. Programando con Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. 1 La curva de aprendizaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Prepararse para un shock cultural . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tirar código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Trabajo incremental . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .No hay archivos fuentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 El camino es largo, mejor no ir solo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Literales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Índice
10S 11S
Precedencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Cascading messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Los nombres de clases son, también, variables globales . . . . . . . . . . . . . . . . . . . . . . . .Bloques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Método de Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5 Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6 Librería de Clases y Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7 Máquina Virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Capítulo 3. ¡Manos a la Obra! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1 Modificando Objetos Vivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . El Mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Browser de Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Tipos de Browser de Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Categorías de Clase y Métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Squeak y el Ratón . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Foco de teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Tipos de Workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Evaluando Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Sentencias de Ejemplo para evaluar, imprimir, inspeccionar o explorar . . . . . . . . . .Inspector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Hot-Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Parser de XML basado en una Pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Smalltalk con Estilo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .NombreDeClase>>nombreDeMétodo
3.3 Importador de Wikipedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Wikipedia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Métodos de Clase vs. Métodos de Instancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Plantilla para nuevos métodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Archivos de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Pre-Depurador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Depurador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Depurador 100% en Smalltalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Convención de nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Valor de retorno por defecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #add: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Explorador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – OrderedCollection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Inicialización de objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .MessageTallyTimeProfilerBrowser
3.4 Motor de Workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Workflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Test Driven Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .SUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Refactoring Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Los métodos de testing comienzan por #test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Consejo: Pensar primero en la interfaz pública . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Estructura de los test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .SUnit Test Runner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #collect: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Consejo: Es más barato escribir código limpio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Explaining Temporary Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Consejo: Probar las situaciones límite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Mensaje #halt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Consejo – El depurador nos brinda más información a la hora de implementar . . .Colecciones – mensaje #includes: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Senders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Los tests son, también, documentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #anySatisfy: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Composed Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Los tests aumentan la confianza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #select:thenCollect: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #select: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – mensaje #at:ifAbsentPut: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Colecciones – Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Transcript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Patrón de Diseño – Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Métodos privados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Browser Jerárquico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Usar el fuente de un método para crear otro método parecido . . . . . . . . . . . . . . . . .Mensaje #subclassResponsibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Capítulo 4. La yapa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 Mensaje #become: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Mensaje #doesNotUnderstand: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3 Mensajes #perform:, #perform:with:, #perform:withAll:, etc. . . . . . . . . . . . . . . . . . . . . 4.4 Pseudo-variable thisContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 SLang, máquina virtual y plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.6 FFI – Foreign Function Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.7 Metaprogramación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.8 Multithreading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9 SqueakMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.10 MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.11 Morphic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.12 Algunos proyectos con Squeak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Introducción
Smalltalk es mucho más que un lenguaje de programación. Se trata del resultado de un
proceso de investigación y desarrollo, liderado por Alan Kay, que ya lleva más de 30 años
tratando de inventar el ordenador personal. Smalltalk, además de ser un lenguaje de pro-
gramación muy poderoso y versátil, es una interpretación de cómo deberían utilizarse los
ordenadores, ya que estos deberían ser herramientas para amplifi car el espíritu creativo de
las personas.
Smalltalk no es la culminación de esa visión, es sólo un paso. Pero si tiene un objetivo en sí
mismo, ése es el de servir como instrumento para crear la herramienta que vuelva obsoleto
al mismo Smalltalk.
Lamentablemente, las ideas que subyacen de Smalltalk no son las más extendidas en el
mundo de la informática, y aunque muchas innovaciones del proyecto han perdurado
hasta nuestros días, éstas han llegado despojadas de lo fundamental, adquiriéndose sólo la
parte más superfi cial de las ideas. Este hecho ha provocado que la primera experiencia o
toma de contacto con Smalltalk sea negativa y desconcertante, incluso para una persona ya
acostumbrada al manejo de ordenadores y con conocimientos de programación.
No obstante, el esfuerzo de entrar en un área desconocido tiene su recompensa. Muchos,
entre ellos nuestro autor, son los que sienten que Smalltalk les ha devuelto la fascinación
que les produjo usar un ordenador por primera vez. No es extraño ver cómo gente que
comienza a programar, gracias a Smalltalk cambia su concepción de los fundamentos de
los ordenadores. Prácticamente, todos los programadores de Smalltalk utilizan el entorno
no sólo para trabajar y producir software, sino también como una herramienta de investi-
gación. Resulta muy común, por tanto, encontrar proyectos muy peculiares e innovadores
desarrollados con Smalltalk porque, justamente, este entorno sirve como amplifi cador del
espíritu creativo que todos llevamos dentro.
13S
Capítulo 5. Futuroa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1 Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Tweak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 64 bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4 OpenCroquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Cómo continuara. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Libros Papers o artículos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Grupos de Usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conclusión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ApéndicesBibliografía . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Herramientas usadas en el libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14S
ProgramandoconSmalltalk
15S
Enfoque
Este libro pretende mostrar a personas con algunos conocimientos de programación,
cómo el hecho de vivir en un ambiente de objetos impacta sobre el ciclo de desarrollo del
software.
La programación con Smalltalk es muy diferente al clásico ciclo edición/compilación/eje-
cución que predomina sobre la mayoría de las herramientas usadas hoy en día, ya que con
este entorno se da un proceso mucho más interactivo. De esta manera, no se penaliza la
investigación ni se castiga de manera más fuerte las primeras decisiones. Al minimizar el
costo de los cambios, se pueden posponer las decisiones más importantes del diseño hasta
el momento en que tengamos los conocimientos lo sufi cientes. Esto es, en Smalltalk se pro-
mueve un método de desarrollo donde el software se modifi ca conforme se va generando.
Esta obra pretende mostrar una parte de todo ese proceso, para lo que desarrollaremos
algunas aplicaciones de ejemplo paso a paso e iremos introduciendo conceptos,
descripciones, etc. conforme lo vayamos necesitando. Bien, estos casos prácticos nos
servirán de excusa para mostrar el uso del entorno de Smalltalk, no son objetivos en sí
mismos y no se van a desarrollar al 100%.
Metodología
Aunque todos los programadores Smalltalk comparten ciertas costumbres, cada uno lo
confi gura y utiliza de forma diferente, ya que programar en este entorno se convierte
en tarea muy personal, puesto que no olvidemos que ha nacido como la concepción del
ordenador personal.
En este manual el autor nos va a mostrar cómo usa él el entorno, lo que no quiere decir que
sea la única o mejor manera de trabajar en Smalltalk. Del mismo modo, tampoco pretende
imponer su forma de trabajo ni nada parecido, desea que cada uno de nosotros hagamos
uso de Smalltalk de la forma que más nos guste, y que compartamos con la comunidad las
mejoras que consigamos en nuestros entornos.
Probablemente, reconoceremos algunos aspectos de las, ahora denominadas, metodologías
ligeras. Bien, esto no debería extrañarnos, ya que la principal metodología ágil, que es la
Extremme Programming (Programación Extrema), se formalizó en un proyecto desarrollado
con Smalltalk, del que Kent Beck formaba parte.
Alcance del libro
Este libro no es una guía completa de Smalltalk, sólo pretende mostrar la fi losofía de uso
del entorno y servir de referencia para nuevos usuarios de Smalltalk.
La mayoría de las respuestas a las preguntas que nos hacemos cuando programamos en este
entorno, están dentro del mismo Smalltalk. Por esta razón, esta documentación pretende
enseñarnos a buscar esas respuestas en el entorno, en lugar de contestarlas directamente.
En el apéndice dedicado a la bibliografía, el autor ha incluido un listado de referencias de
manuales sobre Smalltalk que pueden servir como complemento a este libro. Algunos de
esos títulos pueden ser descargados gratuitamente de Internet.
Audiencia
El libro que el lector tiene en sus manos no pretende ser una guía para aprender a progra-
mar desde cero, sino que se presupone que el lector tiene conocimientos básicos de progra-
mación, incluso, que está familiarizado con los conceptos de la programación orientada
a objetos.
Por tanto, este manual pretende ser la referencia para programadores que estén buscando
nuevas y mejores formas de producir software y que vean en Smalltalk una opción alter-
nativa y válida. Es más, aunque no se tenga la oportunidad de usar Smalltalk en el día a
día, conocer este entorno y el paradigma de objetos tal cual fue creado, les convertirá en
mejores programadores.
Licencia
Hemos pretendido ser fi eles al ideal de libertad que rigió el desarrollo del proyecto Smalltalk,
por lo que hemos publicado este libro bajo una licencia que permite la libre circulación de
los contenidos de sus contenidos.
Se permite la copia y distribución de la totalidad o parte de esta obra sin ánimo de lucro.
Toda copia total o parcial deberá citar expresamente el nombre del autor, nombre de la edi-
torial e incluir esta misma licencia, añadiendo, si es copia literal, la mención “copia literal”.
16S 17S ¿Qué es Smalltalk / Squeak?
?
Se autoriza la modifi cación y traducción de la obra sin ánimo de lucro siempre que se haga
constar en la obra resultante de la modifi cación el nombre de la obra originaria, el autor de
la obra originaria y el nombre de la editorial. La obra resultante también será libremente
reproducida, distribuida, comunicada al público y transformada en términos similares a los
expresados en esta licencia.
Sitio web
El enfoque de esta obra hace que sea prioritario reducir al mínimo el tiempo de espera
antes de adentrarse en el entorno, ya que la experiencia es fundamental para aprender a
usar Smalltalk.
Para acelerar el proceso inicial, nuestro autor ha trabajado sobre una imagen de Squeak
pre-confi gurada con las herramientas más habituales a la hora de programar. En uno de
los apartados del libro se explica en detalle qué aspectos se instalaron y modifi caron sobre
un Squeak virgen.
Podemos encontrar la imagen, así como otros datos de interés relacionados con este libro,
en el wiki:http://smalltalk.consultar.com
SMALLTALK/SQUEAKCAPÍTULO 1CAPÍTULO 1
Resulta muy complicado ofrecer una explicación exacta acerca de qué es Smalltalk y por
qué es tan diferente a otras herramientas de programación más populares y utilizadas por
los desarrolladores.
El origen del distanciamiento por parte de la comunidad de desarrolladores respecto a este
entorno, es consecuencia de que el proyecto Smalltalk estuvo - y sigue estando - regido por
una concepción diferente acerca de cómo usar los ordenadores. Este concepto que siempre
ha marcado la línea de Smalltalk puede resumirse, en palabras de nuestro autor, en esta
frase:
“Las computadoras deben ser herramientas que sirvan como amplifi cadores
del espíritu creativo de las personas”.
Esto implica, entre otras cosas, las siguientes afi rmaciones:
ß Todo el sistema tiene que ser inteligible y, por consiguiente, modifi cable, por una sola persona.
QUÉ ES¿
18S
ProgramandoconSmalltalk
19S ¿Qué es Smalltalk / Squeak?
ß El sistema tiene que estar construido con un mínimo juego de partes intercambiables.
Cada parte puede ser modifi cada sin que altere el resto del sistema.
ß Todo el sistema tiene que estar construido basándose en una metáfora que pueda ser
aplicada en todas las partes que lo constituyen.
ß Cada componente del sistema ha de poder ser inspeccionado y modifi cado.
“Smalltalk es un ambiente de objetos.
Los objetos interactúan enviándose mensajes entre sí”.
Smalltalk incluye un lenguaje de programación que también ha sido desarrollado usando los
principios que hemos enumerado anteriormente. Esto quiere decir, por ejemplo, que el lenguaje
Smalltalk puede ser modifi cado - como cualquier otra parte - y reemplazado por otro, o que pueden
convivir diferentes lenguajes de programación en el mismo ambiente, entre otros aspectos.
Un entorno de Smalltalk típico también está constituido por muchas herramientas que
asisten en la tarea de programación. Y, como todo en este entorno, son suceptibles de ser
modifi cadas o reemplazadas por otras.
Asimismo, el entorno de Smalltalk incluye objetos para crear interfaces gráfi cas de usuarios.
Eso sí, todo escrito en Smalltalk, todo a la vista y todo modifi cable.
Los lectores tienen ante sí un libro de programación para desarrolladores con un mínimo
de conocimiento, por lo que vamos a centrarnos en las posibilidades que ofrece un entorno
Smalltalk para la creación de software. No obstante, hemos de tener muy presente que las
ideas sobre las que se fundamenta Smalltalk son lo sufi cientemente poderosas como para
generar un sistema operativo completo. Estas ideas tienen la capacidad de convertir el
uso de los ordenadores en una actividad mucho más creativa que la mera experiencia que
tenemos al trabajar con software que usamos normalmente.
Desde el punto de vista de simples programadores, podemos decir que Smalltalk:
ß Incluye un lenguaje de programación
ß Incluye una librería de clases
ß Incluye un entorno de desarrollo.
Tanto el lenguaje, como la librería de clases y el entorno de desarrollo llevan más de 30
años de uso y depuración. No en vano, Smalltalk ha sido la referencia para el desarrollo de
lenguajes y entornos de programación.
1.1 Conceptos
Smalltalk está defi nido por un conjunto muy pequeño de conceptos, pero que poseen un
signifi cado muy específi co. Resulta fácil confundir los conceptos porque estos son usados
en distintos lenguajes, con defi niciones muy diferentes también.
Objetos
Todo en Smalltalk es un objeto.
Un objeto es una parte perfectamente diferenciada del resto del ambiente, con características
y responsabilidades bien defi nidas.
Mensajes
Un mensaje es un requerimiento que se le hace a un objeto determinado para que éste lleve
a cabo algunas de sus responsabilidades.
Por tanto, un mensaje especifica qué es lo que se espera del receptor (el objeto que
recibe el mensaje) pero no obliga a responder de una determinada manera. Esto es, el
receptor es el responsable de decidir cómo se lleva a cabo la operación para contestar
al mensaje.
Interfaz
El juego de mensajes que un determinado objeto puede entender se denomina su interfaz.
Por tanto, la única forma de interactuar con un objeto es a través de su interfaz.
Encapsulación
Se llama encapsulación al hecho de que ningún objeto puede acceder a la estructura interna
de otro objeto. Así pues, sólo el objeto conoce, y puede manipular, su propia estructura
interna.
Mensajes
Interfaz
Encapsulación
S20S
ProgramandoconSmalltalk
21S ¿Qué es Smalltalk / Squeak?
Esto, sumado a que los mensajes sólo especifi can qué se espera del receptor pero no indican
cómo se debe realizar la tarea, asegura que ningún objeto dependa de la estructura interna
de otro.
El envío de mensajes junto a la encapsulación permiten el desarrollo de sistemas muy
modulares, ya que cualquier parte del mismo puede ser reemplazada por otra mientras
ésta última respete la interfaz de la parte reemplazada.
Polimorfi smo
Dos objetos son polimórfi cos entre sí cuando un determinado emisor no puede distinguir
uno del otro. Dicho de otra forma, si podemos cambiar un objeto por otro, en un
determinado contexto, es porque son polimórfi cos.
Clases
Una clase describe la implementación de un conjunto de objetos.
Los objetos individuales descritos por las clases se llaman instancias. La clase describe la
estructura interna de los objetos, al igual que especifi ca, también, cómo se responde a los
mensajes.
Variables de instancia
La estructura interna de los objetos está compuesta por variables de instancia. Así pues, las
variables de instancia son nombres que el objeto puede usar para hacer referencia a otros
objetos.
Métodos
Los métodos son la manera de especifi car cómo responden a los mensajes los objetos de
una determinada clase. Cada método especifi ca cómo se lleva a cabo la operación para
responder a un determinado mensaje.
Un método puede acceder a la estructura interna del objeto así como, también, enviarse
mensajes a sí mismo o a otros objetos. Los métodos describen, igualmente, cuál es la
respuesta que recibe el emisor (el objeto que envía el mensaje).
Todos los objetos de Smalltalk son instancia de alguna clase. La programación en Smalltalk,
por tanto, consiste en crear clases, crear instancias de esas clases y especifi car la secuencia
de envío de mensajes entre esos objetos.
Herencia
Las clases, en Smalltalk, están organizadas jerárquicamente. Una clase refi na el concepto de
otra clase más abstracta. La clase más abstracta es Object. Todas las clases son herencias de
Object (porque todo es un objeto) o herencia de alguna clase que hereda de Object.
La relación que existe entre la clase más abstracta (la superclase) y la clase más concreta (la
subclase) permite clasifi car el conocimiento que tengamos del dominio diseñado.
1.2 Programar es simular
Programar con objetos es programar una simulación. La metáfora en la programación
con objetos está basada en personifi car a un objeto físico o conceptual del dominio real en
objetos simulados del ambiente. O lo que es lo mismo, tratamos de dar vida a los objetos
reales en el ambiente dotándolos de las mismas características y funcionalidades que los
objetos reales a los que representan.
1.3 Historia del Smalltalk
Smalltalk (no se escribe SmallTalk, ni Small-talk, ni Small-Talk) es un proyecto que lleva
más de 30 años de desarrollo. Éste fue inventado por un grupo de investigadores liderados
Polimorfi smo
Clases
Variables de instancia
Métodos
Herencia
Smalltalk en el año 1977
por Alan Kay, en Xerox PARC
(Palo Alto Research Center), du-
rante la década de los 70. Dicho
proyecto de investigación dio lu-
gar a varios resultados interme-
dios que fueron conocidos como
Smalltalk/71, Smalltalk/72, Sma-
lltalk/76 y Smalltalk/80 (http://
en.wikipedia.org/wiki/Smalltalk)
22S
ProgramandoconSmalltalk
23S ¿Qué es Smalltalk / Squeak?
La versión del Smalltalk usadaactualmente es un descendiente directo del Smalltalk/80 y
conserva prácticamente todas sus características. El proyecto, desde sus inicios, ha generado
una gran cantidad de invenciones que han llegado hasta nuestros días.
Aportes del Smalltalk
Por razones que desconoce nuestro autor, el proyecto Smalltalk no cuenta con la reputación
y la fama que se merece. En la actualidad, es muy frecuente encontrarse con profesionales
de la informática que no conocen nada acerca del proyecto ni de sus aportes. Sin embargo,
utilizamos a diario algunas de las ideas que se han desarrollado en Smalltalk y muy pocos
desarrolladores son conscientes de ello.
Ordenador personal
La idea de que cada persona pueda disponer de un ordenador de uso personal, con una
potencia de cálculo sufi ciente como para ser la herramienta de acceso a la información,
tiene sus raíces en las ideas de la Dynabook de Alan Kay (http://en.wikipedia.org/wiki/
Dynabook).Dynabook).
S Maqueta de la Dynabook.
Interfaces gráfi cas de usuario
Smalltalk, desde sus inicios, ha contado con una intuitiva interfaz gráfi ca de usuario ma-
nejable por un ratón. Las ventanas superpuestas, las barras de desplazamiento, la función
de copiar y pegar, los menús de contexto, etc... forman parte de todos los desarrollos del
proyecto que, años más tarde, Apple ayudó a masifi car.
Informática
Resulta difícil enumerar la cantidad de aportes que Smalltalk ha hecho a la informática en
general. Empezando, por ejemplo, con el paradigma de orientación a objetos y terminando
con aspectos más técnicos como la operación BitBlt (http://en.wikipedia.org/wiki/Bit_blit).
Pero la lista continúa con las metodologías ágiles (http://en.wikipedia.org/wiki/Extreme_
Programming), los patrones de diseño (http://en.wikipedia.org/wiki/Design_Patterns), el
concepto de entorno de desarrollo... o conceptos como Unit Testing (http://en.wikipedia.
org/wiki/Unit_test), Refactoring y Refactoring Browser (http://en.wikipedia.org/wiki/Refac-
toring).
Asimismo, el proyecto Smalltalk también ha ayudado a afi anzar el uso de los lenguajes
dinámicos, los recolectores de basura, el uso de máquinas virtuales y un largo etcétera.
1.4 ¿Qué es Squeak?
Squeak es un Smalltalk moderno, de código abierto, escrito en sí mismo, muy portable y
muy rápido. En la actualidad, Squeak puede correr en plataformas Windows, Apple, Linux,
iPaqs, etc.
Puede considerarse a Squeak como la continuación del proyecto original de Smalltalk y
cuenta con los aportes de varios de los desarrolladores originales de los años 70, como Alan
Kay, Dan Ingalls, Ted Kaehler...
S Smalltalk en el año 80
Aportes del Smalltalk
Ordenador personal
Interfaces gráfi cas de usuario
24S
ProgramandoconSmalltalk
25S ¿Qué es Smalltalk / Squeak?
S
Maqueta de la Dynabook.
La comunidad de desarrolladores y usuarios de Squeak se extiende por todo el mundo,
cubriendo un gran abanico de áreas, entre ellos: educación, investigación, desarrollo mul-
timedia, desarrollo web, entre otros.
Squeak versión 3.4.
El proyecto Squeak comenzó en Apple, en el año 1995, porque los autores necesitaban un
entorno de desarrollo de software educativo que pudiera ser usado – e incluso programado –
por personas no técnicas. Los detalles de la motivación que dio lugar al proyecto Squeak
pueden leerse en el documento “Back to the Future – The Story of Squeak, a practical Small-
talk written in itself ” (ver bibliografía).
Squeak, en cierta forma, es un regreso a las ideas que habían motivado todo el desarrollo
del proyecto Smalltalk en los 70. Algunos de los dialectos comerciales de Smalltalk han
perdido parte de la concepción original y Squeak vuelve a retomar esos valores. Todo lo
que pueda hacerse con un ordenador, tiene que poder hacerse con Squeak.
La aplicación de dicho concepto convierte a Squeak en un ambiente muy multimedia,
donde todos los medios de expresión y soporte de información pueden confl uir en una
única herramienta. Squeak cuenta con excelente soporte para gráfi cos 2D y 3D, procesa-
miento de texto y numérico, música (MP3, MIDI, generador de sonido FM), procesamien-
to de vídeo, entre otras muchas funcionalidades.
Además, Squeak también dispone de excelentes herramientas de programación como el Re-
factoring Browser, editores de código con sintax-highlight, framework para unit-testing, etc.
Y, por supuesto, incluye inmejorables opciones para desarrollo de software comercial como
Seaside (un frameworks de desarrollo de aplicaciones web basado en continuations), acceso
a bases de datos ODBC, bases de objetos como Magma, rST – Remote Smalltalk (soporte
de objetos distribuidos), etc.
A día de hoy, fi nales de enero de 2006, existen más de 600 paquetes con aplicaciones para
instalar en Squeak registradas en SqueakMap (http://map1.squeakfoundation.org/sm).
Squeak es el dialecto de Smalltalk escogido para este libro. Sin embargo, hay que saber que
el 95% de lo que se aprenda en un determinado dialecto es perfectamente válido para el
resto de los dialectos. Por tanto, si Squeak no es la herramienta que más nos guste, pode-
mos probar con cualquiera de las otras alternativas que nos ofrece Smalltalk.
27S Programando con Smalltalk
El comienzo con Smalltalk, en un principio, puede resultar complicado para personas
que proceden de otros entornos. Por ello, vamos a hacer mención a algunos consejos y
comentarios con el objetivo de facilitar su acceso.
2.1 La curva de aprendizaje
¿Preparados para un nivel de aprendizaje alto en nuestro primer contacto con Smalltalk?
Como hemos comentado en la Introducción, Smalltalk ofrece una perspectiva distinta
acerca de cómo debería ser nuestra experiencia con los ordenadores. Esa novedosa
concepción es la causa del desconcierto que puede provocar el primer acercamiento con
Smalltalk entre los usuarios ya iniciados en informática.
2.2 Shock cultural
La mayoría de las desavenencias iniciales tienen que ver con esta forma distinta de ver y
entender la informática.
Smalltalk no es difícil, sólo es diferente.
CONSMALLTALK
CAPÍTULO 2CAPÍTULO 2 PROGRAMANDO
2.1
2.2
28S
ProgramandoconSmalltalk
29S Programando con Smalltalk
2.3 Tirar código
El código escrito es importante, pero lo es mucho más el conocimiento que vamos
obteniendo a conforme programamos.
Si adquirimos conocimiento en el día a día, probablemente el código antiguo ya no nos sea
de tanta utilidad. Tirar código no es malo, sin embargo, el no aprender a diario sí que nos
puede resultar negativo como programadores.
2.4 Trabajo incremental
Un ambiente como Smalltalk es ideal para trabajar de forma incremental. Los vicios que
hemos adquirido, como consecuencia del ciclo de desarrollo edición/compilación/prueba,
no tienen sentido en Smalltalk y, en cierta manera, tenemos que aprender a programar de
forma distinta.
Por otro lado, los cambios en Smalltalk suponen mucho menos esfuerzo que en otros
lenguajes, aspecto que permite relajarse y demorar las decisiones importantes de diseño
hasta que sepamos sufi ciente acerca del dominio. No es de extrañar, por tanto, que las
metodologías ágiles hayan sido desarrolladas en Smalltalk.
2.5 No hay archivos fuentes
En Smalltalk no existe un archivo con los fuentes, es más, el código está dentro del ambiente
de objetos. No obstante, esto no impide mover código entre imágenes de Smalltalk, incluso,
existen distintas opciones para trasladar código de un ambiente Smalltalk a otro.
2.6 El camino es largo, mejor no ir solo
Quizás la forma más fácil de aprender Smalltalk sea al lado de un experto, aunque sabemos
que es difícil conseguir un tutor a tiempo completo y de forma presencial. Sin embrago, al
igual que en otras comunidades de programadores, para Smalltalk también existen listas
de correos y grupos de usuarios que pueden ayudarnos bastante en los comienzos.
2.7 Sintaxis del lenguaje
Prácticamente todo el paradigma de objetos se puede resumir en objetos que reciben men-
sajes. La sintaxis Smalltalk es una consecuencia directa de ello, así, su estructura básica es:
2.4
2.7.1 Expresiones
Smalltalk es un lenguaje que está basado en expresiones. Por tanto, una expresión es una
secuencia de caracteres que puede ser evaluada.
La expresiones pueden ser de cuatro tipos: literales, variables, expresiones de mensajes y
expresiones de bloque.
Literales
Algunos objetos son conocidos por el compilador y pueden ser instanciados (crear
un objeto a partir de una clase específi ca en la programación orientada a objetos) con
literales.
El valor de una expresión literal es siempre el mismo objeto. A continuación vamos a ver
los distintos tipos de constantes literales.
Números
Smalltalk cuenta con una rica variedad de objetos numéricos: enteros (SmallInteger,
LargePositiveInteger y LargeNegativeInteger), coma fl otante (Float), fracciones (Fraction),
decimales (ScaledDecimal), etc.
Estos están representados por una secuencia de dígitos que pueden estar precedidos por un
signo – y/o un punto decimal.
Algunos de esos objetos pueden instanciarse usando los siguientes literales.
2.7
objeto mensaje.
Literales
Números
“Enteros”1.-1.12345678901234567890.-12345678901234567890.
“Coma fl otante”1.1.-1.1.12345678901234567890.0.-12345678901234567890.0.
30S
ProgramandoconSmalltalk
31S Programando con Smalltalk
Caracteres
Los caracteres (Character) pueden instanciarse, también, usando constantes literales.
Siempre deben estar precedidos por el símbolo $.
Caracteres
“Caracteres”$a.$b.$á.$1.$$.
Cadenas de caracteres
Las cadenas (String) son una secuencia de caracteres. Éstas responden a mensajes para
acceder a caracteres individuales, sustituir secuencias, compararlas con otras y concate-
narlas.
La secuencia de caracteres se representa encerrada entre comillas ‘’.
Símbolos
Los símbolos (Symbol) son cadenas de caracteres (String) usadas por el sistema como
nombre de clase, métodos, etc. Nunca aparecerán, en todo el sistema, dos símbolos con los
mismos caracteres, lo que permite comparaciones muy rápidas.
Los símbolos se representan como una secuencia de caracteres alfanuméricos precedidos por #
Cadenas de caracteres
Símbolos
“Cadena de Caracteres”‘!Hola mundo!’.‘Smalltalk’.‘áéíóú’.‘Un string con una comilla simple (‘’)’
Cada elemento que constituye un Array es un literal. Por lo que el literal para crear un
Array es una secuencia de literales separadas por espacios en blanco, encerradas entre pa-
réntesis ( ). y precedidas por #.
En Squeak (y en ningún dialecto más) se puede instanciar un Array con el resultado de la
evaluación de expresiones Smalltalk separadas por un punto y encerradas entre llaves {}.
ß Mensajes Los mensajes representan la interación entre los componentes de un sistema
Smalltalk. Un mensaje es un encargo que un objeto le hace a otro objeto.
Por tanto, el mensaje está compuesto por el receptor (el objeto al que se le da el aviso), el
selector (el nombre del mensaje) y, si corresponde, por los argumentos.
Desde el punto de vista sintáctico, hay tres tipos de mensajes: Unary, Binary y Keyword.
ß Mensajes Unary Los mensajes Unary son mensajes sin argumentos. De ahí que sean los
más simples y sólo consten de:
receptor mensaje.
“Símbolos”#unSímbolo.#’un símbolo con espacios’.
ArrayUn Array (arreglo) es una estructura de datos simple que permite acceder a los elementos
contenidos indicando la posición con un número.
Array
En Squeak (y en ningún dialecto más) se puede instanciar un Array con el resultado de la
“Array”#(1 2 3 4).#(1 1.0 $a ‘un string’ #unSímbolo).#(#(1) #(2)).
“Arrays en Squeak”{1 + 1. 2 class}.{#(1) class}.
“Ejemplos de mensajes Unary”-1 abs.2 class.1000 factorial.‘aeiou’ size.Date today.Time now.OrderedCollection new.#símbolo class.String category.
32S
ProgramandoconSmalltalk
33S Programando con Smalltalk
al receptor. Por otro, el receptor devuelve información con un objeto como resultado
del envío de mensajes.
Los métodos, que son la forma que tienen los objetos de responder a los mensajes,
pueden especifi car el valor de retorno usando el carácter ^.
Precedencia
Los mensajes se evalúan de izquierda a derecha. Así, los mensajes Unary tienen pre-
cedencia sobre los Binary y, a su vez, los Binary tienen precedencia sobre los mensajes
Keyword.
No obstante, siempre se pueden romper las reglas de precedencia utilizando el paréntesis.
Las simples reglas de precedencia, de la sintaxis Smalltalk, tienen algunas implicacio-
nes imprevistas para personas acostumbradas a otros lenguajes de programación. Por
ejemplo, en Smalltalk el compilador no sabe de sumas y multiplicaciones. Esto impli-
ca que el compilador no puede determinar que, cuando operamos con números, la
multiplicación tiene precedencia sobre la suma.
Bien, analicemos la siguiente sentencia:
ß Mensajes Binary La sintaxis de los mensajes binarios tiene un sólo argumento, además
del receptor y selector.
Los mensajes Binary están constituidos de la siguiente forma:
receptor unOperador argumento.
Los operadores válidos están compuestos por uno o más de los siguientes caracteres:
- ~ ! @ % & * + = \ | ? / > < ,
“Ejemplos de mensajes Binary”3 + 5.3 > 7.3 = 5.2 @ 10.‘Un String ‘, ‘concatenado a otro’.‘Alan Kay’ -> Smalltalk.
ß Mensajes Keyword Los mensajes Keyword están formados por una o más palabras
claves, con sus respectivos argumentos, con la siguiente estructura:
receptor palabraClave1: argumento1.
O, también, de esta manera:
receptor palabraClave1: argumento1 palabraClave2: argumento2.
Y así con tres, cuatro o más palabras claves y sus respectivos argumentos, pero la palabra
clave siempre termina en :
Valor de retorno
El lenguaje Smalltalk provee un mecanismo doble de comunicación. Por un lado, el
selector y los argumentos del mensaje permiten que el emisor le envíe información
Valor de retorno
“Ejemplos de mensajes Keyword”‘Un String’ fi rst: 3.‘Un String’ allButFirst: 3.‘Un String’ copyFrom: 2 to: 5.5 between: 1 and: 10.1 to: 5.Array with: 1 with: nil with: ‘string’
unMétodoConValorDeRetorno2 “Este es un ejemplo de método que responde al mismo receptor como valor de retorno.
Si el método no tiene un valor de retorno explícito, el receptor es el resultado”
unMétodoConValorDeRetorno1 “Este es un ejemplo de método que responde nil como valor de retorno”
^ nil
Las simples reglas de precedencia, de la sintaxis Smalltalk, tienen algunas implicacio-
‘string’ at: -2 negated >>> ‘string’ at: (-2 negated)
‘string’ at: 2 + -1 negated >>> ‘string’ at: (2 + (-1 negated))
3 + 2 * 4 >>> 20
Los nombres de clases son, también, variables globales
Cuando se crea una clase, Smalltalk genera una variable global que hace referencia
al objeto clase. De ahí se desprende que la convención de nombres de clase sea la
misma que la convención de nombres de variables globales.
34S
ProgramandoconSmalltalk
35S Programando con Smalltalk
Según las reglas de precedencia de Smalltalk, se envía primero el mensaje + (con el
argumento 2) y al resultado (5) se le envía el mensaje * (con el argumento 4). De esa
forma, el resultado es 20 y no 11 como hubiese sido lo normal en otros lenguajes.
No obstante, hemos de saber que siempre podemos utilizar paréntesis para forzar la
precedencia que deseamos:
La estructura de un nombre de variable se compone de una secuencia de letras y dígitos,
pero siempre empezando por una letra. Las variables temporales y de instancia comienzan
con una letra minúscula y las globales comienzan con una mayúscula.
Otra convención en lo que respecta al nombre de las variables es que, si éste está compuesto de
varias palabras, cada una (excepto la inicial en algunos casos) debe comenzar por mayúscula.
Una constante literal siempre se refi ere a un único objeto, pero una variable puede referirse
a distintos objetos en diferentes momentos.
Tipos de variables
Existen dos tipos de variables: variables privadas y variables compartidas.
Variables privadas
Estas variables sólo son accesibles por un objeto y, a su vez, pueden ser: variables de
instancia y variables temporales.
3 + (2 * 4) >>> 11
Cascading messages (mensajes en cascada)
En algunas ocasiones es necesario enviarle varios mensajes al mismo receptor. En el
lenguaje Smalltalk existe una forma sintáctica de enviar más de un mensaje al mismo
receptor.
Esta manera de la que hablamos consiste en terminar el envío del primer mensaje con
el carácter punto y coma ; y, a continuación, escribir el siguiente mensaje.
ß Variables La memoria disponible para un objeto se organiza en variables. Todas las va-
riables tienen un nombre y cada una de ellas hace referencia a un único objeto en cada
momento.
Todas las variables han de declararse antes de usarlas, la declaración consiste en una sen-
tencia en la que fi gura el tipo de dato y el nombre que asignamos a la variable. Una vez
declarada se le podrá asignar valores.
El nombre de las variables puede ser usado en expresiones que quieran hacer referencia a
ese objeto.
La memoria disponible para un objeto se organiza en variables. Todas las va-
“Mensajes en cascada”Transcript clear; show: ‘algo en el Transcript’; cr; show: ‘y algo más’; cr.
métodoDeEjemplo: argumento “Este método muestra el uso de diferentes tipos de variables.
La estructura de un nombre de variable se compone de una secuencia de letras y d
argumento: argumento al método variableTemporal: variable temporal al método variableDeInstancia: variable de instancia Smalltalk: variable global each: argumento para el bloque ” | variableTemporal |
variableTemporal := Smalltalk allClasses.
variableDeInstancia := variableTemporal select:[:each | | variableTemporalAlBloque | variableTemporalAlBloque := 1. each name beginsWith: argumento
].
36S
ProgramandoconSmalltalk
37S Programando con Smalltalk
Las variables de instancia representan el estado del objeto y perduran durante toda
la vida de éste. Dos objetos diferentes, aunque pertenezcan a la misma clase, pueden
tener valores diferentes en sus variables de instancia.
Además, las variables de instancia pueden ser nombradas (cuando tienen asociado un
nombre y se las identifi ca a través de él) e indexadas (no tienen nombre, por lo que
sólo se puede acceder a ellas enviando un mensaje a la instancia con un índice espe-
cifi cando a qué variable quiere accederse).
Las variables temporales están defi nidas al comienzo de un método. A diferencia de
las variables de instancia, éstas sólo perduran mientras permanece en activo el méto-
do, bloque o programa, representando, así, un estado transitorio del objeto.
variable := ExpresiónSmalltalk.
Pseudo-variables
Una pseudo-variable es un identifi cador que referencia a un objeto. La diferencia con
las variables normales es que no se pueden asignar y siempre aluden al mismo objeto.
Esto es, el valor de una pseudo-variable no puede modifi carse con una expresión de
asignación.Variables compartidas
Las variables compartidas son..........................................................................................
..............................................................y también pueden ser de tres tipos: variables de
clase, variables pool y variables globales.
Las variables de clase son compartidas por las instancias de una clase y sus subclases,
manteniendo el mismo valor para todas las instancias. Estas variables se declaran en
la defi nición de la clase.
Las variables pool son variables compartidas por un subconjunto de clases en el sis-
tema y almacenadas en diccionarios Pool (que son colecciones de variables cuyo
ámbito es un subconjunto defi nido de clases en el sistema). Es necesario declarar en
la defi nición de la clase el nombre del diccionario Pool que las contiene, para poder
acceder a ellas.
Y, por último, las variables globales son aquellas variables compartidas por todos los
objetos.
Asignación
El objeto referenciado por una variable cambia cuando una asignación es evaluada.
Las asignaciones, en Smalltalk, tienen la siguiente estructura:
“Ejemplos de asignaciones”x := 0.y := 1.punto := x @ y.clases := Smalltalk allClasses.
“Pseudo-variables constantes”
nil. “Referencia a un objeto usado cuando hay que representar el concepto de ‘nada’ o de ‘vacío’. Las variables que no se asignaron nunca, referencian a nil”
true. “Referencia a un objeto que representa el verdadero lógico.”
false. “Referencia a un objeto que representa el falso lógico.”
“Pseudo-variables no-constantes”self. “Referencia al receptor del mensaje.”
super. “Referencia al receptor del mensaje, pero indica que no debe usarse la clase del receptor en la búsqueda del método a evaluar. Se usa, sobre todo, cuando se especializa un método en una subclase y se quiere invocar el método de la superclase.”
thisContext. “Referencia al objeto contexto-de-ejecución que tiene toda la
información referente a la activación del método.”
ß Bloques En Smalltalk el comportamiento (el código) también es un objeto. Los bloques
son una forma de capturar comportamiento (código) en un objeto para utilizarlo a nuestra
discreción.
38S
ProgramandoconSmalltalk
39S Programando con Smalltalk
La forma básica de un bloque es una lista de expresiones, separadas por un punto,
encerradas entre corchetes [].
[ExpresiónSmalltalk1].
[ExpresiónSmalltalk1. ExpresiónSmalltalk2].
La forma de activar un bloque (de ejecutar el código que encierra) es enviándole el mensaje
#value. La última expresión del bloque será el valor de retorno.
“Ejemplo con un bloque sin argumentos ni variables temporales”
| bloque |bloque := [World fl ash]. “En este punto el mundo NO parpadea”bloque value. “Ahora que evaluamos el bloque el mundo SI parpadea”
Un aspecto a muy interesante del lenguaje Smalltalk es que no existen, como parte de
la sintaxis, las estructuras de control (Las formas de iteración sirven para ejecutar ciclos
repetidamente, dependiendo de que se cumplan ciertas condiciones. Una estructura de
control que permite la repetición de una serie determinada de sentencias se denomina
bucle1 (lazo o ciclo).
La funcionalidad que, en los lenguajes tradicionales, se da con extensiones en la sintaxis, en
Smalltalk se resuelve con mensajes a objetos.
“Estructuras tipo IF”Smalltalk allClasses size > 2000 ifTrue:[Transcript show: ‘¡Cuantas clases!’; cr].
Smalltalk allClasses size > 2000 ifTrue:[Transcript show: ‘¡Cuantas clases!’] ifFalse:[Transcript show: ‘¡No son tantas las clases!’].
Transcript cr.
Resulta muy interesante analizar la implementación del los métodos True>>ifTrue:
ifFalse: y False>>ifTrue:ifFalse: para entender cómo funcionan las estructuras de control
con objetos y mensajes.
Igualmente, a los bloques también se les puede activar con argumentos. La sintaxis para
defi nir un bloque que necesita argumentos para evaluarse es la siguiente:
[:argumento1 | Expresiones].
[:argumento1 :argumento2 | Expresiones].
“Estructuras tipo IF”| número paridad |
número := 11.
(número \\ 2) = 0 ifTrue:[paridad := 0] ifFalse:[paridad := 1].
Transcript show: ‘el número ‘, número asString, ‘ tiene paridad igual a ‘, paridad asString; cr.
“Estructuras tipo IF”| número paridad |
número := 11.paridad := (número \\ 2) = 0 ifTrue:[0] ifFalse:[1].
Transcript show: ‘el número ‘, número asString, ‘ tiene paridad igual a ‘, paridad asString; cr.
“Estructura tipo WHILE”| index |index := 1.[index < 10] whileTrue:[ Transcript show: index; cr. index := index + 1 ].
“Estructuras tipo FOR”1 to: 10 do:[:index | Transcript show: index; cr].1 to: 10 by: 2 do:[:index | Transcript show: index; cr].
25 timesRepeat:[Transcript show: ‘.’].Transcript cr.
40S
ProgramandoconSmalltalk
41S Programando con Smalltalk
Para activar un bloque que requiere un argumento se usa el mensaje #value:, para activar
uno que requiere dos argumentos se usa el mensaje #value:value:, y así sucesivamente para
bloques con tres o más argumentos.
“El mensaje #do: requiere un bloque con un argumento”| suma |suma := 0.#(2 3 5 7 11 13) do:[:primo | suma := suma + primo].Transcript show: suma; cr.
“Lo mismo el mensaje #collect:”| productos |productos := #(2 3 5 7 11 13) collect:[:primo | primo * primo].Transcript show: productos; cr.
Y, por último, se pueden declarar variables temporales al bloque con la siguiente sintaxis.
[| variableTemporal | Expresiones].
[:argumento1 | | variableTemporal | Expresiones].
ß Comentarios Se pueden insertar comentarios en cualquier parte del código, los cuales
han de estar encerrados entre comillas dobles. El primer comentario de un método se
considera comentario del método.
El comentario es un texto adicional que se añade al código para explicar su funcionalidad,
por lo que es parte importante de la documentación de una herramienta. Además, no
incrementa el tamaño del archivo porque es ignorado por el compilador.
ß Método de ejemplo El siguiente método de ejemplo muestra muchos de los aspectos de
la sintaxis del lenguaje Smalltalk.
“A las colecciones SortedCollection se les puede especifi car un bloque con 2 argumentos como comparador”| colección |colección := SortedCollection sortBlock:[:x :y | x size < y size].colección add: ‘un string más largo’.colección add: ‘un string’.colección add: #(#un #array #con @simbolos).Transcript show: colección; cr.
2.8 Herramientas
Un ambiente Smalltalk típico cuenta con muchas herramientas que asisten en la tarea
de desarrollo de software. Según el estilo que hemos escogido para el libro, explicaremos
las herramientas en función lo vayan requiriendo los ejemplos que desarrollamos en el
capítulo ¡Manos a la Obra!
2.9 Librería de Clases y Frameworks
La imagen de un Smalltalk cuenta con cientos, incluso miles, de clases cuyas funcionalidades
son perfectamente aprovechables para nuestro desarrollo.
métodoDeEjemplo: argumento “Este es un pequeño método que muestra varias de las partes de la sintaxis del lenguaje Smalltalk.
El método tiene envío de mensajes Unary, Binary y Keyword; declara argumentos y variables temporales; accede a una variable global; usa literales como array, character, symbol, string, integer y fl oat; usa las pseudo-variables true, false, nil, self y super; usa bloques con y sin argumentos, con y sin variables temporales al bloque; hace una asignación; devuelve un resultado al fi nalizar y, fundamentalmente, no hace nada útil.
Basado en el método encontrado en: http://wiki.cs.uiuc.edu/VisualWorks/A+small+method+that+uses+all+of+the+Smalltalk+syntax”
| variableTemporal bloqueConVariableTemporal |
true & false not & (nil isNil) ifFalse: [self halt].
variableTemporal := self size + super size.
bloqueConVariableTemporal := [ | variableTemporalAlBloque | variableTemporalAlBloque := 1. variableTemporalAlBloque := variableTemporalAlBloque + 1 ]. bloqueConVariableTemporal value.
#($a #a ‘a’ 1 1.0) do: [:each | Transcript show: (each class name); show: ‘ ‘; cr].
^ argumento < variableTemporal
2.8
42S
ProgramandoconSmalltalk
43S Programando con Smalltalk
Las clases incluidas en Smalltalk nos ofrecen, entre otras cosas, la siguientes utilidades:
Números
Existen todo tipo de números (enteros, coma flotante, fracciones, etc.).
Colecciones
El framework de colecciones de Smalltalk es uno de los más antiguos y más funciona-
les que existen en la actualidad. La lista de colecciones incluye Bag, Set, OrderedCollec-
tion, SortedCollection, Dictionary, etc.
String
Soporte para cadenas de caracteres de bytes y cadenas que soportan caracteres unicode.
Boolean
Las clases Boolean, True y False se usan, entre otras cosas, para implementar algunas
de las estructuras de control.
Cronología
Clases como Date, Time, DateAndTime, Month, Week, Year,
Gráficos
Smalltalk está muy relacionado con el desarrollo de las interfaces de usuario gráfi-
cas. En los diversos ambientes de Smalltalk completamente auto-contenidos (como
Squeak), todo lo referente al procesamiento gráfico está implementado en el mis-
mo Smalltalk y, por ende, se puede inspeccionar y modificar por el usuario. Así,
se cuenta con operaciones 2D básicas (como BitBtl) hasta soporte para gráficos
3D con OpenGL. Squeak, a sía de hoy, tiene soporte para colores con el canal alfa
(transparencia), anti-aliasing, renderizado de TTF (True Type Fonts), etc.
Stream
En algunas ocasiones es necesario combinar operaciones de acceso a los elementos
de una colección con operaciones de inserción de elementos. Los típicos mensajes de
enumeración de las colecciones de Smalltalk no permiten insertar elementos mien-
tras se lleva a cabo la iteración. La jerarquía de clases de Stream permite la iteración
(repetición de una secuencia de instrucciones) de colecciones a la vez que la inserción
de elementos. La metáfora de los streams de objetos funcionó tan bien en Smalltalk
que, a partir de entonces, se usa para acceder a fuentes de datos externas en el ambien-
te y en muchos lenguajes orientados a objetos.
Weak References
Se puede hacer un uso avanzado del recolector de basura (mecanismo implícito
de gestión de memoria) utilizando referencias débiles a objetos. Las referencias
débiles, al contrario de las referencia normales o fuertes, no evitan que un objeto
sea reclamado por el recolector. Un objeto puede ser requerido por el recolector
cuando no tenga referencias en absoluto, o sólo tenga referencias débiles. Resulta
muy útil para implementar cachés, pool de instancias, mecanismos de finalización
de objetos, etc.
Multithreading
Smalltalk soporta multithreading desde los inicios. Se cuenta con una rica variedad
de clases para hacer programación concurrente de forma sencilla. Las clases Process
(Proceso = Thread) y Semaphore (Semáforo) sirven de base para la programación
con threads.
ExcepcioneS
Smalltalk cuenta con un moderno esquema de excepciones. A diferencia de otros
lenguajes, toda la implementación de excepciones está escrita en el mismo lenguaje.
Entre otras cosas, el mecanismo de excepciones de Smalltalk permite continuar con
la ejecución en el punto siguiente donde se produjo la excepción.
Metaclases
Todo en Smalltalk es un objeto. Todos los objetos tienen una clase y, las clases, como
todo, son objetos. Las clases, a su vez, tienen su clase, que se llama Metaclase. Todo el
mecanismo de herencia está modelado con clases y metaclases.
44S
ProgramandoconSmalltalk
45S
Seaside
Es un framework para hacer aplicaciones web basado en continuations. Un framework
como Seaside simplifi ca muchísimo el manejo del fl ujo de un sitio web.
SUnit
Éste es el framework del que derivan todos los frameworks de unit-testing que existen.
Magma
Base de datos de objetos, multiusuario, que permite una completa transparencia a la
hora de persistir objetos.
2.10 Máquina Virtual
Recordemos que la máquina virtual (virtual machine - VM) permite ejecutar distintos
sistemas operativos simultáneamente sobre el mismo hardware.
La máquina virtual de Squeak está escrita en Smalltalk. El truco consiste en la utilización de
un subconjunto del lenguaje Smalltalk que puede ser traducido a lenguaje C y compilado
para la plataforma necesaria. Esto permite, entre otras cosas, un nivel de portabilidad muy
alto que convierte a Squeak en una de las piezas de software más portable contando, a día
de hoy, unas 25 plataformas diferentes.
Todo el proceso de depuración de la máquina virtual puede hacerse en Squeak, lo que
facilita mucho la investigación con arquitecturas de VM diferentes, extensiones, etc.
La VM de Squeak, en el momento de escribir este libro, es de 32 bits (aunque ya exista una
VM de 64 bits) y cuenta con un recolector de basura (garbage collector) generacional de
muy altas prestaciones.
2.10 OBRA !CAPÍTULO 3CAPÍTULO 3
En este capítulo ya nos vamos a introducir de lleno en la programación con Smalltalk.
Veremos cómo aprender a programar en este entorno trasciende al mero aprendizaje de
una sintaxis y una librería de clases. Digamos que es mucho más importante saber cómo
utilizar el entorno para nuestro benefi cio y cómo trabajar con un ambiente de objetos
vivos.
Smalltalk no es sólo un lenguaje de programación, sino que también es un entorno donde
conviven objetos que interactúan entre sí mediante el envío de mensajes. Por tanto, toda la
programación se desarrolla como el resultado del envío de mensajes a los objetos. Cuan-
do un usuario interactúa con un ambiente Smalltalk, éste último se ve modifi cado como
efecto de esa interacción.
Recordemos que en Smalltalk todo es un objeto. Tanto las clases como los métodos tam-
bién son objetos, por lo que, para impactar sobre ellos, debemos enviarles mensajes. Esto
implica que la programación no es diferente a cualquier otra tarea que se haga en un en-
torno Smalltalk: objetos que reciben mensajes y que reaccionan como efecto de ese envío.
Manual de referencia
MANOS A LA!
“Cambiar el color al Mundo” World color: Color lightYellow muchLighter. World color: Color white.
“El Mundo se oscurece por un instante” World fl ash.
“Juguemos con el borde del Mundo” World borderWidth: 4. World borderColor: Color red.
Figura 2
SS
S
Figura 3
46S
ProgramandoconSmalltalk
47S Manos a la obra
En Smalltalk no existe diferencia entre desarrollo y ejecución, sino que la programación se
hace modifi cando objetos mientras estos están en funcionamiento. Para ilustrar mejor esta
idea vamos a desarrollar, paso a paso, tres ejemplos sencillos que también nos servirán para
tomar un primer contacto con las principales herramientas de desarrollo en Smalltalk.
3.1 Modifi cando objetos vivos
Vamos a crear una clase, de nombre
Cliente, que será la representación en
nuestro entorno de los clientes reales
de un supuesto sistema de factura-
ción. Para esto utilizaremos una de las
herramientas que nos ofrece Smalltalk
para interactuar con el ambiente: el
Browser de Clases.
Esta opción la encontraremos acti-
vando el menú del Mundo de Squeak
con un clic y seleccionando la opción
open... para obtener el submenú de la
Figura 1.
El Mundo
El objeto gráfi co que contiene a todos los demás objetos se llama Mundo. El Mundo
de Squeak es similar al escritorio de los sistemas operativos actuales, pero respetan-
do la regla básica de Smalltalk: todo es un objeto.
Por tanto, el Mundo es un
objeto y disponemos de
una variable global lla-
mada World para acce-
der a él. Podemos evaluar
algunas de las siguientes
sentencias para ver cómo
reacciona el Mundo:
A continuación seleccionamos la op-
ción browser (b), tal y como vemos en
la Figura 2.
Y obtenemos el Browser de Clases, cuya
apariencia es la que podemos ver en la
Figura 3.
Browser de Clases
Esta herramienta nos permite ver y modifi car todas las clases que tenemos en nues-
tro entorno. Además, también tenemos la posibilidad de crear o eliminar Categorías
de Clases, Clases, Categorías de Métodos y Métodos.
Como Squeak está escrito en sí mismo, a través de esta utilidad podemos ver abso-
lutamente todo su funcionamiento, desde el compilador hasta las ventanas, desde los
números enteros hasta los tipos booleanos, todo es visible y todo es modifi cable.
S
Figura 1
S
Figura 1
S
Figura 2
SSS
Figura 4Figura 4
S
Figura 5Figura 5
SFigura 6Figura 6
S48S
ProgramandoconSmalltalk
49S Manos a la obra
Tipos de Browser de Clases
El Browser está compuesto principalmente por 4 paneles superiores y un panel inferior. En
los paneles superiores encontraremos, de izquierda a derecha, las Categorías de Clases, las
Clases, las Categorías de Métodos y los Métodos, tal como se indica en la Figura 4.
Existen diferentes tipos de Browser de Clases, sin embargo, prácticamente todos
comparten la funcionalidad básica. En este manual usaremos el Refactoring Browser
que, además de brindarnos las características del Browser básico, nos ofrece opciones
para la refactorización del código ( concepto que veremos más adelante).
Tanto las Categorías de Clases como las Categorías de Métodos no tienen una se-
mántica para Smalltalk en sí mismo, sino que sirven para documentar el diseño
agrupando clases y métodos por funcionalidad para facilitar al usuario el manejo
del sistema.
En Smalltalk se lee mucho más código del que se escribe, por lo que todo el entorno
promueve la escritura de código limpio y documentado, viéndose recompensado
así el tiempo que utilicemos en la programación.
Categorías de Clases y Métodos
Bien, si seleccionamos una de las opciones del panel, en los paneles subsiguientes veremos
la información correspondiente a la selección. Por ejemplo, si elegimos en el primer panel
la categoría de clases Kernel-Objects, en el segundo panel tendremos las clases dentro de
esa categoría como Boolean, DependentsArray, False, MessageSend, etc. De la misma
forma, si ahora seleccionamos la clase Boolean en el segundo panel, en el tercero veremos
las categorías de métodos de esa clase; y si elegimos una categoría de métodos como con-
trolling, obtendremos los métodos en el último panel. Por último, si seleccionamos uno de
los métodos, veremos en el panel inferior el código de dicho método (ver Figura 5).
Ahora vamos a crear una categoría de clases para incluir dentro la nuestra, Cliente. Para
conseguir esto, pedimos el menú contextual, haciendo clic con el botón azul del ratón, del
panel de Categorías de Clases y seleccionamos la opción add item...( ver Figura 6). S
Figura 7Figura 7
S
Figura 8Figura 8
S
50S
ProgramandoconSmalltalk
51S Manos a la obra
Squeak es multiplataforma y a día de hoy funciona en más de 20 plataformas
diferentes: GNU/Linux, varios sabores de Unix, Windows, Apple, PDA como iPaq
y un largo etcétera. Sin embargo, esta variedad conlleva algunos problemas como,
por ejemplo, el uso de los distintos tipos de ratones que posee cada una de ellas.
Por motivos históricos que se remontan al ratón de la computadora Xerox Alto, a
los 3 botones del ratón se les nombra con colores: Rojo, Amarillo y Azul.
Botón Rojo:
El Botón Rojo es el que se usa para seleccionar. En prácticamente todas las plata-
formas este botón se mapea al botón principal de la plataforma (normalmente, el
botón izquierdo del ratón, o el tap del lápiz).
Botón Amarillo:
El Botón Amarillo se utiliza para pedir el halo de los objetos gráfi cos. En GNU/Li-
nux se utiliza el botón derecho, en Windows el botón del medio o ALT-clic y, en
Apple, Opt-clic.
Botón Azul:
El Botón Azul se usa para obtener el menú contextual. En GNU/Linux (y todos
los sabores de Unix) obtenemos el menú contextual haciendo clic con el botón del
medio del ratón, en Windows se obtiene el menú contextual con un clic del botón
derecho y, en Apple, con Cmd-clic.
A continuación tecleamos el nombre de la nueva categoría, en nuestro caso, Facturacion
(ver Figura 7).
Squeak y el Ratón Presionamos Intro o hacemos clic en el botón Accept(s) y obtenemos el resultado que
nos muestra la Figura 8.
Al crear una nueva categoría, ésta se selecciona automáticamente y en el panel inferior
aparece una plantilla (template) para crear una clase nueva. Para incluir una nueva clase
tenemos que enviarle un mensaje a la superclase, acción que podemos ejecutar a través de
la plantilla que nos ofrece el Browser de Clases. Simplemente reemplazamos NameOfSub-
class por el nombre de la clase que queremos crear (ver Figuras 9 y 10).
Ahora debemos aceptar nuestro cambio usando la opción accept (s) el menú contextual
del panel inferior, tal y como nos muestran las Figuras 11 y 12.
En Squeak el foco del teclado está en el control al que esté apuntado el puntero del
ratón. Por ello antes de teclear algo hay que asegurarse que estemos apuntando al
control con el ratón.
Foco del Teclado
Figuras 9 y 10
S
S
Figura 9
Figura 10
Figura 11 Figura 11
S Figura 12
52S
ProgramandoconSmalltalk
53S Manos a la obra
El Workspace (espacio de trabajo) es una ventana que nos permite ordenar el código
que vamos evaluando de forma interactiva en nuestro entorno. Por tanto, se trata
de una herramienta importante y útil, aunque en Smalltalk se puede evaluar código
en cualquier panel de texto y no sólo en los espacios de trabajo.
Workspace
S
Figura 15
Figura 13 y 14
Figura 15
Figura 13 y 14
S
Seguidamente escribimos en el re-
cién abierto Workspace la siguien-
te instrucción:
Para abrir un Workspace o Shout Workspace, de nuevo usaremos el menú del Mundo
(World) | Open... | Workspace o Shout Workspace.
Cliente new
S54S
ProgramandoconSmalltalk
55S Manos a la obra
Un Shout Workspace es un espacio de trabajo que muestra sintax-highlight (añade
colores según la semántica de la sintaxis) conforme lo utilizamos. Para este libro
usaremos esta herramienta porque es más vistosa que el Workspace tradicional. No
obstante, desde el punto de vista de funcionalidad no existe ninguna diferencia en-
tre ambas alternativas.
Lo seleccionamos con el ratón,
activamos el menú contextual y
elegimos la opción inspect it (i)
(ver Figura 15).
Tipos de Workspace
Sentencias de ejemplo para evaluar, imprimir,inspeccionar o explorar
“Algunas cositas con String” ‘un string’. ‘un string’ , ‘ concatenado a otro’. ‘un string de cierto tamaño’ size. ‘*string*’ match: ‘un string’.
“Algunos números” 34. 1 / 3. (1 / 3) asFloat. 3.141592653589793. 1000 factorial.
“Algo del Mundo” World extent. World color.
“Expresiones Booleanas” 1 > 3. true. false.
“Otras Expresiones” nil. Date today. Time now. Time millisecondsToRun: [1000 factorial].
“Algunas cositas con colecciones” Smalltalk allClasses. Smalltalk allClasses collect:[:each | each name]. Smalltalk classNames. Smalltalk classNames size. Smalltalk classNames select:[:each | ‘*String*’ match: each].
Evaluando Código
Al evaluar código Smalltalk de forma interactiva, disponemos de cuatro opciones
sobre cómo tratar la respuesta.
do it evaluar el código e ignorar la respuesta.
print it evaluar el código e imprimir por pantalla la representación
como String de la respuesta.
inspect it evaluar el código e inspeccionar el resultado. Ésta es la opción
utilizada en el ejemplo.
explore it evaluar el código y explorar el resultado. El Explorer es otro
formato de Inspector.
SS
Figura 16Figura 16
S
Figura 17Figura 17
S
Figura 18Figura 18
S
Figura 19Figura 19
S56S
ProgramandoconSmalltalk
57S Manos a la obra
Esta acción nos brindará otra de las herramientas de Smalltalk: el Inspector (ver Figura 16).
El Inspector es una herramienta que nos permite ver cómo está compuesto un obje-
to, qué variables de instancia tiene y qué valores tienen estas variables de instancia.
En Smalltalk, absolutamente todos los objetos pueden ser inspeccionados.
Veamos ahora cómo está compuesto el Inspector que, como hemos dicho, nos ofrece
la posibilidad de ver un objeto por dentro y enviarle mensajes.
En el panel de las variables de instancia tenemos dos variables especiales: self y all
inst var. Al seleccionar self, en el panel de la derecha vemos la representación como
String del objeto inspeccionado. Y, si seleccionamos all inst var, vemos todas las
variables de instancia como si fuesen una sola.
Ahora vamos a modifi car ligeramente la clase Cliente, pero lo haremos sin cerrar el Inspec-
tor a la instancia. Es decir, vamos a cambiar la estructura del objeto mientras éste esté vivo.
Inspector
Así, volvemos al browse y nos aseguramos que estén seleccionadas la categoría de clase
‘Facturacion’ y la clase Cliente. Entre las comillas simples que están detrás de la palabra
instanceVariableNames tecleamos lo que vemos en la Figura 18.
Y aceptamos los cambios como lo hicimos anteriormente (menú contextual, opción
accept (s)).
Algunas de las opciones que hemos ido utilizando durante el ejemplo tienen una
letra entre paréntesis, el hot-key. Podemos invocar esas opciones presionando ALT
y la letra.
Ejemplo:
browser (b) » ALT-b
workspace (k) » ALT-k
accept (s) » ALT-s
inspect it (i) » ALT-i
explore it (I) » ALT-MAYÚSCULA-i
Si todo ha salido bien, ya deberíamos ver cómo el Inspector a la instancia de Cliente que
dejamos abierto nos muestra una nueva variable de instancia (ver Figura 19).
Hot - Keys
Figura 17
S
Figura 18Figura 18
S Figura 19
xml
58S
ProgramandoconSmalltalk
59S Manos a la obra
Ahora vamos a ponerle un nombre a nuestro cliente, para lo que seleccionamos la variable
de instancia en el panel de la izquierda y, a continuación, escribimos ‘pedro’ en el panel
de la derecha:
Aceptamos los cambios (menú contextual y opción accept (s) o ALT-s), y el resultado lo
vemos en la Figura 18.
El ejemplo que acabamos de hacer deja en evidencia una diferencia fundamental de Smalltalk
respecto a los lenguajes de programación tradicionales, en los que un objeto nunca sobre-
vive a un cambio del programa. En Smalltalk es posible, y habitual, modifi car un sistema
mientras está funcionando.
Este ejemplo, además, nos ha mostrado algunas de las herramientas principales de Smalltalk
(Browser, Workspace e Inspector) de las que hemos visto su uso básico. Por tanto, ya esta-
mos preparados para pasar a un ejemplo más complejo.
‘pedro’
Para terminar este ejemplo saldremos de nuestro
entorno grabando todos los cambios. Para ello,
abrimos el menú del Mundo (Word) y escogemos
la opción save and quit ( ver Figura 19).Cuando
entremos nuevamente a nuestro Smalltalk, ten-
dremos todo el entorno en el mismo estado en que
lo dejamos.
3.2 Parser de XML basado en una Pila
Para procesar archivos XML, tarea que realizaremos en el próximo ejemplo, usaremos una
herramienta que simplifi ca mucho la escritura de parsers de XML. El parser que utilizare-
mos funciona de la siguiente manera: cada vez que éste encuentra un nuevo tag de XML,
envía un mensaje al objeto que está más arriba en su pila y el resultado del envío se apila.
Cuando ese mismo tag termina, el objeto se desapila. El primer objeto apilado es uno
escrito manualmente, y es el que procesará los tags de más alto nivel. El mensaje que se
envía al objeto superior de la pila tiene como parámetro un diccionario que contiene los
atributos del tag.
Veamos un ejemplo lo sufi cientemente simple como para analizarlo por completo. Imagi-
nemos un archivo XML como el siguiente:
Este archivo contiene los datos de una agenda, la cual tiene dentro personas y las personas
direcciones y datos de contacto y, a su vez, las informaciones de contacto tienen teléfonos
y direcciones de email.
A continuación, instruimos al parser para que envíe los siguientes mensajes según el tag
que se procese:
3.2
<agenda>
<persona apellido=”Gates” nombre=”Bob”>
<direccion ciudad=”Los Angeles” numero=”1239” provincia=”CA” calle=”Pine Rd.”/>
</persona>
<persona apellido=”Smith” nombre=”Joe”>
<informacion-contacto>
<email direccion=”[email protected]”/>
<telefono numero=”888-7657765”/>
</informacion-contacto>
<direccion ciudad=”New York” numero=”12789” provincia=”NY” calle=”W. 15th Ave.”/>
</persona>
</agenda>
Tag de XML Mensaje a Enviar
<agenda> #crearAgenda:
<persona> #crearPersona:
<direccion> #crearDireccion:
<informacion-contacto> #crearInformacionContacto:
<email> #crearEMail:
<telefono> #crearTelefono:
una AgendasLa pila del parser contendrá,
por tanto, lo siguiente:
una Agenda
una Agendas
60S
ProgramandoconSmalltalk
61S Manos a la obra
Antes de comenzar el parseo, incluiremos en la pila el objeto Agendas. Ese objeto es ca-
paz de crear y almacenar agendas, es decir, posee la capacidad de responder al mensaje
#crearAgenda:
Como el resultado de la evaluación del mensaje #crearAgenda: es una Agenda, ésta se apila
y es ahora la responsable de contestar al mensaje #crearPersona:
Agendas>>crearAgenda: aDictionary “Crea una nueva agenda y la guarda en el receptor” | agenda | agenda := Agenda new. self addAgenda: agenda. ^ agenda.
Smalltalk con Estilo
En Smalltalk existe un idioma que consiste en nombrar a los objetos con a o an
(uno o una en inglés) y el nombre de la clase.
De esa forma, a una instancia de la clase String se le llama aString o a String, una
instancia de Date se llama aDate o a Date, una instancia de Integer es anInteger o
an Integer, etc.
El lugar ideal para conocer todas las convenciones e idiomas que se usan en Small-
talk es el libro “Smalltalk with Style” (ver bibliografía).
NombreDeClase>>nombreDeMétodo
Cuando es necesario volcar el código fuente de un método en panel, se utiliza la
convención de escribirlo con el nombre de la clase, seguido de >> y, a continuación,
el nombre del método.
Esto es una convención para el papel y no es parte de la sintaxis de Smaltalk.
El método de la clase Agenda es muy similar al anterior método #crearAgenda: excepto
porque éste último considera los atributos presentes en el tag <persona>, que vienen en el
argumento aDictionary, para crear una Persona con nombre y apellido.
Bien, ahora se ha apilado una Persona, por lo que este objeto es el responsable de contestar
al mensaje #crearDireccion:
Agenda>>crearPersona: aDictionary “Crea una nueva persona y la guarda en el receptor” | persona | persona := Persona apellido: (aDictionary at: ‘apellido’) nombre: (aDictionary at: ‘nombre’). self addPersona: persona. ^ persona.
una Persona nombre=’Bob’ apellido=’Gates’
una Agenda
una Agendas
Persona>>crearDireccion: aDictionary “Crea una nueva direccion y la guarda en el receptor” | direccion | direccion := Direccion calle: (aDictionary at: ‘calle’) numero: (aDictionary at: ‘numero’) ciudad: (aDictionary at: ‘ciudad’) provincia: (aDictionary at: ‘provincia’). self addDireccion: direccion. ^ direccion.
La Direccion devuelta por el método anterior se suma a los demás objetos, dejando la pila
de la siguiente forma:
La pila, en este punto, contiene lo siguiente:
62S
ProgramandoconSmalltalk
63S Manos a la obra
Llegados a este punto, el parser se enfrenta, por primera vez en este ejemplo, al cierre del
tag. Por tanto, al cerrarse el tag <direccion>, el parser desapila la dirección.
A continuación, se cierra el tag <persona>, así que se desapila la persona.
Ahora se envía otro mensaje #crearPersona: a la Agenda, creando una nueva Persona y
apilándola.
una Direccion calle=’Pine Rd.’ numero=’1239’ ciudad=’Los Angeles’ provincia=’CA’
una Persona nombre=’Bob’ apellido=’Gates’
una Agenda
una Agendas
una Persona nombre=’Bob’ apellido=’Gates’
una Agenda
una Agendas
una Agenda
una Agendas
una Persona nombre=’Joe’ apellido=’Smith’
una Agenda
una Agendas
Persona>>crearInformacionContacto: aDictionary “Crea una nueva informacion de contacto y la guarda en el receptor” ^ informacionContacto := InformacionContacto new.
Seguidamente se envía un mensaje #crearInformacionContacto: a la Persona como reac-
ción al tag <informacion-contacto>.
La pila queda ahora de la siguiente forma:
A continuación, el objeto superior de la pila (una InformacionContacto) recibe el men-
saje #crearEMail:
El resultado del método anterior (un EMail) se apila, pero inmediatamente después se
desapila puesto que el tag <email> se cierra y la Información de Contacto nuevamente
queda en la parte superior de la pila.
El tag <telefono> se procesa de forma similar al tag <email>.
A continuación se cierra el tag <informacion-contacto> y se desapila la Información de
Contacto.
Después, procesamos el tag <direccion> del mismo modo en que se procesó para la perso-
na anterior, apilando el resultado y desapilándolo en cuanto se cierra el tag.
El estado de la pila, en este punto, es el siguiente:
una Persona nombre=’Joe’ apellido=’Smith’
una Agenda
una Agendas
InformacionContacto>>crearEMail: aDictionary “Crear un nuevo email y lo guarda en el receptor” | eMail | eMail := EMail direccion: (aDictionary at: ‘direccion’). self addEMail: eMail. ^ eMail.
InformacionContacto>>crearTelefono: aDictionary “Crear un nuevo teléfono y lo guarda en el receptor” | telefono | telefono := Telefono numero: (aDictionary at: ‘numero’). self addTelefono: telefono. ^ telefono.
xml64S
ProgramandoconSmalltalk
65S Manos a la obra
Y, ya estamos terminando de procesar el XML, sólo resta el cierre del tag <persona> y el
del tag <agenda>. Es decir, que se desapila la persona y, a continuación, la agenda, quedan-
do la pila en el mismo estado que estaba antes de comenzar el parseo.
Una vez explicado cómo funciona el parser de XML basado en una pila, podemos comen-
zar ya con el importador de Wikipedia.
3.3 Importador de Wikipedia
En este apartado nos ocuparemos de un ejemplo un poco más complejo y, a su vez, más
completo, hablamos de crear un importador de los datos de la Wikipedia.
una Persona nombre=’Joe’ apellido=’Smith’
una Agenda
una Agendas
una Agenda
una Agendas
3.3
Wikipedia
Wikipedia es un proyecto para escribir de manera comunitaria enciclopedias libres
en todos los idiomas. Éste fue fundado por Jimmy Wales y Larry Sanger basándose
en el concepto wiki, que permite crear colectivamente documentos web sin que sea
necesaria la revisión del contenido antes de su aceptación para ser publicado en la
Red. La versión en inglés comenzó el 15 de enero de 2001 y tres años y medio des-
pués, en septiembre de 2004, unos 10.000 editores activos trabajaban en 1.000.000
de artículos en más de 50 idiomas. Podemos obtener más información en http://
es.wikipedia.org/wiki/Wikipedia
- Archivos de la Wikipedia:
http://download.wikipedia.org
http://download.wikipedia.org/wiki/Importing_a_Wikipedia_database_dump_
into_Mediawiki
Por tanto, el proyecto Wikipedia ofrece toda la información de sus sitios disponibles para
bajar desde Internet, pudiéndose descargar todos los artículos de las distintas enciclope-
dias de diferentes idiomas en un archivo XML.
Con este concepto claro, vamos a llevar a cabo un ejemplo programando de la forma más
incremental posible, es decir, ejecutaremos todo el código que podamos en el Depurador.
Veamos un XML de ejemplo sacado de la Wikipedia en Latín, pero simplifi cado y acortado
para poder analizarlo en toda su extensión:
<mediawiki>
<siteinfo>
<sitename>Vicipaedia</sitename>
<base>http://la.wikipedia.org/wiki/Pagina_prima</base>
<generator>MediaWiki 1.6devel</generator>
<case>fi rst-letter</case>
<namespaces>
<namespace key=”-1”>Specialis</namespace>
<namespace key=”0” />
<namespace key=”1”>Disputatio</namespace>
</namespaces>
</siteinfo>
<page>
<title>Astronomia</title>
<id>1</id>
<revision>
<id>46556</id>
<timestamp>2005-10-31T22:28:43Z</timestamp>
<contributor>
<ip>80.136.214.100</ip>
</contributor>
<text>’’’Astronomia’’’</text>
</revision>
</page>
<page>
<title>Auxilium</title>
<id>2</id>
<revision>
<id>44264</id>
<timestamp>2005-10-11T16:43:35Z</timestamp>
<contributor>
<ip>217.72.33.184</ip>
</contributor>
<comment>better latin from italy [[:it:Utente:OrbiliusMagister]]</
comment>
<text>’’Hic addendae sunt notiones ad usum lectoris</text>
</revision>
Figura 22Figura 22
SFigura 24Figura 24
S66S
ProgramandoconSmalltalk
67S Manos a la obra
Observamos que
un <mediawiki>
contiene <sitein-
fo> y <page>, que
los <page> inclu-
yen <revision> y
que las <revision>
tienen el texto de
las páginas.
Del mismo modo,
vemos que algu-xml
</page>
<page>
<title>Usor:Archibald Fitzchesterfi eld</title>
<id>3</id>
<revision>
<id>25286</id>
<timestamp>2002-11-26T18:33:53Z</timestamp>
<contributor>
<ip>host155-164.pool21345.interbusiness.it</ip>
</contributor>
<comment>*</comment>
<text>Studiosus sum in Universitate Torontoniense</text>
</revision>
</page>
</mediawiki>
Las clases en Smalltalk son, obviamente, también objetos. Así, la clase modela las
instancias y, la clase de la clase (la metaclase), modela la clase.
Las clases reciben mensajes (como cualquier objeto) y los métodos de clase son la
forma de responder a esos mensajes.
Por ejemplo: en Smalltalk, los constructores no tienen una semántica especial
(como sí la tienen en Java, C++, etc), aquí son simplemente métodos de clase que
tienen la responsabilidad de crear instancias.
También es frecuente encontrarse, en la clase, con métodos que operen sobre
todas las instancias de la clase, por ejemplo: #allInstances, #allSubInstances, #inspectAllInstances, etc.
Métodos de Clase vs. Métodos de Instancia
nos tags se usan
sólo para meter datos en forma de String (<sitename>, <base>, <generator>, <case>,
<namespace>, <title>, <id>, <timestamp>, <ip>, <text>, <comment>, etc). Para estos
casos, el parser nos ofrece otra forma de procesar el tag, así, defi nimos un text-tag que pro-
vocará que el objeto superior de la pila reciba un mensaje con un String como argumento
con el texto del tag, apilándose igualmente.
Con este ejemplo pretendemos mostrar las facilidades que nos brinda Smalltalk para la
programación incremental. Y, dicho esto, ya hemos analizado demasiado el XML antes de
comenzar. Empecemos.
Lo primero que tenemos que hacer es instar al parser e incluirle la información básica para
empezar a programar. Así, creamos una clase Mediawiki que incluya unos métodos de
clase para instanciar Mediawikis desde archivos XML.
Para esto, abrimos un Browser de Clases (menú del Mundo | open... | browser (b) o ALT-
b). A continuación, creamos una categoría de clases para incluir nuestra clase dentro. Para
conseguirlo, pedimos el menú contextual (haciendo clic con el botón azul del ratón) del
panel de Categorías de Clases y seleccionamos la opción add item... (ver Figura 22).
Seguidamente, tecleamos el nom-
bre de nuestra categoría de clases,
tal y como vemos en la Figura 23.
Aceptamos y obtenemos el resulta-
do que nos muestra la Figura 24.
Figura 23Figura 23
S
Figura 25Figura 25
SFigura 26Figura 26
S
Figura 27Figura 27
S
Figura 29Figura 29
S68S
ProgramandoconSmalltalk
69S Manos a la obra
Ya estamos listos para crear la primera clase de nuestro ejemplo. Para ello, reemplazamos
la orden NameOfSubclass por el nombre de la clase que queremos crear, en este caso,
Mediawiki (ver Figura 25).
Aceptamos y obtenemos el siguiente resultado (ver Figura 26 y Figura 27).
Para crear métodos de clase en lugar de métodos de instancia, debemos seleccionar el bo-
tón class, que está debajo del panel de Clases. Cuando lo hagamos, los otros dos paneles
mostrarán categorías de métodos de clase en lugar de instancia. En el caso que queramos
volver a modifi car o ver categorías de métodos de instancia, no hay más que presionar el
botón instance (ver Figura 28).
Volvemos, entonces, al botón class y creamos una categoría de métodos (de clase, en este
caso) llamada instance creation con la opción new category... del menú contextual del
panel de Categorías de Métodos y, seguidamente, elegimos la categoría entre las opciones
disponibles (ver Figura 29).
Una vez tenemos nuestra clase creada, haremos lo mismo con los métodos de clase, a
modo de constructores, para instanciar Mediawikis desde archivos XML.
Figura 29Figura 29
SFigura 29Figura 30Figura 30
S
Figura 31Figura 31
S
70S
ProgramandoconSmalltalk
71S Manos a la obra
Si todo ha ido bien, la recién creada categoría de métodos (de clase) quedará seleccionada
y el browser nos ofrecerá una plantilla para crear un método dentro de esa categoría en el
panel inferior, tal y como vemos en la Figura 30.
fromFileNamed: aString “Crea una nueva instance del receptor desde el archivo llamado aString”
| archivo instancia parser |
“abre el archivo” archivo := FileStream readOnlyFileNamed: aString.
“crea la instancia del receptor” instancia := self new.
“instancia el parser” parser := XMLStackParser on: archivo.
“la instancia recién creada es el primer objeto de la pila” parser push: instancia.
“instruimos al parser sobre como procesar el tag <mediawiki>” parser onTag: ‘mediawiki’ send: #onMediawiki:.
“le pedimos al parser que haga su trabajo” parser parseDocument.
“cerramos el archivo” archivo close.
“devolvemos la instancia al remitente del mensaje” ^ instancia
A continuación, ejecutamos las siguientes órdenes en el panel inferior y aceptamos:
El Browser de Clases nos ofrece una plantilla que podemos utilizar para crear nuevos
métodos.
Plantilla para nuevos métodos
Figura 29
SS
Figura 33Figura 33
S
Archivos de ejemplo
pre-Depurador
72S
ProgramandoconSmalltalk
73S Manos a la obra
Ahora, ya estamos en condiciones de ejecutar, por primera vez, nuestro importador de
Wikipedia. Abrimos un Workspace a través del menú del Mundo | open... | Shout Work-
space y tecleamos lo que aparece en la Figura 32.
El archivo tiny.xml y otros archivos usados en este libro se pueden descargar desde http://wiki.gnulinex.org/LibroProgramacionSmalltalk/
El archivo tiny.xml contiene exactamente el texto expuesto en la página 22.
Ahora seleccionamos la sentencia que acabamos de escribir y la evaluamos con do it (d)
o ALT-d. Veremos que, por primera vez, aparece un error (ver Figura 23). ¡Bienvenido al
maravilloso y dinámico mundo de Smalltalk!
El pre-Depurador es una herramienta que nos ofrece información sobre los errores
que se hayan producido, además de permitirnos ejecutar alguna acción para subsa-
nar el problema (ver Figura 34).
Los errores de cualquier tipo siempre pueden hacernos refl exionar sobre nuestro siste-
ma, aparte de permitirnos re-validar nuestras concepciones sobre el dominio que estamos
modelando.
El título de la ventana muestra el tipo de error, en este caso, es un típico MessageNot-
Understood (Mensaje No Entendido). Es decir, el pre-Depurador nos está indican-
do que algún objeto no supo cómo responder a un determinado mensaje. Si segui-
mos analizando un poco más, veremos que el objeto en cuestión es un Mediawiki y
el mensaje que no entiende es #onMediawiki:
Esta ventana nos ofrece varias alternativas para procesar el error:
Proceed: continuar con la ejecución ignorando el error.
Adandon: abandonar la ejecución.
Debug: depurar el error con el depurador “completo”.
Create: crear el método que falta en la clase del objeto receptor o en alguna
de sus superclases. Esta opción sólo aparece ante los errores del tipo Message-
NotUnderstood.
Figura 32Figura 32
SFigura 34Figura 34
S
Figura 35Figura 35
SSDepurador
74S
ProgramandoconSmalltalk
75S Manos a la obra
Si analizamos la información que nos da el pre-Depurador, es fácil entender que lo que su-
cede es que al parser le hemos dado la orden de enviar el mensaje #onMediawiki: cuando
encuentre un tag <mediawiki>. De esta manera, el parser se lo envía al objeto superior de
la pila, es decir, a nuestro objeto Mediawiki.
Ahora, vamos a utilizar una de las acciones que nos ofrece el pre-Depurador, la opción de
crear el método, para lo que presionamos sobre el botón Create y obtenemos lo siguiente
(ver Figura 35):
Según observamos en la Figura 34, el pre-Depurador nos ofrece la posibilidad de crear
el método en la clase del objeto receptor o en cualquiera de sus superclases. En este caso
elegimos la clase Mediawiki.
A continuación, Squeak nos pregunta en qué categoría de métodos queremos crear el
nuestro (ver Figura 36).
Figura 36Figura 36
SComo ninguna de las categorías ofrecidas sirve para nuestro cometido, seleccionamos la
opción new... y creamos la categoría de métodos parsing (Ver Figura 37).
Figura 37Figura 37
SBien, tenemos ya el Depurador, una herramienta con la que pasaremos gran parte del
tiempo programando en Smalltalk.
El Depurador permite, por un lado, examinar cómo se desarrolla la ejecución
del código de Smalltalk y, por otro, modifi car los métodos on-the-fl y y seguir el
proceso desde el punto anterior al método modifi cado. (ver Figura 38).
El Depurador es, tal vez, una de las herramientas que más opciones nos ofrece,
entre ellas: ver la pila de contextos (que forman el call-stack), ver y modifi car
los métodos involucrados en la ejecución, inspeccionar los objetos receptores,
analizar los contextos de ejecución, ver el valor de las variables temporales y los
argumentos, evaluar el código paso a paso, etc.
Figura 38Figura 38
SFigura 39Figura 39S
Depurador 100% en Smalltalk
76S
ProgramandoconSmalltalk
77S Manos a la obra
Además, a través de las siguientes funciones del Depurador, incluso podemos pro-
cesar el error (ver Figura 39).
Proceed: continuar con la ejecución ignorando el error.
Restart: reiniciar la ejecución desde el punto seleccionado en la pila de contextos.
Si el contexto seleccionado no es el superior de la pila, los que están encima de él
se descartan.
Into: seguir depurando y ver el contexto que genera el mensaje que se va a enviar.
Over: seguir depurando, pero se ignoran los detalles del envío del mensaje que se
va a enviar.
Through: continuar con la ejecución y detenerse en la primera sentencia del si-
guiente bloque.
Full Stack: ver la pila de contextos completa y no la versión reducida que se muestra
por defecto.
Where: volver a resaltar el mensaje que se está enviando.
Este Depurador está escrito en Smalltalk donde, insistimos, todo es un objeto. Tanto
las clases como los métodos y los contextos de ejecución también son objetos, por
lo que es totalmente posible escribir un Depurador 100% en Smalltalk.
Así pues, podemos llevar a cabo las mismas acciones que con el resto de los objetos.
Por ejemplo, podemos grabar la imagen con un Depurador abierto y, cuando vol-
vamos al entorno de Squeak, seguir depurando el código exactamente en el mismo
punto donde lo dejamos, aunque pasen meses desde que grabamos la imagen y la
volvemos a abrir.
Bien, el Depurador se abre en ese punto porque la implementación del método que se creó
por defecto contenía un error indicando que tenía que completarse.
Ahora vamos a ejecutar la implementación de este método, que tiene “truco“. Un objeto
de clase Mediawiki recibió el mensaje #onMediawiki: que, en teoría, debería crear un
Mediawiki. Como el tag <mediawiki> sólo aparece una vez en el archivo (por lo que sólo
recibiremos un mensaje #onMediawiki:), simplemente ignoramos el tag y devolvemos el
mismo objeto para su apilación.
S
Figura 40Figura 40
S
Figura 42Figura 42
S78S
ProgramandoconSmalltalk
79S Manos a la obra
Tecleamos la siguiente instrucción en el panel que muestra el código del método del Depu-
rador y aceptamos los cambios (menú contextual y opción accept (s) o ALT-s).
El Depurador se nos mostrará con la apariencia que vemos en la Figura 40.
A continuación, presionamos el botón Proceed y seguimos con el desarrollo (ver Figura 41).
Figura 41Figura 41
SAhora, si todo ha ido bien, el parser nos va a indicar que no sabe qué hacer con el tag <si-
teinfo>, puesto que no le hemos dado ninguna orden de cómo actuar con los tags que no
sean <mediawiki>.
onMediawiki: aDictionary “No tiene sentido crear un Mediawiki si somos un Mediawiki”
^ self
parser onTag: ‘siteinfo’ send: #onSiteinfo:.
Seguidamente, pedimos el Depurador con el botón Debug y seleccionamos, en la pila de con-
textos, el que corresponde con nuestro método Mediawiki class>>fromFileNamed: (por el
que instruimos al parser para que procese cada tag), tal y como vemos en la Figura 42.
A continuación, en este mismo contexto, agregamos la siguiente secuencia:
Figura 43Figura 43
SFigura 44Figura 44
S80S
ProgramandoconSmalltalk
81S Manos a la obra
Para ganar más tiempo podemos teclear las siguientes órdenes para varios tags, quedando
el método completo de la siguiente manera:
fromFileNamed: aString
“Crea una nueva instance del receptor desde el archivo llamado aString”
| archivo parser instancia |
“abre el archivo”
archivo := FileStream readOnlyFileNamed: aString.
“crea la instancia del receptor”
instancia := self new.
“instancia el parser”
parser := XMLStackParser on: archivo.
“la instancia recién creada es el primer objeto de la pila”
parser push: instancia.
“instruimos al parser sobre como procesar el tag <mediawiki>”
parser onTag: ‘mediawiki’ send: #onMediawiki:.
parser onTag: ‘siteinfo’ send: #onSiteinfo:.
parser onTag: ‘namespaces’ send: #onNamespaces:.
parser onTag: ‘namespace’ send: #onNamespace:.
parser onTag: ‘page’ send: #onPage:.
parser onTag: ‘revision’ send: #onRevision:.
parser onTag: ‘contributor’ send: #onContributor:.
“le pedimos al parser que haga su trabajo”
parser parseDocument.
“cerramos el archivo”
archivo close.
“devolvemos la instancia al remitente del mensaje”
^ instancia
Aceptamos los cambios y el Depurador quedará como vemos en la Figura 43.
Es interesante que veamos qué ha pasado con la pila de contextos después que hemos acep-
tado los cambios. Pues bien, el Depurador se ha deshecho de los contextos que estaban por
encima del método modifi cado, así que ya podemos presionar el botón Proceed que nos
permitirá seguir con la ejecución desde el método cambiado (ver Figura 44).
Figura 45Figura 45
S
Figura 46Figura 46
SFigura 47Figura 47
S
Convención de nombres
82S
ProgramandoconSmalltalk
83S Manos a la obra
A continuación, el entorno nos dice que nuestro Mediawiki no sabe cómo contestar al
mensaje #onSiteinfo:, por lo que creamos el método siguiendo el mismo procedimiento
que utilizamos para crear el método #onMediawiki:
Para ello, presionamos el botón Create, seleccionamos la clase Mediawiki, la categoría de
métodos parsing y obtenemos el Depurador de la forma que vemos en el Figura 45.
Ahora, escribimos la siguiente instrucción:
onSiteinfo: aDictionary “Crea un SiteInfo y lo guarda en el receptor” siteInfo := SiteInfo new. ^ siteInfo
Antes de aceptar, observamos que el sintax-highlight, con el color rojo, nos está indicando
que desconoce qué es siteInfo y SiteInfo, variable de instancia y clase, respectivamente.
Aceptamos y vemos cómo reacciona el entorno (ver Figura 46):
Los nombres de variables de instancia, los argumentos y las variables temporales, por defecto, comienzan por una letra minúscula. Igualmente, las variables de clase, las globales y los nombres de clases comienzan por letra mayúscula.
Un sitio de referencia donde pueden consultarse todas las convenciones e idiomas usados en Smalltalk es el libro “Smalltalk with Style” (ver bibliografía).
Bien, parece que desconoce una variable llamada siteInfo, por lo que nos ofrece dos al-
ternativas: crear una variable temporal o crear una variable de instancia. En nuestro caso,
elegimos la segunda opción, cuyo resultado lo vemos en la Figura 47.
Ahora, el entorno nos comunica que tampoco conoce SiteInfo, ofreciéndonos varias op-
ciones. En el primer grupo (las tres que están antes de la primera línea) nos propone crear
una clase, una variable global o una variable de clase. En el segundo grupo de opciones
considera que cometimos un error al teclear y nos ofrece alternativas parecidas a lo que es-
cribimos nosotros, pero válidas. Y, fi nalmente, nos da la opción de cancelar la operación.
Figura 49Figura 49
S
Figura 48Figura 48
S
Figura 50Figura 50
SFigura 51Figura 51
S84S
ProgramandoconSmalltalk
85S Manos a la obra
Nosotros queremos crear una nueva clase, así que seleccionamos la opción defi ne new
class. Al hacerlo, Squeak nos pregunta en qué categoría de clases queremos incluirla, así
que elegimos Squeakpedia (ver Figura 48).
Al aceptar, el entorno nos muestra el mensaje que va a enviar para crear la clase, pudiendo
modifi car algo si lo deseamos, tal y como vemos en la Figura 49:
Pero aceptamos la plantilla sin introducir cambio alguno (ver Figura 50).
A continuación, el Depurador nos muestra el método recién aceptado y el sintax-highlight
nos confi rma que siteInfo y SiteInfo ya son términos conocidos. Del mismo modo, vemos
en el Inspector del receptor (los dos paneles inferiores, a la izquierda) que hay una nueva
variable de instancia. Y, si además tenemos un Browser de Clases abierto con la categoría de
clases Squeakpedia seleccionada, veremos que también aparece una nueva clase.
Es importante remarcar que se han ejecutado la creación de métodos, la creación de va-
riables de instancia y la creación de una nueva clase mientras estábamos depurando la
aplicación, es decir, modifi camos el entorno a la vez que lo estamos usando.
En este momento, a nuestro autor le gustaría decir: “prueben esto en su lenguaje de preferen-
cia”, pero no sería políticamente correcto, así que sigamos.
Continuando con el ejemplo, ahora presionamos sobre el botón Proceed para seguir eje-
cutando la aplicación, tal y como vemos en la Figura 51.
Ahora el parser nos indica que no sabe cómo manejar el tag <sitename>. Si analizamos
el XML de ejemplo, vemos que ese tag se usa para contener un texto. Bien, el parser nos
Figura 52Figura 52
S86S
ProgramandoconSmalltalk
87S Manos a la obra
permite manejar estos casos de forma especial, así que le decimos que <sitename> es un
textTag. Después, pedimos el Depurador (presionando el botón Debug) y elegimos el mé-
todo Mediawiki class>>fromFileNamed: añadiendo lo siguiente:
Y ya que estamos metidos en faena, podemos completar algunos text-tags más dejando el
método de la siguiente manera:
“instruimos al parser como procesar los text-tags” parser onTextTag: ‘sitename’ send: #siteName:.
fromFileNamed: aString “Crea una nueva instance del receptor desde el archivo llamado aString”
| archivo parser instancia |
“abre el archivo” archivo := FileStream readOnlyFileNamed: aString.
“crea la instancia del receptor” instancia := self new.
“instancia el parser” parser := XMLStackParser on: archivo.
“la instancia recién creada es el primer objeto de la pila” parser push: instancia.
“instruimos al parser sobre como procesar el tag <mediawiki>” parser onTag: ‘mediawiki’ send: #onMediawiki:. parser onTag: ‘siteinfo’ send: #onSiteinfo:. parser onTag: ‘namespaces’ send: #onNamespaces:. parser onTag: ‘namespace’ send: #onNamespace:. parser onTag: ‘page’ send: #onPage:. parser onTag: ‘revision’ send: #onRevision:. parser onTag: ‘contributor’ send: #onContributor:.
“instruimos al parser como procesar los text-tags” parser onTextTag: ‘sitename’ send: #siteName:. parser onTextTag: ‘base’ send: #base:. parser onTextTag: ‘generator’ send: #generator:. parser onTextTag: ‘case’ send: #case:. parser onTextTag: ‘namespace’ send: #name:. parser onTextTag: ‘title’ send: #title:. parser onTextTag: ‘id’ send: #id:. parser onTextTag: ‘timestamp’ send: #timestamp:. parser onTextTag: ‘ip’ send: #ip:. parser onTextTag: ‘text’ send: #text:. parser onTextTag: ‘comment’ send: #comment:.
Aceptamos los cambios y la ventana del Depurador queda ahora como vemos en la Figura 52.
“le pedimos al parser que haga su trabajo” parser parseDocument.
“cerramos el archivo” archivo close.
“devolvemos la instancia al remitente del mensaje” ^ instancia
Figura 55Figura 55
S
Figura 56Figura 56
SFigura 57SFigura 57
S88S
ProgramandoconSmalltalk
89S Manos a la obra
De nuevo, estamos listos para continuar presionando el botón Proceed (ver Figura 53).
Elegimos crear un método (pulsando sobre Create) en la clase SiteInfo, tal y como vemos
en la Figura 54.
Y, seguidamente, dentro de la categoría de métodos accessing (ver Figura 55).
Ahora tenemos que implementar el método en el Depurador, tal y como lo hicimos ante-
riormente (ver Figura 56).
Para ello, escribimos la siguiente instrucción:
siteName: aByteString “Cambia el siteName del receptor” siteName := aByteString
De nuevo, el sintax-highlight nos indica, a través del color rojo, que desconoce siteName.
Bien, aceptamos los cambios y, ante la pregunta, escogemos crear una variable de instan-
cia, como vemos en la Figura 57.
Figura 53Figura 53
SFigura 54Figura 54
SFigura 55
S
Figura 58Figura 58
S
Figura 59Figura 59
S
90S
ProgramandoconSmalltalk
91S Manos a la obra
Después de esto, continuamos el proceso pulsando sobre el botón Proceed.
Ahora implementamos el método #base: de la misma forma que lo hemos hecho reciente-
mente con el método #siteName:, creando también una variable de instancia:
y continuamos presionando sobre Proceed.
Del mismo modo, repetimos el proceso para el método generator:
y para el método case:
Sin embargo, vamos a detenernos y prestar más atención para el método #onNamespa-
ces: (ver Figura 58).
Empezamos creando el método en la clase SiteInfo dentro de una nueva categoría llama-
da parsing, tal y como vemos en la Figura 59.
base: aByteString “Cambia el base del receptor” base := aByteString
generator: aByteString “Cambia el generator del receptor” generator := aByteString
case: aByteString “Cambia el case del receptor” case := aByteString
Si analizamos el XML de ejemplo, veremos que el tag <namespaces> (en plural) contiene
varios tags <namespace> (en singular), así que necesitamos una colección para poder
guardar los distintos namespace que contiene un Mediawiki.
Dicho esto, implementamos el método de la siguiente manera (creando la variable de ins-
tancia namespaces cuando nos pregunte):
onNamespaces: aDictionary “Crea una colección vacía donde guardar los namespaces venideros” namespaces := Set new.
Colecciones
Colecciones - Set
Ejemplos:
92S
ProgramandoconSmalltalk
93S Manos a la obra
Smalltalk cuenta con un framework de colecciones muy poderoso, hecho que no es
de extrañar, ya que lleva más de 20 años de depuración.
Contamos con numerosos tipos de colecciones (Bag, Set, OrderedCollection, Dic-
tionary, Array, etc) y, a su vez, con un rico y extenso protocolo de mensajes.
Algunos de los mensajes que se pueden enviar a todas las colecciones son los si-
guientes:
ß #add: agrega un objeto (dado como argumento) a la colección.
ß #addAll: agrega todos los elementos de la colección dada como argumento a la
colección receptora del mensaje.
ß #remove: remueve un determinado objeto de la colección y genera un error si el
elemento no es parte de la colección.
ß #remove: ifAbsent: remueve un determinado elemento de la colección y evalúa
el bloque dado si el elemento no es parte de la colección.
ß #removeAll: remueve todos los elementos del receptor que están contenidos en
la colección dada en el argumento.
ß #do: evalúa el bloque dado como argumento por cada elemento contenido en la
colección.Éste es el principal mensaje de las colecciones y, prácticamente, el resto
de mensajes están implementados usando el #do: de alguna forma. Es interesante
ver la implementación de, por ejemplo, los mensajes #select:, #collect:, #anyOne,
#detect:ifNone:, #do:separatedBy:, etc.
ß #do:separatedBy: evalúa el primer bloque dado como argumento por cada ele-
mento contenido en la colección y evalúa el segundo bloque entre elementos.
ß #select: evalúa el bloque dado por cada elemento del receptor como argumento
y colecciona los elementos en los que el bloque evalúa a true (verdadero) en una
colección del tipo del receptor. Responde esa colección nueva como resultado.
ß #reject: similar a #select:, pero colecciona los elementos para los que el bloque
evalúa a false.
ß #collect: evalúa el bloque dado por cada elemento del receptor como argumento
y colecciona los resultados en una colección del tipo del receptor. Responde esa
colección nueva como resultado.
ß #size responde el tamaño de la colección.
ß #anyOne responde algún elemento de la colección.
ß #atRandom responde uno de los elementos del receptor de forma aleatoria.
ß #detect: evalúa el bloque dado con cada elemento del receptor, devuelve el pri-
mer elemento donde el bloque evalúa a true y genera un error si ningún elemento
es encontrado.
ß #detect:ifNone: evalúa el bloque dado con cada elemento del receptor, devuelve
el primer elemento donde el bloque evalúa a true y evalúa el otro bloque dado si
ningún elemento es encontrado, devolviendo el resultado de esa evaluación.
ß #isEmpty responde si el receptor está vacío y no contiene elementos.
ß #includes: responde si el objeto dado es un elemento del receptor.
ß #includesAllOf: responde si todos los elementos de la colección dada están in-
cluidos en la colección receptora.
ß #includesAnyOf: responde si algunos de los elementos de la colección dada está
incluido en la colección receptora.
ß #allSatisfy: responde true si el bloque dado se evalúa a true por todos los ele-
mentos de la colección receptora.
ß #anySatisfy: responde true si el bloque dado se evalúa a true por algunos de los
elementos de la colección receptora.
ß #noneSatisfy: responde true si el bloque dado se evalúa a false por todos los
elementos de la colección receptora.
ß #occurrencesOf: responde cuantas veces está incluido el objeto dado en la colec-
ción receptora.
El Set es un tipo de colección que no mantiene ningún orden sobre sus elementos y,
además, no permite que un objeto esté contenido más de una vez. Por tanto, repre-
senta el concepto matemático de conjunto.
| set | set := Set new. set add: ‘un string’. set add: ‘otro string’. set add: ‘un string’. set add: ‘otro string’. set add: ‘un string’. set explore.
94S
ProgramandoconSmalltalk
95S Manos a la obra
Al igual que el método anterior devuelve al receptor (si no se especifi ca una respuesta, el
método termina retornando self), el objeto SiteInfo será también responsable de respon-
der al mensaje #onNameSpace: (ver Figura 60).
Figura 60Figura 60
SonNamespace: aDictionary “Crea un nuevo namespace y lo guarda en la colección de namespaces del receptor” | namespace | namespace := Namespace new. namespaces add: namespace. ^ namespace
“convertir colecciones de diferente tipo a Set para remover los duplicados” #(5 4 1 2 2 2 1 2 1 2 3 4 3 2 3 4 5) asSet. ‘un string que tiene muchos caracteres’ asSet.
“Los 2 sets contienen sólo un elemento” (Set with: 1) = (Set with:1 with:1).
“El orden no importa” (Set with: 1 with: 2) = (Set with:2 with:1).
Valor de retorno por defecto
Los métodos que no tengan un valor de retorno explícito – usando el carácter ^ -
terminan devolviendo al receptor.
Colecciones – mensaje #add:
Agrega un objeto (dado por argumento) a la colección.
Ahora creamos el método en la clase SiteInfo (presionando sobre el botón Create), dentro
de la categoría parsing:
Figura 61Figura 61
S
De nuevo, el color rojo del sixtax-highlight nos avisa del desconocimiento de Namespace.
Para resolverlo creamos una nueva clase en la categoría de clases Squeakpedia y continua-
mos la ejecución presionando el botón Proceed. Seguidamente, el entorno nos avisa de un
nuevo error (ver Figura 61).
name: aByteString “Cambia el name del receptor” name := aByteString.
Vamos a resolverlo creando el método en la clase Namespace, dentro de la categoría de
métodos accessing, de la siguiente manera:
Una vez que aceptemos, se creará la variable de instancia name, y continuamos la ejecu-
ción con el botón Proceed (ver Figura 62).
Figura 62Figura 62
SBien, pues ya es el momento de procesar la creación de la primera página del Mediawiki.
Para ello, generamos el método en la clase Mediawiki, en la categoría parsing, siguiendo
los pasos que ya hemos dado varias veces, y lo implementamos así:
SS
los pasos que ya hemos dado varias veces, y lo implementamos así:
onPage: aDictionary “Crea una nueva página y la guarda en la colección de páginas del receptor” | page | page := Page new.
pages isNil ifTrue:[pages := Set new].
pages add: page.
^ page
Figura 64
SFigura 65Figura 65
S
Figura 64
S
96S
ProgramandoconSmalltalk
97S Manos a la obra
Ahora hemos de crear la clase Page, dentro de la categoría de clases Squeakpedia, y la variable
de instancia pages al aceptar el método.
Debido a una inconsistencia en el formato XML de la Wikipedia, crearemos la colección de
páginas usando lazy-initialization, ya que los tags <page> no están contenidos en un tag <pa-
ges>, por lo que no podemos hacerlo siguiendo el mismo procedimiento que hemos usado
para la colección de namespaces de la clase SiteInfo.
Antes de continuar, vamos a inspeccionar el estado de nuestros objetos. Para ello, selecciona-
mos la opción self de los paneles que forman el Inspector del receptor y, desde el menú contex-
tual (botón azul), seleccionamos la opción explore ( | ) y tal y como nos muestra la Figura 63.
Figura 63Figura 63
S
Para validar si nuestro im-
portador está funcionando
correctamente, sólo hemos
de abrir y cerrar las distintas
ramas del árbol del Explo-
rador. Así, observamos que
hay tres namespaces en la
colección, que la variable de
instancia pages está en nil y
que otros datos del SiteInfo
refl ejan bien los datos del
XML (ver Figura 65). S97SS Manos a la obra
Explorador
El Explorador es una herramienta que cumple las mismas funciones que el Inspector,
pero con un formato de árbol que nos permite ir navegando a través de la estructu-
ra que forman nuestros objetos (ver Figura 64).
Las pequeños triángulos
azules que vemos en la
Figura 63 nos permiten
abrir o cerrar las ramas
para analizar la estructura
según nuestras preferen-
cias.
En el panel de código se
puede evaluar cualquier
sentencia de Smalltalk, la
única condición es que
la variable especial self
apuntará al objeto selec-
cionado en el panel superior y, si no hubiese ningún objeto seleccionado, apuntará
al objeto que está siendo explorado (la raíz del árbol).
Figura 67Figura 67
SFigura 68Figura 68
S98S
ProgramandoconSmalltalk
99S Manos a la obra
Sin embargo, si comparamos los datos que tienen nuestros objetos con el XML de ejemplo,
vemos que los objetos Namespace de la estructura no contienen el dato key que viene
como atributo del tag.
Bien, por lo que vemos con el XML, es obvio que nos hemos olvidado de incluir el dato
key en el método donde hemos creado los objetos Namespace. Por tanto, ahora tenemos
que localizar el lugar en el que se instancian estos objetos, que encontraremos con facilidad
gracias a las herramientas que nos ofrece el entorno de Smalltalk. Para ello, seguimos el
proceso que describimos a continuación.
Empezamos buscando en el Explorador uno de los objetos Namespace y lo seleccionamos.
Después pedimos el menú contextual sobre ese objeto (con botón azul) y seleccionamos la
opción browse full (b) (o presionamos ALT-b), tal y como vemos en la Figura 66.
xml...
<namespaces>
<namespace key=”-1”>Specialis</namespace>
<namespace key=”0” />
<namespace key=”1”>Disputatio</namespace>
</namespaces>
...
Figura 66Figura 66
S
Esta secuencia nos abre el Browser de Clases indicando la clase del objeto seleccionado.
Pedimos el menú contextual sobre la clase SiteInfo y señalamos la opción class refs (N)
(o presionamos ALT-MAYÚSCULA-n), como nos muestra la Figura 67.
Esa opción nos abre un tipo de Browser que muestra los métodos donde se hace referencia
a la clase Namespace. Bien, seleccionamos la clase SiteInfo en el panel superior izquierdo y
después el método en el panel superior derecho, lo que nos mostrará el código del método
que instancia los Namespace (ver Figura 68).
Figura 69Figura 69
S
Figura 70Figura 70
S
100S
ProgramandoconSmalltalk
101S Manos a la obra
¡Encontramos el error! El dato del atributo key del tag <namespace> está incluido en el
diccionario que recibimos por argumento, pero nosotros lo ignoramos. Por esto, ahora
vamos a corregir el método de la siguiente forma:
Ya estamos en disposición de cerrar algunas ventanas que no vamos a usar, como el Brow-
ser de Referencias, el Browser de Clases y el Explorador. Y, en el todavía abierto Depurador,
buscamos el contexto referente al método Mediawiki class>>fromFileNamed:, presio-
namos la opción Restart para reiniciar la ejecución y, acto seguido, pinchamos sobre el
botón Proceed.
onNamespace: aDictionary “Crea un nuevo namespace y lo guarda en la colección de namespaces del receptor” | namespace | namespace := Namespace key: (aDictionary at: ‘key’). namespaces add: namespace. ^ namespace
¿Qué ha ocurrido? Bien, vemos que se ha modifi cado la forma de instanciar los objetos
Namespace y nosotros no hemos actualizamos la clase. Por tanto, ahora vamos a selec-
cionar la opción Create y ordenamos que el método se cree dentro de la clase Namespace
class (ojo, ahora estamos creando un método de clase y no de instancia) y en la categoría
instance creation, tal y como nos muestra la Figura 70.
A continuación, implementamos el método de la siguiente manera:
key: aByteString “Devuelva una nueva instancia del receptor con el key dado” ^ self new initializeKey: aByteString
De nuevo, el sintax-highlight nos indica algo desconocido, en este caso, que no existe en
Smalltalk ningún método con el nombre #initializeKey: y que, probablemente, nos ha-
yamos equivocado de mensaje. No obstante, sabemos que no hemos cometido ninguna
equivocación, sólo que todavía no hemos creado el método correspondiente.
Seguidamente aceptamos el método y, ante la propuesta de alternativas de mensaje, selec-
cionamos la primera, que es la que realmente nos interesa, ya que nuestro mensaje no es
ningún error (ver Figura 71).
Figura 71Figura 71
S
Figura 72Figura 72
SinitializeKey: aByteString “Inicializa el key del receptor” key := aByteString
Figura 73Figura 73
S
Figura 74Figura 74
Stitle: aByteString “Cambia el title del receptor” title := aByteString
id: aByteString “Cambia el id del receptor” id := aByteString
102S
ProgramandoconSmalltalk
103S Manos a la obra
Presionamos el botón Proceed para continuar y vemos el resultado en la Figura 72.
Ahora nos dice que un objeto de clase Namespace no entiende el mensaje #initializeKey:,
así que lo vamos a crear en esa misma clase, dentro de una nueva categoría de método
llamada initialization, y lo implementamos de la siguiente manera:
Nuevamente, el sintax-highlight nos indica que no conoce nada con el nombre key, así que
aceptamos y creamos una variable de instancia, tal y como vemos en la Figura 73.
102SS
Seguimos con la ejecución presionando el botón Proceed, como siempre hacemos (ver
Figura 74).
A continuación, generamos la variable de instancia title al aceptar y hacemos lo mismo
para el método #id: creando, también, la variable de instancia cuando aceptamos:
Llegados a este punto, vamos a detenernos unos instantes para implementar el método
#onRevision: (ver Figura 75).
Creamos el método en la clase Page dentro de la categoría accessing:
Figura 75Figura 75
S
id: aByteString “Cambia el id del receptor” id := aByteString
timestamp: aByteString “Cambia el timestamp del receptor” timestamp := aByteString
104S
ProgramandoconSmalltalk
105S Manos a la obra
Así, procedemos a crearlo dentro de la clase Page, en una nueva categoría llamada
parsing:
Al aceptar este método, creamos la variable de instancia revision y la clase Revision dentro
de la categoría de clases Squeakpedia. Después, continuamos la ejecución con el botón
Proceed (ver Figura 76).
onRevision: aDictionary “Crea una nueva revision guardándola en la coleccion de revisiones del receptor”
| revision |
revisions isNil ifTrue:[revisions := OrderedCollection new].
revision := Revision new.
revisions add: revision.
^ revision
Figura 76Figura 76
STipos de Colecciones OrderedCollection
Las colecciones OrderedCollection mantienen el mismo orden en el que han sido insertados sus elementos.
Al ser una colección con sentido de orden, expone un protocolo que incluye mensa-jes para acceder a los elementos por su posición, por ejemplo: #fi rst, #second, #third, #last, #allButFirst, #allButLast, #addLast:, #addFirst:, #add:after:, #addBefore:, etc.
Ahora hemos de completar la implementación de la clase Revision creando, primero, el
método #id: dentro de la categoría accessing con su correspondiente variable de instancia:
Figura 77Figura 77
SDespués implementamos el método #timestamp: en la clase Revision, dentro de la catego-
ría accessing y creamos, al aceptar el método, la variable de instancia:
Continuamos implementando el método #onContributor: en la categoría parsing, creán-
dose la variable de instancia contributor y la clase Contributor dentro de la categoría
Squeakpedia.
Figura 78Figura 78
S
106S
ProgramandoconSmalltalk
107S Manos a la obra
Tras este paso nos toca implementar el método #ip: en la clase Contributor, dando lugar,
así, a la variable de instancia correspondiente:
Aceptamos e inmediatamente después se creará la variable de instancia correspondiente.
Debido a que el mayor porcentaje de los datos corresponde a textos, los vamos a guardar
comprimidos para que ocupen menos espacio. Ésta es la razón por la que enviamos el
mensaje #zipped al String después de asegurarnos que el argumento sea un ByteString.
Continuamos con el ejemplo (ver Figura 80):
onContributor: aDictionary “crea un contributor y lo guarda en el receptor” contributor := Contributor new. ^ contributor
ip: aByteString “Cambia el ip del receptor” ip := aByteString
Figura 79Figura 79
Stext: aByteString “Cambiar el text del receptor” text := aByteString asByteString zipped
Y llegamos al, posiblemente, método más importante del importador. Creamos el méto-
do en la clase Revision (ver Figura 79), dentro de la categoría accessing, de la siguiente
forma:
Creamos el método #comment: en la clase Revision y dentro de la categoría accessing:
Y, fi nalmente, se generará la variable de instancia al aceptar.
Bien, pues ya hemos terminado el proceso, pero vamos a comprobar el resultado para va-
lidar que los datos del XML de ejemplo se muestren en nuestros objetos. Así, comenzamos
la exploración tal y como nos muestra la Figura 81.
Y navegamos a través de los objetos generados (ver Figura 82).
Figura 80Figura 80
Scomment: aByteString “Cambia el comment del receptor” comment := aByteString
Figura 81Figura 81
S
108S
ProgramandoconSmalltalk
109S Manos a la obra
A simple vista, parece que todo está correcto, sólo que la validación del texto que hemos
comprimido anteriormente requiere más atención. Para ello, vamos a implementar un
método que descomprima el texto.
Figura 82
A simple vista, parece que todo está correcto, sólo que la validación del texto que hemos
Figura 82
SFigura 83Figura 83
S
Primero, seleccionamos en el Explorador algún objeto de clase Revision y tecleamos en el
panel inferior lo que nos muestra la Figura 83.
text “Responde el text del receptor” ^ text unzipped
Figura 85Figura 85
S
Figura 84Figura 84
SPara continuar, sólo tenemos que presionar sobre el botón Proceed, como venimos ha-
ciendo hasta ahora. El resultado lo vemos en la Figura 85 y aparentemente el texto
está bien.
Después lo evaluamos con print it print it (p) (o ALT-p).
Y, seguidamente, lo implementamos en la categoría accessing de la siguiente forma:
xml
...
<page>
<title>Arithmetica</title>
<id>5</id>
<revision>
<id>45467</id>
<timestamp>2005-10-21T01:42:12Z</timestamp>
<contributor>
<username>FlaBot</username>
<id>509</id>
</contributor>
<minor />
<comment>Bot: Fixing wiki syntax</comment>
<text xml:space=”preserve”>== De vocabulo Arithmeticae disciplinae ==...</text>
</revision>
</page>
...
parser onTextTag: ‘username’ send: #username:.
110S
ProgramandoconSmalltalk
111S Manos a la obra
Ahora vamos a validar el importador procesando algún archivo real y de mayor tamaño,
por ejemplo, comencemos importando la Wikipedia en Latín. Es posible que, al procesar
un archivo mayor, encontremos que falta la implementación de algún otro tag que no
teníamos en el ejemplo anterior.
Así, tecleamos las líneas que vemos en la Figura 86, dentro del Workspace, y selecciona-
mos la correspondiente al archivo de la Wikipedia en Latín y lo ejecutamos explorando el
resultado.
Y obtenemos lo que nos muestra la Figura 87.
Bien, parece que tendremos que implementar algunos métodos más para procesar este
ejemplo. No obstante, esto no debe suponernos un problema porque a estas alturas ya
debemos manejar perfectamente el Depurador.
A lo largo del presente capítulo ya hemos trabajado sobre un error como éste, pero lo que
está ocurriendo ahora es que el parser nos indica que desconoce cómo reaccionar ante el
tag <username>.
Así pues, vamos a buscar en el XML el lugar donde se encuentra el tag y vemos que es otro
dato que pueden tener los Contributor. Seguidamente, instruimos al parser, en el método
Mediawiki class>>fromFileNamed: sobre un nuevo text-tag de la siguiente forma:
Ahora, reiniciamos la ejecución (ver Figura 88).
Figura 86Figura 86
SFigura 87Figura 87
S Figura 88Figura 88
S
112S
ProgramandoconSmalltalk
113S Manos a la obra
Y, a continuación, debemos implementar el método #username: como ya sabemos (clase
Contributor dentro de la categoría de métodos accessing, variable de instancia, etc.)
Seguimos tal y como nos indica la Figura 89,
y ejecutamos el mismo procedimiento para el método #id:
Ahora observamos el mensaje que nos muestra la Figura 90,
Seguimos tal y como nos indica la Figura 89
username: aByteString “Cambia el username del receptor” username := aByteString
Figura 89
y ejecutamos el mismo procedimiento para el método #id:
Figura 89
SAhora observamos el mensaje que nos muestra la Figura 90
id: aByteString “Cambia el id del receptor” id := aByteString
Figura 90Figura 90
S...
<page>
<title>Arithmetica</title>
<id>5</id>
<revision>
y vemos que ha aparecido otro tag que desconoce el parser.
Algunas revisiones tienen una especie de marca que indica que son revisiones-menores.
Seguidamente, vamos a instruir al parser con esta orden:
Y reiniciamos el proceso de nuevo (ver Figura 91).
Ahora implementamos el método #onMinor: dentro de la categoría parsing, de la siguien-
te manera:
y creamos la variable de instancia al aceptar.
Debido a la forma en que el XML nos avisa cuando alguna revisión es menor (el tag apa-
rece solo para las revisiones menores y no hay nada que indique lo contrario), deberíamos
xml
<id>45467</id>
<timestamp>2005-10-21T01:42:12Z</timestamp>
<contributor>
<username>FlaBot</username>
<id>509</id>
</contributor>
<minor />
<comment>Bot: Fixing wiki syntax</comment>
<text xml:space=”preserve”>== De vocabulo Arithmeticae disciplinae ==</text>
</revision>
</page>
...
parser onTag: ‘minor’ send: #onMinor:.
onMinor: aDictionary “Marca al receptor como minor” minor := true.
Figura 91
Ahora implementamos el método #onMinor: dentro de la categoría parsing, de la siguien-
Figura 91
S
114S
ProgramandoconSmalltalk
115S Manos a la obra
preguntarnos qué pasaría con la variable minor en las revisiones no-menores. Bien, tal y
como tenemos el código en este punto, disponemos de un true para las revisiones menores
y un nil para las demás. Para verifi carlo, podemos inicializar la variable en false.
Para ejecutar esta acción podemos utilizar el Browser de Clases y dejamos el Depurador
por unos momentos. Así, abrimos el menú del Mundo | open... | browser (b) o ALT-b,
seleccionamos la categoría de clases Squeakpedia, después, la clase Revision, y en el panel
correspondiente a las Categorías de Métodos invocamos el menú contextual (botón azul) y
escogemos la opción new category... como vemos en la Figura 92.
Figura 92Figura 92
SA continuación, elegimos la opción new.. (ver Figura 93):
Y tecleamos initialization, como vemos en
la Figura 94.
Figura 93
Y tecleamos
la
Figura 93
S Figura 94Figura 94
S
Al aceptar, el Browser nos muestra una plantilla de método en el panel inferior, tal y como
aparece en la Figura 95:
reemplazamos esta plantilla con la siguiente instrucción:
y aceptamos.
Figura 95Figura 95
Sinitialize “Inicializa el receptor” minor := false.
Inicialización de objetos
En las versiones más actuales de Squeak las instancias reciben el mensaje #initialize inmediatamente después de ser creadas, pero en otros dialectos de Smalltalk esto no es posible.
En el caso que usemos otro dialecto o una versión antigua de Squeak, se debería enviar el mensaje #initialize desde el método de clase #new de forma similar a lo que hicimos con el mensaje de clase #key: y el método de instancia #initializeKey: de la clase Namespace.
xml
116S
ProgramandoconSmalltalk
117S Manos a la obra
Bien, pues ya podemos reiniciar la ejecución. Ahora el proceso durará bastante más, pero
no hemos de preocuparnos porque eso indica que está procesando el XML sin problemas,
y nos muestra lo que vemos en la Figura 96.
Seguidamente, buscamos el tag en el XML y observamos que algunas páginas tienen res-
tricciones.
A continuación, ordenamos al parser que procese los tags <restrictions> como un text-tag
enviando el mensaje #restrictions: y reiniciamos el proceso (ver Figura 97):
...
<page> <title>Pagina prima</title> <id>328</id> <restrictions>sysop</restrictions> <revision> <id>46417</id> <timestamp>2005-10-29T06:52:57Z</timestamp> <contributor> <username>MycÄ“s</username> <id>79</id> </contributor> <comment>svg</comment> <text xml:space=”preserve”><!-- Please note that most </text> </revision> </page>...
Figura 97Figura 97
SDespués, creamos el método y la variable de instancia y seguimos. Al cabo de, aproximada-
mente 1 minuto y 40 segundos (en la máquina del autor), el proceso termina satisfactoria-
MessageTally spyOn:[Mediawiki fromFileNamed: ‘Wikipedia_Latina_pages_current_20051113.xml’]
mente. Pero, antes de abandonar el ejemplo vamos a averiguar dónde se está consumiendo
el tiempo, y para ello hemos de utilizar la clase MessageTally de la siguiente manera:
Figura 98Figura 98
SS
y obtenemos el resultado que nos muestra la Figura 98:
MessageTally
La herramienta MessageTally recolecta información sobre la cantidad de tiempo que
se consume en los métodos, mostrándonoslo de forma clara para poder optimizar
nuestro código en cuanto a velocidad de ejecución.
Figura 96
Seguidamente, buscamos el tag en el XML y observamos que algunas páginas tienen res-
Figura 96
S
118S
ProgramandoconSmalltalk
119S Manos a la obra
Si prestamos atención a los resultados del MessageTally, la Figura 99 nos muestra que el
63.3% del tiempo se está consumiendo al comprimir los textos.
Como es lógico, no tiene ningún sentido comprimir textos de pequeño tamaño, por lo que
vamos a modifi car ligeramente el método #text: para comprimir sólo los textos de más de
2 Kbytes.
En este caso no es necesario que modifi quemos el método #text porque #unzziped es lo
sufi cientemente inteligente como para darse cuenta que el string no está comprimido.
Así pues, volvemos a ejecutar el importador, usando el MessageTally, para ver el impacto de
nuestro cambio (ver Figura 100).
Comprobamos que hemos reducido el tiempo de 115 segundos a 63 segundos, un ahorro
importante para un cambio de 3 o 4 líneas de código.
Otra opción para conseguir esta optimización hubiese sido usar el TimeProfi leBrowser,
pero para ello hay que evaluar lo siguiente (ver Figura 101):
Figura 99
Como es lógico, no tiene ningún sentido comprimir textos de pequeño tamaño, por lo que
Figura 99
S2 Kbytes.
text: aByteString “Cambiar el text del receptor”
| string |
string := aByteString asByteString.
text := (string size > 2048) ifTrue:[string zipped] ifFalse:[string].
TimeProfi leBrowser spyOn:[Mediawiki fromFileNamed: ‘Wikipedia_Latina_pages_current_20051113.xml’].
Figura 100Figura 100
S
TimeProfi lerBrowser
El TimeProfi leBrowser muestra los mismos datos que el MessageTally, pero los pre-
senta en un Browser que permite modifi car directamente los métodos.
Figura 101Figura 101
SS
S
120S
ProgramandoconSmalltalk
121S Manos a la obra
Con esto damos por concluido el ejemplo del importador de Wikipedia. Así, sólo hemos
llevado los datos al Squeak pero sin hacer nada con ellos, no obstante, el objetivo del libro
es enseñar a utilizar el entorno de Smalltalk y no crear una aplicación.
De cualquier forma, los lectores quedan invitados a completar esta aplicación dotándola
de nuevas funcionalidades.
3.4 Motor de Workfl ow
El desarrollo del ejemplo anterior se ha basado en el parseo de un archivo. Smalltalk nos
ofrece excelentes alternativas para interactuar con información externa a la imagen (archi-
vos, bases de datos tanto relacionales como de objetos, sockets de tcp, etc).
Sin embargo, cuando creamos un sistema que sí está contenido completamente en la ima-
gen, las ventajas se multiplican. Prueba de ello es que, al manipular objetos de nuestra
imagen, nos olvidamos de, por ejemplo, abrir y cerrar conexiones, abrir y cerrar archivos,
de situaciones de errores por modifi cación de la información por otros programas, etc. y
sólo aplicamos las reglas de un entorno de objetos: objetos y mensajes.
Vamos a ver el último ejemplo de este capítulo para poner en práctica los conocimientos
que ya hemos adquirido. En este caso, desarrollaremos, también paso a paso, un pequeño
motor de Workfl ow.
Workfl ow
El fl ujo de trabajo (Workfl ow, en inglés) es el estudio de los aspectos operacionales
de una actividad: cómo se estructuran las tareas, cómo se realizan, cuál es su orden
correlativo, cómo se sincronizan, cómo fl uye la información que soporta las tareas
y cómo se lleva a cabo el seguimiento para el cumplimiento de las tareas. General-
mente, los problemas de fl ujo de trabajo se resuelven con redes de Petri.
Si bien el concepto de fl ujo de trabajo no es específi co a la tecnología de la infor-
mación, una parte esencial del software para trabajo colaborativo (groupware) es
justamente el fl ujo de trabajo.
3.4 En nuestro ejemplo vamos a utilizar una metodología muy similar al Test Driven
Development.
Podemos obtener más información en:
ß http://es.wikipedia.org/wiki/Workfl ow
ß http://en.wikipedia.org/wiki/Workfl ow
Test Driven Development
El objetivo de todo programador debería ser el de generar Código Limpio que Fun-
ciona (CLQF). Existen numerosas razones para escribir este tipo de código, entre
ellas, cabe destacar:
ß Generar CLQF es una forma predecible de desarrollar. Se puede saber cuándo
termina el desarrollo y despreocuparnos por un largo ciclo de depuración.
ß El CLQF nos ofrece la posibilidad de aprender todo lo que el código tiene que
decirnos.
ß El CLQF brinda mejores ventajas a los usuarios de nuestro software.
ß El CLQF permite que confi emos en nuestros compañeros y que ellos confíen en
nosotros.
ß Cuando escribimos CLQF nos sentimos mejor con nosotros mismos.
Sin embrago, existen muchos obstáculos para escribir CLQF, entonces ¿cómo po-
demos generarlo? Bien, la metodología Test Driven Development (TDD) consiste
en que los tests automáticos sean los que rijan el desarrollo. Para ello, seguimos la
siguientes reglas:
1. Escribir nuevo código sólo si tenemos un test automático que falla.
2. Eliminar la duplicación.
La aplicación de estas dos reglas imprime el siguiente ritmo al proceso de desarrollo:
ß Rojo: escribir un test que falle.
ß Verde: hacer que el test funcione lo más rápidamente posible. Podemos cometer
122S
ProgramandoconSmalltalk
123S Manos a la obra
Puesto que toda la funcionalidad se implementa sólo después de que un determinado test
lo requiera, un sistema desarrollado de esta forma contiene decenas, incluso centenares de
tests. La administración de los test es, pues, parte del desarrollo, por lo que es conveniente
contar con herramientas que nos ayuden. Para escribir y administrar los tests usaremos el
framework llamado SUnit.
cuantos errores queramos en esta etapa, ya que el objetivo es salirse del rojo cuanto
antes.
ß Refactorizar: eliminar toda la duplicación creada en el paso anterior.
Podemos encontrar más información en el libro: “Test-Driven Development by Exam-
ple” (ver bibliografía) y en http://es.wikipedia.org/wiki/Tdd y http://en.wikipedia.
org/wiki/Test_driven_development
SUnit
SUnit es el origen de todos los frameworks de Unit Testing.
Para más información, podemos consultar:
ß http://sunit.sourceforge.net/
ß http://www.xprogramming.com/testfram.htm
ß http://es.wikipedia.org/wiki/Prueba_unitaria
ß http://en.wikipedia.org/wiki/Unit_test
ß http://www.iam.unibe.ch/~ducasse/Programmez/OnTheWeb/Art8-SUnit.pdf
ß http://www.iam.unibe.ch/~ducasse/Programmez/OnTheWeb/SUnitEnglish2.pdf
Para ayudarnos en la tarea de refactorización nos serviremos de otra herramienta: el
Refactoring Browser.
Refactoring Browser y Refactoring
El Refactoring Browser es una variante del Browser de Clases que nos ofrece opciones
para automatizar las operaciones típicas de refactoring.
Y una vez que aclarados algunos conceptos comencemos con el ejemplo. Nuestro humilde
motor de Workfl ow estará compuesto, principalmente, por dos grandes grupos de objetos:
la defi nición de los procesos y las instancias de los procesos.
Las defi niciones son la explicación de cómo han de ser los procesos. Imaginemos que, por
ejemplo, los procesos llamados Venta comienzan después de recibir una orden de pedido.
Bien, entonces tendríamos que emitir una orden de producción para que la fábrica se pon-
ga en funcionamiento, después, informaríamos al departamento contable sobre la nueva
deuda que el cliente tendrá con nuestra empresa, etc.
Las defi niciones, por tanto, enumeran una serie de estados y los movimientos entre ellos.
Para más información podemos consultar: http://st-www.cs.uiuc.edu/users/brant/
Refactory/
Se llama Refactoring al proceso de modifi car el código de un desarrollo para
mejorar su estructura interna sin alterar la funcionalidad que ofrece externamente,
lo que se conoce informalmente por limpiar el código. Los tests aseguran que la
refactorización no cambia el comportamiento del código.
Podemos encontrar más información en el libro: “Refactoring – Improving the design
of existing code” (ver bibliografía).
Figura 102Figura 102
SSLas instancias de procesos son las representaciones, en nuestro sistema, de una operación
en curso. Siguiendo el ejemplo anterior, podemos decir que una instancia de la defi nición
Venta es cuando el cliente Juan Pérez nos pide 100 escritorios. Otra instancia, de la misma
defi nición, es cuando otro cliente nos pide otro producto. Varias instancias corresponden,
por tanto, a una única defi nición, ya que ésta sólo indica cómo es el procedimiento y las
instancias son una venta en particular.
Para empezar a escribir un poco de código (el autor ya lo está deseando después de tanta
introducción), comencemos con un test que pruebe la defi nición más simple que se nos
Pensar primero en la interfaz pública
Cuando escribimos un test es conveniente que evitemos pensar en cómo vamos
a implementar las clases y sus métodos. Sólo nos concentramos en cómo nos
gustaría usar esos objetos.
Si pensamos en cómo se usan los objetos (su interfaz pública) antes de cómo se
resolverán los mensajes (los métodos), crearemos interfaces mucho más limpios.
124S
ProgramandoconSmalltalk
125S Manos a la obra
ocurra y obtengamos el color rojo. Bien, usamos el Browser de Clases para crear, primero,
una categoría de clases llamada Workfl ow y luego creamos una subclase de TestCase llama-
da Workfl owTest, tal y como vemos en la Figura 103.
A continuación, creamos una categoría de métodos llamada running y, dentro, generamos
un método llamado #testLaMasSimpleDefi nición.
Figura 103
ón, creamos una categoría de métodos llamada y, dentro, generamos
Figura 103
SLos métodos de testing comienzan por #test
Los métodos que comienzan con #test serán considerados métodos de testing por
el framework SUnit. De este modo, cuando le ordenemos al framework que ejecute
todos los test de una determinada clase, éste invocará a todos los métodos cuyos
nombres comiencen con #test. El resto del nombre, por convención, nos describe
qué es lo está probando el método.
La defi nición más simple que se le ocurre a nuestro autor, es algo que comience y termine
inmediatamente, algo como lo que nos muestra la Figura 104:
Figura 104Figura 104
SS
Ahora traducimos ese gráfi co a código Smalltalk. Así, decidimos que vamos a defi nir las
transiciones y éstas harán referencia a los estados:
testLaMasSimpleDefi nición “Prueba la defi nición más simple posible” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar y terminar’ desde: ‘principio’ hasta: ‘fi n’.
self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 1]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 2]
Estructura de los test
Prácticamente todos los test tienen la siguiente estructura:
ß Instanciación de los objetos.
ß Alguna operación sobre los objetos.
ß Especifi cación del resultado esperado.
Para especifi car cuál es el resultado esperado disponemos de varios métodos
(heredados de TestCase), de los que los más importantes son:
Figura 108Figura 108
S126S
ProgramandoconSmalltalk
127S Manos a la obra
Al aceptar el método, el ambiente nos informa que desconoce Defi niciónDeProces. Para
solucionarlo, elegimos crear una clase, herencia de Object, en la categoría Workfl ow. A
continuación, nos dice que #agregarDefi niciónDeTransición:desde:hasta: es un selector
también desconocido, pero nosotros confi rmamos que está bien escrito. Después hacemos
lo mismo con los selectores #defi nicionesDeTransiciones y #defi nicionesDeEstado, por
lo que el Browser nos queda tal y como muestra la Figura 105:
ß #assert: especifi ca que el argumento debe ser true.
ß #deny: especifi ca que el argumento debe ser false.
ß #should: especifi ca que el argumento (normalmente un bloque) debe evaluarse
a true.
ß #shouldnt: especifi ca que el argumento (normalmente un bloque) debe evaluarse
a false.
ß #should:raise: especifi ca que la evaluación del primer argumento (normalmente
un bloque) debe lanzar una excepción de la clase dada (normalmente alguna
subclase de Exception) en el segundo argumento.
ß #shouldnt:raise: especifi ca que la evaluación del primer argumento (normalmente
un bloque) no debería lanzar una excepción de la clase dada (normalmente alguna
subclase de Exception) en el segundo argumento.
Figura 105Figura 105
S
Ahora abrimos la herramienta llamada SUnit Test Runner a través del menú del Mundo |
open... | SUnit Test Runner (ver Figuras 106 y 107), que es la encargada de ejecutar nues-
tros test.
Figuras 106 - 107Figuras 106 - 107
SSUnit Test Runner
Esta herramienta nos per-
mite ejecutar automática-
mente todos los tests de las
clases seleccionadas, mos-
trándonos todas las clases
que son herencias de Test-
Case pudiendo seleccionar
las que queramos.
Los botones tienen las
siguientes funciones: S
128S
ProgramandoconSmalltalk
129S Manos a la obra
Figura 109Figura 109
SSFigura 110Figura 110
S
Al abrirse el SUnit Test Runner, vemos que tiene seleccionadas todas las clases, por lo que
presionamos el botón deselect all para quitar esa selección y, a continuación, presionamos
sobre el botón Filter e ingresamos, en el cuadro de diálogo: Workfl ow* (ver Figura 109).
ß Refresh: vuelve a actualizar la lista de clases. Resulta útil cuando creamos nuevas
clases de test o borramos alguna ya existente.
ß Filter: nos permite seleccionar clases usando caracteres comodines.
ß Stop: detiene la ejecución de los tests.
ß Run One: ejecuta los tests de la clase seleccionada.
ß Run All: ejecuta los tests de todas las clases seleccionadas.
ß select all: selecciona todas las clases de test.
ß deselect all: quita la selección todas las clases de test.
ß togle selections: invierte la selección. Las clases seleccionadas dejan de estarlo y
viceversa.
Los dos paneles inferiores muestran la lista de tests que han fallado. Pueden darse
dos tipos de fallos en los tests:
1. La condición que exigimos en el test no se cumple. A estos tests se los asocia el
color amarillo y se muestran en el panel central.
2. El test cancela y no termina de ejecutar. A estos test se los asocia con el color rojo
y se muestran en el panel inferior.
Haciendo clic sobre el test con el fallo se abrirá el pre-Depurador mostrando el error
correspondiente, pudiéndose utilizar el Depurador para corregir el fallo.
Al aceptar, nuestra clase de test
recién creada queda selecciona-
da, tal y como nos muestra la
Figura 110: S
Figura 111Figura 111
S
Llegados a este punto, ya estamos listos para ejecutar, por primera vez, nuestro test
presionando el botón Run One (ver Figura 111).
¡Eureka! Ya tenemos el rojo que estábamos buscando.
Según el TDD, en este momento debemos escaparnos del rojo lo más rápidamente posi-
ble. Incluso estamos autorizamos a cometer cuantas infracciones necesitemos con tal de
lograrlo cuanto antes. Bien, ahora hacemos clic sobre el test en rojo (en el panel inferior),
tal y como vemos en la Figura 112:
Figura 111Figura 111
Sy obtenemos el pre-Depurador (ver Figura 113):
130S
ProgramandoconSmalltalk
131S Manos a la obra
Figura 114Figura 114
S
Figura 113Figura 113
SLlegamos ante una situación conocida: un objeto no entiende un determinado mensaje
y el pre-Depurador nos ofrece crear el método. Así pues, lo ejecutamos en la clase
Defi niciónDeProceso, dentro de la categoría accessing.
Al aceptar, el entorno nos ofrece la posibilidad de crear la variable de instancia transiciones,
nosotros aceptamos la propuesta y el Depurador nos queda como muestra la Figura 114:
SagregarDefi niciónDeTransición: transiciónString desde: estadoDesdeString hasta: estadoHastaString
“Agrega una defi nición de transición con el nombre dado, que une dos estados de nombres dados”
transiciones add: {transiciónString. estadoDesdeString. EstadoHastaString}
S
Continuamos, presionando el botón Proceed, y obtenemos lo que nos muestra la Figura 115:
Ahora, pinchando sobre el botón Debug, paramos unos instantes para analizar qué es
lo que está pasando. Así, seleccionamos, en el panel superior, el método Defi niciónDe-
Proceso>>agregarDefi niciónDeTransición:desde:hasta:. Tras esta operación, compro-
bamos que el mensaje se está enviando a la recién creada variable de instancia transi-
ciones. Así pues, seleccionamos la variable de instancia en el panel inferior izquierdo y
observamos que, en el panel siguiente, aparece un nil (ver Figura 116).
Figura 115
Ahora, pinchando sobre el botón Debug, paramos unos instantes para analizar qu
Figura 115
SFigura 116Figura 116
S
132S
ProgramandoconSmalltalk
133S Manos a la obra
En la variable que acabamos de ver deberíamos tener algún tipo de colección como, por
ejemplo, un Set. Bien, nos encontramos de nuevo ante un caso ya conocido: podemos
implementar el método #initialize para inicializar la variable de instancia transiciones o
podemos usar el idiom lazy-initialization. Preferimos, en este punto, la opción del lazy-ini-
tialization ya que nuestro objetivo es escapar del rojo con la máxima rapidez. La podemos
implementar directamente en el Depurador con las siguientes órdenes:
agregarDefi niciónDeTransición: transiciónString desde: estadoDesdeString hasta: estadoHastaString
“Agrega una defi nición de transición con el nombre dado, que une dos estados de nombres dados”
transiciones isNil ifTrue:[transiciones := Set new].
transiciones add: {transiciónString. estadoDesdeString. EstadoHastaString}
Aceptamos y continuamos presionando el botón Proceed (ver Figura 117):
Figura 117Figura 117
SSdefi nicionesDeTransiciones “Responde las defi niciones de transiciones del receptor” ^ transiciones
Ahora implementamos el método en la clase Defi niciónDeProceso, dentro de la categoría
accessing, de la siguiente forma:
Seguidamente, aceptamos y continuamos (ver Figura 118).
Figura 118Figura 118
S
De nuevo, implementamos el método en la clase Defi niciónDeProceso, en la categoría
accessing, con las siguientes instrucciones:accessing, con las siguientes instrucciones:
defi nicionesDeEstados “Responde las defi niciones de estados del receptor”
| resultado |
resultado := Set new. resultado addAll: (transiciones collect: [:each | each second]). resultado addAll: (transiciones collect: [:each | each third]).
^ resultado.
Colecciones - mensaje #collect:
Evalúa el bloque dado por cada elemento del receptor como argumento, reúne los
resultados en una colección del tipo del receptor y responde esa colección nueva
como resultado.
Ejemplos: “la tabla del 2” (1 to: 10) collect: [:each | each * 2].
“¿par o impar?” (1 to: 10) collect: [:each | each even].
“todo a mayúsculas” ‘una cadena’ collect: [:each | each asUppercase].
Nuevamente, aceptamos y continuamos, como venimos haciendo en todo el ejemplo.
Ahora la operación termina bien, por lo que podemos volver a ejecutar para obtener el
verde presionando el botón Run One, como nos muestra la Figura 119.
Figura 119Figura 119
S
134S
ProgramandoconSmalltalk
135S Manos a la obra
Según el TDD, debemos refactorizar el código para deshacernos de todas las infracciones
que hayamos cometido en el paso anterior. El método que implementamos no ha
quedado muy claro, así que procedemos a limpiarlo. Para ello, buscamos el método
Defi niciónDeProceso>>defi nicionesDeEstados en el Browser de Clases, tal y como
vemos en la Figura 120:
Figura 120Figura 120
SBien, lo primero que vamos a hacer es mejorar la legibilidad del método usando el patrón
llamado Explaining Temporary Variable.
Resulta barato escribir código limpio
El código se lee y se modifi ca muchísimas veces más de las que se escribe. Por
ello, cualquier inversión que hagamos en generar código limpio y entendible
reportará muchos benefi cios en el futuro.
Explaining Temporary Variable (Variable Temporal Explicativa)
¿Cómo se puede simplifi car una expresión compleja dentro de un método?
Figura 121Figura 121
SSFigura 122SFigura 122
S
Para llevar a cabo esta refactorización utilizaremos una de la funcionalidades del Refacto-
ring Browser. Así, procedemos a seleccionar la sub-expresión que queremos extraer, tal y
como vemos en la Figura 121.
Esta tarea se consigue extrayendo una sub-expresión desde la expresión compleja,
asignando el valor a una variable temporal antes de la expresión compleja y
reemplazando la sub-expresión por la variable. El nombre de la variable indicará
qué signifi ca la expresión extraída, aumentando la legibilidad del método.
Más información en el libro: “Smalltalk Best Practice Patterns” (ver bibliografía).
Después, pedimos el menú
contextual y elegimos la opción
extract to temporary..., que se
encuentra dentro del sub-menú
selection... (ver Figura 122).
S
136S
ProgramandoconSmalltalk
137S Manos a la obra
En el cuadro de diálogo que nos aparece tecleamos el nombre de la variable temporal:
estadosDesde (ver Figura 123).
Figura 123Figura 123
SS Tras aceptar, podemos ver el resultado de esta
operación en la Figura 124:
Figura 124Figura 124
SFigura 125Figura 125
SSDe nuevo, repetimos el proceso
para la otra sub-expresión (ver
Figura 125),
Ahora reescribimos el método de la siguiente forma:
usando estadosHasta como nombre de variable temporal (ver Figura 126).
Figura 126
Ahora reescribimos el método de la siguiente forma:
Figura 126
Sdefi nicionesDeEstados “Responde las defi niciones de estados del receptor”
| estadosDesde estadosHasta | estadosDesde := transiciones collect: [:each | each second]. estadosHasta := transiciones collect: [:each | each third]. ^ (estadosDesde , estadosHasta) asSet.
Colecciones – mensaje #
Concatena el receptor y el argumento en una sola colección y responde la nueva
colección.
Ejemplos: ‘un string’ , ‘ ‘ , ‘otro string’.
#(1 2) , #( 3 4).
#(1 2) , ‘diego’.
(1 to: 10) , (1 to: 5).
138S
ProgramandoconSmalltalk
139S Manos a la obra
Figura 128Figura 128
S
El método, escrito de esta manera, deja muy claro que todas las defi niciones de estados son
la concatenación de los Estadosdesde y los Estadoshasta, sin duplicados.
Ahora es el momento de ejecutar nuevamente los tests automáticos para asegurarnos que
no hemos roto nada con la refactorización (ver Figura 127).
Figura 127Figura 127
SSYa hemos cumplido uno de los ciclos del TDD. A continuación, vamos a escribir un test
antes de implementar la funcionalidad, obtendremos un rojo, por lo que pasamos al verde
lo más rápido posible, y refactorizamos el código para que quede CLQF (Código Limpio
Que Funciona).
De nuevo, escribimos otro test que instancie una defi nición de proceso un poco más
complicada (ver Figura 128):
SSeguidamente introducimos otro test, en la misma clase:
y ejecutamos los tests tal y como vemos en la Figura 129:
testDefi niciónSimple “Prueba una defi nición simple” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘A’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘A’ hasta: ‘fi n’.
self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 2]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 3].
Figura 129Figura 129
SVemos que no está nada mal. La implementación que hemos realizado es sufi ciente para
el caso anterior, pero ahora vamos a introducir un caso aún más complicado que el que
acabamos de ver (ver Figura 130).
140S
ProgramandoconSmalltalk
141S Manos a la obra
Introducimos otro test con el siguiente código:
Figura 130
Introducimos otro test con el siguiente código:
Figura 130
SStestDefi niciónNoTrivial “Prueba una defi nición no trivial” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘principio’ hasta: ‘A’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘A’ hasta: ‘B’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘anterior’ desde: ‘B’ hasta: ‘A’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘B’ hasta: ‘C’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘anterior’ desde: ‘C’ hasta: ‘B’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘C’ hasta: ‘fi n’.
self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 6]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 5].
y ejecutamos de nuevo los tests, tal como nos indica la Figura 131:
Figura 131Figura 131
SBien, en este momento tenemos tres tests en verde con la misma implementación que
realizamos para el primero de ellos. Sin embargo, ya es hora de pensar de otra forma:
disponemos de tres tests que prueban diferentes casos válidos, pero ¿qué pasa si creamos
instancias erróneas?
Probar las situaciones límites
Cuando se hacen tests es importante probar, también, las situaciones de límite.
Ejemplo: Si por alguna causa tenemos que probar que un determinado valor
esté entre 5 y 10, la forma correcta sería que escribamos, además de los tests
para los casos válidos, tests que especifi quen que 4 y 11 son casos inválidos.
En nuestro ejemplo actual enviamos varios mensajes (uno por transición) para crear una
defi nición completa, pero nos resulta imposible validar en cada envío de mensajes ya que
la estructura de datos no sería correcta en los estadios intermedios. La forma más simple
para implementar es pidiendo, explícitamente, a la defi nición que se valide a sí misma y
nos indique si es una instancia válida o no.
Seguimos, ahora modifi camos los tests agregando este código:Seguimos, ahora modifi camos los tests agregando este código:
self should:[defi niciónDeProceso esValida].
142S
ProgramandoconSmalltalk
143S Manos a la obra
testLaMasSimpleDefi nición “Prueba la defi nición más simple posible” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar y terminar’ desde: ‘principio’ hasta: ‘fi n’.
self should:[defi niciónDeProceso esValida]. self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 1]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 2].
quedando estos de la siguiente manera:
testDefi niciónNoTrivial “Prueba una defi nición no trivlal” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘principio’ hasta: ‘A’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘A’
hasta: ‘B’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘anterior’ desde: ‘B’ hasta: ‘A’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘B’ hasta: ‘C’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘anterior’ desde: ‘C’ hasta: ‘B’.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘siguiente’ desde: ‘C’ hasta: ‘fi n’.
self should:[defi niciónDeProceso esValida]. self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 6]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 5].
A continuación, ejecutamos los tests como nos indica la Figura 132:
Figura 132Figura 132
SPrevisiblemente, el resultado es la obtención de 3 rojos. Bien, ahora presionamos sobre
cualquiera de los tests para obtener el pre-Depurador, después sobre el botón Create para
crear el método que falta en la clase Defi niciónDeProceso, dentro de la categoría testing
(ver Figura 133):
testDefi niciónSimple “Prueba una defi nición simple” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘A’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘A’ hasta: ‘fi n’.
self should:[defi niciónDeProceso esValida]. self should:[defi niciónDeProceso defi nicionesDeTransiciones size = 2]. self should:[defi niciónDeProceso defi nicionesDeEstados size = 3].
144S
ProgramandoconSmalltalk
145S Manos a la obra
Figura 133Figura 133
Sy lo implementamos, por ahora (recordemos que hemos de escapar del rojo lo más rápido
posible), de la siguiente forma:
esValida “Responde si el receptor es una instancia válida” ^ true
Seguidamente, procedemos a cerrar el Depurador y ejecutamos todos los tests, como vemos
en la Figura 134:
Figura 134Figura 134
S
Ahora deberíamos escribir algunos tests que muestren cuáles son los casos inválidos. Por
ejemplo, uno de estos casos sería una defi nición de proceso que no tenga un estado inicial;
y lo volcamos en un test de la siguiente manera:
testDefi niciónSinPrincipio “Prueba una defi nición inválida que no tiene un estado inicial llamado ‘principio’” | defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘A’ hasta: ‘fi n’.
self shouldnt:[defi niciónDeProceso esValida].
Y lo ejecutamos, de nuevo, tal y como indica la Figura 135:
Figura 135Figura 135
SObservamos que hemos obtenido un test amarillo (que son los que no causan error, pero
en los que tampoco se cumplen todas las condiciones), hacemos clic sobre él y obtenemos
el pre-Depurador (ver Figura 136).
Figura 136Figura 136
S
146S
ProgramandoconSmalltalk
147S Manos a la obra
Es importante saber que, cuando hacemos clic en un test de color amarillo, el framework
paraliza la ejecución en un punto anterior a nuestro test, lo que nos permite depurar paso
a paso el test antes de llegar a las condiciones. En este caso no nos interesa ejecutarlo
con tanto detalle, por lo que presionamos el botón Proceed para continuar y obtener el
Depurador en el mismo punto del fallo (ver Figura 137).
Figura 137Figura 137
SAhora la ejecución se detiene en el punto exacto del fallo y obtenemos el pre-Depurador para
poder indagar sobre lo que ha sucedido. Presionamos el botón Debug y buscamos, en el pa-
nel superior, el contexto de ejecución correspondiente a nuestro método (ver Figura 138).
Figura 138Figura 138
S
Vemos claramente que el fallo se da en la condición que no se cumple, por lo que se de-
muestra que hemos de implementar más funcionalidad en el método Defi niciónDe-
Proceso>>esValida.
A continuación, usando algún Browser, modifi camos el método Defi niciónDeProceso>>
esValida de la siguiente forma:de la siguiente forma:
esValida “Responde si el receptor es una instancia válida” self halt. ^ true
Hacemos clic sobre el test en amarillo y presionamos el botón Proceed en el pre-Depurador
(ver Figura 139).
Mensaje #halt
El mensaje #halt se puede enviar a cualquier objeto para que se active el Depurador,
al igual que a cualquier punto del código donde necesitemos un breakpoint (punto
de ruptura).
Figura 139Figura 139
SAhora, presionamos sobre el botón Debug y buscamos, en el Depurador, el contexto corres-
pondiente al método Defi niciónDeProceso>>esValida, como vemos en la Figura 140.
El Depurador nos ofrece más información a la hora de implementar
La implementación de un método en el Depurador tiene una ventaja muy im-
portante con respecto a hacerlo en los browser, y es la disposición del contexto
en el cual se activó el método.
148S
ProgramandoconSmalltalk
149S Manos a la obra
De nuevo, volvemos a implementar el método de la siguiente forma:
La implementación de un método en el Depurador tiene una ventaja muy im-
portante con respecto a hacerlo en los browser, y es la disposición del contexto
en el cual se activó el método.
Además, disponemos de información sobre el receptor (en los dos paneles infe-
riores de la izquierda), información del contexto como argumentos y valores de
variables temporales (en los dos paneles inferiores a la derecha), información
de la pila de contextos (el call-stack), entre otros aspectos.
Y, fi nalmente, indicar que la modifi cación de algún método agregando un #halt
para que el Depurador nos muestre la información del contexto, también es una
costumbre muy extendida entre los programadores de Smalltalk.
esValida “Responde si el receptor es una instancia válida”
(self defi nicionesDeEstados includes: ‘principio’) ifFalse:[^ false].
^ true
Figura 140Figura 140
SDe nuevo, volvemos a implementar el método de la siguiente forma:
y ejecutamos los tests tal y como vemos en la Figura 141:
Figura 141Figura 141
SColecciones – mensaje #includes:
Responde si el objeto dado como argumento es uno de los elementos del receptor.
¡Verde! Nuestro autor ha caído en la cuenta que hemos cometido un error ortográfi co en el
nombre de método #esValida. Así que, nuevamente, utilizaremos una de las opciones del
Refactoring Browser para corregirlo y buscamos y seleccionamos el método en cuestión en
un Browser de Clases (ver Figura 142).
Figura 142Figura 142
S
150S
ProgramandoconSmalltalk
151S Manos a la obra
Figura 143Figura 143
S
Ahora, pedimos el menú contextual sobre el método y seleccionamos la opción rename
method, siguiendo la secuencia que nos muestra la Figura 143.
A continuación, obtenemos una ventana que nos permite cambiar el nombre de método,
así como también modifi car el orden de los argumentos, si existieran (ver Figura 144).
SSegún observamos en la Figura 144, en el primer panel, donde indica esValida, escribimos
esVálida (esta vez con tilde), aceptamos con ALT-s y presionamos el botón OK. En este
momento, el Refactoring Browser ha cambiado el nombre del método corrigiendo todos los
métodos donde se ha enviado el mensaje #esValida.
Figura 145Figura 145
S
Figura 144
Para proceder a la validación de los cambios que ha realizado el Refactoring Browser,
presionamos el botón senders del Browser de Clases, seleccionamos esVálida y vemos qué
métodos de toda la imagen envían ese mensaje (ver Figura 145).
Senders
La función senders (disponible como un botón dentro de los Browser, o como op-
ción del menú contextual sobre el método o presionando ALT-n) nos ofrece un bus-
cador con todos los métodos de la imagen que envíen dicho mensaje.
Se trata de una de la principales herramientas para buscar en Smalltalk. Así, cada caso
que nos muestra el senders es un ejemplo de uso del mensaje, pero no un ejemplo
cualquiera, sino que son ejemplos que están funcionando en la imagen y que, si fuese
necesario, podríamos depurar paso a paso. Por tanto, es mucho más útil que una do-
cumentación estática en algún archivo externo.
Figura 144
152S
ProgramandoconSmalltalk
153S Manos a la obra
Figura 146Figura 146
SAhora que estamos en verde, es el momento de limpiar el código que acabamos de generar,
y lo hacemos del siguiente modo:
esVálida “Responde si el receptor es una instancia válida”
(self defi nicionesDeEstados includes: ‘principio’) ifFalse: [^false]. ^true
Una de las causas que restan claridad en el código es el uso de constantes mágicas, que son
constantes (muchas veces un literal) que aparecen en el código sin más, sin especifi car su
signifi cado. Para evitarlo, vamos a utilizar el patrón Constant Method.
Llegados a este punto, deberíamos ejecutar, nuevamente, los tests para asegurarnos que
todo está correcto (ver Figura 146).
Constant Method (Método Constante)
¿Cómo se puede codifi car una constante ofreciendo información sobre su signifi cado?
Muy sencillo, simplemente creando un método que devuelva la constante; aprovecharse
del nombre del método y el comentario de éste para explicar el verdadero signifi cado de la
constante.Más información en el libro: “Smalltalk Best Practice Patterns” (ver bibliografía).
De nuevo, vamos a servirnos de una de las funcionalidades del Refactoring Browser (¡a
nuestro autor le entusiasma el Refactoring Browser!). Para ello, seleccionamos la parte que
corresponde a la constante, pedimos el menú contextual y elegimos la opción extract
method, tal y como nos muestra la Figura 147.
Figura 147Figura 147
SUna vez aquí, añadimos el nombre del método (de la misma forma en que lo hicimos cuando
cambiamos el nombre al método #esValida), nombreDeEstadoInicial (ver Figura 148).
Figura 148Figura 148
S
154S
ProgramandoconSmalltalk
155S Manos a la obra
Figura 150Figura 150
S
Aceptamos (con ALT-s) y presionamos sobre ENTER. El resultado lo tenemos en la Figura
149, donde vemos cómo queda el método.
Figura 149Figura 149
SLa Figura 150 nos muestra la apariencia del método recién creado:
Ahora, comentamos el método de la siguiente forma:
nombreDeEstadoInicial “Responde el nombre del estado inicial”
^ ‘principio’
Seguimos introduciendo los casos inválidos y hacemos lo mismo para el estado fi nal:
testDefi niciónSinFinal “Prueba una defi nición inválida que no tiene un estado fi nal llamado ‘fi n’”
| defi niciónDeProceso | defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘A’. self shouldnt: [defi niciónDeProceso esVálida]
Obtenemos el amarillo y, justo en el siguiente punto, implementamos la funcionalidad de
forma análoga a la anterior.
esVálida “Responde si el receptor es una instancia válida”
(self defi nicionesDeEstados includes: self nombreDeEstadoInicial) ifFalse: [^false].
(self defi nicionesDeEstados includes: self nombreDeEstadoFinal) ifFalse: [^false].
^true
nombreDeEstadoFinal “Responde el nombre del estado fi nal”
^ ‘fi n’
Ejecutamos los tests y, nuevamente, estamos en verde (ver Figura 151):
Figura 151Figura 151
S155SS Manos a la obra
156S
ProgramandoconSmalltalk
157S Manos a la obra
Nos olvidamos de, tal vez, el caso inválido más simple: una defi nición sin transiciones ni
estados:
testDefi niciónVacía “Prueba una defi nición inválida por estar vacía”
| defi niciónDeProceso | defi niciónDeProceso := Defi niciónDeProceso new. self shouldnt: [defi niciónDeProceso esVálida]
y ejecutamos los tests tal y como vemos en la Figura 152:
Seguidamente, hacemos clic sobre el test que muestra el error y abrimos el Depurador para
comprobar qué es lo que ha sucedido (ver Figura 153).
De nuevo, nos encontramos ante el caso donde la variable de instancia transiciones no
está inicializada. Si recordamos, el caso anterior lo resolvimos con lazy-initialization en el
método #agregarDefi niciónDeTransición:desde:hasta:, pero ahora no podemos hacerlo
así porque no hemos invocado dicho método. Por esto, vamos a mover la inicialización de
esa variable al método #initialize dentro de la categoría initialization (ver Figura 154):
Figura 152Figura 152
SFigura 154Figura 154
S
Figura 153Figura 153
S
y removemos el lazy-initialization que hicimos anteriormente, tal y como nos muestra la
Figura 155:
158S
ProgramandoconSmalltalk
159S Manos a la obra
Figura 155Figura 155
SComo viene siendo habitual en todo el proceso, volvemos a ejecutar los tests (ver Figura 156).
Figura 156Figura 156
SLos tests son, también, documentación
Si respetamos las simples reglas que dominan el TDD, no obtendremos ninguna
funcionalidad en el software desarrollado que no tenga, como mínimo, un test que
la valide. Esto implica que el conjunto de los test de un sistema son una excelente
documentación.
Los tests componen una documentación que se puede, además de leer, ejecutar en
el depurador paso a paso. También sabemos a ciencia cierta si esa documentación
está actualizada o no, según el test esté en verde o en rojo. Y, por si fuera poco, los
tests son documentación que se escribe a la vez que se desarrolla el sistema.
Para mejorar la documentación del sistema que estamos desarrollando, vamos a categorizar
de nuevo los tests para indicar cuáles corresponden a instanciaciones válidas y cuales a
inválidas. Bien, usamos el Browser de Clases para crear dos nuevas Categorías de Métodos:
running - instancias válidas y running - instancias inválidas (menú contextual del panel
de Categorías de Métodos, opción new category...).
Figura 157Figura 157
SAhora, arrastramos el método (desde el panel de Métodos) hasta la categoría donde
queremos incluirlo, reorganizamos los métodos y, con la opción remove empty categories
del menú contextual del panel de Categorías, borramos la categoría vacía.
Seguimos con la implementación. Otra condición que han de cumplir las defi niciones
de proceso para ser válidas, es que el estado inicial sea realmente el primero, por lo que
escribimos otro test con esta condición:
testDefi niciónPrincipioInválido “Prueba una defi nición donde el estado inicial no es realmente el inicial”
| defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘A’ hasta: ‘principio’.
160S
ProgramandoconSmalltalk
161S Manos a la obra
A continuación, ejecutamos los tests y obtenemos el amarillo esperado. Para saber si el
estado inicial es realmente el inicial, como hemos dicho más arriba, debemos asegurarnos
que ninguna transición tenga ese estado como destino.
Así, pues, modifi camos el método #esVálida de la siguiente forma.
esVálida “Responde si el receptor es una instancia válida”
(self defi nicionesDeEstados includes: self nombreDeEstadoInicial) ifFalse: [^false].
(self defi nicionesDeEstados includes: self nombreDeEstadoFinal) ifFalse: [^false].
(transiciones anySatisfy: [:each | each third = self nombreDeEstadoInicial]) ifTrue: [^false].
^true
Colecciones – mensaje #anySatisfy:
Evalúa el bloque con los elementos del receptor. Si el bloque responde true (verda-
dero) para cualquiera de los elementos, devuelve true, si no, devuelve false (falso).
Como siempre, ejecutamos los tests y estamos en verde de nuevo.
De la misma forma, tenemos que asegurarnos que el estado fi nal sea realmente el estado
fi nal. Para ello, escribimos otro test:
defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘principio’ hasta: ‘fi n’.
self shouldnt: [defi niciónDeProceso esVálida]
testDefi niciónFinalInválido “Prueba una defi nición donde el estado fi nal no es realmente el fi nal”
| defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new.
defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘fi n’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘fi n’ hasta: ‘A’.
self shouldnt: [defi niciónDeProceso esVálida]
Ahora, la ejecución de los tests nos lleva al amarillo.
esVálida “Responde si el receptor es una instancia válida”
(self defi nicionesDeEstados includes: self nombreDeEstadoInicial) ifFalse: [^false].
(self defi nicionesDeEstados includes: self nombreDeEstadoFinal) ifFalse: [^false].
(transiciones anySatisfy: [:each | each third = self nombreDeEstadoInicial]) ifTrue: [^false].
(transiciones anySatisfy: [:each | each second = self nombreDeEstadoFinal]) ifTrue: [^false].
^true
Tras esta operación, nos encontramos nuevamente con el verde, pero la claridad del método
#esVálida es bastante confusa, así que, procedemos a limpiarlo.
Además, vemos que las expresiones que determinan los cuatro casos inválidos son un poco
crípticas, por lo que vamos a hacer uso del patrón Composed Method, que explicamos a
continuación.
Composed Method (Método Compuesto)
¿Cómo se divide un programa en métodos? Esto se consigue fraccionando el
programa en métodos que lleven a cabo una tarea bien identifi cable y manteniendo
todas las operaciones de un método en el mismo nivel de abstracción.
Más información en el libro: “Smalltalk Best Practice Patterns” (ver bibliografía).
162S
ProgramandoconSmalltalk
163S Manos a la obra
En este punto, usaremos, nuevamente, la funcionalidad extract method del Refactoring
Browser para desmenuzar el método de mayor tamaño en pequeñas partes. Para ello,
primero seleccionamos la expresión de la primera condición (ver Figura 158):
y la extraemos a un método llamado tieneEstadoInicial. Hacemos lo mismo para la
siguiente expresión, extrayéndola en un método llamado tieneEstadoFinal.
Figura 158Figura 158
SSeguimos con la siguiente expresión y la sacamos a un método llamado llegaAlguna-
TransicionAlEstadoInicial. Del mismo modo, operamos igual para la última condición,
extrayéndola a un método de nombre saleAlgunaTransicionDelEstadoFinal.
El método, ahora, se muestra como verdadera documentación:
esVálida “Responde si el receptor es una instancia válida”
self tieneEstadoInicial ifFalse: [^false]. self tieneEstadoFinal ifFalse: [^false]. self llegaAlgunaTransicionAlEstadoInicial ifTrue: [^false]. self saleAlgunaTransicionDelEstadoFinal ifTrue: [^false]. ^true
y comentamos los métodos extraídos por el Refactoring Browser:
tieneEstadoInicial “Responde si el receptor tiene algún estado inicial”
^self defi nicionesDeEstados includes: self nombreDeEstadoInicial
A continuación, volvemos a ejecutar los tests y seguimos en verde (ver Figura 159).
tieneEstadoFinal “Responde si el receptor tiene algún estado fi nal”
^self defi nicionesDeEstados includes: self nombreDeEstadoFinal
llegaAlgunaTransicionAlEstadoInicial “Responde si alguna transición del receptor tiene como llegada el estado inicial”
^transiciones anySatisfy: [:each | each third = self nombreDeEstadoInicial]
saleAlgunaTransicionDelEstadoFinal “Responde si alguna transición del receptor tiene como partida el estado fi nal”
^transiciones anySatisfy: [:each | each second = self nombreDeEstadoFinal]
Figura 159Figura 159
SSHasta este momento tenemos guardada toda la información de las transiciones en un
Array, que no es algo muy orientado a objetos, así que vamos a cambiar el método #agre-
garDefi niciónDeTransición:desde:hasta: de la siguiente forma:
164S
ProgramandoconSmalltalk
165S Manos a la obra
agregarDefi niciónDeTransición: transiciónString desde: estadoDesdeString hasta: estadoHastaString
“Agrega una defi nición de transición con el nombre dado, que une dos estados de nombres dados”
| transición |
transición := Defi niciónDeTransición nombre: transiciónString desde: estadoDesdeString hasta: estadoHastaString.
transiciones add: transición
Al aceptar, creamos la clase Defi niciónDeTransición, en la categoría de clases Workfl ow,y
confi rmamos que el selector #nombre:desde:hasta: es correcto.
Ejecutamos los tests, como es habitual (ver Figura 160).
Figura 160Figura 160
SSAhora elegimos alguno de los tests marcados en rojo para arreglarlo. Nuestro autor propone
comenzar por #testLaMasSimpleDefi nición, ya que parece ser el caso más sencillo de
solucionar. Para ello, pedimos el Depurador sobre ese test con un clic en el panel inferior
(ver Figura 161):
Figura 161Figura 161
SSy creamos el método en la clase Defi niciónDeTransición class, dentro de la categoría de
métodos llamada instance creation, implementándolo de la siguiente manera:
nombre: nombreString desde: desdeString hasta: hastaString “Responde una nueva instancia del receptor”
^ self new initializeNombre: nombreString desde: desdeString hasta: hastaString
Aceptamos y continuamos con nuestro ejemplo.
Ahora debemos aplicar el método #initializeNombre:desde:hasta: en la clase Defi nición-
DeTransición, dentro de la nueva categoría de nombre initialization, de este modo:
initializeNombre: nombreString desde: desdeString hasta: hastaString “Inicializa el receptor”
nombre := nombreString. desde := desdeString. hasta := hastaString.
defi nicionesDeEstados “Responde las defi niciones de estados del receptor”
| estadosDesde estadosHasta | estadosDesde := transiciones collect: [:each | each desde]. estadosHasta := transiciones collect: [:each | each hasta]. ^ (estadosDesde , estadosHasta) asSet.
y creamos las variables de instancia nombre, desde y hasta al aceptar.
De nuevo, detectamos que un objeto de clase Defi niciónDeTransición no entiende el
mensaje #second, aunque esto es lógico porque hemos reemplazado un Array por una de
nuestras clases. Bien, procedemos a modifi car el método #defi nicionesDeEstados de la
siguiente forma:
166S
ProgramandoconSmalltalk
167S Manos a la obra
desde “Responde el nombre de estado de partida del receptor” ^ desde
hasta “Responde el nombre de estado de llegada del receptor” ^ hasta
Como siempre, aceptamos y seguimos trabajando. Ahora, debemos aplicar los métodos
#desde y #hasta en la clase Defi niciónDeTransición, dentro de la categoría accessing con
las siguientes órdenes:
A continuación, observamos que nuestra clase no entiende el mensaje #third. Para solu-
cionarlo, volvemos a modifi car el método #llegaAlgunaTransicionAlEstadoInicial de la
siguiente forma:
llegaAlgunaTransicionAlEstadoInicial “Responde si alguna transición del receptor tiene como llegada el estado inicial”
^transiciones anySatisfy: [:each | each hasta = self nombreDeEstadoInicial]
saleAlgunaTransicionDelEstadoFinal “Responde si alguna transición del receptor tiene como partida el estado fi nal”
^transiciones anySatisfy: [:each | each desde = self nombreDeEstadoFinal]
y cambiamos el método #saleAlgunaTransicionDelEstadoFinal con estas instrucciones:
Figura 162Figura 162
S166SS
Continuamos, pero ahora vamos a ejecutar todos los tests para saber qué es lo que tenemos
que arreglar de nuevo (ver Figura 162).
Los tests aumentan la confi anza
¿Cuántas veces hemos dudado en cambiar código erróneamente escrito, pero no lo
hemos hecho por miedo a romper el sistema?
Los sistemas desarrollados con TDD están controlados por decenas, incluso cente-
nares, de tests. Podemos estar prácticamente seguros que, si refactorizamos algo y
los tests funcionan, entonces, no hemos alterado el comportamiento del código.
¡Perfecto! Ya no queda nada por arreglar.
testDefi niciónConEstadosInalcanzables “Prueba una defi nición donde hay estados que no se pueden alcanzar desde el estado inicial”
| defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar y terminar’ desde: ‘principio’ hasta: ‘fi n’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘A’ hasta: ‘fi n’.
self shouldnt: [defi niciónDeProceso esVálida]
Después, ejecutamos los tests y estamos, de nuevo, en amarillo. Así, vamos a modifi car el
método #esVálida de la siguiente forma:
esVálida “Responde si el receptor es una instancia válida”
self tieneEstadoInicial ifFalse: [^false]. self tieneEstadoFinal ifFalse: [^false]. self llegaAlgunaTransicionAlEstadoInicial ifTrue: [^false]. self saleAlgunaTransicionDelEstadoFinal ifTrue: [^false]. self tieneEstadosInalcanzables ifTrue:[^false]. ^true
168S
ProgramandoconSmalltalk
169S Manos a la obra
tieneEstadosInalcanzables “Responde si el receptor tiene estados inalcanzables desde el estado inicial”
^ self estadosInalcanzables notEmpty
Seguidamente, implementamos el método #estadosInalcanzables dentro de la categoría
accessing con estas órdenes:
estadosInalcanzables “Responde los estados inalcanzables desde el estado inicial”
^ self defi nicionesDeEstados copyWithoutAll: self estadosAlcanzables
al igual que el método #estadosAlcanzables, también en la misma categoría:
estadosAlcanzables “Responde los estados alcanzables desde el estado inicial”
| aVisitar resultado |
aVisitar := OrderedCollection with: self nombreDeEstadoInicial. resultado := aVisitar asSet.
[aVisitar isEmpty] whileFalse: [ | actual nuevos |
actual := aVisitar removeFirst. nuevos := (self estadosAlcanzablesDesde: actual) copyWithoutAll: resultado.
resultado addAll: nuevos. aVisitar addAll: nuevos. ].
^ resultado
y, fi nalmente, el método #estadosAlcanzablesDesde: en la categoría accessing.
Como nuestro propósito, en este momento, es implementar el método #tieneEstados-
Inalcanzables en la categoría testing, ejecutamos los tests para invocar al pre-Depurador y
ejecutar esta acción:
estadosAlcanzablesDesde: aDefi niciónDeEstado “Responde los estados alcanzables desde el estado dado”
^ transiciones select:[:each | each desde = aDefi niciónDeEstado] thenCollect:[:each | each hasta]
Colecciones – mensaje #select:thenCollect
Mensaje de utilidad que se comporta como un #select: seguido de un #collect:.
Colecciones – mensaje #select
Evalúa el bloque dado por cada elemento del receptor como argumento; colecciona
los elementos en los que el bloque evalúa a true (verdadero) en una colección del
tipo del receptor y responde esa colección nueva como resultado.
Ejemplos:.
‘Un String’ select: [:each | each isUppercase].
#(1 2 3 4 5 6 7 8 9 10) select: [:each | each odd].
Smalltalk allClasses select:[:each | each superclass == TestCase].
Volvemos a estar en verde, tal y como nos indica la Figura 163.
Figura 163
169SS Manos a la obra
Figura 163
S
170S
ProgramandoconSmalltalk
171S Manos a la obra
El último código que hemos escrito no ha quedado muy orientado a objetos precisamente.
Esto se ha debido, sobre todo, a que todavía no hemos creado una clase para representar las
defi niciones de estado, por lo que estamos manipulando strings. Vamos a crear, entonces,
la clase que corresponde y comenzamos modifi cando el método Defi niciónDeProceso>>
agregarDefi niciónDeTransición:desde:hasta: de la siguiente forma:
agregarDefi niciónDeTransición: transiciónString desde: estadoDesdeString hasta: estadoHastaString
“Agrega una defi nición de transición con el nombre dado, que une dos estados de nombres dados”
| estadoDesde estadoHasta transición |
estadoDesde := self estadoDeNombre: estadoDesdeString. estadoHasta := self estadoDeNombre: estadoHastaString.
transición := Defi niciónDeTransición nombre: transiciónString desde: estadoDesde hasta: estadoHasta.
transiciones add: transición
Ejecutamos los tests y vemos que casi todos fallan. Para solucionarlo, hacemos clic sobre
alguno de los tests en rojo para implementar desde el Depurador.
A continuación, creamos el método #estadoDeNombre:, en la categoría accessing, del
siguiente modo:
estadoDeNombre: aString “Responde un estado de nombre dado. Crea uno nuevo si es necesario.”
^ estados at: aString ifAbsentPut:[Defi niciónDeEstado nombre: aString].
Colecciones – mensaje #at:ifAbsentPut:
Responde el elemento de nombre dado. En el caso que no haya ningún elemento
con ese nombre, se evalúa el bloque dado y el resultado de dicha evaluación se guar-
da en el receptor y se responde al remitente.
Ahora, la nueva variable de instancia estados tendrá un diccionario, por lo que que
modifi camos el método Defi niciónDeProceso>>initialize de la siguiente forma:
initialize “Inicializa el receptor”
transiciones := Set new. estados := Dictionary new.
Colecciones – Dictionary
El Dictionary (Diccionario) es una colección que puede verse desde dos
perspectivas:
ß Como un Set de asociaciones clave->valor.
ß Como un contenedor donde los elementos son nombrados desde el exterior y el
nombre puede ser cualquier objeto que responda al mensaje #=.
Ejemplos: “instanciar, y llenar, un diccionario” | diccionario |
diccionario := Dictionary new.
diccionario at: ‘hoy’ put: Date today. diccionario at: ‘ahora’ put: Time now. diccionario at: false put: ‘es false’.
“algunas formas de acceder al diccionario” diccionario at: ‘hoy’. diccionario at: ‘mañana’ ifAbsent:[nil]. diccionario at: ‘mañana’. diccionario at: ‘mañana’ ifAbsentPut:[Date tomorrow]. diccionario at: ‘mañana’.
“otras formas de acceso” diccionario keys. diccionario values.
“algunas iteraciones” Transcript clear.
Creado el método, generamos la variable de instancia estados y la clase Defi niciónDeEs-
tado en la categoría Workfl ow.
172S
ProgramandoconSmalltalk
173S Manos a la obra
Transcript cr; show: ‘#do:’; cr. diccionario do: [:each | Transcript show: ‘ ‘ , each asString; cr. ].
Transcript cr; show: ‘#associationsDo:’; cr. diccionario associationsDo: [:each | Transcript show: ‘ ‘ , each asString; cr. ].
Transcript cr; show: ‘#keysAndValuesDo:’; cr. diccionario keysAndValuesDo:[:eachKey :eachValue | Transcript show: ‘ ‘ , eachKey asString , ‘ - ‘ , eachValue asString; cr. ].
Implementamos el método Defi niciónDeProceso class>>nombre:, en la categoría
instance creation, de la siguiente forma:
Transcript
Transcript es una variable global que apunta a una ventana de Transcript (siempre
que esté abierta), y normalmente se usa para mostrar pequeños mensajes que ayu-
dan a la depuración del código.
Para obtener una ventana de Transcript seguimos la secuencia: menú del Mundo |
open... | transcript (t) (o presionar ALT-t).
Por tanto, el Transcript responde a una serie de mensajes muy útiles a la hora de
depurar:
ß #show: muestra el argumento, convertido a String, en la ventana.
ß #cr hace un salto de línea en la ventana.
ß #clear limpia el contenido de la ventana.
, de la siguiente forma:
nombre: aString “Responde una nueva instancia del receptor”
^ self new initializeNombre: aString
y volvemos a implementar, pero ahora, el método Defi niciónDeProceso>>initializeNombre:,
en la nueva categoría initialization, así:
y creamos la variable de instancia nombre.
Continuamos con el proceso ejecutando los tests y, otra vez, estamos en amarillo, tal y
como vemos en la Figura 164.
y creamos la variable de instancia nombre
initializeNombre: aString “Inicializa el receptor”
nombre := aString
Figura 164Figura 164
S¿Qué ha sucedido? Hasta este momento veníamos utilizando sólo un String en lugar de
un objeto de clase Defi niciónDeEstado para los estados y dos constantes (también String)
para los estados especiales, inicial y fi nal. Si buscamos los senders de ambos métodos de
constante (Constant Method) #nombreDeEstadoInicial y #nombreDeEstadoFinal), segu-
ramente encontraremos las líneas que hemos de modifi car.
Para encontrar los senders de #nombreDeEstadoInicial podemos usar la opción senders
of... (n ) del menú contextual del panel de Métodos en un Browser de Clases, tal y como nos
indica la Figura 165:
174S
ProgramandoconSmalltalk
175S Manos a la obra
y obtenemos un Browser como el que vemos en la Figura 166:
Figura 165
y obtenemos un como el que vemos en la
Figura 165
SSFigura 166Figura 166
SPero nos encontramos con dos métodos que tenemos que cambiar, así que repetimos el
procedimiento para #nombreDeEstadoInicial y, de nuevo, tenemos otros tres métodos
que arreglar. Por ello, tecleamos estas órdenes para ejecutar el arreglo:
tieneEstadoInicial “Responde si el receptor tiene algún estado inicial”
^self defi nicionesDeEstados anySatisfy:[:each | each nombre = self nombreDeEstadoInicial]
tieneEstadoFinal “Responde si el receptor tiene algún estado fi nal”
^self defi nicionesDeEstados anySatisfy:[:each | each nombre = self nombreDeEstadoFinal]
llegaAlgunaTransicionAlEstadoInicial “Responde si alguna transición del receptor tiene como llegada el estado inicial”
^transiciones anySatisfy: [:each | each hasta nombre = self nombreDeEstadoInicial]
saleAlgunaTransicionDelEstadoFinal “Responde si alguna transición del receptor tiene como partida el estado fi nal”
^transiciones anySatisfy: [:each | each desde nombre = self nombreDeEstadoFinal]
estadosAlcanzables “Responde los estados alcanzables desde el estado inicial”
| aVisitar resultado |
aVisitar := OrderedCollection with: (self estadoDeNombre: self nombreDeEstadoInicial). resultado := aVisitar asSet.
[aVisitar isEmpty] whileFalse: [ | actual nuevos |
actual := aVisitar removeFirst. nuevos := (self estadosAlcanzablesDesde: actual) copyWithoutAll: resultado.
resultado addAll: nuevos. aVisitar addAll: nuevos. ].
^ resultado
176S
ProgramandoconSmalltalk
177S Manos a la obra
Nuevamente estamos en verde, así que es momento de limpiar el código.
La anterior implementación del método Defi niciónDeProceso>>defi nicionesDeEstados
puede ser reemplazada por la siguiente:
y seguimos en verde. La siguiente expresión, que introdujimos en el método Defi niciónD
eProceso>>estados-Alcanzables, es la mejor candidata para un Extract Method.
Lo ejecutamos y dejamos los métodos de esta manera:
nombre “Responde el nombre del receptor” ^ nombre
defi nicionesDeEstados “Responde las defi niciones de estados del receptor”
^ estados values
(self estadoDeNombre: self nombreDeEstadoInicial)
estadosAlcanzables “Responde los estados alcanzables desde el estado inicial”
| aVisitar resultado |
aVisitar := OrderedCollection with: self estadoInicial. resultado := aVisitar asSet.
[aVisitar isEmpty] whileFalse: [ | actual nuevos |
actual := aVisitar removeFirst. nuevos := (self estadosAlcanzablesDesde: actual) copyWithoutAll: resultado.
resultado addAll: nuevos. aVisitar addAll: nuevos. ].
^ resultado
e implementamos el método Defi niciónDeProceso>>nombre, en la categoría accessing.
Podemos refactorizar otros senders de #nombreDeEstadoInicial para aprovecharnos del
nuevo método creado y evitar comparaciones por nombre.
estadoInicial “Responde el estado inicial”
^ self estadoDeNombre: self nombreDeEstadoInicial
llegaAlgunaTransicionAlEstadoInicial “Responde si alguna transición del receptor tiene como llegada el estado inicial”
^transiciones anySatisfy: [:each | each hasta = self estadoInicial]
Seguidamente, hacemos lo mismo para el estado fi nal:
estadoFinal “Responde el estado fi nal”
^ self estadoDeNombre: self nombreDeEstadoFinal
y seguimos en verde, pero con el código un poco más limpio.
Finalmente, lo último que debemos validar es que no exista más de una transición con el
mismo nombre para el estado de salida o, lo que es lo mismo, la combinación estadoDesde-
nombreDeTransición debe ser única. Llevamos a la práctica esta explicación en un test:
testDefi niciónConTransicionesDuplicadas “Prueba una defi nición donde hay transiciones con el mismo nombre que salen de un determinado estado”
| defi niciónDeProceso |
defi niciónDeProceso := Defi niciónDeProceso new.
defi niciónDeProceso
saleAlgunaTransicionDelEstadoFinal “Responde si alguna transición del receptor tiene como partida el estado fi nal”
^transiciones anySatisfy: [:each | each desde = self estadoFinal]
178S
ProgramandoconSmalltalk
179S Manos a la obra
agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘fi n’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar’ desde: ‘principio’ hasta: ‘A’. defi niciónDeProceso agregarDefi niciónDeTransición: ‘terminar’ desde: ‘A’ hasta: ‘fi n’.
self shouldnt: [defi niciónDeProceso esVálida]
y obtenemos el amarillo, lo que nos lleva a implementar lo que vemos a continuación:
esVálida “Responde si el receptor es una instancia válida”
self tieneEstadoInicial ifFalse: [^false]. self tieneEstadoFinal ifFalse: [^false]. self llegaAlgunaTransicionAlEstadoInicial ifTrue: [^false]. self saleAlgunaTransicionDelEstadoFinal ifTrue: [^false]. self tieneEstadosInalcanzables ifTrue:[^false]. self tieneAlgunEstadoConTransicionesDuplicadas ifTrue:[^false]. ^true
tieneAlgunEstadoConTransicionesDuplicadas “Responde si el receptor tiene algún estado con transiciones duplicadas “ ^ self defi nicionesDeEstados anySatisfy: [:eachEstado | | transicionesDesde nombresDesde | transicionesDesde := self transicionesDesde: eachEstado. nombresDesde := transicionesDesde collect: [:eachTransicion | eachTransicion nombre]. transicionesDesde size ~= nombresDesde asSet size ]
transicionesDesde: aDefi niciónDeEstado “Responde las transiciones que tienen el estado dado como ‘desde’” ^ transiciones select:[:each | each desde = aDefi niciónDeEstado]
nombre “Responde el nombre del receptor” ^ nombre
Tras esta ejecución, estamos en verde.
Figura 167Figura 167
SAhora deberíamos mover la funcionalidad que tenemos en Defi nicióndeProceso, pero
que le corresponde a la recién creada clase Defi niciónDeEstado.
Para mover dicha funcionalidad vamos a modifi car la instanciación de las transiciones.
Para ello, haremos que la transición informe a cada uno de los estados involucrados
sobre su propia existencia para que estos, a su vez, conozcan la estructura de la cual
forman parte.
Así, cambiamos los nombres de los argumentos en el constructor Defi niciónDeTransición,
indicando que ahora esperamos objetos Defi niciónDeEstado y no objetos String.
nombre: nombreString desde: desdeDefi niciónDeEstado hasta: hastaDefi niciónDeEstado “Responde una nueva instancia del receptor”
^ self new initializeNombre: nombreString desde: desdeDefi niciónDeEstado hasta: hastaDefi niciónDeEstado
Seguidamente, modifi camos el método de inicialización arreglando, también, los nombres
de los argumentos y notifi cando a las defi niciones de estado la transición recién creada.
180S
ProgramandoconSmalltalk
181S Manos a la obra
nuevaTransiciónSaliente: aDefi niciónDeTransición “Agrega, en la colección de transiciones salientes del receptor, la defi nición de transición dada”
transicionesSalientes add: aDefi niciónDeTransición
nuevaTransiciónEntrante: aDefi niciónDeTransición “Agrega, en la colección de transiciones entrantes del receptor, la defi nición de transición dada”
transicionesEntrantes add: aDefi niciónDeTransición
initialize “Inicializa al receptor” transicionesSalientes := Set new. transicionesEntrantes := Set new.
estadosAlcanzables “Responde los estados alcanzables desde el receptor”
^ transicionesSalientes collect:[:each | each hasta]
Ahora, implementamos el método #estadosAlcanzables, en la clase Defi niciónDeEstado,
que reemplazará al método Defi niciónDeProceso>>estadosAlcanzablesDesde:.
e inicializamos las recién creadas variables de instancia:
Continuamos implementando los métodos #nuevaTransiciónSaliente: y #nuevaTran-
siciónEntrante:, dentro de la clase Defi niciónDeEstado, creándose así las dos nuevas
variables de instancia necesarias:
initializeNombre: nombreString desde: desdeDefi niciónDeEstado hasta: hastaDefi niciónDeEstado “Inicializa el receptor”
nombre := nombreString. desde := desdeDefi niciónDeEstado. hasta := hastaDefi niciónDeEstado.
desde nuevaTransiciónSaliente: self. hasta nuevaTransiciónEntrante: self.
Por tanto, vamos a modifi car el método Defi niciónDeProceso>>estadosAlcanzables con
las siguientes instrucciones:
estadosAlcanzables “Responde los estados alcanzables desde el estado inicial”
| aVisitar resultado |
aVisitar := OrderedCollection with: self estadoInicial. resultado := aVisitar asSet.
[aVisitar isEmpty] whileFalse: [ | actual nuevos |
actual := aVisitar removeFirst. nuevos := actual estadosAlcanzables copyWithoutAll: resultado.
resultado addAll: nuevos. aVisitar addAll: nuevos. ].
^ resultado
Procedemos a borrar el método Defi niciónDeProceso>>estadosAlcanzablesDesde: a
través del menú contextual sobre el panel de Métodos | remove method (x) y ejecutamos
los tests. En este momento, estamos de nuevo en verde.
Otra funcionalidad que debemos mover es la prueba por transiciones salientes duplica-
das, para lo que agregamos el siguiente método en la clase Defi niciónDeEstado.
tieneTransicionesDuplicadas
“Responde si el receptor tiene más de 1 transición saliente con el mismo nombre”
| nombres |
nombres := (transicionesSalientes collect:[:each | each nombre]) asSet.
^ transicionesSalientes size ~= nombres size
Tras esta operación, cambiamos el siguiente método en la clase Defi niciónDeProceso con
estas órdenes:
182S
ProgramandoconSmalltalk
183S Manos a la obra
tieneAlgunEstadoConTransicionesDuplicadas “Responde si el receptor tiene algún estado con transiciones duplicadas “ ^ self defi nicionesDeEstados anySatisfy: [:each | each tieneTransicionesDuplicadas]
Al aceptar el método, creamos la clase Proceso, en la categoría Workfl ow.
A continuación, ejecutamos los tests y, previsiblemente, estamos en rojo. Después, creamos
el método #nuevaInstancia en la clase Defi niciónDeProceso, dentro de la nueva categoría
instancias.
testLaMasSimpleInstanciaDeProceso “Prueba la defi nición más simple posible, con su instancia”
| defi niciónDeProceso defi niciónEstadoInicial proceso |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar y terminar’ desde: ‘principio’ hasta: ‘fi n’.
defi niciónEstadoInicial := defi niciónDeProceso estadoInicial.
proceso := defi niciónDeProceso nuevaInstancia. self should:[proceso class = Proceso]. self should:[proceso estado defi nición = defi niciónEstadoInicial].
y borramos el método Defi niciónDeProceso>>transicionesDesde:.
De nuevo, ejecutamos los tests y seguimos en verde, pero esta vez el código está mucho más
orientado a objetos.
Ha llegado la hora de hacer algo con las Defi niciones de Proceso. Bien, lo primero que tiene
que hacer una Defi nición de Proceso es crear una Instancia de Proceso. Sólo una aclaración:
cuando hablamos de Instancia de Proceso estamos usando terminología de Workfl ow, por
lo que no nos referimos a instancias de Smalltalk.
La Instancia de Proceso más simple que se le ocurre a nuestro autor es la siguiente:
nuevaInstancia “Crea una nueva instancia de proceso”
^ Proceso defi nición: self.
defi nición: aDefi niciónDeProceso “Crea una nueva instancia del receptor”
^ self new initializeDefi nición: aDefi niciónDeProceso
Procedemos a crear el método Proceso class>>defi nición:, en la categoría instance crea-
tion, de la siguiente forma:
y el método Proceso>>initializeDefi nición: en la categoría initialization.
Aceptamos, creándose así la variable de instancia defi nición. Ahora debemos implementar
el método Defi niciónDeProceso>>estado.
Las instancias de proceso siempre tienen un estado asociado. Así, los procesos recién em-
pezados se encuentran en el estado inicial y, los procesos terminados, en el estado fi nal.
Dicho esto, modifi camos el método Proceso>>initializeDefi nición: así:
initializeDefi nición: aDefi niciónDeProceso “Inicializa la defi nición del receptor”
defi nición := aDefi niciónDeProceso
initializeDefi nición: aDefi niciónDeProceso “Inicializa la defi nición del receptor”
defi nición := aDefi niciónDeProceso.
estado := defi nición estadoInicial nuevaInstancia.
y creamos la variable de instancia estado al aceptar. Del mismo modo, generamos el
siguiente método:
184S
ProgramandoconSmalltalk
185S Manos a la obra
Y seguimos creando, ahora, este método en la clase Defi niciónDeEstado, en la categoría
instancias:
Al aceptar, creamos la clase Estado, en la categoría Workfl ow. Y, dentro de la clase Estado,
el método de clase #defi nición:, el método de instancia #initializeDefi nición: y la variable
de instancia defi nición, de la misma forma que lo hicimos en la clase Proceso.
También implementamos, en la clase Estado, como vemos a continuación:
y ejecutamos los tests. ¡Estamos en verde otra vez!
Al aceptar, creamos la clase Estado, en la categoría Workfl ow. Y, dentro de la clase
nuevaInstancia “Crea una nueva instancia de estado”
^ Estado defi nición: self
defi nición “Responde la defi nición del receptor”
^ defi nición
estado “Responde el estado actual del receptor”
^ estado
Figura 168Figura 168
S
De nada sirve un motor de Workfl ow si no podemos ejecutar código arbitrario en cada
cambio de estado. A su vez, este código será el responsable de decidir qué transición tomar
para pasar de un estado a otro. Para ello, indicamos a la Defi nición de Proceso que utilice un
objeto gestor que le comunique los cambios de estado. Este gestor, además de encargarse
de los cambios de estado, será el que decida qué transición usar para pasar del estado actual
al siguiente.
Bien, vamos a desarrollar el ejemplo más sencillo posible con un gestor que haga avanzar
el proceso desde el estado inicial hasta el estado fi nal.
testLaMasSimpleInstanciaDeProcesoConGestor “Prueba la defi nición más simple posible, con su instancia de proceso y su gestor”
| defi niciónDeProceso proceso gestor estados |
defi niciónDeProceso := Defi niciónDeProceso new. defi niciónDeProceso agregarDefi niciónDeTransición: ‘comenzar y terminar’ desde: ‘principio’ hasta: ‘fi n’.
gestor := GestorConBloque bloque: [:proc | (proc estado nombre = ‘principio’) ifTrue:[‘comenzar y terminar’] ifFalse:[nil] ]. defi niciónDeProceso gestor: gestor.
proceso := defi niciónDeProceso nuevaInstancia. estados := proceso estados.
self should:[estados size = 2]. self should:[estados fi rst defi nición = defi niciónDeProceso estadoInicial]. self should:[estados second defi nición = defi niciónDeProceso estadoFinal]. self should:[estados second transición nombre = ‘comenzar y terminar’].
Con este test estamos defi niendo prácticamente el funcionamiento completo del motor de
Workfl ow. Pero antes de empezar a trabajar en la ejecución del test, vamos a examinar su
contenido para ver cómo va a funcionar el motor. La primera novedad con que nos encon-
tramos es que, a la Defi nición de Proceso, hemos de indicarle cual será su gestor.
Vamos a crear una clase GestorConBloque, que será un Adapter que transforme la interfaz
de un bloque en la interfaz de un Gestor.
186S
ProgramandoconSmalltalk
187S Manos a la obra
El siguiente elemento nuevo es el envío del mensaje #estados (en plural). Este mensaje
tendrá como respuesta una colección con todos los estados incluidos en el proceso, por
lo que es diferente al mensaje #estado (en singular), que usamos anteriormente, que sólo
contesta el estado actual.
Y, por último, tenemos el mensaje #transición, que enviamos a un estado. Este mensaje
responderá qué transición ha sido la responsable de que ese estado se haya convertido en
estado actual.
Al aceptar el método creamos la clase GestorConBloque, en la categoría Workfl ow.
Como siempre, procedemos a ejecutar los tests, acción que nos lleva al rojo.
Patrón de Diseño - Adapter
El patrón Adapter se utiliza para transformar una interfaz en otra, de tal manera
que, una clase que no pueda utilizar la primera, haga uso de ella a través de la
segunda.
ß Problema que soluciona:
Se tiene un componente con cierta funcionalidad que se desea aprovechar, pero que
no se adapta a una determinada interfaz, siendo esto último obligatorio.
ß Implementación:
Crear una nueva clase que será el Adaptador, que extienda del componente existente
e implemente la interfaz obligatoria. De este modo, conseguimos la funcionalidad
que queríamos y cumplimos la condición de implementar la interfaz.
La diferencia entre los patrones Adapter y Facade, es que el primero reutiliza una
interfaz ya existente, mientras que el segundo defi ne una nueva.
Para más información:
– http://es.wikipedia.org/wiki/Adapter_(patrón_de_diseño)
– Libro “Design Patterns - Elements of Reusable Object Oriented Software”.
Implementamos el método GestorConBloque class>>bloque:, en la categoría instance
creation, de la siguiente forma:
y el método GestorConBloque>>initializeBloque:, en la categoría initialization. Segui-
damente, al aceptar el método, creamos la variable de instancia bloque.
Ahora, implementamos el método Defi niciónDeProceso>>gestor:, en la categoría acces-
sing, y generamos la variable de instancia gestor al aceptar el método.
De nuevo, implementamos el método Proceso>>estados, en la categoría accessing:
bloque: aBlockContext “Responde una nueva instancia del receptor” ^ self new initializeBloque: aBlockContext
initializeBloque: aBlockContext “Inicializa el bloque del receptor” bloque := aBlockContext
De nuevo, implementamos el método Proceso>>estados, en la categor accessing
gestor: aGestor “Cambia el gestor del receptor” gestor := aGestor
estados “Responde una colección con todos los estados por los que atravesó el receptor. Primero el más viejo.”
| resultado estadoActual |
resultado := OrderedCollection new.
estadoActual := self estado. [estadoActual isNil] whileFalse: [ resultado add: estadoActual. estadoActual := estadoActual estadoAnterior. ].
^ resultado reversed
188S
ProgramandoconSmalltalk
189S Manos a la obra
y el método Estado>>estadoAnterior, también en la categoría accessing, y creamos la
variable de instancia transición al aceptar.
En este punto, generamos el método Proceso>>cambiarEstado:, en la categoría private.
estadoAnterior “Responde el estado anterior. Si no hay ningún estado anterior, responde nil”
^ transición isNil ifTrue: [nil] ifFalse: [transición desde]
cambiarEstado: aEstado “PRIVADO - Cambia el estado actual del receptor.”
estado := aEstado. self cambioDeEstado.
Métodos privados
Los métodos privados, por convención, se organizan dentro de la categoría private.
Y, del mismo modo, el método #cambioDeEstado, en la categoría private.
cambioDeEstado “PRIVADO - El estado del receptor acaba de cambiar. Notifi car al gestor si existe.”
| nombreDeTransición |
defi nición tieneGestor ifFalse:[^ self].
nombreDeTransición := defi nición gestor estadoCambiadoEn: self. nombreDeTransición isNil ifTrue:[ estado defi nición tieneTransicionesSalientes ifTrue:[self error: ‘Nombre de transición inválida: ‘, nombreDeTransición asString] ]
Ahora, cambiamos el método Proceso>>initializeDefi nición: que es, hasta el momento,
el único lugar donde cambiamos de estado:
e implementamos el método Defi niciónDeProceso>>tieneGestor, en la categoría testing.
Nuevamente, implementamos el método Defi niciónDeProceso>>gestor, en la categoría
accessing.
Seguimos con la creación del método GestorConBloque>>estadoCambiadoEn:, en la
categoría events.
E, igualmente, generamos el método Estado>>nombre, en la categoría accessing.
initializeDefi nición: aDefi niciónDeProceso “Inicializa la defi nición del receptor”
defi nición := aDefi niciónDeProceso.
self cambiarEstado: defi nición estadoInicial nuevaInstancia.
Nuevamente, implementamos el método Defi niciónDeProceso>>gestor, en la categor
tieneGestor “Responde si el receptor tiene un gestor o no” ^ gestor notNil
ifFalse:[ self aplicarTransiciónDeNombre: nombreDeTransición ]
Seguimos con la creación del método GestorConBloque>>estadoCambiadoEn:
gestor “Responde el gestor del receptor” ^ gestor
estadoCambiadoEn: aProceso “Un proceso ha cambiado su estado actual” ^ bloque value: aProceso
nombre “Responde el nombre del receptor” ^ defi nición nombre
190S
ProgramandoconSmalltalk
191S Manos a la obra
A continuación, creamos el método Defi niciónDeEstado>>tieneTransicionesSalientes,
en la categoría testing.
Y, del mismo modo, el método Proceso>>aplicarTransiciónDeNombre:, en la categoría
private.
De nuevo, ejecutamos la misma operación con el método Defi niciónDeProceso>>transición
DeNombre:desde:, en la categoría accessing.
Ahora, el método Defi niciónDeTransición>>nuevaInstanciaDesde:, en la categoría ins-
tancias y creamos la clase Transición, en la categoría Workfl ow, al aceptar el método.
Seguimos con la creación del método Transición class>>defi nición:desde:, en la categoría
instance creation.
Y, del mismo modo, el método Proceso>>aplicarTransiciónDeNombre:, en la categor
tieneTransicionesSalientes “Responde si el receptor tiene o no transiciones salientes” ^ transicionesSalientes notEmpty
aplicarTransiciónDeNombre: aString “PRIVADO - Aplica la transición de nombre dado”
| defi niciónDeTransición transición |
defi niciónDeTransición := defi nición transiciónDeNombre: aString desde: estado.
transición := defi niciónDeTransición nuevaInstanciaDesde: estado.
self cambiarEstado: transición hasta
transiciónDeNombre: aString desde: anEstado “Responde una transición de nombre dado y que sea saliente del estado dado”
^ transiciones detect:[:each | each nombre = aString and:[each desde = anEstado defi nición]]
Seguimos con la creación del método Transición class>>defi nición:desde:, en la categor
nuevaInstanciaDesde: anEstado “Crea una nueva instancia de transición” ^ Transición defi nición: self desde: anEstado
Y del método Transición>>initializeDefi nición:desde:, en la categoría initialization.
Después, creamos las variables de instancia defi nición, desde y hasta al aceptar el método.
El siguiente paso es la creación del método Defi niciónDeEstado>>nuevaInstancia
Transición:, en la categoría instancias.
Y, a continuación, generamos el método Estado class>>defi nición:transición:, en la cate-
goría instance creation.
Del mismo modo, el método Estado>>initializeDefi nición:transición:, en la categoría
initialization.
defi nición: aDefi niciónDeTransición desde: anEstado “Crea una nueva instancia del receptor” ^ self new initializeDefi nición: aDefi niciónDeTransición desde: anEstado
El siguiente paso es la creación del método Defi niciónDeEstado>>nuevaInstancia
initializeDefi nición: aDefi niciónDeTransición desde: anEstado “Inicializa el receptor”
defi nición := aDefi niciónDeTransición. desde := anEstado. hasta := defi nición hasta nuevaInstanciaTransición: self.
Y, a continuación, generamos el método Estado class>>defi nición:transición:, en la cate-
nuevaInstanciaTransición: aTransición “Crea una nueva instancia de estado”
^ Estado defi nición: self transición: aTransición
Del mismo modo, el método Estado>>initializeDefi nición:transición:, en la categor
defi nición: aDefi niciónDeEstado transición: aTransición “Crea una nueva instancia del receptor”
^ self new initializeDefi nición: aDefi niciónDeEstado transición: aTransición
initializeDefi nición: aDefi niciónDeEstado transición: aTransición “Inicializa la defi nición y la transición del receptor”
defi nición := aDefi niciónDeEstado. transición := aTransición
192S
ProgramandoconSmalltalk
193S Manos a la obra
Ahora, implementamos los métodos Transición>>hasta, Transición>>desde,
Estado>>transición y Transición>>nombre, en la categoría accessing.
nombre “Responde el nombre del receptor” ^ defi nición nombre
transición “Responde la transición que dio origen al receptor” ^ transición
desde “Responde el estado ‘desde’ del receptor” ^ desde
hasta “Responde el estado ‘hasta’ del receptor” ^ hasta
Bien, lo primero que nos encontramos es algo que ya habíamos previsto al comenzar. Tene-
mos dos tipos de objetos: las Defi niciones y las Instancias. Si prestamos atención, observamos
que todas las instancias (Proceso, Estado y Transición) comparten aspectos comunes, lo que
suele indicar que la jerarquía de clases no es correcta. Lo que está ocurriendo es que cono-
cemos todos los Procesos, Estados y Transiciones, pero no volcamos ese conocimiento en el
entorno. Para hacerlo, vamos a crear una nueva clase llamada Instancia (ver Figura 170).
Figura 170Figura 170
S
Después de tanto trabajo, ejecutamos los tests y ¡estamos en verde!
Llegados a este punto del ejemplo, hemos de limpiar el código, tal y como hicimos para el
último test, por lo que, según nuestro autor, nos espera un buen trabajo de limpieza.
Figura 173Figura 173
S
A continuación, modifi camos las clases Proceso, Estado y Transición para que ahora sean
subclases de Instancia (en lugar de ser subclases de Object). Para modifi car la superclase
usamos el Browser de Clases y, en la defi nición de la clase (vemos la defi nición cuando
tenemos seleccionada la Clase, pero no tenemos seleccionados ni Categoría de Métodos ni
Métodos), reemplazamos Object por Instancia, tal como indican las Figuras 171 y 172, y
aceptamos.
Figuras 171 - 172Figuras 171 - 172
S
Figuras 171 - 172
SRepetimos el mismo proceso para las clases Proceso y Transición.
Ahora, seleccionamos la clase Instancia, en el Browser, y presionamos el botón hier (hie-
rarchy – jerarquía), abriéndose el Browser Jerárquico (ver Figura 173).
194S
ProgramandoconSmalltalk
195S Manos a la obra
Figura 174Figura 174
S
Con este Browser confi rmamos, visualmente, que hemos ejecutado los cambios de super-
clase para las tres clases.
Entre los aspectos comunes a las tres clases encontramos la variable de instancia defi -
nición. Ahora, usamos otra funcionalidad del Refactoring Browser para subir la variable
desde las subclases a la clase Instancia. Seleccionamos la clase Instancia, pedimos el menú
contextual y seleccionamos la opción pull up... del submenú instance variables.
Browser Jerárquico
El Browser Jerárquico ofrece un formato diferente a la hora de presentar las clases. En
lugar de presentar dos paneles con las Categorías de Clases y las Clases, este Browser
muestra sólo un panel con un árbol que indica la relación superclase-subclase.
Figura 175Figura 175
SSeguidamente, seleccionamos una de las variables defi nición (ver Figura 175):
Buscamos el método Estado>>initializeDefi n
ición: y, desde el menú contextual del método,
seleccionamos la opción push up, tal y como
nos indica la Figura 176.
Figura 176Figura 176
SA continuación, repetimos la operación para el método Estado>>nombre y confi rmamos
con yes cuando el Refactoring Browser nos pregunta Do you want to remove duplicate sub-
class methods? (¿Quiere remover los métodos duplicados en las subclases?). Al contestar
afi rmativamente esta pregunta, el Refactoring Browser borrará cualquier implementación
similar a la subida a la clase Instancia en sus subclases.
Volvemos a ejecutar la misma operación para el método Estado>>defi nición. Y, de nuevo,
hacemos lo mismo para el método Estado class>>defi nición:, contestando afi rmativa-
mente a la pregunta de remover métodos duplicados.
S
196S
ProgramandoconSmalltalk
197S Manos a la obra
Ahora, cambiamos el método Proceso>>initializeDefi nición: de la siguiente forma:
y modifi camos el constructor Estado class>>defi nición:transición:.
initializeDefi nición: aDefi niciónDeProceso “Inicializa la defi nición del receptor”
super initializeDefi nición: aDefi niciónDeProceso.
self cambiarEstado: defi nición estadoInicial nuevaInstancia.
Procedemos a borrar el método Estado>>initializeDefi nición:transición: y, de nuevo,
modifi camos el constructor Transición class>>defi nición:desde:
defi nición: aDefi niciónDeEstado transición: aTransición “Crea una nueva instancia del receptor”
^ (self defi nición: aDefi niciónDeEstado) initializeTransición: aTransición
initializeTransición: aTransición “Inicializa la transición del receptor”
transición := aTransición
Usar el fuente de un método para crear otro método parecido
Cuando necesitamos crear un método similar a uno ya existente, podemos
modifi car el método antiguo a modo de plantilla, simplemente cambiando su
nombre y aceptando después.
Al realizar esta operación, el Browser crea otro método y deja el anterior intacto.
En este punto, creamos el método Estado>>initializeTransición:, en la categoría initia-
lization.
A continuación, creamos el método Transición>>initializeDesde:
defi nición: aDefi niciónDeTransición desde: anEstado “Crea una nueva instancia del receptor”
^ (self defi nición: aDefi niciónDeTransición) initializeDesde: anEstado
initializeDesde: anEstado “Inicializa el ‘desde’ del receptor”
desde := anEstado
Y, lo mismo, con el método Transición>>initializeDefi nición:
Después, borramos el método Transición>>initializeDefi nición:desde:
Finalmente, ejecutamos todos los tests y vemos que seguimos en verde. Bien, sigamos lim-
piando.
Las dos especializaciones del método Instancia>>initializeDefi nición: (en las clases Pro-
ceso y Transición) son para llevar a cabo alguna operación con la defi nición cuando la
instancia la reconoce. Así, modifi camos el método Instancia>>initializeDefi nición: de la
siguiente forma:
initializeDefi nición: aDefi niciónDeTransición “Inicializa la defi nición del receptor”
super initializeDefi nición: aDefi niciónDeTransición.
hasta := defi nición hasta nuevaInstanciaTransición: self.
initializeDefi nición: aDefi niciónDeEstado “Inicializa la defi nición del receptor”
defi nición := aDefi niciónDeEstado.
self defi niciónCambiada.
198S
ProgramandoconSmalltalk
199S Manos a la obra
e implementamos el método Instancia>>defi niciónCambiada con la siguiente instrucción:
Ahora, usando la función Extract Method extraemos las expresiones que están inmediatamente
después de la llamada a super, hacia nuevos métodos de nombre #defi niciónCambiada.
defi niciónCambiada “La defi nición ha cambiado en el receptor”
Y borramos los métodos Proceso>>initializeDefi nición: y Transición>>initialize-
Defi nición:
De nuevo, ejecutamos los tests y seguimos en verde.
En este momento, nos tocaría refactorizar la jerarquía de Defi nición. De este modo,
crearíamos una clase de nombre Defi nición, modifi caríamos las superclases de
Defi niciónDeProceso, Defi niciónDeEstado y Defi niciónDeTransición, subiríamos la
variable de instancia nombre, los métodos de instancia #nombre y #initializeNombre:,
el método de clase #nombre, etc. Al crear la jerarquía nos hubiésemos dado cuenta que
nos olvidamos de ponerle nombre a la Defi niciónDeProceso y, al colgarla de la clase
Defi nición, la heredaría.
Así pues, dejamos al lector la tarea de completar la factorización de la jerarquía
Defi nición.
El último paso que nos queda por dar es la creación de la clase Gestor y convertir el
GestorConBloque en una subclase. Para ello, creamos la clase e implementamos el método
#estadoCambiadoEn:
defi niciónCambiada “La defi nición ha cambiado en el receptor”
hasta := defi nición hasta nuevaInstanciaTransición: self
defi niciónCambiada “La defi nición ha cambiado en el receptor”
self cambiarEstado: defi nición estadoInicial nuevaInstancia
estadoCambiadoEn: aProceso “Un proceso ha cambiado su estado actual.
Hace lo que se necesite por el cambio de estado, luego se responde el nombre de la transición a tomar para pasar al próximo estado, o nil cuando el estado sea el fi nal”
^ self subclassResponsibility
Mensaje #subclassResponsibility
El mensaje #subclassResponsibility es la forma de indicar que ese método debe ser
implementado en las subclases. Es la forma de marcar un método como abstracto.
Con esto damos por concluido el ejemplo del motor de Workfl ow. Se puede descargar el
código completo del ejemplo desde el sitio web del libro (http://smalltalk.consultar.com)
aunque lo que, realmente, recomienda nuestro autor es que se realice el ejemplo, paso a
paso, siguiendo las instrucciones del libro.
201S La Yapa
YAPACAPÍTULO 4CAPÍTULO 4 LA
YAPA: Sust. Regalo que el vendedor hace al comprador. (De....): además, por añadidura.
En este capítulo hablaremos, sólo superfi cialmente, de algunas funcionalidades y caracterís-
ticas que Smalltalk posee pero que, por el enfoque y el tamaño del libro, no seremos capaces
de desarrollar en su plenitud. Nuestro autor no oculta que el objetivo principal de este capí-
tulo es despertar la curiosidad de los lectores y que, esa curiosidad, sirva de motor para que
continúen en el proceso de aprender Smalltalk.
4.1 Mensaje #become:
El mensaje #become:, presente en prácticamente todos los dialectos, permite intercam-
biar un objeto (el receptor) por otro (el argumento) en toda la imagen. Los objetos que
hacían referencia al receptor, después del envío del mensajes, harán referencia al objeto
dado como argumento.
Este mensaje es muy poderoso y, a su vez, peligroso. Como sucede con todas las herra-
mientas poderosas, existen algunos casos donde su uso es la mejor solución, pero hay
4.1
202S
ProgramandoconSmalltalk
203S La Yapa
muchos más casos en los que su uso sería un error.
Uno de los casos donde suele utilizarse con éxito es cuando queremos reemplazar una
instancia “falsa” por la real; es frecuente tener que entregar instancias incompletas o,
simplemente, entregar un falso objeto que, al usarse, instancie el objeto real y se inter-
cambie por él (un lazy-instantiation).
4.2 Mensaje #doesNotUnderstand:
Cuando el receptor no entiende un mensaje, la máquina virtual le da otra oportunidad
para reaccionar enviándole el mensaje #doesNotUnderstand: con un objeto de clase
Message como argumento, donde se almacena toda la información del mensaje fallido.
Al igual que en el caso anterior, el mensaje #doesNotUnderstand: es, a la vez, poderoso y
peligroso. Y, también, como en el caso anterior, existen algunos casos donde su uso es la
mejor opción. Tal vez, el ejemplo más representativo es el de hacer proxies que deleguen
todos los mensajes que reciben a un objeto que conozcan. Combinado con el mensaje
anterior, se puede hacer que en el primer mensaje no entendido por el proxy, éste instan-
cie (o localice) el objeto real y se intercambie con el usando el #become:.
4.3 Mensajes #perform:, #perform:with:, #perform:withAll:, etc.
Estos mensajes permiten enviar un mensaje por nombre. El SUnit, por ejemplo, lo utili-
za para enviar al objeto de clase TestCase un mensaje por cada uno de los métodos que
comienzan por #test.
4.4 Pseudo-variable thisContext
La pseudo-variable thisContext permite acceder al objeto que representa el contexto de
activación del método. Con esta variable se puede acceder, entre otros, a los valores de las
variables locales, al emisor del mensaje, etc. El Depurador hace un uso intensivo de esta
pseudo- variable, al igual que el Seaside la utiliza para crear los continuations.
4.5 SLang, máquina virtual y plugins
El SLang es un subconjunto de la sintaxis y funcionalidad del lenguaje Smalltalk, que
permite generar código en lenguaje C con la misma funcionalidad. La máquina virtual
está escrita en SLang.
Esta funcionalidad también permite escribir plugins, que son fragmentos de código
Smalltalk que, al convertirlos a C y compilarlos, se ejecutan a mayor velocidad. El SLang,
al ser un subconjunto, permite que la depuración se haga con la ricas herramientas de
Smalltalk.
4.6 FFI – Foreign Function Interface
El FFI es un conjunto de objetos que permiten llamar a funciones que residen en libre-
rías compartidas. Por ejemplo, el paquete ODBC hace uso intensivo de esta funcionali-
dad para acceder a las funciones de la DLL (o el .so de Linux) de ODBC.
4.7 Metaprogramación
En Smalltalk las clases son objetos y se las puede manipular como se hace con cualquier
objeto: enviando mensaje.
Disponemos de mensajes para indagar sobre qué métodos tiene la clase, para conocer
sus superclases, para ver todas las instancias, etc. Pero también podemos cambiar la es-
tructura de las clases usando mensajes, lo que quiere decir, entre otras cosas, que pode-
mos generar programas que, a su vez, creen programas (metaprogramas).
4.8 Multithreading
Smalltalk permite la programación con procesos (el nombre que Smalltalk le da a sus
threads) desde sus primeras versiones. Contamos con objetos para sincronizar el trabajo
entre diferentes procesos como semáforos, colecciones thread-safe, etc.
4.2
4.3
4.4
4.7
4.8
4.6
4.5
Figura 1
203SS La Yapa
Figura 1
S
204S
ProgramandoconSmalltalk
205S La Yapa
Figura 5Figura 5
S
Figura 4Figura 4
S
4.9 SqueakMap
SqueakMap es un repositorio de proyectos Squeak. A día de hoy, se pueden descargar e
instalar más de 600 aplicaciones.
Figura 3Figura 3
S
4.9
4.10 MVC
MVC es el nombre que recibe el tradicional framework para hacer aplicaciones gráfi cas
que incluía el Smalltalk/80. Ese framework recibe el mismo nombre que el patrón de
diseño ya que es parte fundamental de su arquitectura. Se trata de es un framework muy
liviano que puede ser utilizado para el desarrollo de aplicaciones donde el consumo de
memoria sea una limitación.
4.10
Figura 2
4.10 MVC 4.10
Figura 2
S
4.11 Morphic
En Squeak existe otro framework para la construcciones de interfaces gráfi cas de usua-
rio. Morphic permite una manipulación más concreta de los objetos representados en
la pantalla. Los conceptos básicos de Morphic derivan del proyecto Self (http://research.
sun.com/self/).
4.11
4.12 Algunos proyectos con Squeak
A continuación mostramos algunos proyectos bastante ilustrativos de lo que se puede
diseñar en Squeak.
4.12
VideoFlow, procesamiento de vídeo.
206S
ProgramandoconSmalltalk
207S
SMALLTALKCAPÍTULO 5CAPÍTULO 5 EL FUTURO DE
El futuro próximo de Smalltalk es servir como herramienta para generar el entorno de
desarrollo que vuelva obsoleto al mismo Smalltalk. Se trata de un objetivo ambicioso, y
cuyo desarrollo ya se está llevando a cabo en diferentes áreas.
A continuación, enumeramos algunos de los proyectos que se están ejecutando en la
actualidad, y de los que se puede obtener más informaciónen los enlaces de referencia.
Traits
Smalltalk, desde los inicios, optó por un sistema de reutilización basado en herencia simple
para, sobre todo, evitar las complicaciones que conlleva un sistema de herencia múltiple
como, por ejemplo, el de C++.
Traits es un proyecto para ampliar el poder de expresión de un sistema de herencia simple,
pero sin caer en las complicaciones de un sistema de herencia múltiple. Probablemente, la
próxima versión de Squeak, la 3.9, incluirá una primera versión para entornos de producción.
Figura 6 - 7Figura 6 - 7
SProyecto Magrathea.
Figura 8 - 9 - 10Figura 8 - 9 - 10
S
Proyecto E-Alterego.
Control de Robots.
Procesamiento
fotográfi co.
Leyendo un GPS.
ProgramandoconSmalltalk
208S El Futuro de Smalltalk209S
Para más información: http://www.iam.unibe.ch/~scg/Research/Traits/
Tweak
El proyecto Smalltalk fue, desde el principio, uno de los motores que empujó el diseño
de interfaces de usuario. Tweak es un nuevo framework que pretende trasladar la sencillez
de la programación tipo eToys a un entorno con la potencia completa de Smalltalk.
Para más información: http://tweak.impara.de/
64 bits
Las primeras máquinas virtuales de Smalltalk eran de 16 bits y permitían un limitadísimo
direccionamiento de objetos. La primera implementación de Squeak ya era de 32 bits,
aumentando tremendamente la capacidad de direccionamiento. Sin embargo, para que
Smalltalk siga liderando la evolución de los entornos de programación, es necesario
pasar a una arquitectura de objetos de 64 bits.
Para más información: http://www.squeakvm.org/squeak64/
OpenCroquet
¿Qué pasaría si desarrollásemos un sistema operativo, hoy en día, teniendo todos los
conocimientos que ya se tienen? Ésta es la pregunta que rige el desarrollo de OpenCroquet,
un sistema de objetos distribuido, 100% colaborativo, con una rica interfaz de usuario
de objetos 3D (ver Figuras 177, 178 y 179).
Para más información: http://www.opencroquet.org
Tweak
64 bits
Diferentes capturas del proyecto OpenCroquet Diferentes capturas del proyecto OpenCroquet
Figura 177Figura 177
S Figura 178
ProgramandoconSmalltalk
210S 211S
Figura 179Figura 179
S
Cómo continuar
Sin duda, lo más importante para aprender Smalltalk es meter mano en un Smalltalk.
No vale de nada leer cientos de libros si no nos metemos de lleno en un ambiente de
Smalltalk y comenzamos a hacer cosas.
Pero también es cierto que la experiencia puede ser más fácil si logramos percibir algo de
la fi losofía del desarrollo con Smalltalk desde algunos libros. A nuestro autor, particular-
mente, le impactaron, de manera especial, las siguientes lecturas:
Libros
ß Smalltalk-80 - The Language: en este libro se puede entender, de personas que pati-
ciparon en el desarrollo de Smalltalk, la fi losofía del lenguaje y las motivaciones que lo
hicieron posible.
ß Smalltalk-80 - Bits of History, Words of Advice: este libro le impactó por lo actual de
los comentarios hechos por personas que probaron Smalltalk a principios de los años 80.
ß Smalltalk Best Practice Patterns: esta obra le enseñó que una de las cosas más im-
portantes, para ser un buen programador Smalltalk, es prestar muchísima atención a la
claridad del código.
ß Smalltalk with Style: éste le mostró la mayoría de las convenciones que se utilizan a la
hora de escribir código Smalltalk.
ß Design Patterns - Elements of Reusable Object Oriented Software: no es posible
decir hoy en día que se sabe programar con objetos si no se ha leído este libro. Éste es
el culpable de que hoy nuestro autor esté escribiendo un libro de Smalltalk y no uno de
212S 213S
Conclusión
Este libro no pretendía ser un manual de Smalltalk, ni tampoco una guía de referencia,
como indicamos en la Introducción. El objetivo del autor al escribir esta obra ha sido
el de mostrar la fi losofía inherente a Smalltalk, la manera en que ésta impacta en el de-
sarrollo de software y, a su vez, cómo esta forma de trabajar genera software de mayor
calidad en menos tiempo que con otras herramientas.
En palabras de nuestro autor: “elegí mostrar cómo un ambiente del poder de Smalltalk per-
mite un estilo de programación muy incremental, ya que el costo de los cambios es muy bajo.
Sin embargo, creo que es necesario aclarar que Smalltalk no requiere que se trabaje así, y que
es perfectamente posible utilizar un ambiente de Smalltalk con una forma de trabajo menos
incremental y menos dinámica. Cada programador tiene que decidir, entre otras cosas, qué
tipo de programación prefi ere. En este libro sólo intenté mostrar el tipo de programación que
yo prefi ero, y que no puedo desarrollar en otras herramientas que no sean Smalltalk”.
“Es posible que en algún lector quede la idea de que esta metodología no es muy rápida. Si
eso les ha ocurrido consideren que tuve que mostrar, paso a paso, las herramientas y cómo
se usan, también tuve que introducir notas con explicaciones sobre tal o cual clase, y eso
podría haber impactado negativamente en la percepción del tiempo de desarrollo. Reto a
cualquiera que se haya podido quedar con ese sabor de boca a probar la metodología en
algún sistema real y que extraigan las conclusiones sólo después de esa experiencia”.
Por todo esto, Smalltalk es una pieza increíble de software, no obstante, no es la meta en sí
mismo, sino que es parte de una visión sobre cómo deberíamos hacer uso de los ordenadores.
Los objetivos planteados en las ideas que rigen el desarrollo de Smalltalk todavía no han
sido alcanzados en su plenitud. Sin embargo, Smalltalk sí que nos permite, en la actuali-
dad, imaginar cómo podría ser la experiencia de utilizar un ordenador y, para alcanzar
Java o .NET. A través de las explicaciones de algunos patrones, se enteró de la existencia
de Smalltalk y de su impacto sobre la tecnología de objetos. Éste es el libro que despertó
su curiosidad por aprender Smalltalk.
ß The Dessign Patterns Smalltalk Companion: esta obra le enseñó mucho Smalltalk
por comparación con la implementación de los patrones en el libro anterior.
Papers o artículos
ß Design Principles Behind Smalltalk: excelente descripción de las motivaciones que
rigen el desarrollo del proyecto Smalltalk. Parece ciencia fi cción si consideramos que la
nota fue publicada en la revista Byte de agosto de 1981.
ß Personal Dynamic Media: excelente explicación de las motivaciones que subyacen del
desarrollo de Smalltalk, escrita por Alan Kay en el año 1976.
ß The Early History of Smalltalk: ésta es una historia de Smalltalk, escrita por Alan
Kay, en el año 1993 (antes de que comenzara el proyecto Squeak, allá por el 1995) en la
que cuenta de dónde vienen las ideas principales. Termina con una frase que también le
marcó especialmente: “¿Dónde están los Dan (Ingalls) y las Adele (Goldberg) de los 80s y
los 90s que nos lleven al próximo nivel de desarrollo?”.
Grupos de Usuarios
Existen diferentes grupos de usuarios y listas de distribución en Internet. Las comunida-
des de usuarios de Smalltalk suelen ser muy amables, a diferencia de otras comunidades,
contestando a preguntas de personas que acaban de introducirse en el ambiente.
Papers o artículos
Grupos de Usuarios
214S
ProgramandoconSmalltalk
el objetivo máximo (que cualquier persona, y no sólo programadores, pueda interactuar
con la información y construir conocimiento a través de esa interacción) debemos seguir
mejorando este entorno.
Por ello, los programadores tienen una tarea muy clara: trabajar para convertirse en mejo-
res programadores y compartir con todo el mundo - programadores y no programadores
– los conocimientos que van adquiriendo en el día a día.
APÉNDICE
217SS
Herramientas usadas en el libro
Para mostrar la forma de trabajo que permite el uso de un entorno de objetos como
Smalltalk, en este manual, el autor ha elegido desarrollar algunas aplicaciones de ejemplo
paso a paso, confi ando en que el lector perciba la diferencia entre trabajar con Smalltalk
y una experiencia de primera mano. El sentido de esta obra se perdería si el autor no ha
conseguido que los lectores se animen a probar, en un entorno de Smalltalk, todo lo que
les venga en gana.
Para reducir al mínimo el tiempo necesario para entrar en el ambiente, el autor creó una
imagen de Squeak pre-confi gurada y con algunos paquetes extras instalados.
La imagen utilizada es una 3.8-full ofi cial con varias preferencias cambiadas y con los
siguientes paquetes instalados:
NOMBRE VERSIÓN DESCRIPCIÓN
YAXO 2.2 Parser de XML.
XML Stack Parser 1 Parser de XML basado en una pila.
Copy as HTML 1 Utilidad para copiar al porta papeles una
versión HTML del texto Squeak. Incluye el
formato con sus colores, etc. Usado para el
código mostrado en este libro.
StringEnh 1 Algunos extras para la clase String.
218S 219S
S
NOMBRE VERSIÓN DESCRIPCIÓN
Shout 4 Paquete que muestra sintax-highlight
mientras se escribe.
ShoutMonticello 2 Extensiones Shout para las herramientas
relacionadas con el Monticello.
ShoutWorkspace 2 Un Workspace que, valiéndose del Shout,
hace sintax-highlight mientras se escribe.
Refactoring Browser
for 3.8
3.8.43 Extensiones para los browsers para hacer
refactorizaciones del código.
SUnit 3.1.22 Framework para hacer UnitTesting con
Smalltalk. Derivado directo del primer
framework de UnitTesting escrito por Kent
Beck.
DynamicBindings 1.2 Framework para hacer UnitTesting con
Smalltalk. Derivado directo del primer
framework de UnitTesting escrito por Kent
Beck.
KomServices 1.1.1 Paquete requerido por KomHttpServer.
KomHttpServer 7.0.2 Paquete requerido por Seaside. Servidor web
íntegramente desarrollado en Smalltalk.
Seaside 2.5 Framework de desarrollo de aplicaciones
web basado en continuations. Para más
información ver el sitio www.seaside.st
La imagen ofi cial se puede descargar desde www.squeak.org, concretamente de http://ftp.
squeak.org/current_stable/Squeak3.8-6665-full.zip.
Para construir una imagen, como la utilizada en este manual, lo más fácil es instalar el
paquete ProgramacionConSmalltalk, usando el SqueakMap, y éste instalará, a su vez, las
herramientas necesarias descargándolas desde Internet.
221S
Bibliografía
Este manual, como ya adelantamos en la Introducción, no es una guía completa de Sma-
lltalk. Por ello, recomendamos al lector que consulte los siguientes libros y documentos
como complemento ideal para continuar aprendiendo Smalltalk.
Algunas de estas referencias se pueden descargar, de forma gratuita, desde:
http://www.iam.unibe.ch/~ducasse/FreeBooks.html
A Little Smalltalk
Budd, Tim
Addison Wesley. 1987.
Back to the future - The Story of Squeak, A Practical Smalltalk Written in Itself
Kay, Alan
http://users.ipa.net/~dwighth/squeak/oopsla_squeak.html
OOPSLA 97 Proceeding. P. 318-326. ACM Press, New York, 1997.
(a postscript by Dan Ingalls in [Guzdial/Rose 2001]).
Design Patterns - Elements of Reusable Object Oriented Software
Gamma, E., R. Helm, R. Johnson, and J. Vlissides
Addison Wesley. 1995.
Design Principles Behind Smalltalk
Ingalls, Dan
http://users.ipa.net/~dwighth/smalltalk/byte_aug81/design_principles_behind_smalltalk.html
222S 223S
eXtreme Programming eXplained - Embrace Change
Beck, Kent
Addison Wesley. 1999.
Guide to Better Smalltalk. A Sorted Collection
Beck, Kent
Cambridge University Press en asociación con SIGS Books. United State of America. 1999.
Inside Smalltalk (vol. one and two)
LaLonde, Wilf. R. Pugh, John. R.
Prentice Hall. 1990.
Object-Oriented Implementation of Numerical Methods. An Introduction with
Java and Smalltalk
Besset, Didier H.
Morgan Kaufmann Publishers. United State of America. 2001.
Personal Dynamic Media
Kay, Alan. Goldberg, Adele
http://www.dolphinharbor.org/dh/smalltalk/documents/
Reprinted in The New Reader. 1976.
Powerful Ideas in the Classroom
B. J. Allen Conn. Rose, Kim
Viewpoints Research Institute, Inc. 2003.
Refactoring Browser Tool
Brant, Hohn, and Don Robert.
http://st-www.cs.uiuc.edu/~droberts/tapos/TAPOS.htm
Refactoring. Improving The Design of Existing Code
Fowler, Martin
Addison Wesley. Massachusetts. 1999.
Simple Smalltalk Testing - With Patterns
Beck, Kent
http:/7www.xprogramming.com/testfram.htm
Smalltalk An Introduction To Aplication Development Using visualwork
Hopkins, Trevor and Horan, Bernard
Pearson Education. 1995.
Smalltalk and Object Orientation - An Introduction
Hunt, John.
Springer-Verlag. 1997.
Smalltalk Best Practice Patterns
Beck, Kent
Pretice Hall, New Jersey. 1997.
Smalltalk by Example - The Developer’s Guide
Sharp, Alex
McGraw Hill Text. 1997.
Smalltalk with Style
Skublics, Suzanne. Klimas, Edward J. Thomas, David A.
Pretice Hall. New Jersey. 1996.
Smalltalk-80 - The Language
Goldberg, Adele. Robson, David
Addison-Wesley Proffesional. 1989.
Smalltalk-80 - Bits of History, Words of Advice
Krasner, Glenn
Addison Wesley. California. 1983.
Smalltalk-80 - The Interactive Programming Enviroment
Goldberg, Adele
Addison Wesley. California. 1984.
Smalltalk-80 - The Language and Its Implementation
Goldberg, Adele. Robson, David
Addison Wesley. California. 1983.
224S 225S
Smalltalk, Object and Design
Chamond, Liu
To Excel. New York. 1996.
Squeak - Object-Oriented Design whit Multimedia Applications
Guzdial, Mark
Prentice Hall. New Jersey. 2001.
Squeak. Open Personal Computing and Multimedia
Guzdial, Mark. Rose, Kim
Prentice Hall. New Jersey. 2002.
Test-Driven Development - By Example
Beck, Kent
Adisson Wesley. 2002.
The Art and Science of Smalltalk
Lewis, Simon
Prentice-Hall. 1995-1999.
The Dessign Patterns Smalltalk Companion
Alpert, Sherman. Brown, Kyle. Woolf, Bobby
Addison Wesley. California. 1998.
The Early History of Smalltalk
Kay, Alan
http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html
The Joy of Smalltalk
Tomek, Ivan. 2000.
The Taste of Smalltalk
Kaehler, Ted. Patterson, Dave
WW Norton & Co. 1986.
Tools For Thought. The History and Future of Mind-Expanding Technology
Rheingold, Howard
The MIT Press Cambridge, Massachusetts. 2000.
http://www.rheingold.com/texts/tft/
Una Explicación de la Programación Extrema - Aceptar el cambio
Beck, Kent
Adisson-Wesley Iberoamericana España, S.A. Madrid. 2002.