6. Diseño evolutivo
ÍNDICE
1. Introducción..............................................................................................................1921.1 El método de trabajo.....................................................................................................193
Primero, lo esencial..........................................................................................................................193Hacer justo, lo necesario..................................................................................................................194Software cerrado y abierto...............................................................................................................194Definiciones flexibles.......................................................................................................................195
2. Diseño de un cajero automático simple...................................................................1972.1 Introducción...................................................................................................................197
2.2 Extracción sobre una cuenta.........................................................................................197Tarea inicial: hacer una sola extracción sobre una cuenta...............................................................197Diagrama de Clases de una extracción sobre una cuenta.................................................................200Código Java de una extracción sobre una cuenta.............................................................................202
2.3 Primera Ampliación. Hacer varias extracciones sobre una cuenta.........................205Diagrama de secuencia de muchas extracciones sobre una cuenta.................................................207
2.4 Segunda Ampliación. Varias extracciones sobre una cuenta de varias posibles......212
2.4 Tercera Ampliación: Control de Acceso......................................................................216Escena de acceso..............................................................................................................................216Código Java ACCESO....................................................................................................................218Integración de acceso y extracción..................................................................................................219Estudio de alternativas de unión......................................................................................................2212.4.1 Una iteración. Otra forma de unión.........................................................................................2242.4.2 Otra iteración...........................................................................................................................2252.4.3 Otra iteración más...................................................................................................................226Interpretación Arquitectónica del Sistema.......................................................................................230
2.5 Cuarta Ampliación: Ingreso de Dinero........................................................................232Ejercicio...........................................................................................................................................233Diseño del mecanismo de ingreso....................................................................................................234Código Java INGRESO....................................................................................................................236Integración de ingreso al sistema.....................................................................................................237Código Java Selector........................................................................................................................240Diagrama de clases del sistema completo........................................................................................241
2. 6 Recapitulemos...............................................................................................................242
191
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
1. IntroducciónDespués de haber hecho un recorrido por los cimientos del modelo orientado a
objetos, por sus conceptos y por partes de su código, debemos comenzar a ver cómo se
pueden conseguir desarrollo y software evolutivo.
Una forma de desarrollo evolutivo es el llamado desarrollo iterativo e
incremental. Iterativo en el sentido amplio de corrección. Es decir, en el sentido de
reducir el error, de aproximarse más. Se trata de corregir el rumbo del proyecto al
evaluar los resultados con el cliente, de eliminar las equivocaciones detectadas durante
las pruebas y de mejorar las cualidades internas del software, por ejemplo su
consistencia. Incremental en el sentido de añadir capacidades y funciones al software de
acuerdo con el crecimiento de las necesidades. Lo incremental y lo iterativo van juntos,
aunque no siempre se presentan los dos.
Hay muchas formas de desarrollo iterativo e incremental, con sus técnicas y
métodos respectivos. Probablemente, casi todas son buenas o, al menos, tienen muchos
aspectos buenos. Por tanto, lo más importante es crear una actitud evolutiva hacia el
diseño y su modo de hacerlo, que se enriquezca después con la diversidad de fuentes
disponibles. Este es el objetivo central del capítulo.
Las palabras iterativo e incremental se utilizan en los procesos de
aproximaciones sucesivas. Se han tomado de ahí porque tienen una finalidad semejante:
resolver problemas con incertidumbre. Tanto el desarrollo de software iterativo e
incremental como los procesos de aproximaciones sucesivas buscan un valor
desconocido. Pero, en el desarrollo de software ese valor desconocido es, además,
inestable; se desplaza con el tiempo. Es como disparar a una diana poco visible que se
mueve con una componente de imprevisilidad.
Esa distinción establece una diferencia cualitativa entre el desarrollo evolutivo y
los procesos de aproximaciones sucesivas, sobre todo al acentuarse la componente
futura de la incertidumbre. Por tanto, son diferentes. No obstante, podemos aprovechar
sus similitudes, al menos en primera instancia.
Los sistemas software se pueden considerar como sistemas complejos alejados
del equilibrio que se organizan con una estabilidad transitoria, gracias al trabajo de los
desarrolladores. En cierto sentido hay una analogía con las denominadas estructuras
192
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
disipativas, presentes en la naturaleza viva e inanimada. De este parecido se pueden
obtener diversas ideas para el diseño software. Una de las más interesantes es la ruptura
y restablecimiento de la simetría, como veremos en este capítulo.
Un sistema software con capacidad de evolución deberá tener holguras para
adaptarse a la incertidumbre. Si todo está rígidamente definido, difícilmente se podrá
amoldar a los cambios. Por tanto, deberá tener holguras en sus definiciones, que son uno
de los elementos adversos para la evolución. Aunque necesite definiciones exactas y
concretas para funcionar, el sistema debe operar con definiciones flexibles. Es decir,
con definiciones parciales, definiciones diferidas e, incluso, con definiciones ausentes,
para conseguir libertad de modificación. En este capítulo veremos cómo se pueden
utilizar las cualidades del modelo de software orientado a objetos para conseguir
definiciones flexibles en el sentido de evolución y de reutilización.
El capítulo tiene como hilo conductor la construcción de dos aplicaciones
software sencillas, pero relevantes. En la primera nos concentraremos en ver un método
de diseño evolutivo, suponiendo incertidumbre y en la segunda, al tema de la
reutilización, técnica común en las ingenierías.
1.1 El método de trabajo
Primero, lo esencial
Como el objetivo primordial de la evolución del software es la supervivencia del
software, nos debemos ocupar primero de los aspectos de mayor riesgo del proyecto.
Donde el riesgo debe evaluarse por la incertidumbre y las consecuencias de esa
incertidumbre.
La secuencia evolutiva debe ser acordada con el cliente después de un estudio de
los riesgos del proyecto; estudio que nunca será exhaustivo. Si no hay elementos
inciertos en la técnica de desarrollo, se deben desarrollar primero las prioridades más
altas del cliente, porque de no cumplirlas, el proyecto fracasa. En general, las
prioridades más altas del proyecto se refieren al comportamiento esencial del sistema, a
sus operaciones más frecuentes. Las excepciones y los detalles, dependiendo del riesgo,
se pueden dejar para después.
193
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Hacer justo, lo necesario
Los procesos evolutivos son costosos porque se enfrentan a la carga adicional de
complejidad que representa la incertidumbre. Por tanto, conviene conducirlos justo con
lo necesario para ahorrar recursos y, también, para no dilapidar. Es decir, para perder lo
menos posible cuando obtenemos resultados negativos. De aquí que, generalmente, se
postergue el desarrollo de las excepciones hasta comprobar el rumbo adecuado de la
esencia del sistema.
Sin embargo, no debemos confundir la chapucería, ni la negligencia, con el
delicado balance de hacer justo lo necesario, expuesto en el párrafo anterior. Construir
software escribiendo su código directamente, tiene malas consecuencias sobre la
capacidad evolutiva del software. El código está hecho para la máquina. Es el intrincado
bosque de los detalles más pequeños del sistema; un bosque impenetrable e
incomprensible. Como si viésemos a un ser humano a partir de sus células.
El software debe ser diseñado con mucho más cuidado si se quiere software
evolutivo. Conviene hacer el diseño con recursos informáticos que faciliten las
modificaciones y de paso, generen la documentación. Pero, si no se tienen con papel y
lápiz. Los documentos de diseño son esenciales al proceso de diseño y construcción. El
delicado equilibrio de hacer justo lo necesario significa detallar lo imprescindible para
la comprensión del equipo de trabajo en ese ciclo de desarrollo. La información
complementaria que aclara y recuerda aspectos para el próximo ciclo debe añadirse
después de alcanzar resultados satisfactorios. Esta documentación adicional se perdería
en el caso de resultados negativos. La queja burocrática sobre el modelo en Cascada no
se debe a la cantidad de documentación. Hasta ahí se pudiera justificar, el problema es
cuando hay que modificarla o peor aún, echarla en el cesto.
Software cerrado y abierto
Cada incremento supone una entrega de software, al menos para su contraste con
el cliente, que decide sobre el curso del producto que estamos desarrollando. Por tanto,
cada entrega supone un software, en cierto sentido cerrado, terminado. Pero, también,
abierto a los próximos incrementos. El software debe estar cerrado, funcionando, para
cobrar o para evaluarlo y, a la vez, debe ser relativamente sencillo de modificar para que
se adecue a las nuevas condiciones inciertas del contexto.
194
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Se puede establecer una similitud con los métodos numéricos aproximados que
aportan, al final de cada vuelta, una solución concreta, cerrada, para su evaluación, pero
también abierta, susceptible de ser mejorada. Cada vuelta del proceso de desarrollo de
software debe ofrecer una solución concreta para ser evaluada, al menos, y debe facilitar
la siguiente. Éste es el sentido de software cerrado y abierto, un equilibrio difícil sobre
el que descansa una buena parte de las cualidades evolutivas del software, y también
nuestras cualidades como diseñadores.
Sobre las cualidades que otorgan modificabilidad al software se puede decir
mucho, pero como mínimo, un software abierto debe permitir que las modificaciones
requieran muy poca revisión de lo anterior y, además, que lo perturben muy poco.
La contradicción cerrado y abierto es antigua en el mundo de la ingeniería.
Cualquier máquina debe tener construidas y ensambladas sus piezas, por lo menos, para
que funcione. Pero también se exige, a veces, que sea fácil de modificar. Sucede igual
en el software, sólo que la exigencia de modificabilidad está presente casi siempre. Para
abordar esa contradicción, la ingeniería tradicional inventó la modularidad. Es decir, el
aislamiento y la independencia de los componentes. Insistimos, se inventó, porque en la
naturaleza no se encuentran módulos al estilo de la ingeniería.
Definiciones flexibles
Para resolver la contradicción cerrado y abierto, la ingeniería de software
también utiliza la modularidad, pero además, usa otras técnicas propias del software, ya
referidas. Aunque los textos las tratan de forma independiente, nosotros proponemos
verlas de conjunto, como variantes de definiciones flexibles: definiciones parciales,
definiciones diferidas y definiciones ausentes.
Las definiciones parciales se consiguen ocultando una parte de la definición, ya
sea dentro de los módulos o delegando tareas a otros módulos. Ambas técnicas son
fundamentales y se aplican desde los tiempos del modelo estructurado para facilitar los
cambios en el software. Nosotros las utilizaremos sistemáticamente en el diseño.
Algunos lenguajes de programación permiten acceder directamente a los datos
contenidos en los objetos. Es decir, sin recurrir a las funciones de los objetos encargadas
de manipular los datos. Es más fácil, pero las consecuencias se pagan después, cuando
se requieren cambios. La técnica de ocultar información tiene una motivación práctica,
no académica, aunque se enseñe en las universidades.
195
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Las definiciones diferidas se obtienen mediante los mecanismos de herencia y
polimorfismo. Procuraremos no utilizar la herencia con el objetivo de clasificar, ni con
el objetivo de ampliar el software, modificando la definición de los progenitores.
Preferiremos la delegación para conectar objetos semejantes, como se explicó en el
capítulo dedicado al mundo de los objetos.
Y por último, trataremos de prescindir de las definiciones innecesarias. Por
ejemplo, la definición del sistema. Esto quiere decir que evitaremos el uso de las
relaciones de agregación, salvo que estén justificadas para conseguir el mismo tiempo
de vida de los objetos o para lograr efectos de transitividad.
196
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
2. Diseño de un cajero automático simple
2.1 IntroducciónSe trata de construir el software para un cajero automático muy simple. Un
problema familiar, por razones didácticas. Pero donde vamos a suponer que las
necesidades del cliente aparecerán poco a poco y que no las conocemos de antemano. El
objetivo es apreciar el método de desarrollo y diseño para abordar la incertidumbre. Si
el problema fuese realmente desconocido se desviaría la atención hacia la comprensión,
que no es nuestro asunto. Debemos concentrarnos en el método de trabajo.
Desarrollaremos el software por aproximaciones sucesivas, comenzando por lo
más importante. Las decisiones complementarias se van dejando para después, cuando
se demuestre que hemos alcanzado lo esencial. Así reducimos nuestro riesgo de hacer
algo más allá y tenerlo que desechar. Este será nuestro modo cíclico de actuar frente a la
incertidumbre: reducir progresivamente el riesgo. Hacer apuestas pequeñas, contrario a
la Cascada, que lo apuesta todo a un disparo, supuestamente certero. Para que el
desarrollo sea evolutivo, el diseño tiene que serlo también.
2.2 Extracción sobre una cuenta
Empezaremos por la extracción de dinero que es la operación de mayor interés.
Siguiendo la línea de desarrollo evolutivo, es decir, crecimiento y mejora progresiva del
software, partiremos de una situación simple.
Tarea inicial: hacer una sola extracción sobre una cuenta.
Como el diseño es un acto creativo se nos pueden ocurrir muchas soluciones
válidas; unas serán mejores que otras, de acuerdo a las condiciones específicas del
problema y su entorno. Ahora, como no tenemos experiencia, comenzaremos con la
primera que se nos ocurre cuando respondemos a la pregunta ¿Qué mecanismo software
se necesita para realizar esta operación?
El mecanismo debe averiguar la cantidad de dinero que desea extraer, solicitar la
autorización de ese retiro sobre la cuenta y averiguar si el dispositivo físico puede
entregar el dinero solicitado. En el caso que no se pueda entregar el dinero, el
197
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
mecanismo software debe explicar la causa. La Figura 1 muestra una implementación
en objetos del mecanismo descrito. Los mensajes forman parte del código del método
“trabaja” del objeto oExtracción.
Figura 1. Diagrama de Secuencia de una extracción sobre una cuenta
En esta implementación hemos diseñado el objeto oExtracción para que su
método “trabaja” se ocupe de ejecutar el mecanismo de extracción. Lo primero que hace
este método es, crear al objeto oInterfazExtracción y pedirle el importe de la extracción.
El objeto oInterfazExtracción, de alguna forma no definida aún, suministra esa
información. Su papel es ocultar al objeto oExtracción la forma de obtener el valor del
importe. Así no se compromete el algoritmo de extracción con obtener el valor del
importe, que es algo accesorio a la extracción. En el primer prototipo, se puede inventar
ese valor y asignarlo directamente en el código de oInterfazExtracción.
Cuando el objeto oExtracción recibe el valor del importe, le pide la autorización
de entrega del importe al objeto oCuenta. Suponiendo que lo autoriza, oExtracción le
dice al objeto oMonedero que entregue el importe y a oCuenta que reste el importe del
saldo.
Siguiendo nuestro estilo de primero lo esencial, nos ocuparemos después de lo
que sucede si, por alguna causa, no se puede entregar el importe.
Los objetos que se han diseñado para que participen en la escena EXTRACCIÓN
son:
198
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
oExtracción : responsable de la tarea de extracción de dinero
oInterfazExtracción: delegado por el objeto oExtracción para obtener el valor del
importe. Aunque el objeto oExtracción puede conseguir este valor directamente
con el usuario, se debe dejar esa tarea a otro objeto porque conseguir el importe
no forma parte de la esencia de su trabajo. La separación de tareas reduce el
impacto del cambio. En definitiva, el papel de oInterfazExtracción es
independizar la forma de solicitar el valor del importe, del uso del importe, que
es lo que es importante para oExtracción. El objeto oInterfazExtracción oculta a
oExtracción cómo averigua la cantidad y así cuando sea necesario modificar ese
“cómo” no hay que leer ni modificar oExtracción.
oCuenta: encargado de operar con la cuenta donde se extrae el importe. Una vez
más, su papel es ocultar la forma de trabajar con la cuenta. Conoce el
mecanismo particular para autorizar una extracción, incluso cómo hacerlo para
cada cliente. Por tanto, es el objeto que debe autorizar la entrega de dinero. La
variante de diseñar el objeto oCuenta sólo para ocultar la información de la
cuenta, por ejemplo el saldo, requeriría demasiada especialización en
oExtracción, que debe ser una operación general.
oMonedero: es el intermediario o interfaz del sistema software con el dispositivo
externo que controla el dinero del cajero. Su razón de ser es independizar el
resto del sistema software de ese dispositivo, ocultando la forma de operar con
él. La presencia de oMonedero permite que el sistema se defienda del impacto de
un cambio en el dispositivo ya que es el único que se debe modificar, si hay un
cambio de efecto moderado.
Resumiendo, oExtracción es la pieza de software que hemos diseñado para que
se ocupe de la extracción de dinero. Este objeto delega en oInterfazExtracción la
obtención del importe, mientras que oCuenta y oMonedero se ocupan de operar con el
dinero en la cuenta y en el monedero del cajero respectivamente. El diseño consiste en
concebir piezas software para que realicen tareas y enlazarlas entre sí. Observe la
importancia que le hemos dado a la delegación de tareas con el objetivo de ocultar
detalles del funcionamiento al método que se ocupa de realizar la extracción para poder
cambiar los detalles sin afectar a este método. Todos los objetos se comunican en forma
individual, uno con otro y de manera secuencial.
199
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Diagrama de Clases de una extracción sobre una cuenta
En el diagrama de clases del mecanismo de extracción sobre una cuenta se
ilustran las clases de cada uno de los objetos que participan en la construcción del
mecanismo y las relaciones que existen entre ellas. Figura 2. Desde el punto de vista
sintáctico, debe existir una línea entre clases si existe al menos un mensaje entre los
objetos de esas clases.
Figura 2 Diagrama de Clases de una extracción sobre una cuenta
La clase Extracción usa a la clase InterfazExtracción: el mensaje dameImporte que
envía el objeto oExtracción al objeto oInterfazExtracción establece una relación de
uso del primer al segundo objeto. En el diagrama de Clases se refleja esta relación,
como una línea que une ambas clases. Algunos autores emplean puntas de flecha
para indicar el sentido de uso.
La clase Extracción usa a la clase Cuenta: esta relación representa los mensajes que
el objeto oExtracción envía al objeto oCuenta: autoriza(importe) y resta(importe).
La clase Extracción usa la clase Monedero: oExtracción envía un mensaje al objeto
oMonedero: entrega(importe).
El diagrama de clases de la Figura 3, también representa la estructura de clases
del mecanismo de extracción sobre una cuenta pero con una notación resumida que une
la clase y su interfaz correspondiente. Esta notación, aunque la mayoría de las
herramientas CASE no la permiten, es la que vamos a usar en este documento porque
facilita la comprensión de los diagramas, al dejar clara la esencia del mecanismo
software.
200
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 3 Diagrama de Clases reorganizado de una extracción sobre una cuenta
Hecho el diseño lo codificamos, Figura 4, lo probamos y le mostramos al cliente
este primer prototipo para evaluarlo y, si es necesario, corregir el rumbo del proyecto.
Sólo arriesgamos el trabajo que hemos hecho sobre la esencia del problema. En caso de
fallo, no se pierde el trabajo hecho sobre los detalles o las excepciones. Es una manera
de ser prudente ante la incertidumbre.
201
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Código Java de una extracción sobre una cuenta
//Clase Extracción Versión 01//Extracción sobre una única cuenta
public class Extraccion { Cuenta oCuenta;
public Extraccion(){ }
public void trabaja(){
InterfazExtraccion oInterfazExtraccion; Monedero oMonedero; float importe;
oInterfazExtraccion= new InterfazExtraccion(); importe=oInterfazExtraccion.dameImporte(); oCuenta=new Cuenta(“12345”,1000000); oMonedero=Monedero.dameSolitario(); oCuenta.autoriza(importe)) oMonedero.entrega(importe); oCuenta.resta(importe); }}
<- 1
<- 2<- 3
1-> oInterfazExtraccion= new InterfazExtraccion();Se crea el objeto oInterfazExtracción.
2-> oCuenta=new Cuenta(“12345”,1000000);Se crea la única cuenta con la que se está trabajando en este prototipo.La cuenta creada tiene número de cuenta 12345 y saldo 1000000, por simplicidad. Después se podrá enriquecer su contenido.
3-> oMonedero=Monedero.dameSolitario();Se obtiene la única instancia del objeto Monedero.Para garantizar que la clase Monedero solo tenga una instancia y que proporcione un único punto de acceso global a ese instancia, se ha decidido usar el patrón “Solitario”. El método dameSolitario es el único punto de acceso a la instancia única del Monedero.
Figura 4. Código Java de una extracción sobre una cuenta
Observe que el prototipo sólo considera casos positivos. Es decir, hay dinero en
la cuenta y en el monedero, porque es la condición más frecuente. Los objetos oCuenta
y oMonedero se pueden implementar de cualquier manera en el primer prototipo, que
sirve para evaluar la esencia del mecanismo. Después, podemos ajustar esas
implementaciones.
202
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Una vez más, si invertimos tiempo, considerando los detalles, y la validación de
la parte esencial resulta negativa, podemos perder el tiempo invertido en esos detalles.
Por esta causa, abordamos el diseño de los detalles y las alternativas después de
validado el prototipo y dejamos la validación de las alternativas para otro momento.
A continuación se muestra el código Java para las clases Monedero y Cuenta.
Figuras 5.1 y 5.2 respectivamente.
//Clase Monedero Versión 01//Monedero Simple
import java.io.*;
public class Monedero {
private static Monedero solitario; private float saldo;
public static Monedero dameSolitario(){ if (solitario==null) solitario=new Monedero(5000000); return(solitario); }
private Monedero(float saldoInicial){ saldo=saldoInicial; }
public void entrega(float importe){ saldo-=importe; }}
<-1<-2
<-3
<-4
<-5
1-> private static Monedero solitario;Se define una variable privada de clase – static – llamada solitario. Esta variable contiene a la única instancia de la clase Monedero.
2-> private float saldo;En esta implementación, por razones de simplicidad, estamos considerando que el único atributo que interesa conocer del Monedero es su saldo.
3-> public static Monedero dameSolitario(){Este método es el punto de acceso a la instancia única del Monedero. Lo que ocurre en este método es que, si la instancia única no ha sido creada - if (solitario==null) -, la crea llamando al constructor del Monedero - solitario=new Monedero(5000000) - ; que es un método privado.Hemos supuesto un saldo inicial de 5,000,000, por razones de simplicidad. Si la instancia única ya ha sido creada, el constructor da su referencia.
4-> private Monedero(float saldoInicial){Este es el método creador de la clase objeto. Observe que el método es privado – private -. Es decir, sólo es visible para la clase
203
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Monedero. Al hacer privado el método creador garantizamos que nadie pueda crear nuevas instancias de Monedero.
5-> public void entrega(float importe){Se ha implementado de manera muy simple, considerando que lo único que le interesa al Monedero es su saldo.
Figura 5.1: Código Java Monedero
//Clase Cuenta versión 01//Cuenta sin persistencia
public class Cuenta{
private String numero; private float saldo;
public Cuenta(String cuentaNumero,float cuentaValorInicial){ numero=cuentaNumero; saldo=cuentaValorInicial; }
public boolean autoriza(float importe){ return(saldo>=importe); }
public void resta(float importe){ saldo-=importe; }}
<-1
<- 2
<- 3
1-> private String numero; private float saldo;En esta primera versión se considera que los únicos datos que interesan de una cuenta son su número y su saldo.
2-> public boolean autoriza(float importe)Se supone que se autoriza la entrega del importe siempre que este sea mayor que el saldo de la cuenta. Se ha omitido, intencionalmente, programar el resto del comportamiento del método.
3-> public void resta(float importe)Se está actualizando el saldo de la cuenta considerando el importe entregado.
Figura 5.2. Código Java de la clase Cuenta
2.3 Primera Ampliación. Hacer varias extracciones sobre una cuenta
204
Extracción
ImporteFechaCuenta
TrabajaDameImporteDameFechadameCuenta
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
En el prototipo anterior nos concentramos en conseguir el mecanismo para una
sola extracción. Ahora debemos ampliarlo a varias extracciones guardando la
información de cada extracción. Por tanto, crearemos un objeto oExtracción por cada
extracción y almacenaremos dentro de él, la información relevante, por ejemplo, el
importe y la fecha. Esta información aparecerá como atributos del objeto oExtracción.
Figura 6.
Figura 6. Representación de la clase Extracción
Se añade una nueva responsabilidad al objeto oExtracción: custodiar los datos
de una extracción.
Este diseño muestra una diferencia notable con un diseño equivalente en
software estructurado. Mientras que en el software estructurado tendríamos una sola
función para hacer todas las extracciones y guardar la información en un almacén,
separado de la función, en el software orientado a objetos hay un objeto por cada
extracción que realiza la tarea y guarda, en su interior, la información.
La causa de esa importante diferencia radica en sus interpretaciones respectivas
del software. El modelo estructurado interpreta el software como funciones que actúan
sobre datos, mientras que el modelo orientado a objetos interpreta el software como
módulos integrales de datos y funciones, que interactúan entre ellos para realizar tareas.
Por tanto, si queremos conservar información debemos hacerlo en objetos, lo que en el
fondo no es tan diferente, porque los objetos son las variables del modelo orientado a
objetos. La novedad es que esas variables son activas, realizan tareas. En nuestro diseño
cada objeto :Extracción se ocupa de hacer una extracción y de almacenar la información
relevante. Hemos sustituido el objeto concreto oExtracción por el objeto
205
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
anónimo :Extracción, porque ya no tenemos uno solo, sino muchos.Después tendremos
que encargarnos de no perder esos objetos cuando se apague la máquina.
Los diversos objetos :Extracción se irán creando según se hagan las extracciones
y formarán una colección que necesitaremos manejar. Agregamos, entonces, un objeto
para gestionar la colección formada por los objetos :Extracción. Se llamará
oGestorExtracciones.
En general, el manejo de colecciones de objetos se hace a través de objetos
especializados que se denominan Gestores. Sus funciones básicas son tres: entregar,
añadir y quitar un objeto de su colección cuando se le solicita. Los gestores de
colecciones no deben modificar los contenidos de los miembros de su colección. Si nos
restringimos a estas funciones básicas todos los gestores serán muy similares entre sí,
independiente de la colección que manejen. Es una decisión de diseño para no elevar,
gratuitamente, la complejidad del software teniendo gestores distintos.
Vistos en el sentido de ocultamiento, el papel fundamental de los gestores es
ocultar los detalles de acceso a la colección. Los que diseñan la parte esencial del
sistema software no tienen que preocuparse por la colección, ni de dónde está, ni cómo
está organizada. Esto es una tarea de los que diseñan los gestores de colecciones. Si se
trabaja con bases de datos externas, los gestores actúan como interfaces con esas bases
de datos.
Con los gestores hemos resuelto el manejo de las colecciones de objetos, pero
nos queda el asunto pendiente de los objetos :Extracción, aún después de apagada la
máquina. Los debemos hacer persistentes. Es decir, objetos que tienen la propiedad de
estar presentes, siempre que se les necesite. Pero, una vez más, diremos que no nos
interesan, por ahora, los detalles de cómo se consigue la persistencia. Alguien se
ocupará de conseguirlo, en su momento.
Diagrama de secuencia de muchas extracciones sobre una cuenta
Cuando cada objeto :Extracción termine de ejecutar la extracción, debe solicitar
al objeto oGestorExtracciones que lo añada a su colección. Si la extracción no ha tenido
éxito, por alguna razón, no se incluye el objeto creado y la colección se mantiene como
antes. Este diagrama de secuencia sólo considera que la extracción ha tenido éxito.
Figura 7.
206
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 7. : Diagrama de secuencia de varias extracciones sobre una cuenta
La nueva solicitud de nuestro cliente ha obligado a aumentar la capacidad del
sistema, respecto a la cantidad de información que maneja. Ha sido un cambio
cuantitativo en los requisitos, que resolvimos con un cambio cuantitativo en la solución,
añadiendo un objeto y un mensaje, sin necesidad de alterar el código anterior. El objeto
oGestorExtracciones y el mensaje agrega(me) están indicados en negrilla en la Figura
7.1. La redacción del mensaje agrega(me) aprovecha un rejuego con el español para
expresar mejor qué solicita. El parámetro (me) contiene la referencia del
objeto :Extracción. Observe que el método de trabajo ha sido diseñar el mecanismo para
un único objeto y después tener en cuenta la colección.
Sin embargo, la sencillez de la modificación para asimilar cambios cuantitativos
no debe inducirnos a pensar que siempre puede ser así. La modificación ha sido simple
porque hemos supuesto un cambio cuantitativo modesto. Si el cambio cuantitativo es
muy pronunciado, puede requerir un cambio cuantitativo en el sistema, de manera
análoga al cambio de velocidad de 10 km/h a 100 km/h.
En las siguientes páginas se muestran el nuevo diagrama de clases del software y
el nuevo código, ambos, ajustados a realizar muchas extracciones sobre una cuenta.
207
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
La Figura 8.1 destaca la clase GestorExtracción que se ha añadido y la Figura
8.2, reorganiza el diagrama para no perder la esencia de la estructura del software entre
tantos elementos accesorios. Por ejemplo la interfaz y el gestor. Pero se debe recordar
que esta forma peculiar de representar el diagrama de clases es una convención sólo
nuestra, inspirada en Coad.
La figura 9 muestra el código Java del programa. Los elementos añadidos se
realzan en negrilla.
208
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 8.1: Diagrama de clases de varias extracciones sobre una cuenta
Figura 8.2: Diagrama de clases reorganizado de varias extracciones sobre una cuenta
//Clase Extraccion Versión 02//Varias extracciones sobre una cuenta//Con registro histórico de las extracciones
import java.io.*;
209
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
import java.util.*;
public class Extraccion implements Serializable{
private Cuenta oCuenta; private Date fecha; private float importe;
public Extraccion(){ fecha=new Date(); }
public float dameImporte() { return (importe); }
public Date dameFecha() { return (fecha); }
public Cuenta dameCuenta() { return (oCuenta); }
public void trabaja(){
InterfazExtraccion oInterfazExtraccion; Monedero oMonedero; GestorExtracciones oGestorExtracciones; oInterfazExtraccion= new InterfazExtraccion(); importe=oInterfazExtraccion.dameImporte(); oCuenta=new Cuenta(“12345”,1000000); oMonedero=Monedero.dameSolitario(); oCuenta.autoriza(importe)); oMonedero.entrega(importe); oCuenta.resta(importe); oGestorExtracciones=GestorExtracciones.dameSolitario(); oGestorExtracciones.agrega(this); } }}
<- 1
<- 2
<- 3
1-> public class Extraccion implements Serializable{Al decir que la clase Extraccion implementa la interfaz Serializable, se está se consigue la capacidad para transformar el estado completo de un objeto en una secuencia de bytes, y poder crearlo nuevamente leyendo la secuencia de bytes correspondientes. Esta cualidad nos permite, entre otras cosas, que un objeto pueda ser guardado y leído de un archivo de objetos. Es importante observar que esta interfaz no define métodos, simplemente indica que la serialización es permitida.
2-> fecha=new Date();Al crear una instancia de la clase Extracción se asigna la fecha actual del sistema a su atributo fecha.
3-> oGestorExtracciones=GestorExtracciones.dameSolitario();La clase GestorExtracciones también tiene una única instancia. Nuevamente se utiliza el patrón “Solitario” y en esta línea se
210
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
solicita la instancia única de la clase GestorExtracciones.
Figura 9: Código Java de varias extracciones sobre una cuenta
211
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
2.4 Segunda Ampliación. Varias extracciones sobre una cuenta de varias posibles Han aumentado los clientes, necesitamos poder realizar varias extracciones sobre una cuenta de varias posibles.
Antes los objetos :Extracción conocían la dirección del objeto oCuenta sobre el
que delegaban la autorización porque había un solo objeto oCuenta. Sin embargo, ahora
hay muchos, de manera que cada objeto :Extracción debe conocer la cuenta en
particular sobre la que debe actuar. El cliente dice que el identificador de la cuenta se
obtiene de una tarjeta. Se diseña un objeto oTarjeta para suministrar el identificador de
la cuenta, ocultando la manera de obtenerlo. Con este identificador se debe localizar el
objeto :Cuenta específico que trabaja con la cuenta referida. Hemos sustituido el objeto
oCuenta conocido por el objeto anónimo :Cuenta. Figura 10.
Figura 10: Diagrama de secuencia de una extracción sobre una cuenta, de
varias posibles.
212
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Puesto que hay una colección de objetos :Cuenta, debe existir un objeto gestor
de esta colección que proporcione el objeto cuenta a partir del identificador de la cuenta.
Ahora tenemos a un gestor de colecciones, en otra de sus funciones básicas: dar un
elemento de su colección. Es decir, un objeto. Para hacer esta nueva ampliación hemos
supuesto que la colección está formada de antemano, justo lo contrario de la situación
anterior, donde formamos la colección al añadir cada objeto :Extracción.
Resumiendo. De nuevo, el incremento de la complejidad del problema ha
obligado a elevar la complejidad de la solución. Hemos logrado resolver el cambio
cuantitativo en los requisitos mediante cambios cuantitativos en la solución, añadiendo
elementos software al diseño. En concreto, dos nuevos objetos:
oTarjeta: suministra el identificador de la cuenta de interés.
oGestorCuentas: suministra al objeto :Cuenta encargado de operar con la
cuenta, cuyo identificador ha dado el objeto oTarjeta. En el prototipo
anterior, el objeto :Extracción accedía al objeto oCuenta mediante el recurso
del Solitario, porque existía un solo objeto oCuenta. Ahora, hay varios
objetos :Cuenta. Por tanto, debe conocer a cuál se debe dirigir y después
solicitarlo al objeto oGestorCuentas.
En esta etapa es suficiente considerar que tenemos un solo objeto :Cuenta en la
colección de cuentas porque estamos diseñando el mecanismo de extracción. El
funcionamiento del objeto oGestorCuentas debe ser simple para no distraer nuestra
atención. Después, podremos ocuparnos de enriquecerlo, sin modificar el mecanismo de
extracción que hemos diseñado.
De manera semejante a la extensión anterior, el aumento de la capacidad
cuantitativa del sistema se ha conseguido añadiendo objetos y mensajes, sin necesidad
de alterar lo que ya estaba escrito. Los objetos añadidos son: oTarjeta y oGestorCuentas
mientras que los mensajes añadidos son: dameIdCuenta y dame(idCuenta) indicados en
negrilla en la Figura 10.
La técnica de diseño se repite: trabajar con un objeto como si fuese único y
después agregar los medios de acceso a ese objeto, en este caso los objetos oTarjeta y
oGestorCuentas. La esencia del mecanismo no se altera, cuando en vez ser un único
objeto, son muchos. Esto permite obtener un prototipo simple, que se concentre en la
213
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
esencia, y después ampliarlo de forma natural. Insistimos, siempre que no se rebasen los
límites que provoquen cambios cualitativos.
Figura 11: Diagrama de clases del software de la segunda ampliación
El camino de considerar primero una situación simple y después hacerla más
compleja nos ha permitido avanzar poco a poco, haciendo apuestas pequeñas de cada
vez, sin desviar la atención de los objetivos prioritarios. Pero, además, ese camino
también nos ha conducido a un diseño de software que nos ha defendido de los cambios
que se han producido y, lo más importante, que nos defenderá contra muchos de los
cambios que se pueden producir en el futuro.
La condición de conocer, desde el principio, la presencia de varias
extracciones y varias cuentas debe conducirnos a un diseño de la misma naturaleza
defensiva que el conseguido, suponiendo incertidumbre, porque es una manera de
protegernos de la incertidumbre del mañana.
214
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
//Clase Extraccion Versión 03//Extracción sobre varias cuentas//Con registro histórico de las extracciones
import java.io.*;import java.util.*;
public class Extraccion implements Serializable{
private Cuenta oCuenta; private Date fecha; private float importe;
public Extraccion(){ fecha=new Date(); } public float dameImporte() { return (importe); }
public Date dameFecha() { return (fecha); }
public Cuenta dameCuenta() { return (oCuenta); }
public void trabaja(){
InterfazExtraccion oInterfazExtraccion; Monedero oMonedero; GestorExtracciones oGestorExtracciones; GestorCuentas oGestorCuentas; Tarjeta oTarjeta; String idCuenta; oInterfazExtraccion= new InterfazExtraccion(); importe=oInterfazExtraccion.dameImporte(); oTarjeta=Tarjeta.dameSolitario(); idCuenta=oTarjeta.dameIdCuenta(); oGestorCuentas=GestorCuentas.dameSolitario(); oCuenta=oGestorCuentas.dame(idCuenta); oMonedero=Monedero.dameSolitario(); oCuenta.autoriza(importe); oMonedero.entrega(importe); oCuenta.resta(importe); oGestorExtracciones=GestorExtracciones.dameSolitario(); oGestorExtracciones.agrega(this); }}
<- 1
<- 2
1-> oTarjeta=Tarjeta.dameSolitario();La clase Tarjeta, por tener una única instancia, se implementa con el patrón “Solitario”.
2-> oGestorCuentas=GestorCuentas.dameSolitario();La clase GestorCuentas se implementan también con el patrón “Solitario”.
215
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 12. Código Java de varias extracciones sobre varias cuentas
2.4 Tercera Ampliación: Control de AccesoHan estafado al dueño del cajero. Se necesita un control de acceso.
Escena de acceso
Nos dice el cliente: el control de acceso se realizará contrastando el número de
identificación personal (pin) que debe teclear el usuario del cajero, con el número de
identificación contenido en una tarjeta que lee un dispositivo del cajero.
El diseño de esta parte se hará sin tener en cuenta el diseño del mecanismo de
extracción para ver, después, un ejemplo de acoplamiento de secciones hechas por
separado.
Los objetos diseñados para participar en esta escena son:
oControlAcceso: objeto especializado en autorizar o rechazar el acceso.
oInterfazControlAcceso: responsable de comunicarse con el usuario para
pedir su pin y dárselo a oControlAcceso.
oLectorTarjeta: al igual que oMonedero, oLectorTarjeta es la interfaz de
nuestro sistema con el dispositivo físico Lector de Tarjeta.
216
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 13: Diagrama de secuencia de acceso
Cuando el objeto oLectorTarjeta recibe una tarjeta le pide al objeto
oControlAcceso que trabaje. El objeto oControlAcceso solicita entonces el pin tanto al
objeto oLectorTarjeta como al objeto oInterfazControlAcceso y si ambos coinciden
autoriza el acceso.
Esta escena de acceso ignora totalmente lo hecho en extracción. La solución
planteada aquí puede servir, potencialmente como idea, para controlar el acceso en otros
sistemas.
Figura 14: Diagrama de Clases para el acceso
217
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Código Java ACCESO
//Clase ControlAcceso versión 01//LectorTarjeta custodia los datos de la tarjeta
public class ControlAcceso{
public ControlAcceso(){ }
public void trabaja(){
InterfazControlAcceso oInterfazControlAcceso; LectorTarjeta oLectorTarjeta; String pinTarjeta;
String pinControl;
oInterfazControlAcceso=newInterfazControlAcceso(); oLectorTarjeta = LectorTarjeta.dameSolitario();
pinTarjeta=oLectorTarjeta.damePin(); pinControl=oInterfazControlAcceso.damePin();
if (pinTarjeta.compareTo(pinControl)==0) System.out.println("Autorizado"); }}
<- 1<- 2
<- 3
1-> oInterfazControlAcceso=new InterfazControlAcceso();Se crea el objeto oInterfazControlAcceso, responsable de la interfaz.
2-> oLectorTarjeta = LectorTarjeta.dameSolitario();En esta línea se obtiene el único objeto de la clase LectorTarjeta.Usando el patrón Solitario garantizamos que en el sistema existe un único representante del dispositivo físico Lector Tarjeta.
3-> (pinTarjeta.compareTo(pinControl)==0)Se verifica si el pin de la tarjeta corresponde al pin escrito por el usuario al identificarse.
Figura 15: Código Java para el control de acceso
218
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Integración de acceso y extracción
Intente unir los mecanismos de acceso y extracción que hemos diseñado, a través
de los principales diagramas de secuencia respectivos. Los hemos copiado aquí para
facilitar el trabajo.
219
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Hoja para trabajar.
220
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Estudio de alternativas de unión
Tratemos de conectar los dos mecanismos, Extracción y Acceso, comenzando
por la línea del menor esfuerzo. Es decir, haciendo cambios mínimos, respecto a sus
diseños independientes.
Como, el objeto oControlAcceso es el último que trabaja en la parte de acceso y
el objeto :Extracción es el primero que lo hace, en la parte de extracción, “pasemos la
pelota” de uno a otro. El objeto oControlAcceso le pide al objeto Extracción que trabaje.
Figura 16.
Figura 16: Conexión de acceso y extracción. Primer paso.
Cuando diseñamos el objeto oExtracción hicimos que solicitara el identificador
de la cuenta al objeto oTarjeta, que fue colocado ahí para dar esa información y ocultar
cómo la obtenía. En ese momento diseñamos un objeto oTarjeta simple, con el objetivo
de estudiar el mecanismo de extracción. Pero, ahora, hay un lector de tarjetas. Por tanto,
el objeto oTarjeta debe solicitarle al objeto oLectorTarjeta el identificador de la cuenta
Figura 17.
221
acceso extracción
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 17: Conexión de acceso y extracción. Segundo paso
Antes de estudiar con más detalle las consecuencias de la conexión que hemos
hecho, observemos la Figura 18. La unión se ha producido con una “hernia” en el
mecanismo de extracción que puede ser dolorosa si hay movimientos en el mecanismo
de acceso, como sucede en todas las hernias. El mensaje dameIDCuenta del objeto
oTarjeta al objeto oLectorTarjetas introduce el mecanismo de extracción en el interior
del mecanismo de acceso, llegando incluso hasta la frontera exterior del sistema. Hemos
hecho muy vulnerable la extracción respecto a cambios en acceso.
Figura 18. “Hernia” en el mecanismo de extracción
222
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Por otra parte, aunque el sistema funciona, hay un problema. oControlAcceso le
solicita el pin a oLectorTarjeta y oExtracción le solicita el número de cuenta a oTarjeta.
Esta diversidad, además de crear problemas de consistencia, eleva la complejidad del
software innecesariamente.
La dualidad aparente entre oLectorTarjeta y oTarjeta puede inducir a eliminar
uno de ellos. El objeto oLectorTarjeta está encargado de ser la interfaz con el software
del artefacto de lectura. No lo debemos eliminar. Quitemos el objeto oTarjeta y
hagamos que el objeto oControlAcceso y el objeto oExtracción soliciten los datos que
necesiten al objeto oLectorTarjeta. Esta solución se presenta en Figura 19.
Figura 19. Conexión que elimina al objeto oTarjeta
Se ha suprimido la diversidad y la posible inconsistencia, pero se agudizó el
problema de la “hernia” porque ahora afecta a la esencia de la extracción, al
objeto :Extracción.
Antes, el objeto oTarjeta protegía el mecanismo de extracción del mundo
exterior, ocultando la forma de conseguir el identificador de la cuenta. Después de
colocar el mecanismo de acceso, el mundo exterior se alejó del mecanismo de
extracción. Una vez más, puede parecer que sobra oTarjeta al alejar el peligro. Pero no
es así.
223
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Desde el punto de vista del mecanismo de extracción se ha sustituido el mundo
exterior por el mecanismo de acceso. Debe seguir existiendo una frontera y, además,
protegida. Al quitar el objeto oTarjeta, perdimos la protección contra los cambios en el
mecanismo de acceso, conectando el corazón de extracción con acceso. El objeto
oTarjeta, que antes era una interfaz con el mundo exterior, ahora debe actuar como
interfaz con el mecanismo de acceso. Es el elemento que independiza el acceso de la
extracción y también que los conecta.
Además, si añadimos responsabilidades al objeto oLectorTarjeta, dándole la
custodia de la información de la tarjeta, estamos reduciendo la cohesión de esta clase,
cuya función fundamental es servir de interfaz con el dispositivo físico de lectura, nada
más.
En conclusión, el diseño funciona, pero no es adecuado desde la perspectiva
evolutiva. Hagamos una iteración para tratar de mejorar esa perspectiva de diseño.
2.4.1 Una iteración. Otra forma de unión
Dejemos el objeto oTarjeta y hagamos que sea el custodio de la información de
la tarjeta para ambas partes. Los objetos oControlAcceso y :Extracción le pedirán a
oTarjeta el dato que necesiten. Así, se gana en homogeneidad. Cuando el primero
solicite el valor del pin, al objeto oTarjeta, éste lo pide al objeto oLectorTarjeta y se lo
entrega a oControAcceso. Algo semejante ocurrirá cuando :Extracción solicite el
identificador de la cuenta.
Sin embargo, este mecanismo se aprecia complejo, sin necesidad de hacerlo
gráfico. Los accesos de oTarjeta a oLectorTarjeta solicitándole información se
distribuyen, según se necesite. Quedan diluidos en la madeja de mensajes. Busquemos
una solución que ofrezca más claridad, que sea menos compleja.
224
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
2.4.2 Otra iteración
Concentremos todos los accesos de oTarjeta al objeto oLectorTarjeta al
comienzo. Después que oLectorTarjeta hace su tarea de interfaz, le dice a oTarjeta que
trabaje, para que cargue la información. Una vez cargada la información, oTarjeta le
pide a oControAcceso que la valide. En la Figura 20 se muestra el mecanismo descrito.
Hemos desplazado la posición de oTarjeta para resaltar su papel de interfaz.
Figura 20. Diagrama de secuencia de la unión, con oTarjeta como interfaz
Hemos ganado en claridad respecto a la división entre los dos mecanismos.
Ahora menos acoplados. Y, además, hemos reducido la posibilidad de inconsistencia al
actualizar oTarjeta completamente, cada vez que oLectorTarjeta trabaje.
Esta solución llega a un compromiso entre dos condiciones contradictorias de
diseño. Por un lado, eliminar las fuentes de información redundantes para evitar
problemas de inconsistencia si se actualizan indistintamente. Por otro, conseguir un
diseño modular protegido contra cambios. Se ha mantenido la redundancia porque nos
interesaba para desacoplar ambos mecanismos, pero se ha minimizado la posibilidad de
inconsistencia.
225
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
2.4.3 Otra iteración más
Sin embargo, ahora tenemos un efecto “hernia” de acceso a extracción. El
mensaje de oControlacceso a Extracción se “desliza” hacia la parte de extracción.
Además de, reforzar el acople entre acceso y extracción porque es una conexión más
entre ellos. Una de oTarjeta y otra de oControlAcceso.
Si pensamos un poco, veremos que podemos concentrar toda la comunicación en
el objeto oTarjeta. Efectivamente, cuando este objeto termine de cargar la información
vuelve a recibir el control, y en ese momento puede invocar a :Extracción para que
trabaje. Figura 21. El acoplamiento entre ambos mecanismos se ha reducido a un
mínimo, al concentrarse en el objeto oTarjeta. Ahora, este objeto actúa como elemento
de enchufe de las dos partes del sistema.
Figura 21. Diagrama de secuencia de la unión, bien diferenciada
Hemos conseguido dos módulos independientes unidos por un solo elemento.
Razonemos. Antes de unir el acceso y la extracción el sistema estaba dividido, que es lo
aconsejable para reducir la complejidad por diversidad, pero no funcionaba porque
estaban separadas.
Cuando las unimos y las mezclamos aumentó la complejidad porque tuvimos
que enfrentarnos, de una vez, a todos los detalles del sistema en su conjunto. El
problema de diseño era, entonces, unirlas para que funcionen, manteniendo la
226
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
complejidad, al menos como antes. Se ha conseguido, porque el sistema funciona y
podemos tratarlo como dos secciones débilmente acopladas con semántica propia, sin
tener que ver sus detalles.
La Figura 22 muestra el diagrama de clases del sistema, después de unidas las
secciones de acceso y extracción. Observe que el acople entre ellas sólo es un punto de
tangencia.
Figura 22 Diagrama de clases del sistema
227
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Pero esto ha requerido de un replanteamiento del diseño con modificaciones
importantes. Por cierto, sólo en la parte de acceso. La extracción no ha cambiado.
Las ampliaciones precedentes no produjeron modificaciones importantes porque
eran cambios cuantitativos en los requisitos, que pudimos resolver con cambios
cuantitativos en la solución. Pero, considerar el acceso significa añadir una nueva
función. Por tanto, es un cambio cualitativo en los requisitos. Es de esperar, entonces,
que se produzcan cambios cualitativos en la solución, como ha sucedido.
El diagrama de secuencia, el diagrama de clases del mecanismo de acceso y el
código Java de la clase ControlAcceso se muestran en las figuras 23, 24 y 25
respectivamente.
Figura 23. Nuevo diagrama de secuencia del control de acceso
Es interesante analizar que el objeto oTarjeta, hace dos accesos a oLectorTarjeta,
uno por cada dato que solicita: dameIdCuenta y damePin. Se puede pensar en
solicitarlos de una vez. Es más eficiente para la máquina, pero restringe la libertad de
cambio al mezclar las dos solicitudes. Si hay que modificar alguna, sólo se toca esa.
La modificabilidad se basa en la separación e independencia de los elementos,
tanto en el diseño, como en el código.
//Clase ControlAcceso versión 02
228
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
//Tarjeta custodia los datos de la tarjeta
public class ControlAcceso{
public ControlAcceso(){ }
public void trabaja(){
InterfazControlAcceso oInterfazControlAcceso; Tarjeta oTarjeta; String pinTarjeta; String pinControl;
oTarjeta = Tarjeta.dameSolitario(); pinTarjeta = oTarjeta.damePin(); oInterfazControlAcceso = new InterfazControlAcceso(); pinControl = oInterfazControlAcceso.damePin(); if (pinTarjeta.compareTo(pinControl)==0) System.out.println(“Autorizado”); }}
<- 1
1-> oTarjeta =Tarjeta.dameSolitario();En esta línea se solicita el único objeto de la clase Tarjeta. El mismo objeto que recibe Extracción cuando lo solicita. En esta versión el objeto Tarjeta es el responsable del pin del cliente.
Falta por incluir la alternativa de No Autorizado
Figura 24. Nuevo código Java de la clase ControlAcceso
Figura 25. Nuevo diagrama de clases de acceso
229
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Interpretación Arquitectónica del Sistema
Si reorganizamos el diagrama de clases y lo relacionamos con su entorno,
pudiéramos hacer una interpretación arquitectónica del sistema, para enriquecer nuestra
visión de lo que hemos construido. Figura 26.
Figura 26 Arquitectura del Sistema
El esquema de la Figura 26 delimita la frontera del sistema con el mundo
exterior y coloca, en esa frontera, las clases que hemos diseñado para trabajar con el
mundo exterior. Unas clases se dedican a interactuar con los dispositivos físicos, Lector
de Tarjeta y Monedero; otras se dedican a interactuar con el cliente y otras con los
lugares donde se guarda la información.
Las clases interiores constituyen el núcleo del mecanismo del cajero, dividido en
dos secciones: el acceso y la extracción, conectadas a través de una clase común.
230
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
El aislamiento del núcleo, respecto al mundo exterior, lo protege de los cambios,
mantiene su estabilidad. Pero además, esta separación permite un tratamiento
relativamente independiente de las interfaces que puede ser beneficioso.
La distinción de las secciones facilita la comprensión del sistema, reduce su
complejidad, porque permite la descripción del sistema sólo en términos de sus
componentes y de la relación que hay entre ellos. Un mecanismo de acceso y otro de
extracción de dinero, sin tener que entrar en detalles de cada uno. Si no distinguimos
las secciones tendríamos que describir el sistema dando todas sus clases y relaciones. La
introducción de las secciones reduce la diversidad y además, abre el camino para la
reutilización de mecanismos, fundamentalmente como ideas.
231
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
2.5 Cuarta Ampliación: Ingreso de Dinero
Ahora, también se quiere ingresar dinero.
Para adicionar este nuevo servicio al cajero se debe crear el mecanismo de
ingreso y analizar después la mejor manera de integrar este mecanismo al sistema
anterior. Como el ingreso es similar a la extracción, a continuación repetimos el
diagrama de clases de extracción para reutilizar los aspectos comunes. Figura 27
Dejamos que intente este ejercicio.
Figura 27. Repetición del diagrama de secuencia de extracción
232
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Ejercicio
Diseñar el diagrama de secuencia del ingreso de dinero. Nuestro cliente desea
que el ingreso no produzca un aumento en el saldo hasta que una persona del banco
valide la cantidad de dinero ingresada. Un ingreso automático a cargo del sistema,
hubiese sido más simétrico con extracción, pero la decisión de nuestro cliente fue
distinta.
233
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Diseño del mecanismo de ingreso
Los objetos oIngreso y oInterfazIngreso de esta escena se corresponden con los
objetos oExtracción y oInterfazExtracción de la escena de extracción. El método
registra(importe) del objeto oCuenta sólo es un registro de la intención del usuario del
cajero, no incrementa el valor del saldo de la cuenta. La autorización del saldo se hará
después, como ya se dijo.
Como el ingreso no se relaciona con ningún artefacto exterior, prescindimos de
un objeto interfaz, del estilo de oMonedero. En el caso de existir un artefacto debemos
crear un objeto interfaz especializado en ese artefacto. No es recomendable usar el
objeto oMonedero para este fin.
Figura 28. Diagrama de secuencia del mecanismo de ingreso
234
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Las figuras 29 y 30 muestran el diagrama de clases y el código de ingreso,
respectivamente.
Figura 29. Diagrama de clases del mecanismo de ingreso
235
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Código Java INGRESO
//Clase Ingreso
import java.io.*;import java.util.*;
public class Ingreso implements Serializable{ private Cuenta oCuenta; private Date fecha; private float importe;
public Ingreso(){ fecha=new Date(); }
public void trabaja(){
InterfazIngreso oInterfazIngreso; GestorIngresos oGestorIngresos; GestorCuentas oGestorCuentas; Tarjeta oTarjeta; String idCuenta;
oInterfazIngreso= new InterfazIngreso(); importe=oInterfazIngreso.dameImporte(); oTarjeta=Tarjeta.dameSolitario(); idCuenta=oTarjeta.dameIdCuenta(); oGestorCuentas=GestorCuentas.dameSolitario(); oCuenta=oGestorCuentas.dame(idCuenta); oCuenta.registra(importe); oGestorIngresos=GestorIngresos.dameSolitario(); oGestorIngresos.agrega(this); }
}
<- 1
<- 2
<- 3
<- 4
<- 5
1-> fecha=new Date();Al igual que en Extracción, al crear una instancia de la clase Ingreso se asigna al atributo fecha la fecha actual del sistema.
2-> oInterfazIngreso= new InterfazIngreso();Se pide la creación de la interfaz correspondiente al Ingreso.
3-> oTarjeta=Tarjeta.dameSolitario();Se pide el único objeto Tarjeta del sistema.
4-> oGestorCuentas=GestorCuentas.dameSolitario();Se obtiene el único objeto GestorCuentas del sistema.
5-> oGestorIngresos=GestorIngresos.dameSolitario();El objeto GestorIngresos, se implementa también usando el patrón “Solitario”. Aquí se pide su única instancia.
Figura 30.Código Java de la clase Ingreso
236
ACCESO EXTRACCION
EXTRACCION
INGRESO
ACCESO
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Integración de ingreso al sistema
Al añadir un servicio nuevo se produce otra vez un cambio cualitativo en los
requisitos, que debe provocar cambios cualitativos en la solución. En este caso, además,
se rompe la estructura más o menos lineal del diseño y aparece un punto de bifurcación.
Se necesita una selección y una distribución. Es decir, un mecanismo de elección del
camino y otro que nos conduzca a él.
Como la función del sistema se ha enriquecido, su geometría debe cambiar para
ajustarse a su nuevo trabajo, siguiendo la idea de que existe una relación entre forma y
función. La figura 31 muestra la geometría anterior y la nueva.
Figura 31. Selección y distribución de la orden de trabajo
El punto negro de la Figura 31 se transforma en un objeto oSelector para recoger
la selección del usuario y generar la orden de trabajo. Y a continuación, una estructura
polimórfica que distribuya esa orden. Figura 32.
237
Selector Operación
Extracción Ingreso
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 32. Distribución de la orden “trabaja” mediante polimorfismo
El polimorfismo permite que la orden de trabajo sea igual para extracción e
ingreso y la encamina según corresponda. Ha introducido homogeneidad en la
diversidad. Cualquier otra función, del mismo tipo definido en Operación, puede ser
añadida sin perturbar a quienes usan la estructura polimórfica. Por tanto, hemos
conseguido estabilizar ese punto del diseño respecto a cambios en las operaciones. La
geometría vuelve a ser adecuada.
Al ofrecer un abanico uniforme y abierto de servicios, el polimorfismo convierte
cambios cualitativos en los requisitos, de añadir y quitar funciones, en cambios
cuantitativos en la solución.
El diseño de la dinámica del sistema incorporando todas sus funciones debe ser
descrito en diagramas de secuencia diferente. Uno para la parte de acceso, que debe
conectarse, ahora, con el selector. Otro para la extracción y otro para el ingreso.
Aunque el objeto selector puede ser una interfaz, hemos seguido el estilo
precedente, y creamos una interfaz de selector para conocer solicitud del usuario. Con
esta información el objeto oSelector envía un mensaje de “trabaja” a quien corresponda.
La dirección del mensaje viene dado desde la interfaz, así que el selector no toma
decisión alguna, la toma el usuario en la interfaz.
238
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Figura 33. Diagrama de secuencia que incorpora la selección
Para alterar lo menos posible al mecanismo de acceso, la selección debe ser un
elemento independiente. Lo hemos incorporado al esquema de acceso con el objetivo de
mostrar su encaje.
EN VEZ DE DEJAR LA FLECHA DEL MENSAJE “TRABAJA” SIN DESTINATARIO, SE
PUEDE DIRIGIR A LA CLASE OPERACIÓN, PORQUE LA DIRECCIÓN DEL MENSAJE ES UNA
VARIABLE DE ESA CLASE, COMO SE APRECIA EN EL CÓDIGO DE LA FIGURA 34.
239
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Código Java Selector//Clase Selector Versión 01
public class Selector{
public Selector(){ }
public void trabaja(){ InterfazSelector oInterfazSelector; Operacion operacion;
oInterfazSelector=new InterfazSelector(); operacion=oInterfazSelector.dameOperacion(); operacion.trabaja(); }}
<- 1
<- 2<- 3
<- 4
1-> Operacion operacion;operación es una variable que pertenece a la clase abstracta Operacion. Esta variable puede contener objetos que pertenezcan a cualquier subclase de la clase Operación; en particular objetos de la clase Ingreso o de la clase Extraccion.
2-> oInterfazSelector=new InterfazSelector();En esta línea se crea el objeto interfaz oInterfazSelector.
3-> operacion=oInterfazSelector.dameOperacion();oInterfazSelector solicita al usuario la operación deseada y retorna el objeto correspondiente a la operación seleccionada por el usuario.
4-> operacion.trabaja();En esta línea de código, si el usuario seleccionó una operación, el selector da la orden a la operación para que trabaje. El atributo operación puede ser de la clase Extracción o de la clase Ingreso.
Figura 34. Código Java de la clase Selector
240
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
Diagrama de clases del sistema completo
La Figura 35 muestra el diagrama de clases del software del cajero.
Figura 6 Diagrama de Clases CAJERO
Observe que no existe ningún objeto cajero. El software del cajero funciona sin
una definición explícita. Este es uno de los recursos de la orientación a objetos que
hemos empleado en el diseño para facilitar su evolución. Empezamos con la extracción,
después añadimos el acceso y por último, agregamos el ingreso sin tener que modificar
ninguna definición, porque no existía. El sistema cumple su función sin necesidad de
una definición explícita de un cajero dentro del código
Un objeto cajero, para definir el sistema, conteniendo los objetos del diseño no
desempeña un papel relevante en el software. De tenerlo, habría que modificarlo cada
vez que se cambiara algo.
Otra observación interesante es que podemos tener una visión integral de la
estructura del sistema con el diagrama de clases. Pero con los diagramas de secuencia,
241
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
sólo tenemos trozos del comportamiento del sistema, como escenas sueltas de una obra
de teatro.
2. 6 Recapitulemos
El método de trabajo ha sido considerar una situación simple y después
enriquecerla. Por ejemplo, una extracción y después, varias extracciones.
Cuando aumentamos la capacidad cuantitativa de nuestro sistema software, para
operar con más de una extracción y con más de una cuenta, sólo tuvimos que añadir
piezas de software, objetos y código, que encajaron sin dificultad en el mecanismo
existente. Las exigencias cuantitativas del entorno se pudieron resolver con cambios
cuantitativos en el sistema, gracias a la aplicación del principio de ocultamiento de
información.
Los diseños deben mostrar esta cualidad, siempre que los cambios cuantitativos
no impliquen cambios cualitativos. Como hemos dicho, manejar diez datos es
cualitativamente distinto de manejar un millón. Un salto cuantitativo de ese orden debe
provocar cambios sustanciales en el mecanismo, que no se resuelven solamente
añadiendo más piezas de software. Pero, mientras que no ocurran tales cambios nuestro
software debe evolucionar de manera simple.
Cuando el entorno exigió agregar la función del control de acceso se produjo un
cambio cualitativo. Hubo que hacer modificaciones en el software precedente para
acoplarlo a la función de acceso. En general, los cambios cualitativos en el problema
provocan cambios cualitativos en la solución software. Se deben aprovechar tales
cambios para mejorar las cualidades del sistema.
Algo parecido sucedió con la función ingreso, hubo que modificar el software
existente. Cambió la geometría del sistema al crearse un punto de bifurcación.
Estabilizamos ese punto de futuros cambios colocando un mecanismo de estabilización
para enfrentar esa incertidumbre. Aquí empleamos el polimorfismo.
En ningún caso, hubo una anticipación a los acontecimientos, sólo se aplicó una
disciplina de diseño: modularizar y aplicar sistemáticamente el Principio de
Ocultamiento de la Información. Lo hicimos cada vez que separamos y delegamos
tareas, al utilizar objetos interfaces con el exterior y entre secciones del sistema, y
242
Curso de OO dirigido por Diseño evolutivola introducción de ambigüedad
también cuando usamos el polimorfismo. Las operaciones particulares quedan ocultas
detrás de la clase abstracta. En fin, introduciendo incertidumbre dentro del diseño.
243