Metodología
de desarrollo software
Tras leer algunos libros y buscar por internet, me da la impresión
de que esto de las metodologías es algo que está todavía
un poco verde. Hay montones de ellas y cada "gurú" tiene la suya.
Sin embargo, casi todas ellas tienen muchas cosas comunes, así que
aquí trataré de resumir la conclusión a la que he llegado.
El resultado es cosa mía, cogiendo lo que he creido mejor de lo que
he leido y tratando de obtener una metodología que me resulte práctica.
FASES
Las fases en las que hay que desarrollar un proyecto, son básicamente
Requisitos, análisis, diseño preliminar, diseño detallado,
codificación y pruebas. Voy a detallar estas fases con un ejemplo,
suponiendo que quiero hacer un programa para jugar al ajedrez.
REQUISITOS
Los requisitos son una lista de cosas que queremos que haga nuestro programa.
Lo normal es que recopilemos dicha lista hablando con todas las personas
que podamos: usuarios de nuestro programa, expertos en el tema de que trata
el programa (ajedrez), etc, etc.
Normalmente la gente con la que hablemos nos dará los requisitos
de una forma vaga y desordenada. Es labor nuestra ordenarlos (por temas,
por dificultad, por importancia para los usuarios, etc) y asegurarnos de
que son claros.
Para asegurarnos de que un requisito es claro, debemos saber qué
persona lo ha propuesto y por qué lo ha propuesto, qué es
lo que cree exactamente que vamos a hacer en nuestro programa cuando nos
ha dicho ese requisito.
Por ejemplo, unos cuantos requisitos para nuestro programa de ajedrez
pueden ser:
- Que avise de los jaques
- Que el movimiento de las piezas del tablero sea suave
- Que tarde menos de 30 segundos en "pensar" un movimiento
- Si el usuario no sabe qué mover, que pueda pedirle ayuda al
ordenador.
- Que pueda aprender aperturas que se le introduzcan.
- Que guarde las partidas en una base de datos.
- ...
y ahora toca aclararlos. ¿Qué es avisar de los jaques? ¿un
pitido? ¿un texto "jaque" en algún sitio de la pantalla? ¿una
voz que diga "jaque"? ¿Debe avisar también cuando el usuario
es el que da jaque al ordenador? ...
E incluso ordenarlos. ¿Cuales son los más importantes para
los usuarios? Los menos importantes se podrían dejar para el final
o darles menos importancia, dedicarles menos tiempo que a los importantes.
¿Cuales son más difíciles de hacer?. Quizás
debamos dedicarnos a ellos primero, para saber si vamos a ser capaces de
hacerlos, o vamos a tener que dejar el programa a medias, después
de haber trabajado unos meses en él. Podríamos empezar a negociar
antes con el cliente para cambiar dicho requisito por otro.
ANÁLISIS
Durante el análisis vamos a definir más claramente qué
es lo que va a hacer nuestro programa. Debemos hacer varias cosas principalmente:
- Identificar actores. En lenguaje UML, actores son
los usuarios y cualesquiera otros sistemas con los que se pueda comunicar
nuestro programa. En nuestro programa de ajedrez, un actor sería
el usuario que va a jugar al ajedrez. Le llamaremos "jugador". También
se indicaba como requisito que pueda aprender aperturas. La persona que
le va a enseñar aperturas a nuestro programa podría ser
otro actor, al que podemos llamar "maestro". Una misma persona puede ser
varios actores. Por ejemplo si alguien compra nuestro programa de ajedrez,
a veces puede jugar y otras veces puede enseñarle aperturas. Es
la misma persona, pero el papel que desempeña en cada caso es distinto.
- Identificar casos de uso. Un caso de uso es algo que
un actor quiera hacer con nuestro sistema. Por ejemplo, un caso de uso
para un "jugador" es "jugar una partida". Está claro que el jugador
quiere nuestro programa para jugar una partida de ajedrez. Otro posible
caso de uso es "análisis de un tablero". El "jugador" pone las
piezas en determinada posición y quiere que el ordenador le dé
una lista de posibles movimientos, etc, etc. En resumen, debemos obtener
una lista de cosas que los actores van a querer hacer con nuestro sistema.
Deben ser cosas grandes y no meternos en los detalles. "Mover una
pieza" no sería un caso de uso, sino una parte de "jugar una partida".
De aquí saldría un diagrama
UML de casos de uso.
- Detallar casos de uso. En un texto vamos poniendo
varios puntos. En cada punto ponemos sentencias del estilo "el usuario
hace tal cosa y el programa hace tal otra". Es decir, explicamos por escrito,
desde el punto de vista del usuario, qué es lo que tiene que hacer
y qué es lo que va a hacer el ordenador. Por ejemplo, para "jugar
una partida"
- El usuario indica que quiere jugar una partida. El programa le pregunta
el color de las piezas con las que quiere jugar
- El usuario elige el color. El programa le dibuja el tablero con las
piezas colocadas en la posición inicial. Las piezas del usuario
en la parte inferior de la pantalla. Si el programa tiene blancas, piensa
y realiza el primer movimiento.
- El usuario mueve su pieza. El programa comprueba que el movimiento
es correcto, piensa y realiza su propio movimiento.
- Se repite el paso anterior hasta llegar a jaque mate o tablas.
En el caso de uso se detalla una situación normal, sin fallos ni
situaciones raras. Al final debería ponerse una pequeña lista
con los posibles fallos o situaciones anómales.
- Si el movimiento del ordenador pone en jaque al usuario, el ordenador
avisa del jaque
- Si el usuario intenta un movimiento incorrecto, el ordenador avisa
y deja al usuario que vuelva a intentarlo
Advertir que en ningún caso nos hemos metido a detallar cómo
va a hacer algo nuestro programa, sólamente qué es
lo que va a hacer.
Siguiendo los esquemas UML, podemos incluso hacer por cada caso de uso un
diagrama de secuencia en el que los objetos implicados son el actor (el
"jugador") y nuestro programa, sin meternos dentro de él.
También en este paso podemos ir empezando a pensar en cómo
van a ser las pantallas de la interface de usuario, de nuestro programa.
- Diagrama de clases del
negocio. Es un diagrama de clases de objetos que tienen sentido
para el usuario. En el caso del ajedrez los objetos serían del
estilo "tablero", "pieza blanca", "torre blanca", "jugador", "cronómetro",
"partida", "torneo" etc. Nunca "lista de piezas", "array de casillas",
etc. También se presentan las relaciones entre ellos, como "las
piezas están en el tablero", "la torre blanca es una pieza blanca",
etc.
Este diagrama a este nivel tiene dos utilidades:
- Asegurar que los que saben del tema y los que hacen el programa están
hablando de lo mismo.
- Servir de base para el diseño detallado orientado a objetos.
Cuando el tema es más complejo o menos conocido que una partida de
ajedrez, es posible que el informático que programa no tenga muy
claros muchos de los conceptos que tiene que manejar. No sabe qué
es un asiento contable, un balance, si hay relación entre ellos,
etc. Es por eso importante hacer un diagrama de este tipo, en conjunto con
un experto o con el cliente, para asegurar que no hay malentendidos.
DISEÑO PRELIMINAR
Aquí ya empezamos a pensar en cómo vamos a hacer las cosas.
En el diseño preliminar tratamos de establecer la arquitectura
de nuestro programa. La arquitectura es un esquema de en qué módulos/paquetes
vamos a dividir nuestro programa, qué librerías. Si el programa
es suficientemente grande, quizás vaya en varios ejecutables, una
arquitectura cliente/servidor, etc.
Viendo los casos de usos, deberíamos ver qué cosas podemos
hacer comunes o como librerias aparte, que podamos reutilizar. Por ejemplo,
en nuestro caso del juego de ajedrez, podríamos hacer los siguientes
paquetes:
- Paquete de ajedrez. Un paquete que sea capaz de jugar al ajedrez, pero
sin ningún tipo de relación con las pantallas. Si es una
clase, tendría métodos del estilo "Poner_Piezas_Posición_Inicial()",
"Mover_Pieza()", "Dame_Siguiente_Movimiento()", etc, etc. Sólo
con este paquete, seríamos capaces de jugar al ajedrez, o de instanciar
dos paquetes y hacer que jueguen entre ellos, o jugar en red, etc.
- Interface con el usuario. Un paquete con el dibujo del tablero, las
piezas, recoger los movimientos del usuario etc.
Dentro de estos paquetes, podemos ir pensando más subpaquetes, etc.
En este punto y con los casos de uso en general, debemos tener cuidado.
Según una crítica generalizada a los casos de uso, estos llevan
a un diseño funcional y no a uno orientado a objetos. Debemos tratar
de pensar en objetos y almacenarlos juntos en la misma librería cuando
estén muy relacionados entre sí, no en funciones. Por ello
es buena idea tratar de agrupar las clases del diagrama de clases del negocio
en paquetes y tratar de desarrollar la arquitectura a partir de ellas.
Es importante en este paso definir las interfaces y relaciones entre paquetes.
Para ello puede servir de ayuda hacer los diagramas
de secuencia de los casos de uso mostrando los actores, los paquetes
y los mensajes entre ellos. Según vayan creciendo los diagramas de
secuencia por aquello de ir entrando en detalles, podremos ir extrayendo
subcasos de uso, como "mover pieza", "elegir color", etc.
DISEÑO DETALLADO
En el diseño detallado ya se entra a nivel de clases y métodos.
Por cada paquete que hayamos extraido en el paso anterior y siguiendo siempre
los casos de uso, debemos ir detallando las clases que vamos a implementar
y los métodos que van a tener. Detallamos aun más los casos
de uso y las interfaces de las clases.
En este punto pueden ser de ayuda los patrones
de diseño. La gente que ha ido diseñando a lo largo de
la historia, se ha encontrado con problemas (¿cómo hago que
la clase A se entere que la clase B ha cambiado sin necesidad de que B vea
a A?, ¿En qué clase pongo el metodo que suma las clases A
y B? ¿Como me aseguro que de esta clase sólo se haga una instancia?,
etc, etc). Algunos de dichos problemas salen con mucha frecuencia (como
los indicados de ejemplo) y se han establecido soluciones "estandard", que
funcionan bien. Dichas soluciones se llaman patrones. No está de
más leer algo sobre patrones de diseño orientados a objetos
para tener una lista de "soluciones" a aplicar.
Hay incluso patrones a nivel de arquitectura. Es bastante conocido, por
ejemplo, el patrón separación
modelo-vista. En nuestro caso "modelo" sería el conjunto de clases
que juegan al ajedrez. "Vista" sería el conjunto de clases que "pintan"
la partida en la pantalla. Hacer esa separación en dos módulos,
hace que nuestro módulo de ajedrez sea reaprovechable, incluso si
cambiamos de entorno de ventanas (de windows a ms-dos o de ms-dos a unix).
Eso sí, es imprescindible que sólo la "vista" vea al "modelo"
y nunca al revés. Si lo hacemos al revés y queremos llevarnos
nuestro "modelo" juego de ajedrez, tendremos que llevarnos también
parte de la "vista".
IMPLEMENTACIÓN Y PRUEBAS
Pues a ello. Hay muchas cosas que se podrían contar aquí,
pero no son de diseño.
De las pruebas podemos decir que hay que escribir los "casos de prueba".
Básicamente son como la descripción de los casos de uso, pero
indicando datos concretos que el operador va a introducir y qué resultados
exactos debe dar nuestro programa.
DESARROLLO ITERATIVO E INCREMENTAL
En casi todas las metodologías que he leido, se habla de hacer
el sistema en varias iteraciones. Es decir, hacer un poco de requisitos,
un poco de analisis, un poco de diseño, un poco de codificación
y pruebas y vuelta a empezar.
Hay varios motivos para realizar esto así:
- No eternizarse en un paso. Por mucho análisis que hagamos, nunca
estaremos seguros de haber acabado y siempre se nos ocurriran más
cosas. Idem para el diseño. Siguiendo este método, nos fijaríamos
unos plazos para cada cosa y haríamos lo siguiente:
- Obtener los máximos requisitos posibles en el tiempo fijado.
Ordenarlos y fijarnos en los más importantes.
- Obtener los máximos casos de uso y actores posibles. Ordenarlos
por importancia (más importantes para el usuario, más
dificiles de implementar o que ayuden a definir lo máximo posible
la arquitectura) y detallar sólo los primeros en la lista.
- Obtener la arquitectura para esos primeros casos de uso.
- Diseño detallado para esos primeros casos de uso, siempre
con un plazo.
- Codificar y probar.
- Vuelta a empezar con los requisitos (¿han cambiado?, ¿hay
nuevos?), los casos de uso (buscar más casos de uso, detallar
los siguientes en importancia), etc, etc.
Cada uno de estos pasos hay que detenerlo cuando creamos que tenemos algo
que puede funcionar. No hay que tratar de dejarlo perfecto. Cada vez que
pasemos al siguiente paso, encontraremos fallos en el paso anterior. Hay
que ir arreglándolos de forma que al final todo quede coherente.
- Ir haciendo versiones intermedias de nuestro programa. Cuando hayamos
implementado varios casos de uso (que son los más importantes),
podemos hacer una versión para enseñar al cliente. Es mejor
que ponga las pegas y sugerencias ahora que no cuando ya lo hayamos terminado
todo. Este punto es especialmente útil cuando el cliente no sabe
muy bien qué es lo que quiere.
En la página de los libros
tienes algunos sobre este tema. Entre los libros tienes el del "Proceso
unificado de desarrollo software" (RUP en inglés). Hay un resumen
de ese libro en http://www.cs.ualberta.ca/~pfiguero/soo/metod/uml-met.html
que también explica los diagramas de UML
La programación extrema es otra metodología
que se sale un poco de este resumen.
Estadísticas y comentarios
Numero de visitas desde el 4 Feb 2007:
- Esta pagina este mes: 9
- Total de esta pagina: 124214
- Total del sitio: 44170352