Escolar Documentos
Profissional Documentos
Cultura Documentos
la Programación
Si un antropólogo del futuro analizase los hábitos de las sociedades occidentales
de principios del siglo XXI, observaría un entusiasmo desaforado de las hoy llamadas
tecnologías de la información. Muchas de éstas se basan en la producción de software.
Estos ciudadanos esperan nuevas prestaciones de sus teléfonos móviles, de sus
diagnósticos médicos, de sus coches, de sus electrodomésticos,... En fin, hasta los
medios de comunicación se hacen eco de la presentación de los nuevos sistemas
operativos, de aplicaciones 3D o de videojuegos.
Tras estos deseos, se encuentran millones de desarrolladores intentando
introducir más inteligencia a los artefactos del siglo XXI. Las abigarradas
comunicaciones entre personas o entre máquinas, los controles de cientos de millones
de procesadores sobre los sistemas productivos, las millares de aplicaciones de ocio, las
transacciones económicas y un sin fin de aplicaciones están basadas en la introducción
de conocimiento a través del software.
Pero la producción de software es una disciplina de la Ciencia muy reciente, de
unas pocas décadas. Todavía no ha habido una estructuración formal de este
conocimiento humano. A diferencia con otras ingenierías no hay una metodología que
tenga procedimientos matemáticos que indique el camino más correcto a seguir en la
elaboración del software.
Recientemente, los investigadores de las Ciencias de la Computación han
decantado una metodología que ayuda a la creación del software. Durante los últimos
treinta años ha habido múltiples experiencias cuantitativas (nuevos lenguajes de
Pero antes de entrar en la tecnología OO, se verán las razones de haber llegado a
este punto de la evolución en los lenguajes informáticos.
Resumen
Algoritmos + datos = programa (programación estructurada)
Pero estas diferencias en sus apariencias son mucho más profundas y de un gran
calado. Resulta fácil de citar los conceptos de abstracción, ocultación de la información,
reutilización del software, polimorfismo y otras palabras claves que todos los autores de
programación orientada a objetos comentan. Pero éstas quedarán en el olvido si sólo se
pasa de codificar de un lenguaje estructurado a otro orientado a objeto.
Para entender qué es la programación orientada a objetos hay que tratarla desde
el punto de vista del método y enmarcarse en su paradigma. Por tanto, hay que examinar
lo que los investigadores acaban de destilar después de más de treinta años y que sólo
desde hace unos pocos de años está algo más claro.
no emplean POO. Justamente, el objetivo del curso es saber POO, para ello se requiere
de:
1. Un marco para el análisis y el diseño OO (A/DOO) aplicando el
Lenguaje Unificado de Modelado (UML).
2. La utilización de patrones de diseño y
3. El Proceso Unificado.
La producción del software requiere seguir un conjunto de etapas que van desde
la recogida de los requisitos, el análisis y diseño de la aplicación hasta la
implementación. Por tanto, en el paradigma orientado a objetos se requiere de
disciplinas y artefactos para realizar estas etapas, a las que se describirán mediante el
acrónimo de AOO/D (Análisis y diseño orientado a objetos).
En el análisis se pone énfasis en una investigación del problema y de los
requisitos, en vez de poner una solución. En el AOO se presta atención a encontrar los
objetos en el dominio del problema. Por tanto, el AOO es un método de análisis que
examina los requerimientos desde la perspectiva de las clases y objetos encontrados en
el vocabulario del dominio del problema.
El diseño pone énfasis en una solución conceptual que satisface los requisitos, en
vez de ponerlo en la implementación. En DOO se presta atención a la definición de los
objetos software y en cómo colaboran para satisfacer los requisitos.
UML es sólo una notación, capaz de hacer los planos del SW. Últimamente han
aparecido muchos cursos de UML, pero UML es una herramienta, no un fin en sí
mismo. A nadie se le ocurriría dar un curso exclusivo de PSPICE, ya que es sólo una
herramienta de simulación de circuitos electrónicos, habrá que darlo dentro del contexto
del análisis de los circuitos eléctricos-electrónicos. Lo mismo pasa con UML, sólo se
entiende si se está dentro de un contexto y éste es el de la producción del software.
Como se verá a lo largo del curso, la POO se basa en la asignación de
responsabilidades, pero ¿cómo se debería asignar las responsabilidades a los objetos?
¿cómo deberían interactuar los objetos?. Ciertas soluciones contrastadas a problemas de
diseño se pueden expresar mediante patrones o buenas prácticas. A éstas se las conocen
como patrones de diseño. Una habilidad clave y fundamental en el A/DOO es la
asignación cuidadosa de responsabilidades a los componentes software. En
consecuencia, este curso se centra en los pasos a seguir para asignar responsabilidades a
los objetos. Se emplearán los patrones GRASP y GoF.
La captura de los requisitos, el A/DOO y la implementación requieren moverse
dentro de un proceso. En este caso, se seleccionará mediante un proceso de desarrollo
iterativo llamado Proceso Unificado. La figura adjunta muestra un gráfico que sintetiza
los temas y habilidades en el que se va a centrar el curso.
A/DOO Captura
requisitos
Proceso
Unificado
Actividades:
Ingeniería y modelado de Sistemas/Información
Ubicación del software en el ámbito donde va a funcionar.
Análisis de requisitos:
Se deben conocer los aspectos relacionados con la información a tratar, la función
requerida, comportamiento, rendimiento, etc.
El cliente debe dar el visto bueno.
Diseño:
Estructura del programa y arquitectura del software.
Representaciones de la Interfaz.
Detalle Procedimental (algoritmo).
Pruebas:
De Caja Blanca: Análisis de los distintos caminos de ejecución de los algoritmos.
De Caja Negra: Análisis de los procesos externos funcionales.
Mantenimiento:
Gestión de cambios en el software debidos a:
Errores durante el desarrollo.
Adaptación a nuevos entornos (e.g. sistemas operativos)
Mejoras funcionales o de rendimiento.
Modelo Incremental
fase
En UP se describe las actividades de trabajo en disciplinas. Una disciplina es un
conjunto de actividades o artefactos relacionados dentro de un área determinada. En UP, un
artefacto es el término general para cualquier producto del trabajo: código, documentos,
gráficos, diagramas, etc.
En UP hay varias disciplinas (ver tabla 1.2), aunque en este temario se centrará en:
Requisitos: Sus artefactos son Casos de Uso, Visión, Especificaciones
Complementarias y Glosario.
Modelo del negocio: El artefacto más empleado en una aplicación única es el
Modelo del Dominio. A nivel más grande se hace ingeniería inversa de todos
los procesos del negocio de toda la empresa.
Diseño: Modelo del Diseño, documentos de Arquitectura SW, Modelo de
Datos.
En este temario se hace especial hincapié en las fases de Inicio y Elaboración, cuyos
artefactos están unidos con las disciplinas de Requisitos, Modelado de Negocio y Diseño,
donde se aplica el análisis de los requisitos, el AOO/D, loa patrones y su notación mediante
UML.
Modelo de Diseño c r
Diseño Documento de Arquitectura SW c
Modelo de Datos c r
Modelo de implementación c r r
Implementación
Plan de Desarrollo SW c r r r
Gestión del Proyecto
Modelo de Pruebas c r
Pruebas
Marco de Desarrollo c r
Entorno
1.6 Problemas
1. Objetivos del curso AOO/D.
2. Características del producto SW.
3. ¿Qué es la Ingeniería del SW?.
4. El paradigma OO.
5. Diferencias entre los métodos estructurados y los métodos OO.
6. Habilidades necesarias para la POO.
7. Enfoques sobre el proceso de desarrollo del SW.
8. ¿Qué es el UP?
9. Fases, iteraciones, disciplinas y artefactos en UP.
Ejercicio 1
Diseñar un juego de lanzar dos dados, si la suma de las caras es siete
se gana en caso contrario se pierde. Emplear métodos estructurados y
métodos orientado a objetos.
METODOLOGÍA ESTRUCTURADA
1. Definición del flujograma e implementación:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
while(continuarJuego)
{
puts("Pulsar cualquier tecla para lanzar dado 1");
getch();
caraDado1 = lanzarDado();
puts("Pulsar cualquier tecla para lanzar dado 2");
getch();
caraDado2 = lanzarDado();
if( (caraDado1 + caraDado2) == 7 )
puts ("Ha ganado");
else
puts("Ha perdido");
puts("Si desea jugar otra vez, pulse C");
letra = getch();
if ((letra == 'c' )||(letra == 'C'))
continuarJuego = 1;
else
continuarJuego = 0;
}
5. Implementación
Utilizando la programación extrema, XP, se empieza a escribir el código de prueba y
luego se construyen desde las clases menos acopladas a las más acopladas. En este caso se
codificará en C++.
#include <iostream>
#include <conio.h>
#include "JuegoDados.h"
void main ()
{
bool ganarPartida, continuarJuego = true;
char tecla;
JuegoDados elJuego;
std::cout << "\nJuego de dados, lanzar dos dados y sumar 7 para ganar\n";
while (continuarJuego)
{
std::cout << "\nPulsar cualquier tecla para lanzar dados\n";
getch();
ganarPartida = elJuego.jugar();
if ( ganarPartida )
std:: cout << "Ha ganando.\n";
else
std:: cout << "Ha perdido.\n";
} /* Dado.cpp */
class JuegoDados
{
public: bool JuegoDados::jugar()
{
bool jugar(); dado1.lanzar();
dado2.lanzar();
private:
Dado dado1; return((dado1.getValorCara()+
Dado dado2; dado2.getValorCara())==7?true:false);
}; /* JuegoDados.h */ } /* JuegoDado.cpp */
Permiso para copiar, distribuir y/o modificar este documento bajo los términos
de la Licencia de Documentación Libre GNU, Versión 1.1 o cualquier otra
versión posterior publicada por la Free Software Foundation; sin secciones
invariantes, sin texto de la Cubierta Frontal, así como el texto de la Cubierta
Posterior. Una copia de la licencia es incluida en la sección titulada "Licencia de
Documentación Libre GNU".
En esta fase se analiza la viabilidad del proyecto, aunque también podría incluir
los primeros talleres de requisitos y la planificación de la primera iteración. La tabla
adjunta muestra los artefactos comunes de la fase de inicio.
Artefactos Comentario
Visión y análisis del negocio Describe los objetivos y las restricciones de alto nivel, el
análisis del negocio y proporciona un informe para la toma de
decisiones.
Modelo de Casos de Uso Cuenta los requisitos funcionales y en menor medida los no
funcionales.
Especificación Complementaria Documenta los requisitos relacionados con la calidad del SW.
Lista de Registros & Plan Detalla los riesgos del negocio, técnicos, recursos,
de Gestión del Riesgo planificación y las ideas para mitigarlos.
darles respuestas.
Prototipos y pruebas de conceptos Para clarificar la visión y validar las ideas técnicas.
En esta etapa se puede dar la mayoría de los casos de uso, aunque sólo un 10%
de ellos con detalles, sobre todo aquellos que son críticos. El resto sólo se enumeran.
Ejemplo 2.1
Ejemplo 2.2
No obstante, el sistema
pretende capturar un
circuito analógico y mostrar
su respuesta en frecuencias.
En la actualidad hay
aplicaciones que realizan
este cometido, como por
ejemplo PSPICE.
Los requisitos son capacidades y condiciones con las cuales debe estar conforme
el sistema. El primer reto del trabajo de los requisitos es encontrar, comunicar y
recordar lo que realmente se necesita, de manera que tenga un significado claro para el
cliente y para los desarrolladores.
En un estudio sobre los costes reales de los proyectos SW, el 37% estaban
relacionados con los requisitos.
Coste de los proyectos %
Entradas de usuarios erróneas 13
Requisitos incompletos 12
Cambios en los requisitos 12
Habilidades técnicas pobres 7
Mala dirección 6
Otros 50
En este informe la granularidad que se refleja es alta, i.e. narra los aspectos más
relevantes del proyecto, sin entrar en los detalles. El documento de Visión y Alcance
resulta complementario con los documentos de Casos de Uso y Especificaciones
Complementarias, los cuales se verán más adelante.
En este documento debe de aparecer cuáles son los objetivos esenciales y los
problemas que hay que resolver. Pero ¿qué técnicas se pueden emplear para
localizarlos?.
Una técnica para identificar los aspectos esenciales del proyecto es utilizar un
esquema a dos niveles. Sucintamente se citará las características esenciales en una
jerarquía de objetivos principales y objetivos secundarios.
Una guía para detectar las características del sistema es el siguiente test
lingüístico:
Ejemplo 2.3
Ejemplo 2.4
Otra sugerencia, resulta que a veces, las características del sistema son
básicamente equivalentes a los nombres de los Casos de Uso. No hay que olvidar que
los casos de uso, como se verá más adelante, hacen referencia a requisitos. Por tanto, los
documentos de Visión y de Casos de Uso son complementarios. En este sentido, se debe
evitar la repetición de conceptos, para mantener fluidez en los documentos y facilitar la
actualización. Una manera eficiente es realizar referencia cruzadas a los otros
documentos. Por ejemplo, se puede mencionar una característica y ampliar sus aspectos
en un caso de uso o en el documento de Especificaciones Complementarias.
2.4 Glosario
El glosario almacena los términos y las definiciones del proyecto. Puede jugar el
rol de diccionario de los datos relevantes. Es muy sorprendente que, a veces, el personal
involucrado en el proyecto esté utilizando distinto nombres para la misma cosa.
El glosario podría almacenarse en una tabla de base de datos. Los campos que
se puede utilizar son: nombre del término, la definición del campo, algún tipo de alias o
cualquier otro campo que pueda ser considerado.
Ejemplo 2.5
Espermatozoide con El grosor del halo está comprendido entre: mayor que 1/3 del HM
Halo mediano diámetro menor del core y menor que el diámetro menor del core
Espermatozoide con El grosor del halo es igual o menor que 1/3 del diámetro menor del HP
Halo pequeño core
Los casos de uso son un mecanismo para ayudar a entender y describir los
requisitos de forma simple y entendible para todo el personal involucrado. Fue
introducido por Jacobson. En UP, el Modelo de Casos de Uso es un artefacto de la
disciplina de Requisitos.
Los actores y los objetivos se identifican a la vez. Una batería de preguntas útiles
para encontrar los actores principales y sus objetivos pueden ser:
Actor Objetivos
Biomédico Clasificar los espermatozoides en cinco tipos
distintos
Microscopio robotizado Entregar al sistema las imágenes de
espermatozoides adquiridas
Actor Objetivos
Ingeniero Respuesta en frecuencia de circuitos analógicos
lineales
Otro enfoque para ayudar en la búsqueda de los actores, objetivos y casos de uso
es identificar los eventos externos que llegan al sistema, ¿Cuáles son?, ¿de dónde
proceden y por qué?. Una lista del evento externo, quién lo produce y cual es el objetivo
son presentados para las dos problemáticas abordadas.
En general, se define un caso de uso, de nivel EBP, por cada objetivo que tiene
un actor. Obviamente se han identificado algunos casos de uso y éstos ni están
completos, ni son perfectos. Habrá que refinar en otra iteración.
Como norma, los casos de uso son nominados comenzando con un verbo.
Los casos de uso se escriben no considerando la interfaz del usuario, hay que
centrarse en la intención.
Los casos de uso de caja negra son la forma más recomendada y común; no
describen el funcionamiento interno del sistema, ni sus componentes o diseño, sino que
especifica cuáles son sus responsabilidades; muy en la línea del análisis orientado a
objetos.
Los cursos alternativos que pueden ocurrir dentro de este caso el uso, describen
cualquier diferencia en la secuencia de los pasos de éxito. Se enumera cada curso
alternativo en la forma "X.Y", donde "X" identifica el paso del caso de uso de éxito y la
“Y” es un número de serie para la alternativa y mayor que cero. Por ejemplo, "5.3"
indicarían el tercer curso alternativo para la etapa número 5 del caso de uso.
Ejemplo 2.6
Ejemplo 2.7
Los Diagramas de Casos de Uso, dados por UML, son secundarios. Los Casos
de Uso son documentos. Dibujar un diagrama de casos de uso junto con la lista actor-
objetivos es complementario al documento.
Los “muñecos” suelen emplearse para actores humanos. Cuando los actores son
otros sistemas informáticos o equipos en general, se representa mediante un cuadrado,
con el estereotipo1 <<actor>>.
Los casos de uso se representan mediante una elipse con el nombre dado. Las
relaciones entre los actores y los casos de uso sen unen mediante líneas que pueden
llevar un estereotipo.
Ejemplo 2.8
1
Un estereotipo es una variante de un elemento de UML. Hay estereotipos estándares, definidos
dentro del UML y hay otros que se pueden definir específicamente en un proyecto. Los estereotipos se
identifican por una palabra clave entre los signos << y >>.
Ejemplo 2.9
Los casos de uso no son orientados a objetos. Los casos de uso podrían ser
utilizados dentro de los métodos estructurados. Es sólo una herramienta para capturar
los requisitos. En UP serán la entrada para las actividades de AOO/D.
A los casos de uso se les puede acompañar con una lista de características de alto
nivel del sistema. Aunque ésta es parte del documento de Visión y Alcance.
Los casos de uso en el UP fomentan el desarrollo dirigido por los casos de uso.
Éstos son una parte importante de la planificación iterativa. Los casos de uso dirigen el
diseño.
UP diferencia entre los casos de uso del sistema (los vistos hasta ahora) con los
casos de uso del negocio. Por ejemplo, en un restaurante, un caso de uso del negocio es
servir una comida. Esta actividad incluye no sólo la aplicación informática sino todo lo
concerniente a este proceso: pedidos, colocación de la mesa, camareros,... Rational Rose
al iniciar un proyecto, siguiendo UP, realiza el diagrama de UML que aparece en la
figura; dando a entender una relación más amplia entre los casos de uso de la aplicación
informática y su entorno ambiental.
Business Use-Case
Model
Use-Case Model
Tabla 2-6 Muestra del esfuerzo de los requisitos a lo largo de las primeras iteraciones; no es
una receta
Requisitos. Modelo de 2 días de taller Al iniciar esta Al iniciar esta Repetir, se Repetir con la
Casos de Uso de requisitos. iteración, tiene iteración, tiene completa el intención de
Se identifican lugar un taller de lugar un taller de 70% de todos clarificar y
por el nombre requisitos de 2 requisitos de 2 los casos de escribir en
de la mayoría días. Se obtiene días. Se obtiene uso en detalle del 80-
de los casos un mejor una mejor detalle. 90% de los
de uso y éstos entendimiento y comprensión y casos de uso.
se resumen en retroalimentación retroalimentación
un párrafo a partir del a partir del
breve. trabajo de trabajo de Sólo una
implementación, implementación, pequeña parte
entonces se entonces se de éstos se
Sólo el 10% completa el 30% completa el 50% construyen
se escribe en de los casos de del los casos de durante la
detalle. uso en detalle. uso en detalle. elaboración; el
resto se aborda
durante la
construcción.
Gestión del Plan de Estimación La estimación Un poco mejor ... Un poco Ahora se
proyecto. Desarrollo de muy comienza a mejor ... pueden
SW. imprecisa del tomar forma. establecer
esfuerzo total racionalmente
la duración
global del
proyecto, los
hitos más
importantes,
estimación del
coste y
esfuerzo.
Las reglas del dominio dictan el modo en el que podrían operar un dominio o
negocio. Con frecuencia, resulta útil identificar y registrar aquellas reglas del dominio
que afectan a los requisitos, ya que pueden clarificar el contenido de un caso de uso
ambiguo o incompleto.
Ejemplo 2.10
MFC (.NET)
ActiveX-Bode VistaFrecuenci a
ELAI
DominioFrecunc
iaEla STL-ANSI C++
2.7 Problemas
2. Tipos de requisitos.
Problema 2.1
Los simuladores
simplificados de los procesos
monovariables (SISO) se basan en
tener modelos lineales e
invariantes en el tiempo (LTI) para
dichos sistemas. Éstos se
caracterizan por tener, cada uno
de ellos, una función de
transferencia, FDT, en el dominio
en
Z:
Y z b0 b1 z 1 ... bm z m
M z
X z a0 a1 z 1 ... an z n
yk
1
b0 xk b1 xk 1 ... bm xk m a1 yk 1 a2 yk 2 ... an yk n
a0
2. Glosario
3. Lista de evento-actor-objetivos
4.Casos de uso
5. Especificaciones complementarias
1. El sistema debe de simular la dinámica del control del avión ante una maniobra.
1.a. El sistema debe tener la función de transferencia del modelo.
1.b. Se debe definir el tipo de maniobra a realizar
1.c. Opcionalmente se podrá introducir algunas perturbaciones
2. El sistema visualizará la evolución temporal del proceso.
2.a El sistema debe visualizar la orientación del avión en función del tiempo.
2.b El sistema debe visualizar la posición del timón en función del tiempo.
Inclusiones:
Prioridad: Máxima. Núcleo del sistema.
Frecuencia de uso: Podría ser casi continuo.
Reglas de negocio:
Requerimientos especiales:
Suposiciones de partida:
Notas y documentos:
Problema 2.2
2. Glosario
3. Lista de evento-actor-objetivos
4. Casos de Uso.
Problema 2.4
b. El sistema debe simular la interacción entre la pelota contra las paredes y la raqueta.
d. El sistema debe de terminar la partida cuando la pelota cruce una determinada línea,
al que se le llamará línea roja.
Problema 2.5
Rellenar los artefactos de la fase de Inicio para el juego del ‘Pang’. Éste
trata de un escenario cerrado donde un jugador pretende eliminar las esferas
existentes. El hombre dispara a las esferas, las cuales se dividen si son
grandes, en caso contrario desaparecen. Además el jugador deberá de evitar
que las esferas le toquen, ya que si le impacta perderá la partida. Se pide:
1. Lista de características principales (jerarquía a dos niveles).
2. Glosario
3. Lista de evento-actor-objetivos.
4. Casos de Uso.
Artefacto Comentario
Modelo del Dominio Es una visualización de los conceptos del dominio; es similar al modelo
de información estático de las entidades del dominio.
Las clases conceptuales del mundo real no son clases software. Hay tres tipos de
clases: conceptuales, de diseño y de implementación. Las primeras corresponden con las
esencias del universo del problema, la segunda categoría surge de la solución lógica
dada en la etapa de diseño y la tercera se da en la implementación.
Hay diferencia entre las clases conceptuales, las clases de diseño y las de
implementación, por tanto, en el modelo del dominio hay que evitar los métodos y los
artefactos del software. Por ejemplo, las relacionadas con el GUI (Graphical User
Interface), las bases de datos,...
“la división por clases conceptuales (objetos) en lugar de la división por funciones”
3. Añadir las asociaciones necesarias para registrar las relaciones que hay que
mantener en mente.
La tarea central del AOO es identificar las clases conceptuales relacionadas con
el escenario que se está diseñando. Se presentan dos técnicas:
Ejemplo 3.1
Ejemplo 3.2
Ejemplo 3.3
comprensión del código existente. Los nombres de algunas clases del diseño coincidirán
con las dadas en las clases conceptuales, luego sabiendo el modelo del dominio
sugerirá, de forma natural, la comprensión del código.
Una asociación es una relación entre clases conceptuales que indica alguna
conexión significativa e interesante. Las asociaciones son abstracciones; no se trata de
conexiones entre entidades software. Se trata de identificar las asociaciones y añadirlas
al modelo del dominio.
DescripcionDelArticulo-Articulo
DescripcionDelVuelo-Vuelo
A es una línea de una transacción o informe de B Histograma-Espermatozoides
LineaDeVenta-Venta
TrabajoMantenimiento-RegistroMantenimiento
A se conoce/registra/recoge/informa/captura en B Venta-Registro
Reserva-ListaPasajeros
A es miembro de B Cajero-Tienda
Piloto-CompañiaAerea
A es una subunidad organizativa de B Departamento-Tienda
Mantenimiento-CompañíaAerea
A utiliza o gestiona B Cajero-Registro
Piloto-Avión
A se comunica con B Procesador-Clasificador
Cliente-Cajero
AgenteDeReserva-Pasajero
A está relacionado con una transacción de B Cliente-Pago
Pasajero-Billete
A es una transacción relacionada con otra Pago-Venta
transacción B Reserva-Cancelación
A está al lado de B LineaDeVenta-LineaDeVenta
Ciudad-Ciudad
A es prioridad de B Registro-Tienda
Avión-CompañíaAerea
A es un evento relacionado con B Venta-Cliente
Salida-Vuelo
• A se registra en B
Los roles son cada extremo de una asociación. Se suele indicar la multiplicidad
(*: cero o muchos, 1...*: uno o más, 1 ...40: de uno a 40, 5: exactamente 5, 3,7,8:
exactamente 3,7 y 8). La multiplicidad define cuántas instancias de una clase A puede
asociarse con una instancia de una clase B.
Ejemplo 3.4
instancia por estudiante. Por tanto, Estudiante si es una clase y el número de matrícula
es un atributo.
En caso de duda sobre un concepto, éste se definirá como una clase conceptual
en vez de un atributo.
Los tipos de datos pueden ser representados en el modelo del dominio, pero
puede producir ruido visual.
Ejemplo 3.5
Ejemplo 3.6
Los eventos del sistema y sus operaciones asociadas deberían expresarse al nivel
de intenciones, en lugar de en términos de interacción con los elementos del interfaz
gráfico. También se mejora la claridad expositiva, si se comienza el nombre de un
evento del sistema con un verbo.
Ejemplo 3.7
A los DSS se les puede añadir una leyenda adicional sobre los eventos del
sistema.
Ejemplo 3.8
Se pueden definir contratos para las operaciones del sistema. Éstas se descubren
identificando los eventos del sistema.
Referencias cruzadas: (opcional) Casos de usos en los que pueden tener lugar
esta operación.
Modificación de atributos.
Los casos de uso son los principales artefactos de los requisitos del proyecto.
Los contratos de operación se emplearán donde la complejidad sea alta y añade valor la
precisión detallada.
b) Modificación de atributos
Ejemplo 3.6
3.4 Problemas
7. Que es un DSS.
Problema 3.1
Problema 3.2
Problema 3.3
Emplear los artefactos necesarios para el AOO del juego del frontón (ver
problema 2.4)
Problema 3.4
• Diagramas de clases
UML ha sido propuesto por OMG a ISO para que sea un estándar. UML es una
cierta unificación de métodos anteriores como:
OMT de Rumbaugh
OOSE de Jacobson
El método de Booch.
Visibilidad nombre_atributo ‘:’ tipo_atributo ‘=’ valor inicial ´{`otras propiedades ‘}’
Ejemplo 4.1
#ifndef _INC_ESFERA_
#define _INC_ESFERA_
Realizar la implementación en
C++ de la siguiente descripción UML class Esfera
{
referente a la clase Esfera. private:
float radio_esfera;
public:
Esfera()
{this->radio_esfera = 2.0f;}
float getRadio()
{return (this->radio_esfera);}
void setRadio(float radio)
{this->radio_esfera=radio;}
};
#endif
UML soporta diferentes tipos de clases que pueden ser implementadas o no por
el lenguaje de programación.
1
Rational Rose emplea los siguientes iconos para señalar la visibilidad del atributo o del
servicio: . El candado significa privado, la llave es protegido y la goma que es público.
Las clases parametrizadas son unas clases que son empleadas para crear una
familia de otra clase. Estas clases son un tipo de contenedor, son conocidas también
como templete. No todos los lenguajes soportan los templetes. Por ejemplo, en ANSI
C++ existe el paquete STL (Standar Templete Library), mientras que JAVA no soporta
este tipo de clases. La biblioteca contenedora STL permite a los programadores
desarrollar aplicaciones con contenedores estándar, tales como pilas, listas, colas de
espera, así como manipular el contenido de dichos contenedores de diversas maneras.
De esta forma, los desarrolladores hacen uso de servicios de alta calidad sobre
componentes muy utilizados en casi todas las aplicaciones. Por ejemplo, la necesidad de
mantener una lista dinámica de objetos es algo muy habitual. Al emplear los templetes,
los desarrolladores no deben de implementar dichos servicios, sólo deben de saber
utilizarlos. Por tanto, una clase parametrizada permite reutilizar el código.
Ejemplo 4.2
La primera tarea del AOO sería la construcción del modelo del dominio, éste
podría ser:
#ifndef _INC_LISTA_PASAJEROS
#ifndef _PASAJERO_INC_
#define _INC_LISTA_PASAJEROS
#define _PASAJERO_INC_
#include <vector>
#include "Pasajero.h";
#include <string>
class Pasajero
class ListaPasajeros
{
{
public:
public:
void setNombre(const char *nom)
void setDatosPasajero (const Pasajero p1)
{nombre = nom;}
{laListaPasajeros.push_back(p1);}
std::string & getNombre( void )
int darNumeroPasajeros ( void ) const
{return nombre;}
{ return laListaPasajeros.size(); }
void setPasaporte(unsigned long
void iniciarLista ( void )
pasaporte)
{ iteradorPasaje = laListaPasajeros.begin();}
{ numPasaporte =DNI;}
Pasajero getDatoPasajero( unsigned numPasajero )
unsigned long getDNI( void ) const
{ return (*(iteradorPasaje + numPasajero)); }
{return (numPasaporte);}
private:
private:
std::vector<Pasajero> laListaPasajeros;
std::string nombre;
std::vector<Pasajero>::iterator iteradorPasaje;
unsigned long numPasaporte;
};
};
#endif
#endif
4.2.1.2 Interfaces
En C++ se emplea las clases abstractas para implementar los interfaces. En Java
y en C# existe la palabra clave “interface”.
Ejemplo 4.3
2
www.codeproject.com
Ejemplo 4.4
#endif
#include <iostream>
#include "../includes/STDNombre.h"
#include "../includes/CNombre.h"
#include "../includes/MFCNombre.h"
}
using namespace std;
int main ( void )
{
INombre *pNombre1 = INombre::factoriaObjetos(ESTANDAR_STL);
INombre *pNombre2 = INombre::factoriaObjetos(ESTILO_C);
INombre *pNombre3 = INombre::factoriaObjetos(CADENA_MFC);
pNombre1->setNombre("Manolo Gonzalez");
pNombre2->setNombre("Pedro Lopez");
pNombre3->setNombre("Ana Rodriguez");
Hay varios tipos de relaciones en UML entre las clases, de las que se destacan:
asociación, generalización y varios tipos de dependencia.
4.4.1 Asociaciones
Las asociaciones son conexiones semánticas entre clases. Cuando una asociación
conecta a dos clases, cada clase puede mandar un mensaje a la otra. Una asociación
permite a una clase conocer los atributos y las operaciones públicas de la otra clase. La
asociación se representa por una línea continua. Esta opción indica que las clases unidas
por la asociación tienen dependencia cíclica. Esta presentación es válida en el AOO y en
su artefacto de Modelo del Dominio. Sin embargo, en el diseño se verá que esta forma
de actuar es impropia con una buena organización de las responsabilidades entre las
clases. Por este motivo, en las asociaciones de diseño suelen aparecer reflejadas unas
flechas que indican el sentido de la navegabilidad. Este concepto indica que la
asociación entre las clases es de carácter unidireccional. La fecha refleja que la clase a
la que apunta puede ser empleada por la clase que emana la asociación, pero no en
sentido contrario.
class B;
class B;
class A
{ class A
public: {
B elB; public:
B elB;
};
};
class B
class A; {
};
class B
{
public:
A elA;
};
Cada conexión de una asociación a una clase puede tener nombre (el rol que
desempeña en la asociación), visibilidad (si se pueden ver los atributos y servicios) y la
propiedad más importante que es la multiplicidad: cuantas instancias de una clase se
puede relacionar con una instancia de otra clase. Si la asociación, además del nombre,
tiene atributos se dice que es una clase de asociación, la cual será tratada más adelante.
Ruedas Coche
4
Puertas
3..5
Motor
En una composición las partes son atributos del todo. Se representa en UML
mediante el diamante relleno.
JuegoDados Dado
• Como recomendación, en caso de duda hay que descartarla. Los noveles del
AOO/D utilizan la agregación y la composición demasiado a menudo.
Recuerde que ambos tipos son una asociación. En caso de duda utilice una
simple asociación.
4.4.2 Generalización
En el ejemplo:
Una subclase potencial debería de estar de acuerdo con la regla del 100% y de la
regla Es-un. Se creará una subclase de una superclase cuando:
Son aquellas que carecen de instancia y que son instanciadas desde una clase
derivada (subclase). Recuérdese el ejemplo utilizado en las interfases.
4.4.2.2 Herencia
Ejemplo 4.5
Ejemplo 4.6
1.b. Las fichas pueden ser de libros o de revistas. Los libros pueden estar
constituidos por uno o más volúmenes. Los libros pueden ser en lengua extranjera.
El glosario estaría constituido por las definiciones de: Ficha, Biblioteca, Libros,
Revista, Referencia, Título,...
4.4.3 Dependencia
Una dependencia es un tipo de relación entre dos o más elementos del modelo.
La dependencia relaciona los elementos del modelo sin la necesidad de tener un
conjunto de instancias para su significado. Indica una situación tal que un cambio en el
elemento servidor puede requerir un cambio en el elemento cliente.
Una relación de dependencia muestra que una clase hace referencia a otra clase.
Cuando hay dependencia entre dos clases, no se añaden atributos a la otra clase. Este
aspecto lo diferencia respecto a la asociación. Cuando no hay una relación de asociación
o de generalización se utiliza normalmente la dependencia para el resto de relaciones.
4.4.4 Realización
La realización se indica con una flecha de línea discontinua con una punta de
flecha hueca cerrada. Es similar al símbolo de generalización pero con una línea
discontinua, para indicar que es similar a un tipo de herencia con relación de
dependencia.
De vez en cuando es útil dar más detalles de los que se tiene sobre una
asociación. En estos casos se puede emplear un calificador. Un calificador es un valor
que selecciona un objeto único del conjunto de objetos relacionados a través de la
asociación. Los calificadores son importantes para modelar nombres y códigos de
identificación.
constituido por
Tablero Cuadrado
1 9
constituido por
Tablero fila : float[3] Cuadrado
columna : float[3]
1 1
1 estudia las
Alumno Asignatura
1..n
3
1..n 1
/enseña
Impartida por
1
Profesor
1..n
Una clase asociativa se representa como una clase colgada del símbolo de la
asociación por medio de una línea discontinua.
4.5 Comentarios
4.6 Paquetes
Hay algunos elementos de un paquete que deben tener visibilidad para que
puedan ser reconocidos desde otros paquetes. Los paquetes se configuran como un
espacio de nombres de propósito general. Cada elemento del paquete es visible y
reconocido dentro del paquete donde se ha declarado y su nombre no puede estar
repetido dentro del paquete.
El nombre del elemento tiene que estar codificado junto con el identificador del
paquete; siendo la regla el nombre del paquete más dos puntos (:) y el nombre del
elemento.
Paquete : Elemento
} }
Resulta útil que todos los elementos relacionados con el modelo del dominio
tenga como raíz un paquete denominado Dominio. Todos los conceptos básicos o
comunes se definan en un paquete que se pueda llamar Elementos Básicos o Conceptos
Comunes.
Si los paquetes más responsables (de los que más se depende) son inestables,
existe un mayor riesgo de extender el impacto de los cambios a quienes depende de
estos paquetes. Hay que poner mucha atención al paquete que más se emplea. Sus
constantes revisiones pueden afectar al resto. Hay que saber aislar para producir un
diseño robusto.
Ejemplo 4.10
Los diagramas de casos de uso sirven para mostrar las funciones de un sistema
SW desde el punto de vista de sus interacciones con el exterior y sin entrar ni en la
descripción detallada ni en la implementación de estas funciones. Reparte la
funcionalidad del sistema en mensajes significativos entre los actores y el sistema. Un
caso de uso es una descripción lógica de una parte de funcionalidad del sistema. El
propósito de un caso de uso es definir una pieza de comportamiento coherente, sin
revelar la estructura interna. Se utilizan tanto en la fase de requisitos como de análisis.
Un caso de uso también se puede especializar en uno o más casos de uso. Esta
relación es la de generalización/especialización de casos de uso. Un caso de uso A es
una especialización de un caso de uso de B, cuando el A es un proceso más específico
que el de B.
En UML, la relación entre los actores y los casos de uso se representarán como
una asociación, indicando la comunicación bidireccional entre usuarios y sistema. Las
relaciones de inclusión y extensión se dibujan como flechas de líneas discontinuas con
la palabra clave <<include>> y <<extend>>, respectivamente. La relación de
inclusión apunta al caso de uso a ser incluido; la relación de extensión señala al caso de
uso que se extenderá. Las generalizaciones de casos de uso serán dibujadas mediante la
flecha de relaciones de herencia, la punta señalará al caso de uso de generalización.
Solicitar catálogo
<<include>>
Hacer Pedido
Vendedor
<<include>>
<<include>>
Pago compras
Pedir producto
Pago al contado
Pago crédito
Se recuerda que lo importante son los documentos de los casos de uso. No hay
que perder mucho tiempo en el diagrama de casos de uso. Es típico de los
desarrolladores noveles dedicarse a dibujar casos de uso más o menos sofisticados,
buscando relaciones y nuevas inclusiones, cuando lo importante es la captura de los
requisitos funcionales (ver capítulo dedicado a la recogida de documentación y
requisitos).
4.8 Problemas
Problema 4.1
Problema 4.2
1..4 1..2 1
1 * *
Avión 1 * Vuelo 1 * Reserva
1
Avión militar Avión comercial Línea aérea
Problema 4.3
Realizar una aplicación gráfica en cuyo mundo esté definida una CAJA que
contiene un número indeterminado de ESFERAS. Se debe simular el rebote
entre las propias ESFERAS y éstas con los SEGMENTOS de la CAJA.
1. Lista de características.
3. Vista de gestión.
1.a. Las esferas rebotan contra las paredes y barreras de la caja y con ellas
mismas.
La vista de gestión:
• Diagramas de estado
• Diagramas de actividad
• Diagrama de componentes
• Diagrama de despliegue
UML incluye los diagramas de interacción para ilustrar el modo en que los
objetos interactúan por medio de mensajes. Esta visión proporciona una vista integral
del comportamiento del sistema; muestra el flujo de control a través de los mensajes
entre objetos.
• Diagramas de secuencia
• Diagrama de interacción
Los objetos son representados por instancias de la clase con o sin identificador.
: ProcesadorImagenes elProcesador :
ProcesadorImagenes
Los mensajes emplearán una sintaxis igual que los servicios de las clases. De
hecho, la mayoría de los mensajes son los servicios de las clases:
Ejemplo
laListaCaracteristica := procesarFichImag(nomFichImag:string):ListaCaracterística
Los mensajes se indican con flechas que comienzan en una activación (al
principio de ésta o en una posición intermedia) y acaban en otra. También, se puede
indicar los mensajes de retorno al final de una activación, en forma de flecha
discontinua y punta abierta. Lo normal es que se excluyan por quienes utilizan UML.
2. A un evento externo.
Ejemplo 5.1
Una colaboración modela los objetos y los enlaces significativos dentro de una
interacción. Un enlace es un camino de conexión entre dos objetos; indica que es
posible alguna forma de navegación y visibilidad entre objetos. De manera más formal,
un enlace es una instancia de una asociación. A lo largo de un enlace pueden fluir
múltiples mensajes.
Cada mensaje entre objetos se representa con una expresión de mensaje, una
pequeña flecha que indica la dirección del mensaje y un número de secuencia para
mostrar el orden.
Ejemplo 5.2
1
Rational Rose convierte automáticamente los diagramas de secuencia a diagramas de
colaboración y viceversa.
Una maquina de estados modela los posibles estados que puede tener en vida un
objeto de una clase. Cada objeto se trata como una entidad aislada que se comunica con
el resto del mundo recibiendo eventos y respondiendo a ellos.
Las transiciones se representan por flechas, etiquetadas con sus eventos. Los
estados se representan por rectángulos de esquinas redondeadas. Es habitual incluir un
pseudo-estado inicial que pasa automáticamente a otro estado cuando se crea la
instancia.
Ejemplo 5.3
Tipos de eventos:
Es preferible utilizar los diagramas de estado para ilustrar los eventos externos y
de tiempo, y las reacciones a ellos; mientras los eventos internos son representados en
los diagramas de interacción.
Cuando se desear tener una idea más amplia de los efectos del comportamiento
dinámico de un sistema, se emplean las vistas de interacción. Por contra, las máquinas
de estados son útiles para entender los mecanismos de control, tales como interfaces de
usuario o controladores de dispositivos.
En un diagrama de
componentes se muestran las
diferentes relaciones de dependencia
que se pueden establecer entre
componentes. Los componentes bien
diseñados no dependen de otros
componentes. Un componente en un
sistema puede ser sustituido por otro
componente que ofrezca las interfaces
apropiadas.
Un componente se representa
mediante tres rectángulos. Figura 5. 5. Diagrama de componentes: ejemplo
tomado de la documentación de UML v1.3
Ejemplo 5.5
5.5 Cuestiones
5.6 Problemas
Ejercicio 1
Realizar una aplicación que ordene de forma creciente los números
dados por el usuario.
1. Caso de Uso
2. Modelo del dominio y DSS
3. Vista de Gestión.
4. Diagrama de secuencia y diagrama de clases de diseño
5. Implementación en C++
1. El caso de uso EBP se llama “OrdenarNumeros” y tendrá un curso de éxito
como:
I. Ordenar los números de forma creciente
I.a. Solicitar al usuario el vector de números a ordenar
I.b. Ordenar de forma creciente el vector
I.c. Visualizar los resultados
4. Diseño de la aplicación
5. Implementación en C++
#if !defined(_ORDENADORNUMEROS__INCLUDED_)
#define _ORDENADORNUMEROS_INCLUDED_
#include <algorithm>
#include <vector>
class OrdenadorNumeros
{
public:
OrdenadorNumeros(std::vector<double> & elVectorNumeros)
{std::sort(elVectorNumeros.begin(),elVectorNumeros.end());}
};
#endif
#if !defined(_GUIORDENARNUMEROS__INCLUDED_)
#define _GUIORDENARNUMEROS__INCLUDED_
#include <vector>
class GUIOrdenarNumeros
{
public:
void visualizarResultados(std::vector<double> &);
void solicitarNumeros(std::vector<double> &);
};
#endif
#include "..\..\CABECERAS\VISUALIZADOR\GUIOrdenarNumeros.h"
#include <algorithm>
#include <iostream>
void GUIOrdenarNumeros::solicitarNumeros(std::vector<double> &elVectorNumeros)
{
bool introducirDatos = true; double valor;
std::cout<<"Esta aplicacion ordena los valores de forma creciente"<<std::endl;
std::cout<<"Introducir la lista de numeros y poner cero para salir"<<std::endl;
while(introducirDatos == true){
std::cin>>valor;
if(valor != 0)
elVectorNumeros.push_back(valor);
else
introducirDatos = false;
}
}
void visualizarDatos(double);
void GUIOrdenarNumeros::visualizarResultados(std::vector<double> &elVectorNumeros)
{
std::cout<<"Lista ordenada"<<std::endl;
std::for_each(elVectorNumeros.begin(),elVectorNumeros.end(),visualizarDatos);
}
void visualizarDatos(double valor)
{
std::cout<<valor<<std::endl;
}
#if !defined(_ORDENARNUMEROS_H__INCLUDED_)
#define _ORDENARNUMEROS_H__INCLUDED_
#include <vector>
#include "../Dominio/OrdenadorNumeros.h"
#include "../Visualizador/GUIOrdenarNumeros.h"
class OrdenarNumeros
{
GUIOrdenarNumeros elVisualizador;
std::vector<double> elVectorNumeros;
public:
void introducirDatos();
void visualizarResultados();
};
#endif
#include "..\..\CABECERAS\ORDENARNUMEROS\OrdenarNumeros.h"
void OrdenarNumeros::introducirDatos()
{
this->elVisualizador.solicitarNumeros(this->elVectorNumeros);
OrdenadorNumeros elOrdenador(this->elVectorNumeros);
}
void OrdenarNumeros::visualizarResultados()
{
this->elVisualizador.visualizarResultados(this->elVectorNumeros);
}
int main()
{
OrdenarNumeros elOrdenar;
elOrdenar.introducirDatos();
elOrdenar.visualizarResultados();
return 0;
}
Ejercicio 2
4. Implementación C++.
4. Una vez acabado el diseño, se pasará a su implementación desde las clases menos
acopladas hasta alcanzar a la función main():
#include <vector> #include "Bloque.h"
#include "Casa.h" #include "Techo.h"
public: public:
Casa(float,float,float);
Urbanizacion(unsigned, unsigned); virtual ~Casa();
virtual ~Urbanizacion(); void setPosicion(float,float,float);
void dibuja(); void dibuja();
}; };
#include "..\..\INCLUDES\DOMINIO\Casa.h"
Casa::Casa(float ancho, float altoBase, float altoTejado)
{
this->laBase.setBase(ancho);
this->elTecho.setBase(ancho);
this->laBase.setAltura(altoBase);
this->elTecho.setAltura(altoTejado);
void Casa::dibuja()
{
this->elTecho.dibuja();
this->laBase.dibuja();
}
#include "..\..\INCLUDES\DOMINIO\Urbanizacion.h"
#include "..\..\INCLUDES\comunes\glut.h"
void Urbanizacion::dibuja()
{
float x_ojo=10;float y_ojo=7.5; float z_ojo=40;
gluLookAt(x_ojo, y_ojo, z_ojo, // posicion del ojo
0.0, y_ojo, 0.0, // hacia que punto mira (0,0,0)
0.0, 1.0, 0.0); // definimos hacia arriba (eje Y)
Ejercicio 3
Atomo* enlace;
};
void main()
{
Atomo hidrogeno1(1);
Atomo hidrogeno2(1);
Atomo oxigeno(16);
hidrogeno1.Enlaza(&oxigeno);
hidrogeno2.Enlaza(&oxigeno);
oxigeno.CalculaPosicion();
hidrogeno1.CalculaPosicion();
hidrogeno2.CalculaPosicion();
hidrogeno1.Dibuja();
hidrogeno2.Dibuja();
oxigeno.Dibuja();
}
void main()
{
VaporAgua vapor(30); //30 moleculas de agua
vapor.CalculaPosicion();
vapor.Dibuja();
}
Ejercicio 4
#include "../Cajero/HWCajero.h"
#include "../Dominio/Ingresos.h"
#include "../Dominio/Devoluciones.h"
class MaquinaExpendedora
{
public:
MaquinaExpendedora();
virtual ~MaquinaExpendedora();
void solicitarProducto();
};
//////////////////////////////////
void MaquinaExpendedora::solicitarProducto()
{
HWCajero miCajero;
Dinero elPrecioProducto = miCajero.solicitarProducto();
Ingresos miDinero;
while(miDinero.haySuficienteDinero(elPrecioProducto) == false)
miDinero.anyadirMoneda(miCajero.recibirMoneda());
Devoluciones elDineroEntregar(miDinero.getDinero(),elPrecioProducto);
std::vector<Moneda> laListaMonedas;
elDineroEntregar.calcularDevoluciones(laListaMonedas);
miCajero.entregarVueltas(laListaMonedas);
miCajero.entregarProducto();
}
//////////////////////////////
#include <iostream>
int main()
{
MaquinaExpendedora laMaquinaExpendedora;
bool continuar = true;char opcion;
while(continuar){
laMaquinaExpendedora.solicitarProducto();
std::cout<<"Nuevo producto (s/n): ";
std::cin>>opcion;
continuar = (opcion == 'n') || (opcion == 'N') ? false :true;
}
return 0;
}
#include <vector>
#include "../Comunes/Moneda.h"
class HWCajero
{
public:
Moneda recibirMoneda();
Dinero solicitarProducto();
void entregarVueltas(std::vector<Moneda> &);
void entregarProducto();
};
////////////////////////////////////
Moneda HWCajero::recibirMoneda()
{
float cantidad;
std::cin >> std::setprecision(2)>> cantidad;
return(Moneda((unsigned)(cantidad*FACTOR_DINERO)));
}
Dinero HWCajero::solicitarProducto()
{
std::cout<<"Elegir tipo producto:"<<std::endl;
std::cout<<"====================="<<std::endl;
std::cout<<"1: Cafe 40 centimos"<<std::endl;
std::cout<<"2: Te 35 centimos"<<std::endl;
std::cout<<"3: Limon 50 centimos"<<std::endl;
unsigned opcion;unsigned valor;
std::cin>>opcion;
switch(opcion) {
case 1: valor = 40;break;
case 2: valor = 35;break;
default: valor = 50;
}
std::cout<<"Introducir monedas:"<<std::endl;
std::cout<<"==================="<<std::endl;
return(Dinero(valor));
}
void HWCajero::entregarVueltas(std::vector<Moneda> &laListaMonedas)
{
std::cout<<"Lista de monedas a devolver"<<std::endl;
for(unsigned i=0;i<laListaMonedas.size();i++)
std::cout<<((float)laListaMonedas[i].getCantidad())/FACTOR_DINERO
<<std::setprecision(2)
<<std::endl;
}
void HWCajero::entregarProducto()
{
std::cout<<"Recoja el producto seleccionado"<<std::endl;
}
class Ingresos
{
Dinero elDineroIngresado;
public:
Ingresos();
virtual ~Ingresos();
void anyadirMoneda(Dinero nuevaMoneda)
{ elDineroIngresado += nuevaMoneda;}
bool haySuficienteDinero(Dinero elPrecioPedido)
{ return (elDineroIngresado>= elPrecioPedido ? true : false); }
Dinero getDinero(){return elDineroIngresado;}
};
#include <vector>
#include "../Comunes/Moneda.h"
class Devoluciones
{
Dinero elDineroAdevolver;
public:
Devoluciones();
Devoluciones(Dinero elIngreso,Dinero elPedido)
{elDineroAdevolver = elIngreso-elPedido;}
virtual ~Devoluciones()
void calcularDevoluciones(std::vector<Moneda> &);
};
/////////////////////////////////
void Devoluciones::calcularDevoluciones(std::vector<Moneda> &laListaMonedas)
{
if(this->elDineroAdevolver.getTipoDinero() == EURO){
while(this->elDineroAdevolver.getCantidad()>= 200){
laListaMonedas.push_back(Moneda(200));
this->elDineroAdevolver-=200;
}
while(this->elDineroAdevolver.getCantidad()>= 100){
laListaMonedas.push_back(Moneda(100));
this->elDineroAdevolver-=100;
}
//Repetición con el resto de monedas
//........
}
}
En el Proceso Unificado, UP, por cada iteración, tendrá lugar una transacción
desde un enfoque centrado en los requisitos, a un enfoque centrado en el diseño y en la
implementación.
Iteración i
Iteración i+1
Requisitos
Requisitos
Diseño tiempo
Diseño
Implementación
Prueba Implementación
Prueba
Integración
Pruebas de sistema Integración
Pruebas de sistema
Este capítulo se organiza en tres apartados. El primero tratará sobre las bases del
diseño y de la implementación, para luego pasar a entrar de lleno en el diseño con
patrones. Los apartados segundo y tercero se estudiarán los patrones GRASP y GoF
respectivamente.
Los diagramas de clase de diseño, DCD, se crean en paralelo con los diagramas
de interacción. En los DCD se encuentran reflejados:
Los patrones.
La navegabilidad.
Las dependencias.
A diferencia de las clases conceptuales 1 del AOO, las clases de diseño de los
DCD muestran las esencias de las futuras clases implementadas o de software.
El primer paso para crear un DCD es identificar aquellas clases que participan en
la solución del paquete a diseñar. Se pueden encontrarlas examinando el modelo del
dominio, donde algunas clases conceptuales pueden ser tomadas como clases de diseño.
También pueden ser localizadas en los diagramas de interacción y listando las clases
que se mencionan.
1
Clases conceptuales abstracciones de conceptos del mundo real
Los tipos de los atributos, los argumentos de los servicios y los valores de
retorno se podrían mostrar. La cuestión sobre si se muestra o no esta información se
debe de considerar en el siguiente contexto:
“Si se emplea alguna herramienta CASE con generación automática del código,
son necesarios todos los detalles. Si se hace para que lo lean los
desarrolladores, los detalles podrían influir negativamente por el ruido visual
que produce tanta información en los DCD.”
Ejemplo 6.1
2
Recuerde que el objeto receptor es el que realiza la operación.
Una vez finalizado los DCD se dispone de los suficientes detalles para generar el
código de la capa del dominio de los objetos.
Una ventaja del AOO/D y la POO, cuando se utiliza UP, es que proporciona una
guía de principio a fin, esto es, se presenta un conjunto de artefactos, procedimientos y
técnicas que van desde los requisitos hasta la generación del código.
Ejemplo 6.2
#include <iostream>
using namespace std;
#include "../Dominio/CoordinadorFrecELAI.h"
class VistaFrecuenciaELAI
{
tipoFiltro elTipo;
float resistencia;
float condensador;
float frecInicial, frecFinal, frecIntervalo;
CoordinadorFrecELAI elCoordinador;
public:
void introducirCircuito(void);
void introducirParametrosRespFr(void);
}; /*VistaFrecuencia.h*/
El siguiente paso será escribir el código de las clases menos acopladas a las que
más lo están. Se implementarán por el siguiente orden: Polinomio, FDT, FiltroLTI,
RespuestaFrecuencia y Coordinador:
class Polinomio
{
std::vector<double> coeficientes;
public:
Polinomio(){}
Polinomio(unsigned grado, double *pCoef)
{
for (unsigned i=0;
i<=grado;coeficientes.push_back(*(pCoef+i)),i++);
}
double getCoeficiente(unsigned n)
{return( coeficientes[n]));}
};
#endif /*Polinomio.h*/
#endif /*FDT.h*/
#ifndef _FILTRO_LINEAL_INC_
#define _FILTRO_LINEAL_INC_
#include "FDT.h"
typedef enum{LF_1,HF_1} tipoFiltro;
class FiltroLineal
{
tipoFiltro elTipo;
FDT *pFDT;
public:
FiltroLineal(tipoFiltro, float, float);
unsigned getGradoFiltro(void){return pFDT->getGrado();}
double getCoefNum(unsigned n)
{return pFDT != NULL ? pFDT->getCoefNum(n) : 0;}
double getCoefDen(unsigned n)
{return pFDT != NULL ? pFDT->getCoefDen(n) : 0;}
~FiltroLineal(){if(pFDT) delete pFDT;}
};
#endif /*FiltroLineal.h*/
#endif /*RespuestaFrecuencia.h*/
class CoordinadorFrecELAI
{
FiltroLineal *pFiltro;
RespuestaFrecuencia *pRespFr;
public:
int ponerCircuito(tipoFiltro ,float , float );
int ponerParamResFr(float,float,float);
int getModuloRespFr(std::vector<double> &);
~CoordinadorFrecELAI()
{if(pFiltro) delete pFiltro; if(pRespFr) delete pRespFr;}
};
#endif /*Coordinador.h*/
Las algoritmias de los métodos serán implementados en los fuentes de las clases.
También de la menos acopladas a la de más acoplamiento. Se usa un código de test para
visualizarlo en consola.
/*coordinador.cpp*/
Una vez depurada la aplicación se procederá a aplicar ingeniería inversa para
obtener el nuevo DCD y pasar a la siguiente iteración UP:
• Patrones de diseño
6.2.1 Patrones
Resumiendo:
Dos tipos de patrones se explicarán: los patrones GRASP y los GoF. GRASP es
el acrónimo de General Responsibility Assignment Software Patterns. Se tratarán los
patrones: Experto en Información, Creador, Alta Cohesión, Bajo Acoplamiento,
Controlador, Polimorfismo, Indirección, Fabricación Pura y Variaciones Protegidas.
Mientras GoF es la abreviatura de Gangs of Four, de los que se tratarán los patrones:
Adaptador, Factoría, Singleton, Estrategia, Composición y Observador.
El patrón Experto indica qué hacen los objetos con la información que
contienen. Sucede muchas veces que la información está dispersa por diferentes clases
de objetos. Esto implica que hay muchos expertos con información “parcial” que
colaboran en la tarea, mediante el paso de mensajes para compartir el trabajo. Por
ejemplo, se han visto varios ejemplos que cuando una tarea llega a un objeto, el trabajo
es distribuido a otros objetos asociados.
Ejemplo 6.3
Este patrón también se conoce como: “Colocar la responsabilidad con los datos”,
“Eso que conoces, hazlo”, “Hacerlo yo mismo”, “Colocar los servicios con los atributos
con los que trabaja”.
6.3.2 Creador
B contiene objetos de A
Ejemplo 6.4
/*coordinador.cpp*/
Una clase con baja cohesión hace muchas cosas no relacionadas o tareas
relacionadas pero con mucho trabajo. Las clases de baja cohesión adolecen de los
siguientes problemas:
Difíciles de entender.
Difíciles de reutilizar.
Difíciles de mantener.
A menudo las clases con baja cohesión representan bien un grado grande de
abstracción o bien se les han asignado demasiadas responsabilidades que deberían de
haberse delegado en otras clases.
Como regla empírica, una clase con alta cohesión tiene un número relativamente
pequeño de métodos, con funcionalidad altamente relacionada y no realiza mucho
trabajo. En el caso de que la tarea sea extensa, colaborará con otros objetos para
compartir el esfuerzo.
El Bajo Acoplamiento y la Alta Cohesión son viejos principios del diseño SW.
Otro de estos principios es promover el diseño modular. La modularilidad es la
propiedad del sistema de haberse descompuesto en un conjunto de módulos cohesivos y
débilmente acoplados. En UML se emplea la vista de gestión del proyecto para la
aplicación de la modularidad. Con un doble motivo: a) organización de las tareas entre
los desarrolladores que van a participar en el proyecto y b) diseño de componentes
altamente cohesivas y con bajo acoplamiento.
Ejemplo 6.5
Beneficios:
Una clase con alto acoplamiento confía en muchas otras clases. Tales clases
suelen ser no deseables. Estas clases adolecen de los siguientes problemas:
Ejemplo 6.6
En general, las clases que son muy genéricas y con una alta probabilidad de
reutilización alta, deberían de tener un acoplamiento especialmente bajo. Por ejemplo,
en el anterior ejercicio, se ha colocado las clases Polinomio y FDT para una aplicación
de Simulación que habían sido definidas en Respuesta en Frecuencia. Ambas se
caracterizan por un Bajo Acoplamiento.
6.3.5 Controlador
El controlador es una especie de fachada del paquete que recibe los eventos
externos y organiza las tareas. No sólo el interfaz genera eventos, también puede
hacerlo el tiempo, si es una aplicación en tiempo real. Otro caso son las aplicaciones de
control de procesos; los sensores y/o dispositivos generan interrupciones que se deben
de atender. Cada uno de estos eventos debería ser mandado al correspondiente
Coordinador.
El patrón Controlador crea un objeto artificial que no procede del análisis del
dominio. Se dice que es una Fabricación Pura, detalle que se analizará más adelante. La
implementación del Controlador hace uso de los patrones GRASP de Fabricación Pura
y de Indirección.
Existe una única clase controlador que recibe todos los eventos del sistema.
Remedios:
Ejemplo 6.7
6.3.6 /*coordinador.cpp*/
Polimorfismo
Beneficios:
6.3.7 Indirección
Beneficios:
Ejemplo 6.9
Suelo y Esfera son clases conceptuales y por tanto candidatas a ser clases de
diseño. Para evitar el acoplamiento entre ambas clases se añade un grado de indirección.
Se creará una clase interacción que resuelva la responsabilidad de la interacción entre
las instancias de las dos clases.
Ejemplo 6.10
AlmacenamientoPersistente::AlmacenamientoPersistente(RespuestaFrecuencia *pRespFr,
const char * pNomFich)
{
ofstream os(pNomFich);
os <<"Modulo de la respuesta en frecuencia"<<endl;
float fr;
for (fr = pRespFr->getFrInicio();fr <= pRespFr->getFrFinal();
fr+=pRespFr->getFrIntervalo())
os << fr << " :" << pRespFr->getModulo(fr)<<endl;
}
Muchos de los patrones del DOO que se van a ver son ejemplos de Fabricación
Pura: Adaptador, Estrategia, Observador, etc. También lo es el Controlador (GRASP) o
Fachada (GoF).
Coordinador o fachada.
Ejemplo 6.11
#ifndef _AREAS_FIGURA_INC_
#define _AREAS_FIGURA_INC_
typedef enum tipoFig {CIRCULO, RECTANGULO} tipoFigura;
class IFiguras
{
public:
virtual double getArea() = 0;
static IFiguras* MetodoFabricacionFiguras
(tipoFigura, double, double);
};
class Circulo: public IFiguras
{
double radio;
friend class IFiguras;
Circulo(double param1):radio(param1) {}
public:
virtual double getArea()
{return (3.1416*radio*radio);}
};
#endif
}//////////////////////////////////////////////////////////////////////////
void CAreasFiguraDlg::OnCalcular()
{
UpdateData(TRUE);
IFiguras *pFigura= IFiguras::MetodoFabricacionFiguras(
this->m_Figura == true ? CIRCULO : RECTANGULO,
this->m_Param1,this->m_Param2);
this->m_Area = pFigura->getArea();
delete pFigura;
UpdateData(FALSE);
anteriores se pueden usar en las nuevas. El cliente las utiliza pero no sabe si éstas han
sido mejoradas. Más aun, los desarrolladores confían en estos servicios y crean
aplicaciones, de más alto nivel, basadas en ellas.
Ejemplo 6.12
El código entregado corresponde con la implementación del patrón
comando, de manera que encapsula un objeto y el cliente lo ve como si fuese
una función (muy utilizado en lenguajes script). Se pide:
1. Ingeniería inversa: Diagrama de clases.
2. Ingeniería inversa: Diagrama de secuencias de la función main().
3. Resultado de su ejecución en la consola.
4. Indicar los patrones GRASP empleados en este patrón.
5. Diseñar e implementar la clase Saludo, de manera que se
despida al añadirse al macro.
#include <iostream>
#include <vector>
using namespace std;
class Comando
{
public:
virtual void ejecutar() = 0;
};
class Macro
{
vector<Comando*> Comandos;
public:
void incluir(Comando* c) { Comandos.push_back(c); }
void realizar() {
for(int i=0;i<Comandos.size();i++)
Comandos[i]->ejecutar();
}
};
int main()
{
Macro macro;
macro.incluir(new Hola);
macro.incluir(new Mundo);
macro.incluir(new Patron);
macro.realizar();
}
1.
2.
5.
Por otro lado, uno de los patrones antiguos GRASP era “No hable con
Extraños” o Ley de Demeter. Este patrón incide en evitar crear diseños que recorran
largos caminos de la estructura de los objetos. No se podía enviar mensajes a objetos
distantes, indirectos o extraños. Tales diseño son frágiles con respecto a los cambios en
las estructuras de los objetos.
No hable con Extraños establecía que un método, sólo, debería enviar mensajes
a los siguientes objetos:
3. A un atributo de él.
Se reduce el acoplamiento.
Hay que saber escoger las batallas. En sistemas maduros, la estructura es más
estable y se puede hablar con extraños. En cambio, en sistemas nuevos es recomendable
utilizar este antiguo patrón GRASP 3. Si se emplea Variaciones Protegidas es posible no
utilizar el patrón No hable con Extraños.
3
Para los programadores noveles se aconseja utilizar este patrón. Empléese en el trabajo de curso.
6.4.1 Adaptador
Objetivo: define los servicios del dominio que usa el cliente. Representa
un interfaz estable con los servicios tal cual espera el cliente.
Cliente: utiliza los servicios del paquete a través del interfaz Objetivo.
Nótese que los nombres de los tipos incluyen el nombre de patrón “Adaptador”.
Ejemplo 6.13
4
http://www.codeproject.com/miscctrl/ntgraph_activex.asp
#include "../../ntgraph.h"
//Tipos de visualizadores
enum PlataformaVisual{NTGRAPH} ;
class IAdaptadorVisualizar
{
public:
virtual void InicializarPlotXY(void) = 0;
virtual void PintarPlotXY(float,float,float, double *)= 0;
//Factoria de Visualizadores
static IAdaptadorVisualizar *factoriaVisualizadores(enum PlataformaVisual,
CNTGraph *p1 = NULL);
};
Ejemplo 6.14
Fn Fn 1 Fn 2
1 1 2 3 5 8 13 21 ...
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
#ifndef FIBONACCIGENERATOR_H
#define FIBONACCIGENERATOR_H
class FibonacciGenerator {
int n;
int val[2];
public:
FibonacciGenerator() : n(0) { val[0] = val[1] = 1; }
int operator()() {
int result = n > 2 ? val[0] + val[1] : 1;
++n;
val[0] = val[1];
val[1] = result;
return result;
}
int count() { return n; }
};
Se trata de diseñar un adaptador que permita utilizar los algoritmos dados por las
STL como for_each( ) o accumulate(). Estos servicios requieren que la información esté
preparada como un tipo vector de las STL, std::vector<>. Para tal fin, se emplea el
patrón Adaptador:
5
"Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. (c) 1995-2004
MindView, Inc.
#include <vector>
#include "FibonacciGenerator.h"
class IAdaptadorFibonacci
{
public:
virtual std::vector<unsigned> & getSerie() = 0;
static IAdaptadorFibonacci *factoriaFibonacci(unsigned grado);
};
#endif
6.4.2 Factoría
Cuando aparece una nueva variación del tipo de los datos se aplica el patrón
Polimorfismo [GRASP], tal cual se comentó en el apartado 6.3.6. Al principio parece
que sólo es necesario implementarlo en el punto de la aplicación que se introduce la
variación. Sin embargo, mayoritariamente sucede que además se requiere un constructor
para la nueva variación y su repercusión se extiende por todo el código. Para estos casos
se debe aplicar Variaciones Protegidas. Por tanto, se considera la creación de un punto
de variación o punto caliente y se coloca una interfase estable a través del
Polimorfismo, la Indirección y el Adaptador. La construcción de estos objetos deben ser
forzados a ser creados en una única Factoría.
Este punto subraya otro principio de diseño fundamental: mantener siempre una
separación de intereses. La elección de un objeto del dominio para crear los adaptadores
no está de acuerdo con los objetivos de separación de intereses. Además disminuye su
cohesión. La lógica de qué clase Adaptador se instancia es resuelto en la Factoría,
leyendo una fuente externa y después cargando la clase dinámicamente.
Una alternativa típica en este caso es aplicar el patrón Factoría. Los objetos
Factoría tienen varias ventajas:
El cliente sólo utilizará las interfases de sus paquetes servidores, dejando que sea
la lógica externa quien decida sobre la implementación y la factoría quien crea los
objetos que implementan los servicios. A este patrón GoF se le llama Factoría Abstacta.
Se empleará cuando:
Producto Concreto: define un objeto producto para que sea creado por la
Factoría correspondiente. Implementa la interfaz de Producto Abstracto.
Ejemplo 6.15
};
class TuercaDIN84 : public ITuerca
{
metrica laMetrica;
friend class FactoriaTuercas;
TuercaDIN84() {laMetrica = DIN84;}
public:
virtual metrica getMetrica() {return laMetrica;}
};
class TuercaDIN316 : public ITuerca
{
metrica laMetrica;
friend class FactoriaTuercas;
TuercaDIN316() {laMetrica = DIN316;}
public:
virtual metrica getMetrica() {return laMetrica;}
};
class FactoriaTornillos;
class ITornillo
{
public:
virtual metrica getMetrica() = 0;
};
class TornilloDIN84 : public ITornillo
{
metrica laMetrica;
friend class FactoriaTornillos;
TornilloDIN84() {laMetrica = DIN84;}
public:
virtual metrica getMetrica() {return laMetrica;}
};
class TornilloDIN316 : public ITornillo
{
metrica laMetrica;
friend class FactoriaTornillos;
TornilloDIN316() {laMetrica = DIN316;}
public:
virtual metrica getMetrica() {return laMetrica;}
};
class IFactoria
{
public:
virtual ITornillo * fabricacionTornillo(metrica) = 0;
virtual ITuerca * fabricacionTuerca(metrica) = 0;
};
class FactoriaTornillos : public IFactoria
{
public:
virtual ITornillo * fabricacionTornillo(metrica laMetrica)
{
if(laMetrica == DIN84) return new TornilloDIN84;
else if (laMetrica == DIN316) return new TornilloDIN316;
else return 0;
}
virtual ITuerca * fabricacionTuerca(metrica laMetrica)
{return 0;}
};
class FactoriaTuercas: public IFactoria
{
public:
virtual ITornillo * fabricacionTornillo(metrica laMetrica)
{return 0;}
#include "IFactoria.h"
#include <iostream>
#include <stdlib.h>
int main()
{
IFactoria *pFactoriaTornillos = new FactoriaTornillos;
IFactoria *pFactoriaTuercas = new FactoriaTuercas;
std::cout<<"Simulacion de sacar tornillo y tuerca de forma aleatoria" <<std::endl;
std::cout<<"La bolsa contiene tornillos y tuercas DIN84 y DIN316"<<std::endl;
std::cout<<"Pulsar c o C para sacar tornillo y tuerca"<<std::endl;
char opcion; std::cin>> opcion;
while(opcion == 'c' || opcion == 'C') {
ITornillo *pTornillo =
pFactoriaTornillos->fabricacionTornillo(rand() % 2 == 1 ? DIN84 : DIN316);
ITuerca *pTuerca =
pFactoriaTuercas->fabricacionTuerca(rand() % 2 == 1 ? DIN84 : DIN316);
if(pTornillo->getMetrica() == pTuerca->getMetrica()){
char *mensaje = pTuerca->getMetrica() == DIN84 ? "DIN84" : "DIN316";
std::cout<<"Ensamblaje correcto: metrica " << mensaje <<std::endl;
}else
std::cout<<"Ensamblaje incorrecto" << std::endl;
una clase quiere que sean sus subclases quienes especifiquen los objetos que ésta
crea.
Hay que tener mucho cuidado con la creación con las Factorías. En C++ es el
programador el que debe posteriormente liberar, con posterioridad, el objeto creado en
la factoría.
Ejemplo 6.16
Primera solución
};
#endif
190 Dpto. Electrónica, Automática e Informática Industrial
Apuntes de Informática Industrial Carlos Platero
pNombre1->setNombre("Manolo Gonzalez");
pNombre2->setNombre("Pedro Lopez");
pNombre3->setNombre("Ana Rodriguez");
Segunda solución:
#endif
#include <iostream>
#include "../includes/STDNombre.h"
#include "../includes/CNombre.h"
#include "../includes/MFCNombre.h"
//Método único para producir los objetos nombres
INombre* INombre::factoriaObjetos(tipoTiraCaracteres tipo)
{
if(tipo == ESTANDAR_STL) return new STDNombre;
else if(tipo == ESTILO_C) return new CNombre;
else if(tipo == CADENA_MFC) return new MFCNombre;
else return NULL;
}
pNombre1->setNombre("Manolo Gonzalez");
pNombre2->setNombre("Pedro Lopez");
pNombre3->setNombre("Ana Rodriguez");
6.4.3 Singleton
Problema: ¿Cómo garantizar que una clase sólo tenga una instancia y
proporciona un punto de acceso global a ella?.
Del uso del patrón Factoría surge un nuevo problema de diseño ¿quién crea a la
Factoría?. Sólo se necesita una única instancia de Factoría y ésta puede ser llamada
desde distintos lugares del código. Una solución sería ir pasando la instancia de Factoría
en los métodos, pero este proceder no es conveniente, ya que implica acoplamiento por
necesitar visibilidad. La solución está en el patrón Singleton.
¿Cómo se puede asegurar que una clase tenga una única instancia y que sea
fácilmente accesible?. Una variable global hace accesible a un objeto, pero no se
previene de crear múltiples instancias de esta clase.
La clave del Singleton es evitar que el cliente no tenga el control sobre la vida
del objeto. Para tal fin se declaran todos los constructores como privados y se previene
para que el compilador no produzca constructores por defecto ni sobrecarga de
operadores de asignación. Se construye el servicio de tipo estático para obtener la
referencia Singleton, getInstancia(), y también se colocará como estática la propia
referencia. En la notación se puede emplear el estereotipo <<1>> para indicar que sólo
existe una única instancia de esta clase
#include <iostream>
using namespace std;
class Singleton {
int i; //Dato por ejemplo
Singleton(int x) : i(x) { }
void operator=(Singleton&); // Para desactivar
Singleton(const Singleton&); // Para desactivar
public:
static Singleton& getInstancia() {
static Singleton unicaInstancia(47); //P.ej valor 47
return unicaInstancia;
}
int getValor() { return i; }
void setValor(int x) { i = x; }
};
int main() {
Singleton& s = Singleton::getInstancia();
cout << s.getValor() << endl;
Singleton& s2 = Singleton::getInstancia();
s2.setValor(9);
cout << s.getValor() << endl;
return 0;
}
Ejemplo 6.17
#include "STDNombre.h"
#include "CNombre.h"
#include "MFCNombre.h"
class FactoriaUnicaNombres
{
FactoriaUnicaNombres(){};
void operator=(FactoriaUnicaNombres&); // Para desactivar
FactoriaUnicaNombres(const FactoriaUnicaNombres&); // Para desactivar
public:
static FactoriaUnicaNombres& getInstancia()
{
static FactoriaUnicaNombres unicaInstancia;
return unicaInstancia;
}
#endif
pNombre1->setNombre("Manolo Gonzalez");
pNombre2->setNombre("Pedro Lopez");
pNombre3->setNombre("Ana Blanco");
cout << pNombre3->getNombre() << endl;
delete pNombre3;
}
6.4.4 Estrategia
Este patrón define una familia de algoritmos relacionados, los encapsula y los
hace intercambiables. La consecuencia de esta estructura es la variación dinámica de los
algoritmos sin que los clientes se vean afectados. Los roles de este patrón son:
Contexto: este objeto usa la interfaz Estrategia para llamar al algoritmo concreto
definido por una Estrategia Concreta. Tiene como atributo a un objeto de
Estrategia Concreta, a través de su interfaz.
Ejemplo 6.18
Siguiendo el Proceso
Unificado, diseñe una
aplicación que sea un
conversor de monedas. En
una primera versión inicial,
considere sólo euros, dólares y libras esterlinas. A la aplicación se le facilitará
los valores de conversión entre las monedas y la cantidad de una moneda
concreta a convertir en el resto de monedas.
void CConversorMonedasDlg::OnCalcular()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
ConversorMoneda elConversor(this->m_factorDolar,this->m_factorLibra,
this->m_elTipo,this->m_Cantidad);
this->m_Euros = elConversor.getEuros();
this->m_Dolares = elConversor.getDolar();
this->m_Libras = elConversor.getLibras();
UpdateData(FALSE);
Solución
#endif
6.4.5 Observador
Observable
o un Observable puede ser observado por cualquier número de objetos
Observador.
o proporciona una interfaz para asignar y quitar objetos Observador.
Observador
o define una interfaz para actualizar los objetos que deben ser notificados
ante cambios en un sujeto observable.
ObservableConcreto
o almacena el estado de interés para los objetos ObservadorConcreto.
o envía una notificación a sus observadores cuando cambia su estado.
ObservadorConcreto
o mantiene una referencia a un objeto ObservableConcreto.
o guarda un estado que debería ser consistente con el sujeto observable.
o Implementa la interfaz de actualización del Observador para mantener su
estado consistente con el sujeto observable.
Así, el ObservableConcreto notifica a sus observadores cada vez que se produce
un cambio en su estado. Después de ser informado de un cambio en el
ObservableConcreto, un objeto ObservadorConcreto puede pedirle al observable más
información. ObservadorConcreto usa esta información para sincronizar su estado con
el del observable.
están bien definidos o mantenidos suelen provocar falsas actualizaciones que pueden ser
muy difíciles de localizar.
6.4.5.1 Implementación
Dos tipos de objetos se utilizan para poner el patrón del observador. La clase
Observable que se encarga de que el estado del Observable concreto sea escuchado por
sus observadores y la clase Observer que está a la escucha. Primero, se presenta
Observer:
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
//
//: C10:Observer.h
// The Observer interface
#ifndef OBSERVER_H
#define OBSERVER_H
class Observable;
class Argument {};
class Observer {
public:
// Called by the observed object, whenever
// the observed object is changed:
virtual void update(Observable* o, Argument* arg) = 0;
virtual ~Observer() {}
};
#endif // OBSERVER_H ///:~
class Observable {
bool changed;
std::set<Observer*> observers;
protected:
virtual void setChanged() { changed = true; }
virtual void clearChanged() { changed = false; }
public:
virtual void addObserver(Observer& o) {
observers.insert(&o);
}
virtual void deleteObserver(Observer& o) {
observers.erase(&o);
}
virtual void deleteObservers() {
observers.clear();
}
virtual int countObservers() {
return observers.size();
}
Tanto las clases que están a la escucha como las observables requieren interfaces
que las permitan mantener la propiedad de bajo acoplamiento. Para este fin se requiere
de algo que tiene Java y no C++: Las clases internas. Se trata de jerarquizar las clases de
forma que se puedan tener acceso a los datos de la clase que las contiene, i.e. una
instancia de la clase interna tenga acceso a datos de la clase que la ha creado, siendo la
clase interna una subclase de la interfaz deseada. Se presenta un ejemplo del idioma
interno. Véase cómo la clase Outer puede ser vista a través de dos interfaces distintos
Bingable y Poingable:
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
//
//: C10:InnerClassIdiom.cpp
// Example of the "inner class" idiom.
#include <iostream>
#include <string>
using namespace std;
class Poingable {
public:
virtual void poing() = 0;
};
class Bingable {
public:
virtual void bing() = 0;
};
class Outer {
string name;
// Define one inner class:
class Inner1;
friend class Outer::Inner1;
class Inner1 : public Poingable {
Outer* parent;
public:
Inner1(Outer* p) : parent(p) {}
void poing() {
cout << "poing called for "
<< parent->name << endl;
// Accesses data in the outer class object
}
} inner1;
// Define a second inner class:
class Inner2;
friend class Outer::Inner2;
class Inner2 : public Bingable {
Outer* parent;
public:
Inner2(Outer* p) : parent(p) {}
void bing() {
cout << "bing called for "
<< parent->name << endl;
}
} inner2;
public:
Outer(const string& nm)
: name(nm), inner1(this), inner2(this) {}
// Return reference to interfaces
// implemented by the inner classes:
operator Poingable&() { return inner1; }
operator Bingable&() { return inner2; }
};
void callPoing(Poingable& p) {
p.poing();
}
void callBing(Bingable& b) {
b.bing();
}
int main() {
Outer x("Ping Pong");
// Like upcasting to multiple base types!:
callPoing(x);
callBing(x);
} ///:~
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
//
//: C10:ObservedFlower.cpp
// Demonstration of "observer" pattern.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include "Observable.h"
using namespace std;
class Flower {
bool isOpen;
public:
Flower() : isOpen(false),
openNotifier(this), closeNotifier(this) {}
void open() { // Opens its petals
isOpen = true;
openNotifier.notifyObservers();
closeNotifier.open();
}
void close() { // Closes its petals
isOpen = false;
closeNotifier.notifyObservers();
openNotifier.close();
}
// Using the "inner class" idiom:
class OpenNotifier;
friend class Flower::OpenNotifier;
class OpenNotifier : public Observable {
Flower* parent;
bool alreadyOpen;
public:
OpenNotifier(Flower* f) : parent(f),
alreadyOpen(false) {}
void notifyObservers(Argument* arg = 0) {
if(parent->isOpen && !alreadyOpen) {
setChanged();
Observable::notifyObservers();
alreadyOpen = true;
}
}
void close() { alreadyOpen = false; }
} openNotifier;
class CloseNotifier;
friend class Flower::CloseNotifier;
class CloseNotifier : public Observable {
Flower* parent;
bool alreadyClosed;
public:
CloseNotifier(Flower* f) : parent(f),
alreadyClosed(false) {}
void notifyObservers(Argument* arg = 0) {
if(!parent->isOpen && !alreadyClosed) {
setChanged();
Observable::notifyObservers();
alreadyClosed = true;
}
}
void open() { alreadyClosed = false; }
} closeNotifier;
};
class Bee {
string name;
// An "inner class" for observing openings:
class OpenObserver;
friend class Bee::OpenObserver;
class OpenObserver : public Observer {
Bee* parent;
public:
OpenObserver(Bee* b) : parent(b) {}
void update(Observable*, Argument *) {
cout << "Bee " << parent->name
<< "'s breakfast time!” << endl;
}
} openObsrv;
// Another "inner class" for closings:
class CloseObserver;
friend class Bee::CloseObserver;
class CloseObserver : public Observer {
Bee* parent;
public:
CloseObserver(Bee* b) : parent(b) {}
void update(Observable*, Argument *) {
cout << "Bee " << parent->name
<< "'s bed time!” << endl;
}
} closeObsrv;
public:
Bee(string nm) : name(nm),
openObsrv(this), closeObsrv(this) {}
Observer& openObserver() { return openObsrv; }
Observer& closeObserver() { return closeObsrv;}
};
class Hummingbird {
string name;
class OpenObserver;
friend class Hummingbird::OpenObserver;
class OpenObserver : public Observer {
Hummingbird* parent;
public:
OpenObserver(Hummingbird* h) : parent(h) {}
void update(Observable*, Argument *) {
cout << "Hummingbird " << parent->name
<< "'s breakfast time!” << endl;
}
} openObsrv;
class CloseObserver;
friend class Hummingbird::CloseObserver;
class CloseObserver : public Observer {
Hummingbird* parent;
public:
CloseObserver(Hummingbird* h) : parent(h) {}
void update(Observable*, Argument *) {
cout << "Hummingbird " << parent->name
<< "'s bed time!” << endl;
}
} closeObsrv;
public:
Hummingbird(string nm) : name(nm),
openObsrv(this), closeObsrv(this) {}
Observer& openObserver() { return openObsrv; }
Observer& closeObserver() { return closeObsrv;}
};
int main() {
Flower f;
Bee ba("A"), bb("B");
Hummingbird ha("A"), hb("B");
f.openNotifier.addObserver(ha.openObserver());
f.openNotifier.addObserver(hb.openObserver());
f.openNotifier.addObserver(ba.openObserver());
f.openNotifier.addObserver(bb.openObserver());
f.closeNotifier.addObserver(ha.closeObserver());
f.closeNotifier.addObserver(hb.closeObserver());
f.closeNotifier.addObserver(ba.closeObserver());
f.closeNotifier.addObserver(bb.closeObserver());
// Hummingbird B decides to sleep in:
f.openNotifier.deleteObserver(hb.openObserver());
// Something changes that interests observers:
f.open();
f.open(); // It's already open, no change.
// Bee A doesn't want to go to bed:
f.closeNotifier.deleteObserver(
ba.closeObserver());
f.close();
f.close(); // It's already closed; no change
f.openNotifier.deleteObservers();
f.open();
f.close();
} ///:~
6.5 Ejercicios
Problema 1
0.0895z 0.085
M z
z 1.883z 0.888
2
>>step(g1)
: Sistema
Ingeniero de Control
: <Actor Name>
Se le pasará los
coeficientes de
la FDT del
introducirModelo() sistema
especificarExcitacion()
Se le dirá cuál es
mostrarSalida() el tipo de señal,
el tiempo final y
el intervalo...
La aplicación dará
los resultados en un
gráfico del tipo y=y(t)
VistaSimulador
DominioSimulador
Patrón Alta
GraficoPlot Cohesion, Bajo
Acoplamiento,
Capa
Patrón
Fachada
: Vista : CoordinadorSimulador
Patrón
Creador
introducirModelo( )
especificarExcitacion( )
create()
: Senyal_Entrada
create()
: Simulador_LTI
Algoritmia para
calcularSalida( )
calcular la señal
de salida getCoefNum( )
getCoefDen( )
getValorInstantek( )
mostrarSalida( )
getSenyaSalida( )
Problema 2
x2 cos sin x1
y2 sin cos y1
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
class Punto {
public:
double x, y;
Punto(double xi, double yi) : x(xi), y(yi) {}
Punto(const Punto& p) : x(p.x), y(p.y) {}
Punto& operator=(const Punto& rhs) {
x = rhs.x;
y = rhs.y;
return *this;
}
friend ostream&
operator<<(ostream& os, const Punto& p) {
return os << "x=" << p.x << " y=" << p.y;
}
};
class Vector {
public:
double magnitud, direccion;
Vector(double m, double d) : magnitud(m), direccion(d) {}
};
class Espacio {
public:
static Punto trasladar(Punto p, Vector v) {
p.x += (v.magnitud * cos(v.direccion));
p.y += (v.magnitud * sin(v.direccion));
return p;
}
};
int main() {
Punto p1(1, 2);
Punto p2 = Espacio::trasladar(p1, Vector(3, 3.1416/3));
cout << "p1: " << p1 << " p2: " << p2 << endl;
return 0;
}
e)
class Espacio {
public:
static Punto trasladar(Punto p, Vector v) {
p.x += (v.magnitud * cos(v.direccion));
p.y += (v.magnitud * sin(v.direccion));
return p;
}
static Punto rotar(Punto p, double theta) {
Punto res(0,0);
res.x = (p.x * cos(theta)) - (p.y *sin(theta));
res.y = (p.x * sin(theta)) + (p.y *cos(theta));
return res;
}
};
int main() {
Punto p1(1, 2);
Punto p2 = Espacio::trasladar(p1, Vector(3, 3.1416/3));
Punto p3 = Espacio::rotar(p2,3.1416/6);
cout << "p1: " << p1 << " p2: " << p2 << " p3: " << p3 <<endl;
return 0;
}
Problema 3
En el cuadro se
entrega el código sobre un
generador de números
primos. Se trata de diseñar
un componente tal que el
cliente le entregue el
número límite de números
primos, n, y el servidor
retorne con un vector que
contenga los n primeros
números primos. En la
figura se presenta el
resultado del cliente. Se
#ifndef NUMEROSPRIMOS_H
#define NUMEROSPRIMOS_H
class NumerosPrimos {
unsigned elUltimoPrimo;
unsigned elNumero;
public:
NumerosPrimos() : elUltimoPrimo(1) { elNumero = elUltimoPrimo+1;}
unsigned getSiguientePrimo()
{
do{
for(unsigned divisor = 2;elNumero % divisor != 0; divisor++) ;
if (divisor == elNumero)
elUltimoPrimo = elNumero;
else
elNumero++;
} while ( elUltimoPrimo != elNumero );
elNumero++;
return (elUltimoPrimo);
}
};
#endif
pide:
1. Realizar ingeniería inversa sobre el generador de números primos.
2. Obtener el diagrama de clase de diseño, DCD, así como el diagrama de
secuencia del componente servidor.
3. Implementación del cliente en C++.
4. Implementación de las clases en C++ del componente servidor.
#include <iostream>
#include "AdaptadorNumerosPrimos.h"
#include <numeric>
#include <algorithm>
}
using namespace std;
void imprimir(unsigned);
int main()
{
unsigned elLimiteNumerosPrimos;
cout<<"Cuantos numeros primos desea que aparezcan : ";
cin >> elLimiteNumerosPrimos;
IAdaptadorNumerosPrimos *pAdaptadorNumPrimos =
IAdaptadorNumerosPrimos::metodoFabricacion(elLimiteNumerosPrimos);
for_each(pAdaptadorNumPrimos->getNumerosPrimos().begin(),
pAdaptadorNumPrimos->getNumerosPrimos().end(),imprimir);
delete pAdaptadorNumPrimos;
return 0;
}
#ifndef _ADAPTADOR_NUMEROSPRIMOS
#define _ADAPTADOR_NUMEROSPRIMOS
#include <vector>
#include "NumerosPrimos.h"
class IAdaptadorNumerosPrimos
{
public:
virtual std::vector<unsigned> & getNumerosPrimos() = 0;
static IAdaptadorNumerosPrimos *metodoFabricacion(unsigned);
};
#endif
Problema 4
Se desea hacer una aplicación que sirva para calcular las nominas de
una compañía. Al salario base de cada empleado hay que quitarle un 20% de
retención del IRPF para calcular su salario neto. Como existen diferentes
políticas salariales en la empresa, se desea hacer un programa fácilmente
extensible a nuevas políticas. De momento se pretende abordar dos de ellas: a)
el sueldo ordinario b) el sueldo con bonus, consistente en aumentar el salario
base (antes de la retención) un 35%. De momento se ha desarrollado el
siguiente programa:
main()
{
Nomina* nomina;
cout<<"1. Nomina ordinaria"<<endl;
cout<<"2. Nomina con bonus"<<endl;
cin>>opcion;
Se pide:
Problema 5
#ifndef _DINERO_INC_
#define _DINERO_INC_
class Dinero
{
TipoDinero elTipoMoneda;
float cantidad;
public:
Dinero(): elTipoMoneda(EURO), cantidad(0) {}
Dinero(float valor, TipoDinero elTipo): elTipoMoneda(elTipo),cantidad(valor) {}
Dinero(Dinero &elValor)
{
elTipoMoneda = elValor.elTipoMoneda;
cantidad = elValor.cantidad;
}
#endif
#ifndef _VENTA_LOCALIDADES_INC_
#define _VENTA_LOCALIDADES_INC_
#include "Fecha.h"
#include "Dinero.h"
#include "OfertasTaquillas.h"
class VentaLocalidades
{
Fecha elDiaSemana;
Dinero elPrecioUnitario;
float elDescuento;
IPrecioLocalidades *pPrecioLocalidades;
public:
VentaLocalidades(Dinero elPrecio, float descuento) :
elPrecioUnitario(elPrecio),elDescuento(descuento) {}
Dinero getPrecio(int numeroLocalidades)
{
pPrecioLocalidades =
IPrecioLocalidades::metodoFabricacionPrecios(elDiaSemana,elDescuento);
Dinero elDinero =
pPrecioLocalidades->getPrecioTotal(numeroLocalidades,elPrecioUnitario) ;
delete pPrecioLocalidades;
return(elDinero);
}
};
#endif
#ifndef _OFERTAS_TAQUILLA_INC_
#define _OFERTAS_TAQUILLA_INC_
#include "Dinero.h"
#include "Fecha.h"
class IPrecioLocalidades
{
public:
virtual Dinero getPrecioTotal(int numeroLocalidades, Dinero elPrecioUnitario) = 0;
static IPrecioLocalidades *metodoFabricacionPrecios(Fecha &, float);
};
#endif
#include "includes/VentaLocalidades.h"
IPrecioLocalidades * IPrecioLocalidades::metodoFabricacionPrecios
(Fecha &hoy, float descuento = 0)
{
if ((hoy.isMiercoles() == true ) && (descuento > 100/3) )
return new DiaDelEspectador(descuento);
else return new DosXTres;
}
Problema 6
Convierte de
un tipo de
longitud a otro Longitudes
(from ConversorLongitudes)
es un tipo de medida de
ConversorLongitud
(from ConversorLongitudes)
Pies
es un tipo de medida de (from ConversorLongitudes)
es un tipo de medida de
es un tipo de medida de
Pulgadas
(from ConversorLongitudes)
Metros
(from ConversorLongitudes)
Millas
(from ConversorLongitudes)
void CConversorLongitudesDlg::OnCalcular()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
ConversorLongitud elConversor(this->m_tipoLongitud,this->m_longitud);
this->m_Metros = elConversor.getMetros();
this->m_Millas = elConversor.getMillas();
this->m_Pulgadas = elConversor.getPulgadas();
this->m_Pies = elConversor.getPies();
UpdateData(FALSE);
}
class ConversorLongitud;
class ILongitudes
{
public:
virtual float getMetros() = 0;
virtual float getMillas() = 0;
virtual float getPulgadas() = 0;
virtual float getPies() = 0;
static ILongitudes * metodoFabricacion(tipoLongitudes,float,
ConversorLongitud *);
};
#endif
--------------------------------------------------------------------------------------------------------------------------------------------------------
#if !defined(AFX_CONVERSORLONGUITUDES_H)
#define AFX_CONVERSORLONGUITUDES_H
#include "Longitudes.h"
class ConversorLongitud
{
ILongitudes *plongitudes;
float millaMetro;
float pulgadaMetro;
float pieMetro;
public:
ConversorLongitud(tipoLongitudes,float);
virtual ~ConversorLongitud();
};
#endif
#if !defined(AFX_LONGUITUDES_SIS_H)
#define AFX_LONGUITUDES_SIS_H
#include "ConversorLongitud.h"
};
#endif
Problema 7
Cálculo de una integral sobre una función monovariable (en este caso,
sólo polinómica), pudiendo elegir la estrategia de integración: a) Lineal o b)
Bilineal.
a) Caso de uso
b) Modelo del dominio
c) DSS
d) Vista de gestión
e) Diagramas de interacción
f) DCD
g) Implementación
Calculo de Integrales
(from <Use Case Name>)
<Actor Name>
<<include>>
(f rom Actors)
definir la funcion
: Sistema
Usuari o Matematico :
<Actor Name>
definirLaFuncion()
calcularLaIntegral()
retornar_valor_integral()
VistaIntegral DominioIntegral
Parámetros
de la funcion
calcularLaIntegral( )
create()
:
ContextoIntegral
Pasar valores de
los intervalos, de
la diferencial y
de la estrate...
Se le pasa la función
y el tipo de
estrategia en el
cálculo de la integral
#endif
#ifndef _IFUNCION_MONO_INC_
#define _IFUNCION_MONO_INC_
#include <vector>
#include <math.h>
typedef enum tipoFun{POLINOMIO,TRIGONOMETRICA} TipoFuncion;
class IFuncionMonoVar
{
public:
virtual double calcularValor(float) = 0;
virtual void setValoresFuncion( unsigned , double *) = 0;
};
class Polinomio : public IFuncionMonoVar
{
std::vector<double> coeficientes;
std::vector<double>::iterator iteratorCoeficientes;
double getCoeficiente(unsigned n)
{return(*(iteratorCoeficientes+n));}
public:
Polinomio(){}
Polinomio(unsigned grado, double *pCoef){
for (unsigned i=0;i<=grado;coeficientes.push_back(*(pCoef+i)),i++);
iteratorCoeficientes = coeficientes.begin();
}
virtual double calcularValor(float valorInd){
double resultado = 0;
for(int i=0;i<= this->coeficientes.size() ;i++)
resultado += getCoeficiente(i)*pow(valorInd,i);
return resultado;
}
virtual void setValoresFuncion( unsigned grado, double *pCoef){
for (unsigned i=0;i<=grado;coeficientes.push_back(*(pCoef+i)),i++);
iteratorCoeficientes = coeficientes.begin();
}
};
#endif
#ifndef _FACTORIA_INC_
#define _FACTORIA_INC_
#include "../Funcion/FuncionMonoVar.h"
#include "../Integrador/Integrador.h"
class FactoriaServicios
{
FactoriaServicios(){};
void operator=(FactoriaServicios&); // Para desactivar
FactoriaServicios(const FactoriaServicios&); // Para desactivar
public:
static FactoriaServicios& getInstancia(){
static FactoriaServicios unicaInstancia;
return unicaInstancia;
}
//Factoria de funciones matematicas
IFuncionMonoVar* FactoriaServicios::
MetodoFabricacionFuncion(TipoFuncion elTipoFuncion){
if (elTipoFuncion == POLINOMIO) return new Polinomio();
else return NULL;
}
//Factoria de estrategias
IIntegrador* FactoriaServicios::
MetodoFabricacionEstrategia(TipoEstrategia elTipoEstr){
if (elTipoEstr == LINEAL) return new IntegradorLineal;
else if (elTipoEstr == BILINEAL) return new IntegradorBilineal;
else return NULL;
}
};
#endif
ContextoIntegrador elIntegrador(pEstrategia);
double resultado = elIntegrador.calcularIntegral( pFuncionMono,
intInf, intSup, diferencial );
delete pEstrategia;
return(resultado);
Problema 8
Luchador Arma
ParedMovil
1
mata detiene 1
1 detiene
Dragón
lanza 1
1..n
1 Cuchillo
0..n BolaFuego 1 1
lucha contra
mata 1 0..n
1..n
lanza
1
Hombre
1
#endif
class EpsonPrinterDriver
{
public:
bool Print(char filename[]);
};
class HPControladorImpresora
{
public:
//este metodo devuelve 1 en caso de exito y -1 en caso de error
int ImprimeFichero(char* nombre_fichero);
};
int main()
{
char fichero[255],nombre_impresora[255];
Impresora* impresora=NULL;
if(impresora==NULL)
{
cout<<"Impresora no existe"<<endl;
return -1;
}
if(impresora->Imprime(fichero))
cout<<"Impresion correcta"<<endl;
else
cout<<"Impresion fallida"<<endl;
return 1;
}
Problema 10
typedef
enum{GANCHO,GANCHO_ESPECIAL,LANZA} #include "Disparo.h"
tipoDisparo;
class Lanza : public Disparo
class Disparo {
{ public:
public: void Dibuja();
Disparo(); Lanza();
virtual ~Disparo(); virtual ~Lanza();
protected: };
tipoDisparo tipo;
}; #endif
#endif
protected: };
float radio;
#endif
};
#endif
typedef
enum{GANCHO,GANCHO_ESPECIAL,LANZA} #include "Disparo.h"
tipoDisparo;
class Lanza : public Disparo
class Disparo {
{ public:
public: void Dibuja();
virtual ~Disparo(); virtual ~Lanza();
protected: protected:
float radio; GanchoEspecial();
Gancho(); friend class FactoriaObjetos;
friend class FactoriaObjetos;
};
};
#endif
#endif
#if !defined(_FACTORIA_INC_)
#define _FACTORIA_INC_
#include "GanchoEspecial.h"
#include "Lanza.h"
class FactoriaObjetos
{
FactoriaObjetos() {}
public:
static FactoriaObjetos &getInstancia() {
static FactoriaObjetos laFactoria;
return (laFactoria);
}
Disparo * metodoFabricacionDisparo(tipoDisparo tipo) {
if(tipo == GANCHO) return new Gancho;
else if(tipo == GANCHO_ESPECIAL) return new
GanchoEspecial;
else if(tipo == LANZA) return new Lanza;
else return 0;
}
};
#endif