Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Metod UML
Metod UML
En las etapas de captura de los requisitos y del análisis orientado a objetos se han
centrado en aprender a realizar la definición del proyecto sin decir cómo. En esta otra
etapa se pondrá el énfasis en implantar las especificaciones con eficiencia y fiabilidad.
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 conceptuales1 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 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 coactadas a 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, 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
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
RespuestaFrecuencia
modulos : std::vector<double>
argumentos : std::vector<double> FDT
frInicio : float grado : int
frFinal : float esta formado por
frIntervalo : float
esta definido por1
2 Polinomio
necesita conocer coeficientes : std::vector<double>
FiltroLineal
tipoFiltro : int
Patrón
Fachada
create()
: FiltroLineal
create()
: FDT
introducirParametrosRespFr()
ponerParamResFr()
create()
: RespuestaFrecuencia
getModuloRespFr( )
getModuloRespFr()
visualizarBode()
CoordinadorRespFr
RespuestaFrecuencia
ponerCircuito() frecuenciaInicio : float
ponerParamResFr() frecuenciaFin : float
getModuloRespFr() intervaloFrec : float
modulo : vector<double>
argumento : vector<double>
Patrón Experto
getModuloRespFr() (GRASP)
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*/
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;}
};
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.
RespuestaFrecuencia
calcularModulo() vector<double>
VistaFrecuenciaELAI
calcularArgumento()
RespuestaFrecuencia() 1
introducirCircuito() getFrInicio()
introducirParametrosRespFr() getFrFinal()
getFrIntervalo() <<typedef>> 1
getModulo() tipoFiltro Polinomio
1
getModuloRespFr()
Polinomio()
Polinomio()
CoordinadorFrecELAI 1 getCoeficiente()
FDT 2
CoordinadorFrecELAI() FiltroLineal
ponerCircuito() 1
ponerParamResFr() FDT()
FiltroLineal()
getModuloRespFr() 1 FDT()
1 getGradoFiltro()
~CoordinadorFrecELAI() 1 1 getGrado()
getCoefNum()
getCoefNum()
getCoefDen()
getCoefDen()
• 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, en el problema 3 del capítulo anterior, cuando había que mandar a dibujar la
urbanización, esta tarea era dividida por cada casa y cada casa por su tejado y bloque.
Para asignar la responsabilidad se emplearán las clases del DCD. En una primera
iteración, se utilizará el Modelo del Dominio, en versiones posteriores se consultaran
los DCD generados en las iteraciones anteriores. La idea es ir ampliando o actualizando
las nuevas clases del diseño.
Ejemplo 6.3
RespuestaFrecuencia
frecuenciaInicio : float
frecuenciaFin : float
intervaloFrec : float
modulo : vector<double>
argumento : vector<double>
Patrón Experto
calcularBode() (GRASP)
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
: CoordinadorRespFr
Patrón
creador
ponerCircuito()
create()
: FiltroLineal
create()
: FDT
ponerParamResFr()
create()
: RespuestaFrecuencia
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
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.
Otros 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
MFC (.NET)
ActiveX-Bode VistaFrecuencia
ELAI
DominioFrecunc
iaEla STL-ANSI C++
Beneficios:
Ejemplo 6.6
Polinomio
FDT 1 2
(f rom Analy sis Model)
CoordinadorSimulador coeficientes
<<create>> setCoeficientesFDT()
introducirModelo()
especificarExcitacion()
mostrarSalida()
notificarResultados() Simulador_LTI
(f rom Analy sis Model)
tiempoFinal
intervaloTiempo
valoresSalida
SenyalExcitacion calcularSalida()
tipoSenyal <<create>> setSimulador(laFDT : FDT, laSenyalExc : SenyalEscalon)
<<create>> setSenyalExcitacion()
Una clase con alto acoplamiento confía en muchas otras clases. Tales clases
podrían no ser deseables; adolecen de los siguientes problemas:
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 han 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 Acomplamiento.
6.3.5 Controlador
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
: VistaFrecuenciaELAI
: FiltroLineal
create()
2: FiltroLineal(tipoFiltro, float, float)
create()
: RespuestaFrecuencia
RespuestaFrecuencia
calcularModulo() vector<double>
VistaFrecuenciaELAI
calcularArgumento()
RespuestaFrecuencia() 1
introducirCircuito() getFrInicio()
introducirParametrosRespFr() getFrFinal()
getFrIntervalo() <<typedef>> 1
getModulo() tipoFiltro Polinomio
1
getModuloRespFr()
Polinomio()
Polinomio()
CoordinadorFrecELAI 1 getCoeficiente()
FDT 2
CoordinadorFrecELAI() FiltroLineal
ponerCircuito() 1
ponerParamResFr() FDT()
FiltroLineal()
getModuloRespFr() 1 FDT()
1 getGradoFiltro()
~CoordinadorFrecELAI() 1 1 getGrado()
getCoefNum()
getCoefNum()
getCoefDen()
getCoefDen()
6.3.6 Polimorfismo
Beneficios:
Ejemplo 6.8
Casa
Casa()
<<virtual>> ~Casa()
setPosicion()
<<virtual>> dibuja()
CasaConGaraje
CasaConGaraje()
dibuja()
6.3.7 Indirección
Beneficios:
Ejemplo 6.9
Suelo y Esfera son clases conceptuales y por tanto candidatas a 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.
Interaccion
interaccionEsferaSuelo()
Esfera Suelo
Muchos de los patrones 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.10
AlmacenamientoPersistente
AlmacenamientoPersistente()
<<local>> <<parameter>>
CoordinadorFrecELAI
RespuestaFrecuencia
ponerCircuito()
ponerCircuito() getModulo()
ponerParamResFr() calcularModulo()
guardarResultadosRespFr() calcularArgumento()
ponerParamResFr() RespuestaFrecuencia()
~CoordinadorFrecELAI() getFrInicio()
getFrFinal()
getFrIntervalo()
: AlmacenamientoPersistente
: VistaFrecuenciaELAI 3: getModulo(float)
1: guardarResultadosRespFr(const char*)
: RespuestaFrecuencia
create()
: CoordinadorFrecELAI
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;
}
los puntos calientes y a éstos se les cubre con una interfaz estable, permitiendo variar el
componente sin interferir en las aplicaciones clientes.
Ejemplo 6.11
<<interface>>
IFiguras
<<abstract>> getArea()
<<typedef>>
<<static>> MetodoFabricacionFiguras()
tipoFigura
Rectangulo
Circulo lado1 : double
radio : double lado2 : double
Circulo() Rectangulo()
<<virtual>> getArea() <<virtual>> getArea()
#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);
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.
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) <<interface>>
vector<Comando*> Comando
<<abstract>> ejecutar() : void
2) Macro
incluir()
realizar()
realizar( )
ejecutar( )
ejecutar( )
Macro
Saludo
incluir()
realizar() ejecutar()
Hola
Patron
ejecutar() Mundo
ejecutar()
ejecutar()
Por otro lado, uno de los patrones antiguos GRASP era “No hable con
Extraños” o Ley de Demeter. Significa 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. Pero ahora, con el principio de Variaciones Protegidas, se
sustituye a No hable con Extraños.
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 GRASP3.
3
Para los programadores noveles se aconseja utilizar este patrón. Empléese en el trabajo de curso.
Ejemplo 6.13
MFC (.NET)
ActiveX-Bode VistaFrecuencia
ELAI
DominioFrecunc
iaEla STL-ANSI C++
4
http://www.codeproject.com/miscctrl/ntgraph_activex.asp
<<interface>>
IAdaptadorVisualizar Interfaz
CRespFrMFCDlg estable
<<abstract>> InicializarPlotXY()
<<abstract>> PintarPlotXY()
<<static>> factoriaVisualizadores()
Solución
Cliente del tecnológica
paquete
Visualizador AdaptadorVisualCNTGraph
CNTGraph
AdaptadorVisualCNTGraph()
Constructor privado <<virtual>> InicializarPlotXY()
Método de Fabricacion <<virtual>> PintarPlotXY()
(GoF)
#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);
};
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.
<<interface>>
Cliente
Objetivo
Implementación
del servicio
Adaptador Adaptable
Nótese que los nombres de los tipos incluyen el nombre de patrón “Adaptador”.
Ejemplo 6.14
Fn = Fn−1 + Fn− 2
{1 1 1 2 3 5 8 13 ...}
// 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 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.
<<interface>>
IAdaptadorFibonacci
<<abstract>> getSerie()
<<static>> factoriaFibonacci()
FibonacciGenerator
AdaptadorFibonacci n : int
tamanyo : unsigned val[2] : int
AdaptadorFibonacci() 1 1 FibonacciGenerator()
<<virtual>> getSerie() operator()()
count()
#include <vector>
#include "FibonacciGenerator.h"
class IAdaptadorFibonacci
{
public:
virtual std::vector<unsigned> & getSerie() = 0;
static IAdaptadorFibonacci *factoriaFibonacci(unsigned grado);
};
#include <iostream>
#include "AdaptadorFibonacci.h"
#include <numeric>
#include <algorithm>
void imprimir(unsigned);
int main()
{
const unsigned numeroFibo = 20;
IAdaptadorFibonacci *pAdaptadorFibo =
IAdaptadorFibonacci::factoriaFibonacci(numeroFibo);
for_each(pAdaptadorFibo->getSerie().begin(),pAdaptadorFibo->getSerie().end(),
imprimir);
//Ver GRASP "No hable con extraños" y el código maduro
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:
<<interface>>
IProductoA
Cliente
ProductoA1 ProductoA2
<<interface>>
IFactoriaObjetos
<<interface>> MetodoFabricacion() FactoriaConcretaA
IProductoB
MetodoFabricacion()
ProductoB2
ProductoB1
FactoriaConcretaB
Producto Concreto: define un objeto producto para que sea creado por la
Factoría correspondiente. Implementa la interfaz de Producto Abstracto.
Ejemplo 6.15
IFactoria
ITornillo <<abstract>> fabricacionTornillo()
ITuerca <<abstract>> getMetrica() <<abstract>> fabricacionTuerca()
<<abstract>> getMetrica()
TornilloDIN84 FactoriaTornillos
<<typedef>>
metrica
};
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.
<<interface>>
<<interface>>
IFactoriaObjetos
IProductoA
MetodoFabricacion() : IProductoA
ProductoA1
FactoriaConcretaA
ProductoA2
MetodoFabricacion()
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
<<interface>>
Cliente INombre
(from Logical Vi ew)
<<enum>>
<<abstract>> setNombre() Plataforma
<<abstract>> getNombre()
<<interface>>
IFactoriaNombre STDNombre CNombre MFCNombre
<<abstract>> MetodoFabricacionNombre() elNombre[80] : char (from MetodoFabriacion)
<<virtual>> setNombre()
<<virtual>> getNombre() <<virtual>> setNombre() <<virtual>> setNombre()
STDNombre() <<virtual>> getNombre() <<virtual>> getNombre()
CNombre() MFCNombre()
FactoriaNombre
<<virtual>> MetodoFabricacionNombre()
pNombre1->setNombre("Manolo Gonzalez");
pNombre2->setNombre("Pedro Lopez");
pNombre3->setNombre("Ana Rodriguez");
Segunda solución:
<<interface>>
INombre
<<abstract>> setNombre()
<<abstract>> getNombre()
<<virtual>> setNombre()
<<virtual>> getNombre() <<virtual>> setNombre() <<virtual>> setNombre()
STDNombre() <<virtual>> getNombre() <<virtual>> getNombre()
CNombre() MFCNombre()
#include <iostream>
#include "../includes/STDNombre.h"
#include "../includes/CNombre.h"
#include "../includes/MFCNombre.h"
//Método único para producir los objetos nombres
INombre* INombre::MetodoFabricacionNombre(enum Plataforma 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
<<1>>
Singleton
<<static>> unicaInstancia : Singleton
datosDelSingleton retorna unicaInstancia
Singleton()
operator=()
<<static>> getInstancia()
operacionesDatosSingleton()
#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
Cliente1
<<interface>>
INombre
Cliente2 <<abstract>> setNombre()
<<abstract>> getNombre()
MFCNombre CNombre
FactoriaUnicaNombres()
operator=()
FactoriaUnicaNombres()
<<static>> getInstancia() MFCNombre
MetodoFabricacionNombre()
<<enum>>
Plataforma <<virtual>> setNombre()
<<virtual>> getNombre()
#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
EstrategiaConcreta, a través de su interfaz.
<<interface>>
Contexto
IEstrategia
EstrategiaConcretaB
EstrategiaConcretaA
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
ConversorMoneda
<<interface>>
convertirDolarAEuro() IMoneda
<<abstract>> getEuros() <<typedef>>
convertirLibraAEuro()
<<abstract>> getDolar() tipoMoneda
ConversorMoneda()
1 1
getEuros() <<abstract>> getLibras()
getDolar() <<static>> MetodoFabricacion()
getLibras()
MonedaLibra
cantidad : double
MonedaLibra()
<<virtual>> getEuros()
MonedaEuro MonedaDolar <<virtual>> getDolar()
cantidad : double <<virtual>> getLibras()
cantidad : double
MonedaEuro() MonedaDolar()
<<virtual>> getEuros() <<virtual>> getEuros()
<<virtual>> getDolar() <<virtual>> getDolar()
<<virtual>> getLibras() <<virtual>> getLibras()
#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.
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 del sujeto.
o Implementa la interfaz de actualización del Observador para mantener su
estado consistente con el del sujeto.
Así, el ObservableConcreto notifica a sus observadores, cada vez que se produce
un cambio. 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.
Observable
<<virtual>> ~Observable()
<<parameter>>
<<interface>> <<virtual>> notifyObservers()
Observer <<virtual>> hasChanged()
<<virtual>> countObservers()
<<virtual>> ~Observer()
<<virtual>> deleteObservers()
<<abstract>> update()
<<virtual>> deleteObserver()
<<virtual>> addObserver()
<<virtual>> clearChanged()
<<virtual>> setChanged()
ObservadorConcreto
SujetoConcreto
: SujetoConcreto : ObservadorConcreto
addObserver(Observer&)
notifyObservers(Argument*)
update(Observable*, Argument*)
observadores y sus objetos dependientes. Más aún, los criterios de dependencia que no
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. El patrón del Observer
permite que se modifique desde fuera el código y luego se le indica al patrón del
Observer, en ejecución, que haga las correspondientes actualizaciones. La clase
Observable llama a la función miembro notifyObservers() para cada observador en la
lista de observadores a la escucha.
Primero, se presenta el 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();
}
virtual bool hasChanged() { return changed; }
// If this object has changed, notify all
// of its observers:
virtual void notifyObservers(Argument* arg = 0) {
if(!hasChanged()) return;
clearChanged(); // Not "changed" anymore
std::set<Observer*>::iterator it;
for(it = observers.begin();it != observers.end(); it++)
(*it)->update(this, arg);
}
virtual ~Observable() {}
};
#endif // OBSERVABLE_H ///:~
Las clases que están a la escucha requieren de una interfaz que las permitan
tener la propiedad de ser observables. 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 no estático de la clase que las contiene implícitamente, esto es,
una instancia de la clase interna tenga acceso a datos de la clase que la ha creado. Se
presenta un ejemplo del idioma interno:
// 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;
};
void callPoing(Poingable& p) {
p.poing();
}
class Bingable {
public:
virtual void bing() = 0;
};
void callBing(Bingable& b) {
b.bing();
}
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; }
};
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
6.5 Ejercicios
Problema 1
0.0895 z − 0.085
M (z ) = 2
z − 1.883 z + 0.888
>>step(g1)
Simulador_LTI
tiene definido el sistema mediante un tiempoFinal
ModeloSistema
periodoMuestreo
valoresSalida
getValorInstantek()
: 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( )
ModeloSistema
(f rom Analy sis Model)
FDT
(f rom Analy sis Model)
CoordinadorSimulador grado : unsigned
(f rom Design Model) getCoefNum()
getCoefDen()
1
introducirModelo()
especificarExcitacion()
mostrarSalida()
notificarResultados()
Senyal_Entrada
(f rom Analy sis Model) 2
elTipoEntrada Polinomio
(f rom Analy sis Model)
getValorInstantek() coeficientes : std::vector<double>
Simulador_LTI
(f rom Analy sis Model)
tiempoFinal
periodoMuestreo
valoresSalida
calcularSalida()
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;
}
Espacio
Punto
x
y Vector
magnitud
Punto(xi : double, yi : double)
direccion
Punto(p : const Punto&)
operator=(rhs : const Punto&) : Punto&
Vector(m : double, d : double)
<<friend>> operator<<(os : ostream&, p : const Punto&) : ostream&
: main : Espacio
trasladar(Punto, Vector)
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.
NumerosPrimos
elUltimoPrimo : unsigned
elNumero : unsigned
NumerosPrimos()
getSiguientePrimo() : unsigned
<<interface>>
Cliente IAdaptadorNumerosPrimos
<<abstract>> getNumerosPrimos()
<<static>> metodoFabricacion()
NumerosPrim os
AdaptadorNumerosPrimos
elUltimoPrimo : unsigned
elNumero : unsigned
AdaptadorNumerosPrimos()
<<virtual>> getNumerosPrimos()
NumerosPrimos()
AdaptadorNumerosPrimos()
getSiguientePrimo() : unsigned
: Cliente
metodoFabricacion(unsigned)
: IAdaptadorNumerosPrim os
AdaptadorNumerosPrimos( )
: AdaptadorNumerosPrimos : NumerosPrimos
getNumerosPrimos( )
getNumerosPrimos( )
getSiguientePrimo( )
#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
: Vista
VentaLocalidades(Dinero, float) :
VentaLocalidades
getPrecio(int)
getPrecioTotal(int, Dinero)
VentaLocalidades
<<interface>>
Vista elDescuento : float IPrecioLocalidades
<<abstract>> getPrecioTotal()
VentaLocalidades()
<<static>> metodoFabricacionPrecios()
getPrecio()
DiaDelEspectador
DosXTres
DiaDelEspectador()
<<virtual>> getPrecioTotal() DosXTres()
Fecha <<virtual>> getPrecioTotal()
isMiercoles()
Dinero <<typedef>>
cantidad : float TipoDinero
elTipoMoneda : TipoDinero
Dinero()
Dinero()
Dinero()
operator=()
setCantidad()
getCantidad()
setTipoDinero()
getTipoDinero()
Dinero
cantidad : float
elTipoMoneda : TipoDinero
Dinero()
Dinero(valor : float, elTipo : TipoDinero)
Dinero(elValor : Dinero&)
operator=(elValor : Dinero&) : Dinero&
setCantidad(laCantidad : float) : void
getCantidad( : void) : float
setTipoDinero(elTipo : TipoDinero) : void
getTipoDinero( : void) : TipoDinero
<<interface>>
IPrecioLocalidades
<<abstract>> getPrecioTotal(numeroLocalidades : int, elPrecioUnitario : Dinero) : Dinero
<<static>> metodoFabricacionPrecios(hoy : Fecha&, descuento : float) : IPrecioLocalidades*
DiaDelEspectador
elDescuento : float
DiaDelEspectador(descuento : float)
<<virtual>> getPrecioTotal(numeroLocalidades : int, elPrecioUnitario : Dinero) : Dinero
DosXTres
DosXTres()
<<virtual>> getPrecioTotal(numeroLocalidades : int, elPrecioUnitario : Dinero) : Dinero
VentaLocalidades
elDescuento : float
elDiaSemana : Fecha
elPrecioUnitario : Dinero
pPrecioLocalidades : IPrecioLocalidades *
#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 5
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)
ConversorLongitud <<interface>>
millaMetro : float ILongitudes
pulgadaMetro : float <<abstract>> getMetros()
pieMetro : float <<abstract>> getMillas()
<<abstract>> getPulgadas()
ConversorLongitud() 1 <<abstract>> getPies()
<<virtual>> ~ConversorLongitud() 1 <<static>> metodoFabricacion()
getMetros()
getMillas()
getPulgadas()
getPies()
Pies
factorMillaMetro()
Metros Millas Pulgadas cantidad : float
factorPulgadaMetro()
factorPieMetro() cantidad : float cantidad : float cantidad : float
Pies()
Millas() Pulgadas() getMetros()
Metros()
getMetros() getMetros() getMillas()
getMetros()
getMillas() getMillas() getPulgadas()
getMillas()
getPulgadas() getPulgadas() getPies()
getPulgadas()
getPies() getPies() getPies()
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();
};
#if !defined(AFX_LONGUITUDES_SIS_H)
#define AFX_LONGUITUDES_SIS_H
#include "ConversorLongitud.h"
};
#endif
Problema 6
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
Polinomio
(f rom DominioIntegral)
OperadorIntegral
es el nucleo del IntervaloInferior
FunciónMatemática
IntervaloSuperior
1 1 diferencial
: 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
IntegradorLineal IntegradorBilineal
definirLaFuncion()
calcularLaIntegral()
<<1>>
IIntegrador FactoriaServicios
<<local>>
<<abstract>> calcularIntegral() <<static>> getInstancia()
CoordinadorIntegrales MetodoFabricacionFuncion()
MetodoFabricacionEstrategias()
CoordinadorIntegrales()
<<local>>
definirLaFuncion() ContextoIntegrador Estrategia Factoria(GoF) y
calcularLaIntegral() (GoF) Singleton (GoF)
~CoordinadorIntegrales() ContextoIntegrador()
calcularIntegral()
Polinomio
IFuncionMonoVar
Controlador getCoeficiente()
(GRASP) Polinomio()
<<abstract>> calcularValor()
Polinomio()
<<abstract>> setValoresFuncion()
<<virtual>> calcularValor()
<<virtual>> setValoresFuncion()
#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 7
Luchador Arm a
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
<<1>>
FactoriaObjetos ClienteVideoJuego
FactoriaObjetos()
getInstance()
metodoFabricacionLuchadores()
metodoFabricacionArmas()
metodoFabricacionObstaculos()
<<Interface>>
<<Interface>> <<Interface>>
IArma
ILuchador IObstaculo
moverseTrayectoria()
lanzarArma() moverseAleatorio()
haChocadoObjeto()
moverse() pintar()
pintar()
haRecibidoDisparo()
pintar()
ParedMovil
(f rom Analy sis Model)
#endif
Problema 8
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;
}