Você está na página 1de 763

Prefacio

Captulo 1

El origen de la Orientacin a Objetos

Captulo 2

Fundamentos tericos de la Orientacin a Objetos

Captulo 3

UML: El Lenguaje de Modelado Unificado

Captulo 4

La programacin Orientada a Objetos

Captulo 5

Una metodologa Orientada a Objetos

Captulo 6

Modelado y programacin de problemas

Referencias y Bibliografa sugerida


ndice de Figuras
ndice de Tablas

UNIVERSIDAD NACIONAL

EXPERIMENTAL DE GUAYANA

SECRETARA

_______________________________________________
Modelo y Programacin Orientado a Objetos
Un efonque prctico
Mauricio Paletta Nannarone
_______________________________________________
Editor
Fondo Editorial UNEG
http://fondoeditorial.uneg.edu.ve
Cuidado de la edicin
Ing. Ana Mara Contreras
Diseo, diagramacin y montaje
TSU. Emerson Guerrero Ibez
Diseo de portada
TSU. Emerson Guerrero Ibez
Coordinacin editorial
Ing. Ana Mara Contreras
Impresin
Copiados Unidos C.A.
Tiraje
100 Ejemplares
Hecho el Depsito de Ley
Depsito Legal: lfx93320140011741
ISBN:
978-980-6864-54-2
Todos los ttulos publicados bajo el sello Fondo Editorial UNEG. Reservados
todos los derechos.El contenido de esta obra est protegido por la ley que
establece penas de prisin y/o multa adems de las correspondientes
indemnizaciones por daos y perjuicios para quienes reproduzcan, plagien,
distribuyan o comuniquen pblicamente en todo una obra literaria, artstica
o cientfica, o su transformacin, interpretacin o ejecucin artstica fijada en
cualquier tipo de soporte o comunicado a travs de cualquier medio sin la
respectiva autorizacin.

Autoridades
Dra. Mara Elena Latuff
Rectora
Dr. Arturo Franceschi
Vice - Rector Acadmico
Dr. Wilfredo Guaita
Vice - Rector Administrativo
Dra. Leonarda Casanova
Secretaria
Dr. Alexnder Mansutti
Coord. General de Investigacin y Postgrado
Dra. Juana Ordaz
Coord. General de Pregrado
Dra. Zulema Melndez
Coord. Gral. de Extensin y Difusin Cultural

Universidad Nacional Experimental de Guayana


Sede Administrativa: Edificio General de Seguros,
Av. Las Amricas Puerto Ordaz, Estado Bolvar - Venezuela
Telfonos: 058(0286) - 713.72.16 / 713.72.17 / 923.32.04
www.uneg.edu.ve

PREFACIO
Varios aos de grata experiencia en el rea acadmica en la cual he tenido la oportunidad de
ensear diseo y programacin orientados a objetos, me permiti identificar una necesidad
que lleg a ser la principal motivacin en escribir este libro. Para ese entonces, muchos
estudiantes me consultaron en relacin a alguna bibliografa que explicara la manera en la cual
se tienen que resolver problemas similares a los que usualmente se asignan como trabajos
prcticos en asignaturas en las cuales se ensea a programar, como es el caso particular de
la programacin orientada a objetos.
Si bien es cierto que existe una gran cantidad de bibliografa que abordan temas especficos,
como por ejemplo: 1) usar algn lenguaje de programacin particular (entre los cuales estn
C++, Java y C#); 2) el modelado de problemas con alguna herramienta especfica (por ejemplo
el lenguaje de modelado unificado o UML); 3) el uso de alguna metodologa o mtodo de
resolucin de problemas, entre otros, no se encuentran opciones, al menos en el momento
en la cual fue escrito este libro, que aborden todos estos aspectos en conjunto, es decir, el
modelado y resolucin de problemas utilizando tecnologa orientada a objetos y siguiendo una
metodologa especfica.
En este sentido, los estudiantes que deben resolver problemas prcticos con diferentes niveles
de complejidad, encuentran libros que les ensean a programar en Java, por ejemplo, y a
usar UML, pero no les ensean cmo proceder desde el momento que tienen un enunciado
de algn problema hasta lograr la implementacin orientada a objetos de ese problema. Esta
necesidad de los estudiantes que se les ayude a cmo proceder en la resolucin de los trabajos
prcticos que le son asignados como requisito para aprobar asignaturas relacionadas con
programacin, es el principal objetivo de este libro. En este sentido, este libro es para ustedes
los estudiantes de asignaturas relacionadas con la programacin orientada a objetos; gracias
por haberme dado la oportunidad de descubrir su necesidad; espero haber logrado el objetivo
y haber producido un libro que les ayude tanto como sea posible.
En otro orden de ideas, adems de varias figuras y tablas identificadas con un nmero
consecutivo ordenado por captulo, este libro contiene otra serie de elementos cuyo formato se
muestra a continuacin. Mis notas sobre ciertos aspectos se presentan como sigue:

Nota: Esta es una nota.

En algunos casos es posible establecer cierta relacin entre conceptos de la orientacin a


objetos. Me tom la libertad de llamar a estas relaciones frmulas conceptuales las cuales
se muestran en el libro como sigue:
4

VOLVER A MEN

Finalmente, una gran cantidad del libro est formado por cdigo fuente de los problemas
resueltos. Cabe mencionar que todos los problemas fueron resueltos haciendo uso tanto
de C++ como de Java y C#. Adicionalmente, en todos los problemas resueltos se incluye el
modelado en UML correspondiente. Se quizo hacer de esta manera para que el libro sea de
utilidad para la mayor cantidad de estudiantes posible. El cdigo fuente se muestra dentro de
una caja gris en la cual los comentarios aparecen en estilo cursiva y las palabras reservadas
del lenguaje en estilo negrilla. A continuacin un ejemplo del formato utilizado.

// Esto es un comentario
//
Palabra reservada
Sentencia

Por otro lado y a fin de evitar que el libro sea muy voluminoso, no se incluye en el texto todo
el cdigo fuente inherente a los problemas resueltos. Se obvian los detalles relativos a la
interfaz. Sin embargo, el libro viene acompaado de un CD con la totalidad de los cdigos. El
CD incluye tambin los diagramas UML y todo el software utilizado para la construccin de los
modelos y los programas.
Este libro est constituido por seis captulos con los siguientes contenidos:
El Captulo 1 presenta una introduccin sobre los orgenes y la razn de ser de la orientacin
a objetos.
En el Captulo 2 se aborda toda la fundamentacin terica inherente a la orientacin a
objetos.
El Captulo 3 describe el lenguaje de modelado unificado o UML.
La programacin orientada a objetos haciendo uso de los lenguajes de programacin C++,
Java y C# se encuentra en el Captulo 4.
En el Captulo 5 se describe un proceso metodolgico para la resolucin de problemas bajo
5

VOLVER A MEN

un enfoque orientado a objetos.


Finalmente en el Captulo 6, utilizando la metodologa referida en el Captulo 5, los
lenguajes de programacin descritos en el Captulo 4 y UML presentado en el Captulo 3,
se enfoca en la resolucin de una serie de problemas seleccionados con diferentes niveles
de complejidad.
Para finalizar creo importante mencionar que al final del libro se incluyen todas las referencias
que se muestran a lo largo del texto, as como tambin una lista de bibliografa recomendada
que he tenido la oportunidad de usar durante mi carrera acadmica. Adicionalmente, la seccin
final de cada uno de los seis captulos que componen el libro, se refiere a una lista de ejercicios
y problemas seleccionados de una amplia cantidad de material acadmico.

VOLVER A MEN

Escribir un libro como este requiere de mucha dedicacin, tiempo este


que se dej de dar y compartir con la familia. Este libro est dedicado a mi
familia. Por entenderme, por apoyarme, por estar conmigo siempre. Para
Maria Elena, Andrea Fabianna y mbar. Las amo.

VOLVER A MEN

CONTENIDO

Prefacio

Captulo 1
El origen de la Orientacin a Objetos
1.1 Introduccin
1.2 Descomposicin
1.3

Abstraccin

1.4 Jerarqua
1.5

Orientacin a Objetos = Descomposicin + Abstraccin + Jerarqua

1.6

Algo de historia

1.7 Ejercicios

11
14
14
16
16
17
21
25

Captulo 2
28
2.1 Introduccin
28
2.2 Principios de la OO 28
2.3 Trmino tcnico
38
2.4 Conceptos bsicos de la OO
39
2.5 Ejercicios
50
Fundamentos tericos de la Orientacin a Objetos

Captulo 3
UML: El Lenguaje de Modelado Unificado

3.1

Introduccin

3.2

Componentes

3.3

Diagramas

3.4

Mecanismos Generales

3.5

Resumen de la sintaxis

3.6

Ejercicios

VOLVER A MEN

54
54
55
57
82
84
85

Captulo 4
La programacin Orientada a Objetos
4.1 Introduccin
4.2

Definicin de clases

4.3

Instanciacin de objetos

4.4

Acceso a los elementos de la clase

4.5 Constructor
4.6

Polimorfismo

4.7

Inicializacin por defecto

4.8 Destructor
4.9

Elementos estticos

4.10 Operador de asignacin o copia


4.11 Sobrecarga de operadores
4.12 Referencia al objeto actual
4.13 Herencia
4.14 Clases abstractas y mtodos virtuales
4.15 Paquetes
4.16 Otros conceptos
4.17 Ejercicios

90
90
92
96
98
99
101
103
105
106
110
112
116
118
127
128
131
144

Captulo 5
Una metodologa Orientada a Objetos
5.1 Introduccin
5.2

Anlisis OO

5.3

Diseo OO

5.4 Resumen
5.5 Ejemplo
5.6 Ejercicios

148
148
150
158
162
163
171

Captulo 6
Modelado y programacin de problemas
6.1 Introduccin

6.2

La calculadora de operaciones bsicas

6.3

Simulacin de trfico de un solo carril y sentido

6.4

Manejo de conjuntos de enteros

6.5

Manejo inteligente de colas


VOLVER A MEN

172
172
173
219
240
297

6.6

El generador de energa

6.7

La fbrica de galletas

6.8

Grafo de asignacin de recursos

6.9

La conquista del nuevo mundo

6.10 Agentes recolectores de desperdicio


6.11 Ejercicios

ndice de Tablas

743
746
755

ndice

756

Referencias y Bibliografa sugerida


ndice de Figuras

10

390
416
459
513
647
732

VOLVER A MEN

CAPTULO 1

El origen de la Orientacin a Objetos


1.1 Introduccin
Hablar del origen de la orientacin a objetos requiere remontarse a los aos 80 en la cual
ocurri, por un lado, uno de los fenmenos de mayor importancia en la era de las computadoras
y, por otro lado, como consecuencia de lo anterior, un segundo fenmeno, esta vez negativo
relacionado tambin con la era de la computacin. En el primer caso se trata de la aparicin de
la computadora personal, mejor conocida por sus siglas en ingls como PC. El auge tecnolgico
en los dispositivos electrnicos permite que las computadoras se reduzcan significativamente
en tamao y se abaraten sus costos de adquisicin, lo que permite que se origine la PC. Las
computadoras dejan de ser de uso exclusivo de universidades, grandes instituciones o centros
de investigacin; ahora pueden llegar a los hogares de las personas, es decir, la computadora
llega a convertirse en un equipo personal.
Ahora bien, la aparicin de la PC hace que exista una gran cantidad de computadoras en manos
de muchos usuarios que demandan la necesidad de software para la automatizacin de una
gran cantidad de procesos que, para ese entonces se hacan manualmente. La demanda de
software origina la aparicin de muchos desarrolladores, la mayora de los cuales desarrollan
los programas sin ningn tipo de estndar, metodologa o control. En este sentido, la mayora
del software se construye a la medida, en vez de utilizar componentes existentes que pudieran
facilitar la construccin de software libre de errores. Adicionalmente, los programadores no
disponen / usan de componentes de software pre-existentes y reutilizables, es decir, empiezan
desde cero. Todo esto trae como consecuencia que la gran mayora de los desarrollos de
software realizados en ese entonces, demoren mucho tiempo en su finalizacin y sean de muy
baja calidad.
Producto de la problemtica antes presentada se empieza a generar como consecuencia, un
descontento por parte de los usuarios de las computadoras, llegando entonces a concretarse
un fenmeno al cual se le dio el nombre de la crisis del software. En general y, a manera de
sntesis, la crisis del software es el resultado de:
No terminar los proyectos a tiempo.
Consumir ms presupuesto del planificado.
Tener una baja productividad.
Hacer productos de baja calidad.
Tener gran cantidad de personal especializado dedicado a labores de mantenimiento.
11

VOLVER A MEN

Haber usuarios insatisfechos con los sistemas y con los departamentos o grupos de desarrollo que se preguntan: Por qu el desarrollo de software es tan costoso? Por qu
toma tanto tiempo? Hay alguna perspectiva de mejora?
Como bien lo dijo Brooks en su momento, sobre esta crisis en la cual lleg a estar el desarrollo
de software, es una propiedad esencial, no accidental (Brooks, 1987), es natural que
esto haya ocurrido y, gracias a ello hubo respuestas para tratar este fenmeno. Una de las
primeras respuestas tiene que ver con analizar la complejidad como caracterstica inherente
del desarrollo del software que, segn Booch, es necesario entenderla y administrarla (Booch,
1996). En este sentido, este autor identifica cuatro factores que influyen en la complejidad del
software:
1) Complejidad del dominio del problema:
Problemas que involucran elementos de gran complejidad (muy especficos) versus problemas
que involucran elementos simples (ms conocidos y comunes).
Existencia de requerimientos contradictorios cuando hay varios usuarios involucrados en el
problema.
Distancia entre el usuario y el desarrollador lo que genera diferentes perspectivas del problema.
2) Dificultad de administrar el proceso de desarrollo:
Se quiere dar la ilusin de simplicidad pero no lo es.
Manejo de un grupo de trabajo y los posibles problemas de comunicacin, coordinacin e
integracin que pudieran haber.
3) Flexibilidad exigida al software:
Eficiencia (tiempo de respuesta) versus eficacia (calidad en la respuesta).
Optimizacin de recursos versus calidad en la respuesta.
El usuario quiere que se haga todo, que todo se haga bien y, que adems no haya que pagar
mucho por ello (bueno, bonito y barato).
4) Problemas que plantean la caracterizacin del comportamiento de sistemas discretos:
Administracin de la cantidad de elementos (variables, estructuras, funciones, entre otras) que
pueden haber en un programa de cierta envergadura.
Relacionado con el mantenimiento del software, el cual puede ser de tres tipos:
o Correctivo: corregir errores en los programas (20 % del tiempo total que se dedica al
mantenimiento).
o Adaptativo: implementar cambios en los requerimientos producto de una adaptacin a los
mismos (25 % del tiempo total que se dedica al mantenimiento).
o Perfectivo: tratar de mantener el software lo ms actualizado posible en base a los avances
tecnolgicos, a fin de garantizar su continuidad operativa (55 % del tiempo total que se
dedica al mantenimiento).
12

VOLVER A MEN

En este mismo orden de ideas, a medida que aument la complejidad del software requerido,
se redujo proporcionalmente las habilidades para manejar la complejidad inherente a ste,
agravando entonces el problema de la crisis del software. Esta problemtica agravada lleg
a caracterizarse como una situacin de desorden y descontrol hasta el punto de asociarse o
identificarse con el concepto que los fsicos dan a los sistemas en desorden, es decir, caos.
La llegada del caos al desarrollo de software y, por ende tratar esta situacin, origin una
reaccin de parte de los entes interesados que se puede resumir en las siguientes acciones:
Tratar el software como una ingeniera.
Promover el uso de metodologas estructuradas.
Promover una mayor participacin del usuario en las etapas de desarrollo.
Realizar pruebas planificadas y documentadas.
Promover el uso de herramientas automatizadas para el control de los desarrollos.
El esfuerzo del mundo cientfico para sacar al desarrollo de software del caos plante entonces
un nuevo paradigma de visualizacin de los proyectos de desarrollo de software que implica
la realizacin de tres cosas: descomposicin, abstraccin y jerarqua. Posteriormente, a este
nuevo paradigma de hacer software se le dio el nombre de Orientacin a Objetos.

Figura 1.1. Hacer descomposicin, abstraccin y jerarqua para salir del caos
(imagen realizada por Juan C. Candelori / www.pietrogiordano.info).

13

VOLVER A MEN

1.2 Descomposicin
La tcnica de dominar la complejidad se conoce desde tiempos remotos: divide et impera
(divide y vencers) (Dijkstra, 1979). La descomposicin de un problema basado en un
sistema de software se fundamenta en esta tcnica. A medida que el problema o sistema es
ms complejo, es esencial descomponerlo en partes, cada una de las cuales con un nivel de
complejidad menor al problema total, de manera tal que a la hora de resolver el problema,
es decir hacer el sistema, el impacto de algo muy complejo se reduce al impacto de varios
sub-problemas de complejidad no tan alta. La solucin del problema macro ser entonces la
integracin de las partes. Para entender un nivel dado de un sistema, basta con comprender
unas pocas partes a la vez, la descomposicin del todo en sus partes permite disminuir la
complejidad inherente al software (Dijkstra, 1979).
Es necesario hacer una divisin inteligente del problema, ya que los dos extremos posibles no
son adecuados: 1) tener partes con una complejidad que sigue siendo alta y que pueden ser
descompuestas an ms (quedarse muy arriba); 2) tener partes con una complejidad casi nula
que indica que se descompuso ms de la cuenta (quedarse muy abajo). En el primer caso se
pierde la principal ventaja de descomponer: reducir la complejidad para tener una visin ms
simple del problema y permitir as que ste sea resuelto adecuadamente. El segundo caso
puede llevar a la generacin de muchos mdulos que por su cantidad, pueden hacer ms difcil
el proceso de integracin.
De manera de ejemplo y para entender mejor lo dicho anteriormente, suponga que se tiene
el problema de hacer un vehculo automotor. Una primera descomposicin de este problema
nos puede llevar a las siguientes partes: motor y sistemas mecnicos, carrocera y accesorios
incluyendo el tablero, chasis, sistema elctrico, resto de elementos. Est claro que cada una
de estas partes, en particular el motor y sistemas mecnicos, puede ser descompuesto en s
mismo en otro conjunto de partes. Ntese que la integracin (o ensamblado) de un vehculo
teniendo cada una de las partes antes identificadas es ms sencillo que tener todas las subpartes de cada parte juntas. En otras palabras, no es lo mismo tener el motor listo para ser
usado como una parte del vehculo que tener todas las partes del motor para resolver el mismo
problema. Un problema es hacer el vehculo y para ello se ha identificado que una de sus
partes es el motor; otro problema es hacer el motor y para ello se han identificado otro conjunto
de partes. La Figura 1.2 muestra una ejemplificacin del caso anterior y en la cual se observa
al robot OO ensamblando otros robots.

14

VOLVER A MEN

Figura 1.2. Resolver un problema con sub-problemas ya resueltos versus resolver


el mismo problema como un todo (imagen realizada por Juan C. Candelori / www.pietrogiordano.info).

Cabe mencionar que la descomposicin se ha estado aplicando en el desarrollo de sistemas de


software desde hace varios aos atrs, desde que la programacin dej de ser un espaguetis
electrnico (con el uso excesivo de sentencias GOTO) y pas a ser programacin estructurada
y/o programacin modular.

15

VOLVER A MEN

1.3 Abstraccin
Una de las tareas que se deben hacer cuando se quiere descomponer un problema en partes,
con el objeto de administrar o reducir la complejidad del problema, es identificar esos elementos
potenciales que van a representar cada una de esas partes. Para ello es conveniente basarse en
conceptos conocidos, en ignorar los detalles no significativos de cada elemento. La abstraccin
es esta capacidad para adquirir conceptos que, combinada con la descomposicin brinda un
enorme poder para manejar la complejidad de los problemas.
Hacer una adecuada conceptualizacin del dominio del problema y/o definir tipos abstractos
de datos son ejemplos de actividades que tienen que ver con la abstraccin.

1.4 Jerarqua
Muchos de los conceptos que han sido identificados haciendo uso de la abstraccin con
miras de lograr una adecuada descomposicin del problema, pueden relacionarse entre s
de manera de lograr definir uno de estos conceptos basado en la existencia de otros. La
adecuada identificacin de estas relaciones permite lograr una organizacin de los conceptos
que, no slo incrementa el contenido semntico de las piezas de informacin identificadas,
sino que tambin ayuda a la comprensin del funcionamiento del sistema. Por otro lado, es de
gran ayuda a posteriori para la adecuada implementacin de los conceptos.
Por un lado, los conceptos pueden ser organizados en niveles de categora mediante una
relacin semntica del tipo caso particular de. A este tipo de relacin se le suele llamar una
generalizacin y su objetivo es especializar un concepto basado en la existencia de otros
menos generales. Algunos ejemplos de este tipo de relacin son:
Trabajador es un caso particular de Persona.
Mamfero es un caso particular de Animal.
Vehculo automotor es un caso particular de Vehculo.
Una recomendacin para saber si un concepto C1 es un caso particular de otro concepto C2,
es considerar el hecho que C1 es un C2 con algunos elementos adicionales. Por ejemplo, un
Trabajador es una Persona con un salario y posiblemente algo ms; un Mamfero es un Animal
con sangre caliente y posiblemente algo ms; un Vehculo automotor es un Vehculo que tiene
un motor y posiblemente algo ms.
16

VOLVER A MEN

Por otro lado, los conceptos se pueden organizar mediante la composicin o agregacin
entre ellos a travs de una relacin semntica del tipo forma parte de. El objetivo es agregar
conceptos como elementos de otros conceptos que tienen una definicin ms compleja.
Algunos ejemplos de este tipo de relacin son:
Trabajador forma parte de una Empresa.
Disco duro forma parte de una PC.
Motor forma parte de un Vehculo automotor.
Una recomendacin para saber si un concepto C1 forma parte de otro concepto C2, es
considerar el hecho que para que C2 exista, ste puede o no requerir de uno o ms elementos
del tipo C1. Es importante notar en la oracin anterior, el uso de puede o no y de uno o
ms. Esto quiere decir que la inclusin de C1 en C2 es opcional y que esta relacin puede
implicar que varios elementos de C1 pueden estar en C2. En la literatura se suele llamar a
la inclusin no obligatoria composicin; mientras que a la inclusin obligatoria se le suele
llamar agregacin. En general, a la cantidad de elementos de un mismo concepto que estn
relacionados o asociados con otro concepto se le denomina multiplicidad. Por ejemplo, est
claro que 1 o ms Trabajadores pueden formar parte de una Empresa. Lo mismo ocurre con
el Disco duro y una PC, ms an, pudiera concebirse la existencia de una PC sin Disco duro,
por lo que esta relacin puede verse como opcional. Con respecto al ejemplo Motor forma
parte de un Vehculo automotor, es notorio el hecho que no existe un Vehculo automotor sin
un Motor por lo que esta relacin es definitivamente obligatoria.

1.5 Orientacin a Objetos = Descomposicin


+ Abstraccin + Jerarqua
La Orientacin a Objetos (OO), como una respuesta hacia la crisis del software, surge entonces
como una nueva forma de pensar usando conceptos del mundo real. Significa que el software
se organiza como un conjunto de objetos discretos cada uno de los cuales incorpora tanto
su estructura de datos como su comportamiento. Es decir, datos y operaciones se encierran
(encapsulan) en una misma estructura para ser visto como una unidad de software el cual
puede ser creado, destruido, asignado y ser usado, de la misma manera como se manejan
variables en un programa. Se ver ms adelante que el trmino ser usado en OO se refiere
al intercambio de mensajes entre objetos. Ya lo dijeron Goldberg y Robson cuando se refiere a
la OO de la siguiente manera: ejecutar un programa o sistema, es algo tan sencillo como crear
objetos y disparar mensajes (Goldberg y Robson, 1983).

17

VOLVER A MEN

Para hacer OO se requiere entonces descomponer el problemas en partes, hacer abstraccin


de forma tal de identificar las partes suficientes y necesarias para descomponer el problema y
organizar semnticamente estas partes con el objeto de entender mejor el problema y reducir
la complejidad de obtener cada una de esas partes. En este sentido, no es un error decir que
hacer OO es hacer descomposicin, abstraccin y jerarqua, en cualquiera de los contextos en
los cuales se hace OO (anlisis, diseo y programacin). Esto nos lleva a escribir la siguiente
frmula conceptual:

Hacer descomposicin, abstraccin y jerarqua es algo que se va aprendiendo con la prctica.


Una buena forma de empezar es responder algunas preguntas bsicas asociadas a cada uno
de estos tres sub-procesos caractersticos de la OO. La Tabla 1.1 presenta estas preguntas
bsicas.
Tabla 1.1. Preguntas bsicas relativas a hacer OO.

Descomposicin

Cmo hacer que el conjunto de elementos


resuelvan el problema?

Abstraccin

Cmo identificar los elementos, basado en los


conceptos del problema?
Cmo relacionar los elementos para reducir la
complejidad de cada uno de ellos?

Jerarqua

Como consecuencia de que en la OO los problemas son tratados como un conjunto


de partes discretas llamadas objetos, se tienen varios beneficios importantes.
En primer lugar, alguna de estas partes puede ya existir de la solucin de otros
problemas previamente resueltos, lo que implica que la solucin del problema
original se puede obtener ms rpidamente. No es lo mismo, por ejemplo, hacer un
Vehculo automotor teniendo ya un Motor disponible que tener que hacer tambin
el Motor. Esta facilidad, muy importante dentro del contexto de la OO, es lo que se
conoce con el nombre de reusabilidad de elementos.
En segundo lugar y, basado en la reusabilidad de elementos, la calidad de la solucin
de un problema resuelto con OO siempre va a ser ms alta que un problema resuelto
con estrategias tradicionales. Esto es as porque todo elemento (objeto) existe si ste
ya ha sido debidamente probado y puede, por lo tanto, ser reutilizado. Quiere decir
18

VOLVER A MEN

entonces que si en la solucin de un problema se utilizan elementos ya existentes,


se puede tener la confianza que estos elementos existentes ya han pasado por un
adecuado control de calidad y slo bastara hacer una adecuada integracin de
todos los elementos para resolver el problema (intercambio de mensajes entre
objetos). El Motor, por ejemplo, que ya se tiene disponible para hacer el Vehculo
Automotor, seguro que funciona bien, basta con que sea integrado correctamente
con las otras partes del Vehculo Automotor.
Como consecuencia de lo anterior, otro beneficio de la OO tiene que ver con la
facilidad en los procesos de mantenimiento (correctivo, perfectivo y adaptativo)
que seguramente se va a requerir hacer a la solucin del problema una vez que
sta se tenga. Esto se debe a que, al utilizar objetos libres de errores, el esfuerzo
en mantenimiento se concentra primeramente en la integracin de los elementos y
no en los elementos como tal.
Lograr hacer los desarrollos ms rpido, tener productos de mejor calidad y reducir
la complejidad en las etapas de mantenimiento son una clara evidencia de la
reduccin de los costos para la obtencin de los productos (anlisis, diseos y
programas). Lo anterior tambin justifica el hecho de que la OO sea una buena
opcin para soportar la elaboracin de sistemas de gran escala.
Por otro lado, la representacin de conceptos en la OO se fundamenta en una
completa estructura de informacin conocida con el nombre de Clase. Dado el
hecho que en una Clase es posible no slo representar los datos asociados al
concepto sino tambin las operaciones correspondientes, permite que la OO sea
adaptable a cualquier conjunto de conceptos o problemas que tengan que ser
representados. A diferencia de la programacin tradicional, que est basada en
una seccin de declaraciones representada principalmente por variables y, una
seccin de funciones / procedimientos (llamado tambin descomposicin funcional
del problema), la OO est basada en la definicin de Clases y el intercambio de
mensajes entre los Objetos definidos o declarados con base a estas Clases. La Tabla
1.2 muestra otras diferencias entre la descomposicin funcional y la OO. Todo se ir
aclarando a lo largo del resto de los captulos que forman parte de este libro.

19

VOLVER A MEN

Tabla 1.2. Tabla comparativa entre la descomposicin funcional y la OO.

Descomposicin funcional

Orientacin a Objetos

Mdulos construidos alrededor


de las operaciones

Mdulos construidos alrededor de


las clases

Datos globales o distribuidos


entre los mdulos

Clases dbilmente acopladas y sin datos


globales

Entrada / Proceso / Salida


Organigramas de flujos de datos

Encapsulamiento / Mensajes
Diagramas jerrquicos de clases

La parte izquierda de la Figura 1.3 muestra un ejemplo de un caso tpico de programacin


basada en descomposicin funcional, en el cual aparecen tres procedimientos que se deben
ejecutar una vez recibida la entrada, para luego poder calcular una salida. La parte derecha
muestra, en cambio, la manera como se pudiera visualizar el mismo escenario pero esta vez
enfocado en OO. Ntese que en lugar de procedimientos o funciones aparecen Objetos, cada
uno de los cuales con los atributos (datos) y mtodos (operaciones) que fueron definidos en
la Clase del cual estos Objetos fueron declarados. Ntese que en el enfoque OO el flujo de
ejecucin del programa viene dado por el intercambio de mensajes entre los objetos.

Figura 1.3. Ejemplo de descomposicin funcional versus OO.

20

VOLVER A MEN

1.6 Algo de historia


Para iniciar a comentar brevemente parte de la historia de la OO, hay que primero
mencionar que el trmino OO fue introducido en 1970 en el lxico con la llegada del
lenguaje de programacin Smalltalk, en el Centro de Investigaciones de Palo Alto
(PARC). Sin embargo, gracias a que tomaba en cuenta una estructura de datos
tal como se conoce a la Clase hoy en da, es el lenguaje de programacin Simula
creado en 1967 por K. Nygaard, el que se lleva el mrito de ser el primer lenguaje
de programacin OO.
Un breve resumen de los hechos ms importantes relativos a la historia de la OO
se encuentra en la Tabla 1.3.
Tabla 1.3. Resumen de la historia de la OO.

1957
1967

1970

1970
1975

1976
1977
1980

1986
1986

21

VOLVER A MEN

Ten Dyke & Kunz usaron tcnicas rudimentarias de OO en el


diseo del misil Minuteman.
Desarrollo del lenguaje de simulacin de sucesos discretos
Simula, en Noruega, por K. Nygaard. Introdujo los conceptos
bsicos de OO. La idea fue definir un lenguaje para construir
modelos de sistemas fsicos complejos que pueden contener
miles de componentes. Los mdulos estn basados en los
objetos fsicos presentes en el entorno del problema.
Desarrollo del lenguaje de programacin Smalltalk en el
Centro de Investigaciones de Palo Alto (PARC), basado en el
trabajo doctoral de Alan Kay. Visin de un PC pequeo, universal, capaz de tratar cualquier clase de problema de gestin de informacin.
Desarrollo de la mquina Flex Dynabook en PARC, basado
en Smalltalk.
Fertilizacin recproca entre la OOP y la investigacin y el
desarrollo en Inteligencia Artificial: LISP, FLAVORS, LOOPS y
CLOS, KEE y ART (ideas sobre la representacin del conocimiento fundamentadas en redes y marcos semnticos importante para el concepto de herencia).
Desarrollo del lenguaje Alphard por Wulf y compaa.
Desarrollo del lenguaje CLU por Liskov y compaa.
Explosin de inters por las interfaces de usuario: WIMP
Windows, Icons, Mice and Pointers (Apple); GUI Graphical
User Interface; Presentation Manager, Microsoft Windows;
Sistemas estandarizados abiertos basados en UNIX.
Sistemas de Actores Ahga, dirigidos normalmente a aplicaciones en tiempo real y concurrentes.
Desarrollo del lenguaje de programacin C++, por Bjarne
Stroustup (AT&T).

1988
1989
1989
1990

1990
1990
1996
2000

Desarrollo del lenguaje de programacin Eiffel, por Bertrand


Meyer.
Sistemas de pizarra (Englemore y Morgan) para la representacin y manejo del conocimiento.
Desarrollo de herramientas CASE e IPSE.
Inters hacia el anlisis y diseo OO: Biggerstaff y Richter,
Prieto-Daz y Freeman, Sommerville, Booch, Rumbaugh
(OMT), Jakobson (OOSE).
Inters hacia la definicin de estndares. El principal protagonista es el grupo OMG (UML, OMA, CORBA, MDA, etc.).
Base de datos OO.
Desarrollo del lenguaje de programacin Java, por Sun Microsystems.
Desarrollo del lenguaje de programacin C#. Bsqueda de
conceptos de niveles de abstraccin ms altos que un objeto: Agente, Cubo, etc. Base de toda plataforma de software:
operativa, desarrollo, grfica, etc.

Haciendo un anlisis de la Tabla 1.3, es posible dividir la historia de la OO en las cuatro fases
que se muestran en la Tabla 1.4. Ntese que de un enfoque inicial orientado principalmente
en la programacin, la OO evoluciona a otros enfoques como la interfaz grfica, las bases de
datos, la Inteligencia Artificial y el modelado.

Tabla 1.4. Anlisis de la historia de la OO.

Fase I

Fase II

Fase III

Fase IV

dcada de los
70 (poca de
invencin)
Simulacin de
sucesos discretos

dcada de los
80 (poca de
confusin)
Interfaces
WIMP

dcada de los
90 (poca de
madurez)
nfasis en el
anlisis y el diseo

del 2000 hasta hoy


(poca actual)

Simula

Xerox y Apple

Kay: mquina
FLEX

Extensiones
LISP

Sistemas abiertos
Aplicaciones

PARC: Dynabook

Entornos de
Inteligencia
Artificial
Nuevos lenguajes: Eiffel,
C++, Java

Smalltalk

22

VOLVER A MEN

Bases de datos
OO
Estndares

Bsqueda de niveles
ms altos de abstraccin (Agentes,
cubos, etc.)
C#
Paradigma de todas
las plataformas de
desarrollo
UML

Software => Objetos

La OO ha tenido mucho auge desde sus inicios llegando a convertirse en un estndar para
el desarrollo de productos de software. Una prueba de ello es la evolucin que han tenido
los lenguajes de programacin desde Fortran, considerado como el primer lenguaje de
programacin de alto nivel. La Figura 1.4 muestra esta evolucin; ntese que los lenguajes
encerrados en un rectngulo son no OO mientras que los que estn encerrados en un valo si
son OO. Como puede verse en la Figura 1.4, todos los lenguajes creados desde los inicios de
1980 hasta la actualidad son OO.

Figura 1.4. Evolucin de los lenguajes de programacin.

En otro orden de ideas y, dado el hecho de la existencia en la dcada de los 90s de muchos
lenguajes de modelado basados en OO utilizados principalmente para cubrir necesidades de
anlisis y diseo, surgi la intencin de buscar un estndar o unificacin en este sentido. El
resultado fue UML (Lenguaje de Modelado Unificado). La Figura 1.5 muestra la evolucin de
los lenguajes de modelado OO hasta UML, estndar universalmente aceptado hoy en da.

23

VOLVER A MEN

Figura 1.5. Evolucin de los lenguajes de modelado.

24

VOLVER A MEN

1.7 Ejercicios
1) Explique brevemente por qu surge la OO? Qu significa hacer OO?
2) Identifique las caractersticas resaltantes que describen la evolucin histrica de la OO,
desde los aos 70 hasta los aos 90.
3) Por qu se da el fenmeno de la complejidad del software?
4) Identifique y explique los elementos que dieron origen a la OO.
5) Cul fue el aporte de Simula en la evolucin histrica de la OO?
6) Cules tres puntos se proponen para traer orden al caos en la Ingeniera de Software?
a) Descomposicin, abstraccin y jerarqua.
b) Objeto, clase y herencia.
c) Anlisis, diseo y programacin OO.
d) Ninguna de las anteriores.
7) Cul de las siguientes observaciones relativas a la evolucin histrica de la OO es falsa?
a) Simula fue el primer lenguaje con estructuras OO.
b) Primero hubo inters por el diseo OO y luego por la programacin.
c) GUI es un concepto importante en la historia de la OO.
d) Todas las observaciones son verdaderas.
8) La abstraccin es: ?
a) La capacidad de adquirir conceptos.
b) La capacidad de construir jerarquas de generalizaciones.
c) Ninguna de las anteriores.
d) Tanto la opcin (a) como la (b).
9) Entre los conceptos Equipo y Jugador se puede establecer una relacin del tipo: ?
a) Jerarqua: Caso-particular-de.
b) Agregacin: Parte-de.
c) Ni (a) ni (b).
d) Tanto (a) como (b) dependiendo del contexto.
10) Entre los conceptos Pieza de Ajedrez y Piezas Negras se puede establecer una relacin
del tipo: ?
a) Jerarqua: Caso-particular-de.
25

VOLVER A MEN

b) Agregacin: Parte-de.
c) Ni (a) ni (b), se trata de una instancia particular.
d) Tanto (a) como (b) dependiendo del contexto.
11) Cul de los siguientes fenmenos relativos al software fue importante para que se originara
la OO?
a) La aparicin del computador personal.
b)

La evolucin de los lenguajes de programacin.


c) La crisis del software.
d) Ninguna de las opciones.

12) Cules tres puntos propone la Ingeniera de Software para traer orden al caos?
a) Descomposicin, abstraccin y jerarqua.
b) Objeto, clase y herencia.
c) Anlisis, diseo y programacin OO.
d) Ninguna de las anteriores.
13) Cul de los siguientes lenguajes de programacin fue el primero en tener estructuras
OO?
a) Simula.
b) Pascal.
c) C++.
d) Java.
14) Indique si las siguientes afirmaciones relativas a la evolucin histrica de la OO son
verdaderas falsas.
a) El lenguaje de programacin Simula fue el primero en definir estructuras OO.
b) Primero hubo inters por el diseo OO y luego por la programacin.
c) A medida que pasa el tiempo hay mayor inters en aumentar los niveles de abstraccin.
d) C# es uno de los primeros lenguajes de programacin OO, precursor de C++.
15) Con los siguientes conceptos que se enumeran a continuacin, indique las relaciones de
composicin o agregacin (forma parte de) y las relaciones de generalizacin (caso
particular de)
a) Circuito electrnico, resistencia, condensador, resistencia de 100 K, resistencia de 10 K,
condensador de 1 microfaradio, componente electrnico, circuito integrado, transistor.
26

VOLVER A MEN

b) Universidad, estudiante, facultad, facultad de ingeniera, facultad de educacin,


asignatura, profesor, aula, pizarrn, pupitre, aula #13, programacin OO.
c) Dispositivo de Entrada/Salida, Memoria RAM, CPU, Disco Duro, Impresora, Computador Personal, Monitor, Teclado, Memoria.
d) Jugador, Defensa, Delantero, Equipo, Baln, Juego.
e) Ajedrez, tablero, piezas, piezas blancas, piezas negras, torre, alfil, pen, reina, rey,
caballo.
f) Biblioteca, Libro, Captulo, Autor, Ttulo, Novela, Libro de Msica, Editorial, ISBN.

27

VOLVER A MEN

Captulo 2

Fundamentos tericos de la Orientacin a Objetos


2.1 Introduccin
En este captulo se presentan los fundamentos tericos inherentes a la OO. El objetivo es
conocer y dominar conceptualmente todos los elementos que hay que saber antes de pasar al
modelado y programacin de problemas. En resumen, la fundamentacin terica de la OO la
conforman ocho principios, un mecanismo tcnico y tres conceptos bsicos. Los principios de
la OO permiten establecer las reglas que determinan la realizacin de un adecuado modelado
y programacin OO. Son los que determinan si realmente se est o no haciendo abstraccin,
descomposicin y jerarqua, es decir, OO. No respetar alguno de estos principios, es decir, usarlo
indebidamente, puede implicar hacer el trabajo pero usar mal las herramientas para lograrlo.
Son del tipo de cosas que se aprenden con la prctica; lo importante es buscar no hacer lo que
en principio se sabe no se tiene que hacer. Los ocho principios, que sern desarrollados a lo
largo de este captulo, son: la abstraccin, el encapsulamiento, la modularidad, la jerarqua, los
tipos, la concurrencia, la persistencia y el polimorfismo.
Por otro lado, el trmino tcnico a conocer es el enlace dinmico y es requerido principalmente
para poder soportar el principio de polimorfismo. Finalmente, los tres conceptos bsicos a
conocer son: el objeto, la clase y la herencia.
Nota: Hacer abstraccin, descomposicin y jerarqua es respetar los ocho principios
de la OO mediante el uso de sus tres conceptos bsicos.

2.2 Principios de la OO
La Abstraccin. El mundo que nos rodea est completamente representado por conceptos:
persona, mesa, vehculo, pelota, rbol, empresa, universidad, estudiante, son alguno de los
innumerables ejemplos que se pueden citar. No es error decir entonces que todo problema
particular que requiera ser modelado, estudiado o resuelto, tambin tenga que ver con un
conjunto de conceptos. El principio de abstraccin determina la capacidad para adquirir los
conceptos necesarios y suficientes involucrados en el entorno del problema que se quiere
modelar. Es importante tener en cuenta que los conceptos son ideas particulares o lo que
vemos y comprendemos del mundo que nos rodea.
Hacer un breve anlisis de la complejidad de los conceptos o elementos involucrados en
el problema es una sugerencia para hacer abstraccin. Si se tiene la conviccin de que un
28

VOLVER A MEN

concepto tiene un nivel alto de complejidad y ste puede ser descompuesto en otros conceptos,
la abstraccin permite entonces identificar otros conceptos involucrados. Por ejemplo, un
vehculo automotor tiene la suficiente complejidad para observar en l otros conceptos como
el motor, el sistema elctrico, el chasis, entre otros.
Nota: La abstraccin es una forma de manejar la complejidad de los objetos.

Una segunda recomendacin a tener en cuenta para hacer una adecuada abstraccin, tiene
que ver con el acto o resultado de eliminar diferencias entre los conceptos, es decir, que se
puedan ver los aspectos comunes y no comunes. Por ejemplo, en el modelado de un problema
relacionado con un entorno universitario, es casi seguro que como resultado de la abstraccin
del problema se identifiquen los conceptos de profesor y estudiante. Al observar estos dos
conceptos, es notorio el hecho que hay varios aspectos comunes: un nombre, un documento
de identidad, un sexo, nacer, vivir y morir. Tambin es claro que hay diferencias, como por
ejemplo el hecho que un profesor tiene un rango acadmico mientras que un estudiante tiene un
nmero de crditos inscritos. Ahora bien, es correcto el haber decidido en tener los conceptos
de profesor y estudiante, no hacer sin embargo nada en relacin a los aspectos comunes es
un error. Ntese que lo que profesor y estudiante tienen en comn es que ambos son tipos
particulares de persona. Es decir, la aparicin de un tercer concepto (persona) permite abordar
este hecho de la existencia de aspectos comunes entre los dos conceptos previos (profesor
y estudiante). Al modelar un problema como el antes ejemplificado, en posteriores anlisis y
aplicando otros principios, como por ejemplo el de Jerarqua, para casos como este es posible
determinar las relaciones entre profesor y persona y entre estudiante y persona.
Hacer slo nfasis sobre los detalles importantes relativos al contexto que se est modelando
y dejar por alto los aspectos no relevantes, es otro criterio a tener en cuenta cuando se hace
abstraccin. Por ejemplo, si el contexto a tratar es el vehculo automotor y se ha identificado el
motor como un concepto propio de este problema tratado, no se requiere identificar al pistn del
motor como un concepto ya que no tiene relevancia para el vehculo automotor. Sin embargo,
si ahora el contexto a modelar es el motor entonces un pistn s tiene relevancia.
Nota: La abstraccin se implementa mediante la definicin de clases.
El Encapsulamiento. Encapsular significa encerrar, proteger, esconder. El objetivo con este
principio es buscar no hacer visible a los usuarios finales de los objetos, todos los detalles
correspondientes que no contribuyen al entendimiento de las caractersticas esenciales de
esos objetos. Por ejemplo, imaginemos que un televisor o cualquier otro aparato electrnico,
estuviera sin la carcasa que protege la electrnica y circuitos del aparato. Cmo se puede
garantizar que el usuario utilice correctamente el aparato electrnico de esta manera? Est
claro que el aparato se construye tomando en cuenta lo que un usuario final del mismo puede
29

VOLVER A MEN

y no puede ver y tocar. La misma situacin ocurre con la definicin de los conceptos en OO.
El principio de encapsulamiento se refiere entonces a la capacidad para determinar qu
informacin debe estar escondida y cul no debe de estarlo. Se refiere a incluir dentro de un
objeto todo lo que ste necesita y hacerlo de forma tal que ningn otro objeto vea su estructura
interna. Ntese que, de la misma manera que ocurre con el televisor en el ejemplo antes descrito,
llevar este enfoque a la programacin permite proteger los datos para que stos no sean
usados de manera arbitraria. Esto, adems, tiene una influencia positiva en el mantenimiento
de los programas, ya que al encapsular informacin, los cambios a los programas pueden
ser realizados con menos esfuerzo comparado con los enfoques tradicionales (no OO) de
programacin.
Respetar este principio tiene que ver con el hecho de no hacer visible (pblico) algo que
debe estar escondido (privado / protegido) y viceversa. Ntese que los dos extremos son
completamente indeseables. Es decir, tener todos los elementos pblicos es hacer lo mismo
que se haca antes de la llegada de la OO (es decir, caos como fue descrito en el Captulo
1); tener todos los elementos privados es como comprar una caja negra que no tiene ninguna
interaccin con el usuario (imagnese comprar un televisor que no tenga un botn para
encenderlo). En trminos de la OO, la visibilidad de los elementos de los objetos se denomina
interfaz del objeto o interfaz de los elementos de la clase.

Nota: El encapsulamiento se implementa mediante la interfaz de los elementos que


definen una clase.

No existe una frmula que pueda ser usada para determinar cunta de la informacin de un
objeto debe ser pblica y/o saber qu informacin debe ser pblica y cul debe ser privada.
Esto depende en gran medida del concepto relacionado con el objeto. Hay que tomar en
cuenta que, muy poca informacin y comportamiento pblico permite que el objeto sea simple
de usar pero puede limitar la flexibilidad y funcionalidad que se le quiera dar al objeto. Por el
contrario, mucha informacin o comportamiento pblico aumenta la complejidad de uso del
objeto, as como tambin la flexibilidad y uso del mismo. Por ejemplo, considere un televisor
donde la nica interfaz con el usuario sean tres botones: un botn para encender y apagar
el televisor, un botn para subir y bajar el volumen y un ltimo botn para cambiar de canal.
Ntese que un televisor con estas caractersticas es simple de usar, pero no es posible hacer
otras operaciones como por ejemplo la configuracin de la imagen, subir y bajar el volumen de
manera indistinta, cambiar de canal hacia arriba o hacia abajo del canal actual. Por el contrario,
imagnese ahora un televisor que contiene centenares de botones que le permiten hacer lo que
usted quiera con l. Es evidente que en este ltimo caso usar el equipo es ms complejo.
Una recomendacin es hacer un anlisis cerrado de cada concepto por separado y determinar
30

VOLVER A MEN

la sensatez o no de tener cierto dato u operacin pblico o privado en el contexto del problema
tratado. Por ejemplo, supngase se tiene un concepto para representar una persona y uno de
los datos asociados a la persona es el nombre; es de suponerse que cuando un objeto que
representa a una persona es creado, ste debe tener obligatoriamente un nombre asociado; la
pregunta a hacerse entonces es, puede el nombre de una persona ser cambiado luego que a
esta persona se le haya asignado un nombre en el momento de su creacin?
La Modularidad. La modularidad o resolucin de un problema basado en mdulos trata,
en esencia, de la descomposicin de un problema o sistema en un conjunto de unidades
discretas. Tiene mucho que ver con las ya conocidas programacin modular y programacin
estructurada. En conjunto con la abstraccin, que permite identificar las clases necesarias para
modelar el problema, la modularidad permite visualizar qu unidades indivisibles o mdulos
son requeridos para satisfacer las necesidades del problema. Estas unidades discretas son
los objetos. Esta relacin de creacin de objetos basado en clases se conoce con el nombre
de instanciacin de objetos.

Nota: La modularidad se implementa mediante la instanciacin de objetos, la


identificacin de relaciones entre clases y la manera en la cual stas, en conjunto,
resuelven el problema tratado.

De la referencia (Meyer, 1998) es posible extraer cinco criterios, cinco reglas y cinco principios
asociados a la modularidad que se resumen a continuacin.
Criterio 1 - Descomposicin modular: tiene que ver con la descomposicin del problema
de software en un pequeo nmero de sub-problemas menos complejos. Todas las partes se
interconectan mediante una estructura sencilla y suficientemente independientes para permitir
que el programa pueda seguir creciendo con facilidad (sin padecer problemas de dependencia
con los elementos existentes). Un ejemplo de ello lo representa el diseo top-down (desde
la visin macro a las visiones micros del problema); como contraejemplo se puede citar la
inicializacin global, ya que sta provoca una dependencia obligatoria entre el punto de
declaracin y el uso de las declaraciones.
Criterio 2 - Composicin modular: Favorece la produccin de elementos que se pueden
combinar libremente unos con otros para producir nuevos elementos, posiblemente en un
entorno bastante diferente de aqul en que fueron desarrollados inicialmente. Como ejemplo
se tiene la biblioteca de subprogramas y un contraejemplo es el uso de preprocesadores
(sentencias que se requieren ejecutar antes que se realice la compilacin y ejecucin de los
programas).
31

VOLVER A MEN

Criterio 3 - Comprensibilidad modular: Ayuda a producir software en el cual un lector


humano puede entender cada mdulo sin tener que conocer los otros. El ejemplo es una
biblioteca de subprogramas de ndole especfico (entrada / salida, manejo grfico, entre otros).
Un contraejemplo son las dependencias secuenciales ya que la lectura de un mdulo requiere
de la lectura de otro mdulo y as sucesivamente.
Criterio 4 - Continuidad modular: Se refiere a que un pequeo cambio en la especificacin
de un problema provoca slo cambios en un solo mdulo o en un pequeo nmero de mdulos
relacionados. Como ejemplo se pueden citar las constantes simblicas definidas previamente
en un programa (usar un smbolo PI en lugar del literal numrico 3.1416). El uso de estructuras
estticas como los arreglos que tienen un nmero finito y preestablecido de elementos,
representa un contraejemplo.
Criterio 5 - Proteccin modular: Tiene que ver con la produccin de arquitecturas en las
cuales el efecto de una situacin anormal (error, excepcin, etc.) que se da dentro de un
mdulo durante su ejecucin, queda confinado a dicho mdulo o en el peor caso, se propaga
slo a unos mdulos vecinos. La verificacin de los datos de entrada en su origen y el uso
de estructuras para el manejo de excepciones son un ejemplo. No hacer nada y dejar que las
excepciones aparezcan sin control es un contraejemplo.
Regla 1 Correspondencia directa: La estructura modular que resulta del proceso de
construccin de software debe ser compatible con cualquier otra estructura modular obtenida
en el proceso de modelado del problema.
Regla 2 Pocas interfaces: Cada mdulo debe comunicarse con el menor nmero de mdulos
posible.
Regla 3 Pequeas interfaces+: Si dos mdulos se comunican deben intercambiar la menor
cantidad de informacin posible.
Regla 4 Interfaces explcitas: Siempre que dos mdulos A y B se comuniquen, esto debe
ser conocido tanto en el contexto de A, como en el de B.
Regla 5 Ocultamiento de informacin: Cada mdulo debe tener explcitamente seleccionado
el subconjunto de informacin que est disponible para los otros mdulos. Ntese que esto
est muy relacionado con el principio de encapsulamiento.
Principio 1 Unidades modulares lingsticas: La identificacin de los mdulos debe
corresponderse con las unidades sintcticas en el lenguaje utilizado, relativos al contexto del
problema tratado. Por ejemplo, para representar un conjunto, el uso de la palabra conjunto
para identificar el mdulo es lo ms conveniente.
32

VOLVER A MEN

Principio 2 Auto-documentacin: Toda la informacin relativa a un mdulo debe formar


parte del mismo mdulo. El uso de comentarios en los programas y la identificacin apropiada
de variables son ejemplos de este principio.
Principio 3 Acceso uniforme: Todos los servicios ofrecidos por un mdulo deben estar
disponibles a travs de una notificacin uniforme sin importar la forma como stos estn
implementados. Por ejemplo, si se supone la existencia de las operaciones Abrir y Cerrar
de un mdulo cualquiera que representa una Caja, una manera uniforme de identificar estas
operaciones sera AbrirCaja y CerrarCaja. Una forma no homognea de hacerlo es decir
AbrirCaja y ElCierreDeLaCaja respectivamente. En OO, dado que el comportamiento est
asociado directamente a la definicin del objeto, la Caja sera la clase, se tendra un objeto
identificado por ejemplo como UnaCaja y las operaciones seran UnaCaja.Abrir y UnaCaja.
Cerrar.
Principio 4 Abierto / Cerrado: Los mdulos deben ser a la vez abiertos y cerrados. Abierto
para permitir que el mdulo pueda ser expandido (crecer). Cerrado para permitir que el mdulo
sea utilizado por otros mdulos (ver Criterio 1).
Principio 5 Eleccin nica: Siempre que un sistema de software permita un conjunto de
varias alternativas, habr un mdulo del sistema (y slo uno) que conozca acerca de la lista
completa de estas opciones. Un ejemplo lo determina la existencia de un mdulo principal que
controla la secuencia hacia los otros mdulos.

La Jerarqua. Jerarquizar es el acto o resultado de distinguir cundo un concepto es ms


general que otro. Por ejemplo, persona es ms general que estudiante, vehculo es ms general
que vehculo automotor, animal es ms general que mamfero. En los ejemplos anteriores,
estudiante, vehculo automotor y mamfero son especializaciones de los respectivos conceptos
ms generales. Por otro lado, jerarquizar tambin permite examinar si dos o ms conceptos
tienen algo en comn. Por ejemplo, mamfero, ave, anfibio, pez y reptil tienen en comn el
hecho que poseen un esqueleto, es decir, son animales vertebrados.
Al decir que un concepto es una especializacin de otro concepto que es ms general que
el primero, se est realmente afirmando que todas las instancias (objetos) del concepto ms
especfico son tambin instancias del concepto general. Un mamfero es tambin un vertebrado
que a su vez es un animal.
Por otro lado, la composicin o agregacin de conceptos en otros conceptos tambin es otra
manera de jerarquizar. Por ejemplo, el hecho de decir que un vertebrado tiene un esqueleto no
establece una especializacin de vertebrado en base al esqueleto ni viceversa; lo que indica
es que para tener un vertebrado hay que necesariamente tener un esqueleto. Ahora bien,
33

VOLVER A MEN

ntese que habiendo un esqueleto en vertebrado y, siendo mamfero una especializacin de


vertebrado entonces, por lo que se mencion en el prrafo anterior, mamfero tiene tambin
un esqueleto.

Nota: La especializacin es un tipo de jerarqua que permite indicar que un concepto


es un caso particular de otro concepto ms general. La composicin es otro tipo de
jerarqua que permite indicar que un concepto forma parte de otro concepto.

El uso de jerarquas no slo permite organizar los conceptos que resultan de la abstraccin
sino que adems, permite simplificar el entendimiento del problema.

Nota: La jerarqua se implementa mediante las relaciones de herencia y agregacin


entre clases.

Los Tipos. Mediante los tipos es posible agrupar o formar un conjunto de objetos con
caractersticas o comportamientos similares. La declaracin de variables de un tipo especfico
(entero, carcter, booleano) es una analoga de lo que establece este principio. Usar
correctamente este principio implica tener en cuenta que dos cosas que tienen la misma forma
abstracta son anlogas y, por lo tanto, son del mismo tipo.
Hay dos maneras en las cuales es posible no respetar este principio. En primer lugar, considerar
un concepto no como un tipo sino como un objeto. Por ejemplo, decir que persona es un
objeto que tiene una informacin asociada en lugar de decir que se trata de un tipo (una
clase) que permite instanciar tantos objetos como se desee. Supngase que en un problema
particular se requieren representar a dos personas A y B; si no existe un tipo asociado a
persona no se podrn considerar A y B como dos objetos diferentes del mismo tipo, la clase
que representa a persona en este caso. Irse por este camino implicara necesariamente definir
dos conceptos en lugar de uno, es decir, se requieren los conceptos persona-A y persona-B
que, muy probablemente tendrn una misma definicin.
La segunda manera en la cual es posible no respetar este principio tiene que ver con confundir
o creer que los datos que forman parte de un concepto o tipo es otro concepto diferente.
Dependiendo del contexto del problema que se est tratando, el nombre de una persona, por
ejemplo, es un dato asociado a la persona y no otro concepto.

34

VOLVER A MEN

En la literatura de la OO es posible hallar dos categoras de elementos que forman parte de un


tipo que, a su vez llega a ser una clase: 1) los datos asociados al concepto los cuales reciben
el nombre de atributos y 2) las operaciones o comportamiento asociado al concepto al cual se
le da el nombre de mtodos. Por ejemplo, para una vlvula, el estado (abierta / cerrada) es un
atributo mientras que abrir / cerrar la vlvula son dos mtodos.

Nota: Los tipos se implementan mediante la descripcin o identificacin de los


elementos que describen una clase: atributos y mtodos.

La Concurrencia. Es la propiedad que distingue un objeto activo de uno que no lo es. Permite
a diferentes objetos actuar al mismo tiempo y asumir el hecho que cada uno de ellos tiene su
propia autonoma. Usar correctamente este principio tiene que ver con el hecho de asumir que
los objetos pueden ser instanciados a medida que stos sean requeridos.

Nota: La concurrencia se implementa mediante el proceso de creacin o instanciacin


de objetos a partir de su clase y las propiedades de identidad y estado de los objetos
(ver en Seccin 2.4).
La Persistencia. Es la propiedad que un objeto tiene de existir en el tiempo y en
el espacio. En otras palabras, un objeto existe hasta que no sea ms necesitado
y el espacio que ocupa es reutilizado. Tomar en cuenta este principio es asumir el
hecho que cada objeto instanciado (creado) requiere de un espacio de memoria
que slo ser liberado en el momento en el cual el objeto es destruido o liberado.
Ntese que, basado en el principio de jerarqua, tanto la necesidad de espacio
para almacenar el objeto como su recuperacin, involucran tambin a los objetos
relacionados con el primero.
En la OO la construccin y destruccin de los objetos se tratan de manera particular mediante
mtodos explcitos definidos como parte del comportamiento del objeto. Los mtodos de
construccin se denominan constructores y los mtodos para la destruccin reciben el nombre
de destructores.

Nota: La persistencia se implementa mediante los procesos de construccin y


destruccin de los objetos definidos como parte del comportamiento de la clase.

35

VOLVER A MEN

El Polimorfismo. La mejor forma de explicar este principio es partiendo de un ejemplo.


Supongamos se quiere implementar un Robot y de l, se requiere contar con una operacin
o mtodo para que el Robot pueda comer. Supongamos adems que existen dos tipos de
comidas que puede el Robot comer: lquido y slido. Hay dos maneras de implementar la
operacin comer en los paradigmas no OO:
1) Implementar dos operaciones cada una de los cuales permite comer un tipo de comida
especfico; es decir, se tendran las operaciones ComerLquido(comida) y ComerSlido(comida).
2) Implementar una sola operacin y, adems de la comida, se debe pasar tambin como
parmetro el tipo de comida. Es decir, quedara como Comer(comida, tipo).
En ambos casos se est dejando al usuario del Robot el tener que decidir de qu tipo es la
comida especfica con la cual se quiere dar de comer al Robot y esto, no es lo ms apropiado.
Lo deseable es que el usuario tenga una nica operacin que reciba un nico parmetro y que
su implementacin sea capaz de detectar automticamente el tipo de la comida y saber qu
hacer al respecto. Es decir, lo deseable es que el usuario tenga como parte de la interfaz del
Robot la operacin Comer(comida).
El polimorfismo es el principio de la OO que permite, entre otras cosas, hacer que una operacin
adopte varias formas de implementacin haciendo uso de un mismo smbolo o identificador.
Las diferentes formas de implementacin son posibles slo cuando el contexto es claro y, en el
caso de las operaciones, esto se determina a travs de los parmetros. Ya sea que el nmero
de parmetros cambie o que el tipo de los parmetros sea diferente entonces el contexto es
claro y el polimorfismo es posible.
Lo til del polimorfismo es que para el usuario la visin del operador es una sola, el objeto se
encarga de decidir qu hacer y los detalles de implementacin quedan ocultos. Ntese que
una de las principales ventajas de poder hacer lo anterior es la extensibilidad. Suponga, en el
ejemplo del Robot, que se tiene en el futuro el nuevo tipo de comida Pur. Ntese que en los
dos casos de implementacin no OO mostrados anteriormente, se tienen cambios significativos
en la implementacin que afectan la visin del usuario. Para el caso (1) habra que definir la
operacin ComerPur(comida) y para el caso (2) habra que hacer modificaciones sobre la
operacin Comer(comida, tipo) existente. Mientras que para la solucin OO no hay impacto
sobre el usuario final ya que para l slo existe la operacin Comer(comida); el Robot sabr
cmo comer.
Adicionalmente a la extensibilidad, el contar con una implementacin ms compacta y clara es
tambin una ventaja del polimorfismo.
36

VOLVER A MEN

Ahora bien, lo anterior es slo una manera de hacer polimorfismo. Supongamos el siguiente
otro ejemplo; se desea implementar un nmero Complejo y, en esta implementacin, se desea
contar con la operacin de suma. Haciendo uso del paradigma no OO, la manera de implementar
la operacin de suma es definiendo una funcin del tipo C3 = Sumar(C1, C2), siendo C1, C2 y
C3 del tipo Complejo y Sumar la operacin para implementar la suma. Esto no tiene problema
alguno, sin embargo, cul es el smbolo que todo usuario reconoce como una suma? Est
claro que la respuesta es +. Ntese que sera muy deseable que la implementacin anterior
pudiera hacerse utilizando el smbolo +, es decir, C3 = C1 + C2. La buena noticia es que en
la OO esto s es posible haciendo uso de un tipo particular de polimorfismo conocido como
sobrecarga de operadores.
En general, existen cuatro maneras diferentes de hacer polimorfismo:
1) Especfico: Relativo a smbolos conocidos:
1.1) Coaccin: una abstraccin simple sirve como diferentes tipos mediante una
conversin de tipos implcitas.
2.1) Sobrecarga: Un identificador simple denota muchas abstracciones.
2) Universal: Relativo a operaciones con identificadores propios:
1.1)
2.1) Por parmetro: Una abstraccin opera uniformemente a travs de diferentes tipos.
3.1) Por inclusin: Una abstraccin opera a travs de una relacin de inclusin.
El polimorfismo especfico de coaccin es lo que ocurre, por ejemplo, en varios lenguajes de
programacin de alto nivel en la cual un mismo smbolo, como por ejemplo +, -, *, puede
ser usado por tipos de datos diferentes predefinidos en el lenguaje: entero, real, carcter,
booleano, cadena de caracteres, entre otros. El polimorfismo especfico por sobrecarga es
el que permite hacer lo ejemplificado anteriormente con Complejo. El polimorfismo universal
por parmetro es aqul que permite hacer lo relativo al ejemplo del Robot. Finalmente, el
polimorfismo universal por inclusin se da en una relacin de herencia de clases y va a ser
explicado ms adelante en este captulo.

Nota: El polimorfismo se implementa mediante la sobrecarga de funciones y


operadores.

37

VOLVER A MEN

2.3 Trmino tcnico


Enlace es el trmino utilizado por los entornos de desarrollo para que, una vez compilados
los programas, se resuelvan todos los aspectos inconclusos en relacin a direcciones de
memoria. El ejemplo ms comn es cuando se realiza en el programa la llamada a una funcin
o procedimiento que no ha sido an compilada y, por ende, no se conoce an la direccin de
memoria donde se encuentra el cdigo del cuerpo de la funcin. El enlace, por lo tanto, permite
conectar la llamada de la funcin con el cuerpo de la misma haciendo uso de la direccin
donde se encuentra el cdigo compilado de esta ltima.
Ahora bien, este proceso conocido con el nombre de enlazado, dependiendo del momento en
la cual se realice el proceso, se puede hacer de dos maneras: esttica y dinmicamente. El
enlace esttico es el ms comn y se realiza justo despus del proceso de compilacin para
preparar el ejecutable final del programa, es decir, se lleva a cabo antes de que el programa
se ejecute. El enlace dinmico, en cambio, se realiza durante la ejecucin del programa. La
Figura 2.1 presenta un diagrama esquemtico de lo que ocurre en uno y otro caso. En relacin
al enlace esttico, que se muestra en la parte izquierda de la Figura 2.1, se observa un bloque
de programa en cdigo de mquina (objeto) en la cual aparece la llamada de una funcin f en
la cual se coloca la direccin de memoria donde se encuentra el bloque de cdigo objeto de f
(de all el enlace). En la parte derecha se muestra un ejemplo de un enlace dinmico en la que
se observa la llamada al mensaje o.m que, en tiempo de ejecucin, origina primero un enlace
al objeto o para luego ubicar, en la tabla de smbolos virtual (tabla que se va actualizando a
medida que el programa se va ejecutando), la direccin de memoria del mtodo m.

Figura 2.1. Enlace esttico (izquierda) versus enlace dinmico (derecha).

Ntese que, como consecuencia de la necesidad de poder implementar el polimorfismo, no es


posible realizar el enlace de manera esttica ya que, habiendo la posibilidad de tener ms de
una versin diferente de la misma funcin (mismo identificador con parmetros diferentes), la
nica manera de saber cul es la funcin que corresponde para poder realizar el enlace, es
tener claro el contexto, es decir, el valor de los parmetros. Por lo tanto, para poder dar soporte
38

VOLVER A MEN

al principio de polimorfismo, la OO requiere necesariamente que el enlace sea dinmico. En


este sentido, el enlace dinmico representa uno de los mayores avances de la OO y es una
propiedad comn (no necesaria) de los lenguajes OO.

2.4 Conceptos bsicos de la OO


Tal como se mencion al comienzo de este captulo, los tres conceptos bsicos de la OO
son: objeto, clase y herencia. En esta seccin se describen los aspectos ms importantes
inherentes a estos conceptos.
El Objeto. Son las unidades bsicas de construccin de un producto OO: conceptualizacin,
diseo o programacin. Los objetos son instancias organizadas que se originan de la definicin
abstracta representada por las clases. Todos los objetos definidos de una misma clase, al
tener el mismo origen, poseen caractersticas comunes.

Nota: Desde muy temprana edad el ser humano aprende mediante el reconocimiento
de objetos. Por lo tanto, la OO es una manera ms natural de describir y entender
un problema.

Desde la perspectiva de un programa, los objetos son mdulos que contienen datos (atributos)
y las instrucciones u operaciones (mtodos) que operan sobre esos datos y trabajan juntos
para proveer funcionalidad. Conceptualmente hablando, los objetos tienen las siguientes tres
caractersticas: estado, identidad y comportamiento.

El estado de un objeto es aqul que contiene todas las propiedades del objeto dadas estas por
sus atributos (definidos usualmente de manera esttica), adems de los valores actuales de
estas propiedades (dados usualmente de manera dinmica). Esta combinacin de atributos y
valores hace que cada objeto sea nico y, adicionalmente, la respuesta que un objeto puede
dar en cualquier instante luego de su creacin y posteriormente de la llamada de un mensaje,
depende de este estado actual que el objeto tenga.
39

VOLVER A MEN

Nota: Esta caracterstica del objeto determina cmo son los nodos en un diagrama
de transicin de estados del objeto (ver UML en Captulo 3).

Algunos ejemplos de posibles atributos para un objeto son: color, peso, edad, nmero de tems,
profesin. Posibles valores respectivos que estos atributos pueden tener son: rojo, 75.4 Kg, 24
aos, 190, arquitecto. Luego, un estado posible de un objeto cualquiera con las caractersticas
(atributos) antes mencionadas es aqul en la cual: color = rojo, peso = 75.4, edad = 24, nmero
de tems = 90 y profesin = arquitecto.
Nota: El estado de un objeto est ntimamente relacionado con el principio de
persistencia.

La identidad de un objeto es aquella que permite distinguir un objeto del resto. Es completamente
no dependiente del estado en el sentido que, aun y cuando se realicen cambios de estado, la
identidad se conserva durante toda la vida del objeto. Por otro lado, la identidad o identificador
del objeto es la base para construir o armar un mensaje hacia el objeto correspondiente (ms
detalles aparecen a continuacin). X1, X2, MiVentana y Mauricio_Paletta son ejemplos de
posibles identidades para objetos.
Nota: La identidad de un objeto est ntimamente relacionada con el principio de
concurrencia.

Nota: La identidad de un objeto es una propiedad muy importante para las Bases
de Datos Orientadas a Objetos (OODB de sus siglas en ingls Object-Oriented
DataBase) (Atwood, 1985) (Derrett et al, 1985) (Maier et al, 1985).

El comportamiento de un objeto indica la manera en la cual el objeto acta y reacciona en


trmino de sus cambios de estado. Por lo tanto, est totalmente definido por sus acciones u
operaciones. En este sentido, es importante tener claro y saber diferenciar los dos conceptos
relacionados con esta caracterstica: mtodo y mensaje.
El Mtodo es un procedimiento que reside en el objeto gracias a su previa definicin en la
clase utilizada para instanciar el objeto. Determina cmo el objeto actuar cuando ste reciba
un mensaje. Su ejecucin (una vez que se convierta en mensaje) puede cambiar el estado del
objeto, crear objetos nuevos, enviar mensajes a otros objetos, etc.
40

VOLVER A MEN

Por otro lado, el Mensaje es el canal de comunicacin que usan los objetos para ejecutar
acciones. Por lo tanto, slo cuando se recibe un mensaje, el objeto ejecuta una accin (un
mtodo). En este sentido, todo proceso es activado por mensajes entre objetos. La identidad
del objeto permite direccionar el mensaje al objeto especfico deseado.

La Figura 2.2 muestra un esquema de la manera en la cual se ve de forma general un programa


OO. Cada objeto, con sus atributos y mtodos, se comunican con otros objetos a travs de
mensajes. Los objetos, a su vez, se organizan en paquetes o mdulos basados stos en la
descomposicin que se haya realizado del problema.

Figura 2.2. Vista genrica de un programa OO.

A fin de garantizar la proteccin de los elementos privados de los objetos, es decir, respetar el
principio de encapsulamiento, es muy comn observar en la literatura un par de mtodos para
leer o cambiar el estado del objeto (es decir, sus atributos). Por lo general estos mtodos son
identificados como Obtener o Get para leer el estado y Asignar o Set para cambiar el estado
(ver Figura 2.3).

41

VOLVER A MEN

Tal como se puede observar en la Figura 2.3, un objeto suele representarse como una fortaleza
(encapsulamiento) que rodea y protege principalmente a la data (atributos), y slo los mtodos
son las llaves para abrir las puertas de entrada / salida de la fortaleza. Luego, para poder entrar
a la fortaleza haciendo uso de alguno de los canales de entrada o llaves representados por los
mtodos, se requiere un mensaje correspondiente.
Nota: El comportamiento de un objeto est ntimamente relacionado con los principios
de encapsulamiento y polimorfismo.

Figura 2.3. El encapsulamiento y los mtodos tpicos Set / Get.

Nota: Dado la analoga que se le da a los mensajes entre objetos con los sistemas de
comunicacin, se define el protocolo de un objeto como el conjunto de mensajes a los
cuales un objeto responde o puede responder. Ntese que esto no necesariamente
es igual al conjunto de mtodos del objeto (producto del encapsulamiento en la cual
pueden existir mtodos no pblicos y el polimorfismo en el cual pueden existir varios
mtodos que se denominan igual pero para efectos de los mensajes que se pueden
enviar, es uno solo).
La Clase. En la OO, la clase es la que define la estructura y el comportamiento de una forma
abstracta o concepto para darle vida a los objetos. Es trminos bsicos y generales, se trata
de un patrn (plantilla) que define los atributos y mtodos a ser incluidos en un tipo particular
de objeto. En la literatura asociada a la OO, esta relacin entre clase y objeto se suele llamar
instancia: se dice que un objeto es una instancia particular de una clase.
Desde el punto de vista de estructura de datos, la clase consta de dos partes: una de
declaracin y una de implementacin. La parte de declaracin contiene la lista de elementos
que pertenecen a la clase, es decir, identifica los atributos y mtodos que son miembros de la
42

VOLVER A MEN

clase. La implementacin tiene que ver con la definicin del cuerpo de los mtodos de la clase.

Nota: El concepto de clase tiene relacin directa con los principios de abstraccin,
jerarqua, modularidad y tipos.

Por otro lado y, para soportar el principio de encapsulamiento, a cada uno de los elementos
que se definen en una clase se les debe indicar el nivel de visibilidad y/o proteccin que tiene
el elemento. A esta caracterstica se le llama interfaz de la clase. En este sentido, existen tres
posibles usuarios para acceder a los elementos de una clase:
1) Los elementos de la misma clase.
2) Los elementos de otras clases que tienen alguna relacin con la clase que se est
definiendo (basado en el principio de jerarqua).
3) Cualquier otro elemento externo a la clase.
Cabe mencionar que no existe un estndar relativo a lo que se considera el segundo tipo de
usuario antes mencionado; es decir, la manera en la cual se relacionan las clases puede variar
entre un contexto de implementacin y otro. Por ejemplo, en el lenguaje de programacin C++,
la relacin entre clases se establece con la herencia y, en el lenguaje de programacin Java
esta relacin se establece en clases que pertenecen al mismo paquete.

Nota: El paquete es un mecanismo de agrupamiento para organizar elementos del


modelo en grupos semnticamente relacionados.

As como hay tres tipos de usuarios de los elementos de una clase se tienen entonces tres
niveles de interfaz que determinan el nivel de visibilidad de estos elementos (ver Tabla 2.1):
privado, protegido y pblico.

43

VOLVER A MEN

Tabla 2.1. Tipos de interfaz de los elementos de una clase.

Privada

Visibilidad slo para elementos de la misma


clase

Protegida

Visibilidad para elementos de la misma clase


y clases relacionadas

Pblica

Visibilidad para cualquier elemento

Nota: Una clase con todos sus elementos privados no sirve para nada ya que no hay
manera de interactuar con los objetos correspondientes (se trata de una caja negra).
Una clase con todos sus elementos pblicos viola completamente el principio de
encapsulamiento y es lo mismo que hacer modelado no OO.

En otro orden de ideas, basado en el principio de jerarqua, para la definicin de una clase
nueva se pueden usar clases existentes, ya sea incorporando un objeto como atributo de la
clase (agregacin) o haciendo una especializacin de la clase mediante el establecimiento de
una generalizacin (herencia).
Supongamos se tienen las clases A y B; se dice que A es una agregacin de B si y slo si A
forma parte de B. Es decir, B contiene un objeto de A como atributo. Disco duro forma parte de
computador personal es un ejemplo de agregacin. Por otro lado, se dice que A hereda de B
si y slo si A es un caso particular de B; es decir, B es una generalizacin de A. Memoria RAM
es un caso particular de memoria es un ejemplo de este tipo de relacin entre clases.
La Figura 2.4 muestra un ejemplo de un diagrama de clases escrito en UML en la cual se
aprecian las siguientes relaciones de generalizacin: Avin, Avioneta y Helicptero son casos
particulares de Aeronave. Tambin se representan las siguientes relaciones de agregacin:
Torre, Puerta, Plataforma, Hangar y Pista forman parte de Aeropuerto. Ntese la sintaxis UML
utilizada para representar ambos tipos de relacin. El Captulo 3 aborda la temtica relativa a
UML.

44

VOLVER A MEN

Figura 2.4. Ejemplo de diagrama de clases escrito en UML.

La Herencia. Se trata de una de los dos tipos de relacin que fueron descritas anteriormente.
La herencia permite crear clases nuevas a partir de clases ya existentes, estableciendo niveles
de jerarqua y programando slo las diferencias. Adicionalmente, la herencia permite refinar las
estructuras sin necesidad de duplicar informacin. Esta definicin de clases nuevas partiendo
de clases existentes se puede realizar de dos maneras:
1) Especializar una clase existente. Por ejemplo, se tiene una clase para representar a una
Persona y se desea una clase para representar un Trabajador.
2) Generalizar en un concepto aspectos comunes de una o ms clases existentes. Por
ejemplo, se tienen las clases Equipo-Audio y Equipo-Video y se quiere tener la clase
ms general Equipo que contenga los aspectos comunes de las dos primeras.
La clase que est en la jerarqua inferior (ms especializada) es conocida como subclase o
clase derivada y la clase de mayor nivel (ms genrica) se denomina superclase o clase base.
Por otro lado, la especializacin de un concepto puede involucrar ms de una clase base.
Por ejemplo, un Equipo-Audiovisual combina las caractersticas de un Equipo-Audio con un
Equipo-Video. En este sentido, segn el nmero de clases base involucradas en la relacin, la
herencia puede ser de dos tipos: simple (cuando hay una sola clase base) y mltiple (cuando
hay ms de una clase base). El ejemplo de la Figura 2.5 muestra otro diagrama de clases UML
en la cual tienen las siguientes herencias simples: Ventana con desplazamiento es un caso
particular de Ventana, Lienzo es un caso particular de Ventana, Panel es un caso particular
de Ventana, Ventana de texto es un caso particular de Ventana con desplazamiento, Item
45

VOLVER A MEN

de texto es un caso particular de Item de Panel, Item seleccionable es un caso particular de


Item de Panel, Botn es un caso particular de Item de Panel, Lnea es un caso particular
de Forma, Figura cerrada es un caso particular de Forma, Elipse es un caso particular de
Figura cerrada y Polgono es un caso particular de Figura cerrada. El ejemplo de la misma
figura contiene tambin la herencia mltiple en la cual la clase Lienzo con desplazamiento se
especializa tanto de la clase Lienzo como de la clase Ventana con desplazamiento.
Nota: La lectura de una relacin de herencia corresponde a: 1) una generalizacin desde la
clase conectada al extremo sin flecha hacia la clase conectada al extremo con flecha; 2) una
especializacin desde la clase conectada al extremo con flecha hacia la clase conectada
al extremo con flecha. En el ejemplo de la Figura 2.5, Ventana es una generalizacin de
Lienzo; Lienzo es una especializacin de Ventana.

Figura 2.5. Ejemplo de diagrama de clases escrito en UML.

46

VOLVER A MEN

Es importante tener en cuenta que la relacin de herencia hace que la clase derivada contenga
el total de elementos que forman parte de las clases base del cual sta hereda. Mientras
ms clases base haya ms elementos se agregan a la clase derivada. Adicionalmente, la
clase derivada puede o no tener otros elementos nuevos que permiten especializar esta clase.
Ntese que en una herencia simple no tiene sentido que la clase derivada no contenga nuevos
elementos ya que las dos clases seran exactamente igual.

Nota: Una clase derivada que hereda de una sola clase base (herencia simple) y no
agrega nuevos elementos no tiene sentido ya que la subclase y la superclase seran
exactamente iguales.

En otro orden de ideas, si el identificador de un mtodo o atributo de una clase cualquiera es


similar a alguno de los elementos de una posible clase base, cualquier referencia que se haga a
este identificador en la subclase estar asociado al elemento de la clase derivada, a menos que
se indique lo contrario (esto depende de los esquemas de implementacin). Supongamos, por
ejemplo, que la clase Equipo tiene un atributo identificado con el nombre Serial. Supongamos
tambin que se tiene la relacin de herencia Equipo-Audio es un caso particular de Equipo
y que adems, la clase Equipo-Audio posee tambin un atributo identificado con el nombre
Serial. Entonces, cada vez que se haga referencia al Serial en Equipo-Audio esta referencia
estar asociada al Serial definido en Equipo-Audio y no en el elemento Serial definido en
Equipo.
Un caso particular de lo antes descrito ocurre cuando hay un mtodo que se identifica de la
misma manera en la subclase y la superclase. En este caso ocurre lo que se denomina un
polimorfismo universal por inclusin. Es el cuarto tipo de polimorfismo que qued pendiente en
ser explicado en la Seccin 2.2.

Nota: Cuando un objeto recibe un mensaje que contiene un mtodo que no est
definido directamente en la clase correspondiente, se busca automticamente hacia
arriba en su jerarqua de clases. Igual ocurre al acceder a un atributo del objeto.

47

VOLVER A MEN

En ocasiones es posible que se tenga la necesidad de definir clases con niveles de generalizacin
tan altos que no es posible implementar algunas operaciones. Por ejemplo, supongamos
que se quiere representar con una clase el concepto Figura y en ella, la operacin (mtodo)
dibujar. Ntese que no es posible dibujar una figura si antes no se sabe qu tipo de figura
es: crculo, rectngulo, cuadrado, etc. En estos casos y, dado el hecho que la clase no est
definida completamente, no tiene sentido la instanciacin de objetos. Este tipo de clases cuya
descripcin es incompleta se denominan clases abstractas. Las clases abstractas slo sirven
para ser usadas como clases base en una relacin de herencia y, por ende, para especializar
otras clases que permitan completar lo que falta indicar en la clase abstracta base. Aquellos
mtodos que forman parte de una clase abstracta y no han sido implementados an, reciben
el nombre de mtodos virtuales.
En trminos de diseo, las clases abstractas son tiles para permitir a un usuario refinar o
particularizar un concepto. Por lo general contienen uno o ms mtodos virtuales, aunque
no es obligatorio. Los mtodos virtuales deben ser obligatoriamente definidos en las clases
derivadas; de no hacerse, entonces la clase derivada tambin pasa a ser una clase abstracta.
En algunos casos, los mtodos virtuales contenidos en una clase A pueden tener implementacin.
Una subclase B que hereda de A puede luego hacer una redefinicin del mtodo virtual. Un
mtodo virtual que no tiene implementacin y su definicin es obligatoria en las clases derivadas
recibe el nombre de mtodo virtual puro. Por el contrario, un mtodo virtual que si tiene una
implementacin pero que puede ser redefinida en las subclases recibe el nombre de mtodo
virtual no puro.

Nota: Un mtodo no virtual de una clase base y otro similar (polimrfico) de la clase
derivada ocupan ambos espacios de memoria independientes. En un mtodo virtual
slo se tiene el espacio de memoria ocupado por la redefinicin que se realiza en la
clase derivada.

La Figura 2.6 presenta un modelo de clases UML con un ejemplo de la clase abstracta Motores.
Dado el hecho que no es posible saberlo sin antes conocer el tipo especfico de motor del cual
se est instanciando un objeto, Encender y Apagar el motor son dos posibles ejemplos de
mtodos virtuales puros asociados a esta clase.

48

VOLVER A MEN

Figura 2.6. Ejemplo de diagrama de clases escrito en UML.

Por otro lado, en la herencia de clases tambin est involucrado el trmino de interfaz de los
elementos de la clase. En este caso se denomina interfaz de la herencia y permite determinar
qu criterio de interfaz (pblico, privado o protegido) se va a usar para los usuarios de la clase
derivada en relacin a los elementos pblicos de la clase base. Supongamos por ejemplo que
una clase A tiene un elemento pblico A.p por lo cual, cualquier usuario puede tener acceso
a este elemento; supongamos tambin que se tiene la herencia B es un caso particular de A;
la interfaz de herencia permite indicar cmo se desea manejar o, mejor dicho, qu nivel de
visibilidad se le quiere dar al elemento A.p, que ahora est incluido tambin en B (ver Figura
2.7).

Figura 2.7. Ejemplo de diagrama de clases escrito en UML.

49

VOLVER A MEN

En este sentido y dado que existen tres niveles de interfaz (pblico, privado y protegido), la
interfaz de herencia puede estar asociada a cada uno de estos tres posibles niveles. El pblico
hace que los elementos pblicos de la clase base sean tratados igualmente como pblicos en
la clase derivada (es el escenario ms comn); el privado hace que los elementos pblicos de
la clase base sean tratados como privados en la clase derivada; y el protegido hace que los
elementos pblicos de la clase base sean tratados como protegidos en la clase derivada.
Nota: No todos los lenguajes de programacin manejan los tres niveles de interfaz
de herencia. C++ maneja los tres casos; Java y C# asumen siempre el caso de
interfaz pblica.

2.5 Ejercicios
1) Identifique y explique 4 principios de la OO.
2) En qu consisten el estado, la identidad y el comportamiento de un objeto?
3) Explique la diferencia entre las interfaces pblica, protegida y privada de una clase.
4) En qu consisten los principios OO de Abstraccin, Encapsulamiento, Persistencia y
Polimorfismo?
5) A qu se conoce como el Protocolo del Objeto?
6) Explique los conceptos de clase abstracta y mtodo virtual.
7) Identifique y explique las caractersticas o elementos que forman parte de un objeto.
8) Explique la diferencia entre los siguientes conceptos
a) Mtodo y mensaje.
b) Herencia simple y herencia mltiple.
c) Clase y objeto.
d) Interfaz pblica e interfaz privada de una clase.
9) Enumere y defina los tres conceptos bsicos de la OO?
10) Escriba un ejemplo de herencia simple y otro de herencia mltiple.
11) El encapsulamiento est asociado a: ?
a) Permitir que los datos puedan ser vistos y usados.
b) Permitir que los datos no puedan ser vistos ni usados.
c) Permitir que algunos datos puedan ser vistos y usados y otros no.
d) Ninguna de las anteriores.
50

VOLVER A MEN

12) Cul de las siguientes observaciones relativas a la modularidad es verdadera?


a) Se refiere a la descomposicin de un sistema en un conjunto de unidades.
b) Est ntimamente relacionada con la abstraccin.
c) Ni la opcin (a) ni la opcin (b).
d) Tanto la opcin (a) como la opcin (b).
13) Cul de las siguientes observaciones relativas a la jerarqua es verdadera?
a) Permite examinar si los conceptos tienen algo en comn.
b) Representa el ordenamiento de la abstraccin.
c) Ni la opcin (a) ni la opcin (b).
d) Tanto la opcin (a) como la opcin (b).
14) Cul de las siguientes analogas es correcta?
a) Tipo y Clase.
b) Tipo y Objeto.
c) Ni la opcin (a) ni la opcin (b).
d) Tanto la opcin (a) como la opcin (b).
15) Cul de las siguientes observaciones relativas a la concurrencia no es correcta?
a) Distingue un objeto activo de uno que no lo es.
b) Permite a diferentes clases actuar al mismo tiempo.
c) Hace que cada objeto tenga autonoma.
d) Las tres opciones son correctas.
16) Cul de las siguientes observaciones relativas a la persistencia no es correcta?
a) Propiedad de un objeto de existir en el tiempo y en el espacio.
b) Se implementa mediante la construccin y destruccin de los objetos.
c) Est ntimamente relacionado con la modularidad.
d) Tiene que ver con la memoria ocupada por el objeto y su vida til.
17) El polimorfismo es til porque:
a) Hace que el cdigo sea extensible.
b) Hace que la interfaz con el usuario sea ms clara.
c) Ni la opcin (a) ni la opcin (b).
51

VOLVER A MEN

d) Tanto la opcin (a) como la (b).


18) Cules son las tres caractersticas de los objetos?
a) Estado, comportamiento e identidad.
b) Mtodo, mensaje y protocolo.
c) Clase, herencia simple y herencia mltiple.
d) Atributos, operaciones e interfaz.
19) Cul de las siguientes expresiones es correcta?
a) Objeto = Mtodo + Mensaje + Parmetros.
b) Mtodo = Objeto + Mensaje + Parmetros.
c) Mensaje = Objeto + Mtodo + Parmetros.
d) Ninguna es correcta.
20) Cul de las siguientes afirmaciones es correcta?
a) Una clase es la instancia de un objeto.
b) Un objeto es la clase de una instancia.
c) Una instancia es el objeto de una clase.
d) Un objeto es la instancia de una clase.
21) Cuando los atributos y mtodos de una clase son visibles slo para las clases relacionadas
con sta, se dice que la interfaz es: ?
a) Pblica.
b) Protegida.
c) Privada.
d) No es posible este caso.
22) El siguiente ejemplo de herencia representa:
a) Una herencia simple.
b) Una herencia mltiple.
c) Mltiples herencias simples.
23) Cul de las siguientes observaciones referidas a una clase abstracta es falsa?
a) No es posible instanciar objetos con ellas.
b) Pueden ser tanto clases base como clases derivadas.
52

VOLVER A MEN

c) Tanto (a) como (b) son verdaderas.


d) Tanto (a) como (b) son falsas.
24) Un mtodo virtual es aqul que: ?
a) Pertenece a una clase abstracta.
b) Es usado en la ejecucin de mensajes con objetos virtuales.
c) Puede ser redefinido de forma polimrfica con mtodos similares en clases derivadas.
d) Tanto (a) como (b) como (c).
25) A continuacin se da una lista de principios de la OO y otra de conceptos o trminos
usados en la implementacin. Haga una asociacin 1:1 entre los tems de la primera lista
y los tems de la segunda lista.
Encapsulamiento Clase
Modularidad Objeto
Jerarqua

Herencia

Tipos Interfaz
Persistencia Constructor
Polimorfismo

Sobrecarga de operadores

26) Defina brevemente cada uno de los siguientes conceptos.


a) Enlace dinmico.
b) Protocolo del objeto.
c) Herencia mltiple.
d) Mensaje.
e) Polimorfismo por inclusin.

53

VOLVER A MEN

Captulo 3

UML: El Lenguaje de Modelado Unificado


3.1 Introduccin
UML (http://www.uml.org/), acrnimo de sus siglas en ingls Unified Modeling Language
(Lenguaje de Modelado Unificado) es el estndar mundialmente aceptado para escribir
modelos haciendo uso de un enfoque OO (avalado por el organismo de estndares asociado
a la tecnologa de objetos OMG u Object Management Group - http://www.omg.org/). La
razn del por qu un estndar se debe a la gran diversidad de opciones que lleg a haber
en la dcada de los 90s, lo cual motiv la necesidad de buscar y definir un estndar. Para
mayor informacin, (Grahan, 1996) es una referencia interesante para revisar este historial de
mtodos OO. La motivacin de estandarizar viene de tres de los autores de estos mtodos ya
existentes: Grady Booch y su mtodo de Booch (Booch, 1993), James Rumbaugh y el mtodo
OMT (Object-Modeling Technique) (Rumbaugh et al, 1990) e Ivar Jacobson y el mtodo OOSE
(Object-Oriented Software Engineering) (Jacobson et al, 1992).
Hay una extensa cantidad de bibliografa relativa al modelado de problemas con UML, muchos
libros de los cuales fueron escritos individualmente o en conjunto por los tres autores anteriores.
El objetivo de este captulo es presentar un breve resumen de los aspectos ms importantes a
tener en cuenta en relacin a UML y, servir de apoyo para la elaboracin del Captulo 6 referido
al modelado y programacin de problemas.
Es importante tener en cuenta que, como su nombre lo indica, UML es un lenguaje y, como tal,
tiene una sintaxis y una semntica. Por lo tanto, para escribir o modelar haciendo uso de UML
es necesario conocer la sintaxis y saber la semntica de interpretacin de lo que se escribe.
La sintaxis nos dice cules son los smbolos que se pueden usar y cmo stos se pueden
combinar; la semntica nos da las reglas de interpretacin de cada smbolo segn el contexto
donde se use. Gran parte de la sintaxis de UML se compone de smbolos grficos y, un mismo
smbolo siempre se interpreta de la misma manera en cualquiera de los contextos o diagramas
donde ste se utilice.
Adicionalmente, hay que tomar tambin en cuenta que UML no es una metodologa, es decir,
no describe los pasos a seguir para elaborar un modelo. En la actualidad existen una variedad
de metodologas para el modelado y resolucin de problemas, muchas de ellas integran a
UML dentro de sus procesos para modelar o describir sistemas de una gran variedad de tipos
(principalmente de software). En lo que respecta al desarrollo de un sistema de software,
UML contempla desde la especificacin de requerimientos (lo que hay que hacer) hasta las
54

VOLVER A MEN

pruebas finales del producto. En el Captulo 4 se propone una metodologa para la resolucin
de problemas que requieren un componente de software.
Nota: UML es un lenguaje y como tal tiene una sintaxis y una semntica. La mayora
de la sintaxis est representada por smbolos grficos.

Adems de servir como estndar para el modelado de sistemas, principalmente de software,


otro de los principales objetivos de UML es el de contar con un lenguaje utilizable no slo por
los humanos sino tambin por las mquinas. Esta caracterstica permite el que se tengan, por
ejemplo, herramientas para la generacin de cdigo automtico en la cual, partiendo de un
modelo escrito en UML, es posible escribir los programas correspondientes en algn lenguaje
de programacin OO.
Cabe mencionar que, al ser UML un lenguaje grfico, la mejor manera de trabajar con l es
utilizando una herramienta automatizada. En la actualidad hay una variedad diferentes de
herramientas, algunas de ellas comerciales y otras de distribucin gratuita enmarcadas en el
contexto de la comunidad de software libre. Para el contenido UML que se encuentra en este
libro, se utiliz StarUMLTM versin 5.0.2.1570 (http://staruml.sourceforge.net/en/).

3.2 Componentes
UML est estructurado en cuatro componentes o elementos. Desde un punto de vista macro
a uno ms detallado, estos componentes son: las Vistas, los Diagramas, los elementos del
modelo y los mecanismos generales.
Las Vistas muestran diferentes aspectos del sistema que se est modelando. Su objetivo
es representar una abstraccin que consiste en un nmero determinado de Diagramas. Las
Vistas permiten, adems, enlazar el modelo al proceso escogido para el desarrollo, es decir,
acoplarse a la metodologa que se est usando para realizar el proceso. No existe un nmero
especfico de Vistas que se tienen para agrupar los Diagramas. Puede variar entre un autor
y otro, por lo general en un mnimo de tres y un mximo de cinco Vistas. Las cinco posibles
Vistas que se pueden tener son las siguientes:
1) Vista de Casos de Uso: muestra la funcionalidad del sistema tal como es percibida por
los actores externos. Tiene que ver con la representacin de los requerimientos del
sistema que se va a modelar. Puede estar asociado a las etapas de levantamiento de
informacin y anlisis de requerimientos propias de la metodologa utilizada.
55

VOLVER A MEN

2) Vista lgica: muestra cmo est diseada dentro del sistema la funcionalidad en trminos
de las estructuras estticas y el comportamiento dinmico. Representa la parte conceptual
del modelo. Est asociado a las etapas de anlisis y diseo de la metodologa utilizada.
3) Vista de componentes: muestra la organizacin de los componentes de cdigo para
construir un sistema de software. Tambin puede asociarse a la etapa de diseo de la
metodologa utilizada.
4) Vista de concurrencia: muestra la concurrencia (comunicacin y sincronizacin) en
el sistema. Representa la parte dinmica o de comportamiento del modelo. Permite
demostrar la satisfaccin de los requerimientos previamente obtenidos. Pudiera estar
asociado a las posibles etapas de pruebas que bien pueda tener la metodologa utilizada.
5) Vista de despliegue: muestra el despliegue o distribucin del sistema en la arquitectura
fsica (computadoras y otros dispositivos). Pudiera ser usado como parte de la etapa de
diseo en la metodologa utilizada.
Los Diagramas son los componentes principales de UML. Son elementos grficos que describen
el contenido de una Vista. En la versin 2.0 de UML se reconocen los siguientes 9 tipos de
diagramas que sern descritos en detalle ms adelante:
1) Diagrama de Casos de Uso.
2) Diagrama de Actividades.
3) Diagrama de Clases.
4) Diagrama de Objetos.
5) Diagramas de Estados.
6) Diagrama de Colaboracin.
7) Diagrama de Secuencias.
8) Diagrama de Componentes.
9) Diagrama de Distribucin.
Los elementos del modelo son los que representan la sintaxis de UML y, por lo tanto, estn
asociados a los conceptos usados en los Diagramas, comnmente a conceptos de la OO:
clase, objeto, interfaz, mensaje, etc.

Nota: Un smbolo sintctico o elemento del modelo siempre tiene el mismo significado
en cualquiera de los Diagramas donde ste es utilizado.

56

VOLVER A MEN

Finalmente, los mecanismos generales proveen comentarios, informacin u otros


extras a los elementos del modelo que pueden ser agregados a los Diagramas.

3.3 Diagramas
A continuacin un detalle de los nueve Diagramas que forman parte de la versin 2.0 de UML.
Diagrama de casos de uso. Forma parte de la Vista de Casos de Uso y permite describir
lo que un nuevo sistema debe hacer o lo que un sistema existente hace en trmino de los
requerimientos y funcionalidades. Es una estructura que ayuda a los analistas a trabajar con
los usuarios para determinar la forma en que se usar el sistema a modelar. Adicionalmente,
es una excelente herramienta para estimular a que los usuarios potenciales hablen del sistema
desde sus propios puntos de vista.
En trminos generales el diagrama de casos de uso permite describir el sistema como una
caja negra que provee casos de uso o funcionalidades. El objetivo es que el sistema sea visto
por el usuario desde un punto de vista de interaccin y no los detalles internos del mismo.
Desde el punto de vista sintctico, estos diagramas estn constituidos por tres elementos:
1) Actor: representa un tipo o categora de usuario o cualquier elemento que interacta con
el sistema. Define, adems, los papeles diferentes que un usuario puede desempear:
operador, administrador, gerente, etc. El conjunto de actores, en general, representa
a todo aquello que necesita intercambiar informacin con el software que se est
analizando: usuarios, base de datos, otros sistemas, hardware especializado, entre
otros. La notacin o smbolo sintctico utilizado es el que se muestra en la Figura 3.1.

Figura 3.1. Smbolo para identificar un actor.

Nota: Actor no necesariamente est relacionado con usuario (persona humana) sino
con cualquier elemento que interacta con el sistema y que no forma parte de la
solucin.

57

VOLVER A MEN

2) Caso de uso: representa un requerimiento o funcionalidad que el sistema debe satisfacer.


Especifica la secuencia de interaccin entre el actor y el sistema a modelar. Por lo general
esta interaccin se describe mediante una secuencia de transacciones y mensajes que
se realizan en un elemento de interfaz. La notacin usada es la que se muestra en la
Figura 3.2.

Figura 3.2. Smbolo para identificar un caso de uso.

3) Relaciones: permiten identificar la comunicacin entre actores entre s, entre los casos
de uso y los actores y entre casos de uso entre s. En este sentido, hay cinco tipos de
relacin:
3.1) Relacin de generalizacin entre actores: permite organizar los actores en una
descripcin abstracta del actor haciendo uso de una relacin caso particular de.
En el ejemplo de la Figura 3.3, los actores Persona y Empleado estn relacionados
tomando en cuenta la siguiente lectura semntica: Empleado es un caso particular
de Persona. Ntese que el actor Persona es ms genrico que el actor Empleado.

Figura 3.3. Ejemplo de una generalizacin entre actores.

Nota: En UML la flecha cerrada como la que se usa en la Figura 3.3 representa
generalizacin y, en cualquier diagrama siempre se lee caso particular de.
3.2) Relacin de generalizacin entre casos de uso: identifica que un caso de uso

especfico hereda y aade propiedades a otro caso de uso ms general. En el


ejemplo de la Figura 3.4, los casos de uso Comprar en una mquina y Comprar
un refresco estn relacionados tomando en cuenta la siguiente lectura semntica:
Comprar un refresco es un caso particular de Comprar en una mquina. Ntese
que el caso de uso Comprar en una mquina es ms genrico que el caso de uso
Comprar un refresco.
58

VOLVER A MEN

Figura 3.4. Ejemplo de una generalizacin entre casos de uso.

3.3) Relacin de extensin entre casos de uso (extend / extensin): permite factorizar
las variantes sobre la secuencia bsica de un caso de uso en nuevos casos de
uso que extienden los flujos principales. La idea es presentar funcionalidades
alternas que un actor puede (opcionalmente) realizar como consecuencia de la
funcionalidad base. En el ejemplo de la Figura 3.5, la funcionalidad Alquilar un
vehculo puede opcionalmente extenderse a la funcionalidad Alquilar un chofer.
Sin embargo, no es posible Alquilar un chofer si no se realiza el Alquilar un
vehculo. Esto se representa mediante la asociacin directa utilizando la flecha
abierta y la dependencia representada por la lnea no slida. Adicionalmente, el
ejemplo de la Figura 3.5 tambin tiene la siguiente lectura semntica: Alquilar
un chofer depende de Alquilar un vehculo. Ntese que la asociacin o relacin
de extensin es un estereotipo previamente definido en el estndar UML. Los
estereotipos o categoras de conceptos se identifican porque se encuentran
encerrados entre corchetes angulados << y >>.

Figura 3.5. Ejemplo de una relacin de extensin entre casos de uso.

Nota: En UML la flecha abierta como la que se usa en la Figura 3.5 representa
asociacin directa; una lnea no slida representa dependencia.

3.4) Relacin de inclusin entre casos de uso (include / inclusin): permite sealar que
un caso de uso incorpora (obligatoriamente) el comportamiento de otro caso de
uso como parte de su propio comportamiento. En el ejemplo de la Figura 3.6,
una lectura semntica es: Acceder a un sistema depende que primero se haga
Revisar datos del usuario. Ntese el uso de la lnea no slida para representar
dependencia y el sentido de la asociacin (indicado por la flecha) en concordancia
con el sentido de la lectura semntica.
59

VOLVER A MEN

Figura 3.6. Ejemplo de una relacin de inclusin entre casos de uso.

3.5) Relacin de asociacin entre un actor y un caso de uso: permite sealar la


comunicacin o lnea de interaccin existente entre un actor y una funcionalidad
o caso de uso que hace el sistema. En el ejemplo de la Figura 3.7 se tienen las
siguientes dos lecturas: Una Persona puede Comprar un refresco y Al sistema
Hacer reporte de cierre genera como consecuencia una salida al Administrador.
Ntese que en este caso no existe una dependencia sino una asociacin directa
(lnea slida) y, la flecha indica el sentido de la interaccin: del actor al sistema en
el primer caso y del sistema al actor en el segundo caso.

Figura 3.7. Ejemplo de relaciones entre actores y casos de uso.

Nota: En UML el sentido de la flecha siempre establece el sentido de la asociacin


entre los elementos relacionados. En ocasiones, la interaccin puede leerse en ambos
sentidos; en este caso es posible, ya sea colocar las flechas en ambos extremos o
bien dejar la lnea sin flechas en ninguno de los dos extremos.
La Figura 3.8 muestra un diagrama de casos de uso relativo al manejo de una mquina
expendedora de refrescos (ntese el uso de la notacin relativa al paquete). Se observa lo
siguiente: 1) el Cliente que realiza la accin Comprar un refresco y que a cambio se recibe
un producto (el refresco); ntese la direccin de las asociaciones entre el actor y el caso de
uso; 2) el Proveedor debe Reabastecer la mquina y, para ello se tiene que obligatoriamente
(asociaciones de inclusin) Exhibir el interior y Cubrir el interior; 3) el Recolector puede
Recolectar dinero para lo cual lo recibe a cambio; en este caso tambin se tiene que
obligatoriamente Exhibir el interior y Cubrir el interior.
60

VOLVER A MEN

Figura 3.8. Ejemplo de un diagrama de casos de uso relacionado


con el problema de una mquina expendedora de refrescos.

En el ejemplo de la Figura 3.9 se tiene un diagrama de casos de uso relacionado con un


problema de Gestin de ventas. Se tienen las siguientes lecturas semnticas:
1) Procesar venta es una accin realizada por el Agente ventas. Para ello se tiene que
obligatoriamente Proveer cuota, Procesar pago y Mantener data del cliente; esta
ltima accin asociada a una Base Datos.
2) Al Procesar venta, el Agente ventas puede opcionalmente Proveer listado de
servicios.
3) Procesar PO y Procesar pago giro son dos casos particulares de Procesar pago.
Procesar pago giro es una accin asociada a Autorizacin crdito.
4) El Ingeniero planificacin realiza Crear plan y Generar reporte. Al hacer clic en
Crear plan se pueden ver los escenarios asociados (esto aparece en un comentario
asociado al caso de uso correspondiente).
5) Generar reporte recomendaciones y Generar reporte final son dos casos particulares
de Generar reporte.

61

VOLVER A MEN

Figura 3.9. Ejemplo de un diagrama de casos de uso relacionado


con un problema de gestin de ventas.

Diagrama de Actividad. Se trata de un diagrama muy parecido a los diagramas de flujo utilizados
para representar algoritmos. Por lo tanto, este tipo de diagramas permiten representar la
dinmica del sistema, es decir, muestran el flujo de control entre actividades del sistema, cules
actividades se pueden hacer en paralelo y caminos alternativos del flujo. Hay dos posibles
contextos en los cuales se pueden usar estos diagramas:
1) Como parte de la Vista de Casos de Uso utilizados para mostrar el flujo de las actividades
que detallan la secuencia de un caso de uso particular.
2) Como parte de la Vista Lgica para representar los algoritmos asociados a los mtodos
de las clases.
Los elementos que forman parte de este tipo de diagrama y cuyos smbolos correspondientes
se pueden observar en la Figura 3.10, son los siguientes:
Actividad: representa el desempeo o accin de algn comportamiento en el diagrama de
flujo.
Actividades inicial y final: smbolos especiales usados para iniciar y finalizar el diagrama
de flujo. Slo es posible tener un inicio por diagrama y cualquier nmero de finales.
Transicin: muestran el paso del flujo de control de una actividad a otra.
Cajas de decisin: muestran rutas alternativas del flujo de control basado en una condicin.
Barras de sincronizacin: permite representar actividades que se pueden ejecutar de
62

VOLVER A MEN

forma concurrente (una transicin de entrada y varias de salida) o diversos flujos que,
una vez culminados, convergen en un punto (varias transiciones de entrada y una de
salida). En este sentido, se tienen correspondientemente barras de concurrencia y barras
de convergencia.

Figura 3.10. Elementos sintcticos de un diagrama de actividad.

La Figura 3.11 presenta un ejemplo de diagrama de actividad relacionado al problema de


Gestin de ventas. Ntese la manera en la cual es posible hacer una separacin de los
flujos basado en algn Actor especfico que previamente tuvo que haber sido definido en los
diagramas de casos de uso. Estos actores realizan o estn directamente relacionados con las
actividades correspondientes a la seccin. En el ejemplo de la Figura 3.11 la actividad se inicia
con Enviar Requerimiento la cual es realizada por el Cliente; luego el Agente de Ventas
realiza Revisar Requerimiento y Buscar Informacin del Cliente; si hay informacin el
Agente de Ventas hace Introducir Requerimiento, caso contrario se hace primero Introducir
Informacin. Luego se hace Revisar Solicitud la cual, si sta es aprobada, el Agente de
Ventas realiza Enviar Cargo a Cliente, el Cliente realiza Revisar Cargo y decide si comprar
o no. No comprar implica terminar el proceso; comprar implica Enviar Pago para que luego el
Agente de Ventas haga Procesar Pago y se finaliza el proceso. En caso de que la solicitud
no sea aprobada (es decir rechazada), el Agente de Ventas hace de manera concurrente
63

VOLVER A MEN

Notificar a Cliente y Notificar a Supervisor y, una vez culminada ambas actividades se


finaliza el proceso.

Figura 3.11. Ejemplo de un diagrama de actividad relacionado


con un problema de gestin de ventas.

Diagrama de Clases. Es quizs uno de los diagramas ms importantes y utilizados de UML,


ya que es el que ms se asocia a los conceptos OO. Es tambin uno de los productos ms
importantes asociados a la Vista Lgica que describe la vista esttica de un sistema en trminos
de sus clases y las relaciones entre ellas. Adicionalmente, este tipo de diagramas sirve de
base para otros diagramas que muestran otros aspectos del sistema (como el de estados de
los objetos y el de colaboracin entre objetos que se muestran en los diagramas dinmicos).
Para la definicin de un diagrama de clases es necesario que las clases hayan sido identificadas
y descritas haciendo uso del principio de abstraccin; luego, estas clases se pueden asociar
con otras clases usando relaciones especficas, basado en el principio de jerarqua. El smbolo
grfico utilizado en UML para representar una clase es el rectngulo. Las relaciones entre
clases se pueden clasificar en:
Asociacin: conexin semntica entre clases que indica que existe una conexin entre
los objetos (mdulos) de esas clases a ser creados. Se representa en UML mediante
una lnea slida. Opcionalmente se puede colocar una flecha abierta para representar
el sentido de lectura de la asociacin. No colocar flechas o, lo que es lo mismo, colocar
64

VOLVER A MEN

flechas en ambos extremos, indica que la lectura semntica de la asociacin es en ambos


extremos de la relacin. Tiene un nombre o descriptivo (comnmente un verbo) que
etiqueta el enlace y que permite hacer la lectura semntica de la asociacin. Tambin se
puede indicar opcionalmente la cantidad de elementos involucrados en la asociacin. A
esta caracterstica se le llama multiplicidad de la asociacin. Otro elemento opcional que
puede ser colocado a cada extremo de la asociacin, es el rol que desempea la clase
involucrada en el extremo correspondiente. Como su nombre lo indica, el rol indica el
papel que juega la clase en trminos de la asociacin. Es una tcnica til para especificar
el contexto de una clase y sus objetos.
Generalizacin: relacin entre un elemento ms general y otro ms especfico (herencia).
El elemento ms especfico puede contener informacin adicional. Se representa en UML
mediante una flecha cerrada que se origina en la subclase o clase derivada y termina
(apunta) a la superclase o clase base. Ntese que no tiene sentido y, por ende no es
posible, tener una relacin de generalizacin que se lea en ambos sentidos de la relacin
(dos flechas cerradas a ambos extremos de la lnea slida). La Figura 3.12 muestra
un diagrama de clases en la cual se aprecian varios ejemplos relativos a la relacin de
generalizacin concernientes a vehculos.

Figura 3.12. Ejemplo de una generalizacin de clases.

Dependencia: relacin entre un elemento independiente y otro dependiente. El objetivo


es representar que un cambio en el elemento independiente afecta tambin al elemento
dependiente. Se representa en UML mediante una lnea no slida y, opcionalmente se
puede colocar una flecha abierta para representar el sentido de la dependencia para
expresar quin depende de quin. El no colocar ninguna flecha o colocar flechas a ambos
65

VOLVER A MEN

extremos indica que la dependencia es mutua (en ambos sentidos). El ejemplo de la Figura
3.13 permite representar en UML el concepto de clases amigas propio del lenguaje de
programacin C++. Se lee semnticamente Clase A es amiga y depende de la Clase B.
Ntese, adems, que en este caso se est haciendo uso de un estereotipo (ver Seccin
3.4) para la asociacin (por el uso de los doble corchetes angulados << y >>).

Figura 3.13. Ejemplo de la relacin de dependencia con el estereotipo <<friend>>.

Otro ejemplo del uso de dependencia ocurre con la relacin entre los conceptos de metaclase y
clase instanciada del entorno OO Smalltalk. Las metaclases son clases cuyas instancias no son
objetos sino otras clases, es decir, es una clase para crear otras clases. Son tiles en sistemas
donde se requiere la creacin de clases de forma dinmica. Las clases instanciadas son
instancias de metaclases. Esta relacin de instanciacin se hace mediante una dependencia,
tal como se muestra en la Figura 3.14.

Figura 3.14. Ejemplo de la relacin de dependencia Metaclase.

Refinamiento: se trata de una relacin especfica entre dos descripciones del mismo tipo
pero en niveles diferentes de abstraccin. Por ejemplo, entre un tipo de dato y una clase;
entre una clase en tiempo de anlisis y la misma clase en tiempo de diseo, etc. Ntese
que puede confundirse con la generalizacin, la diferencia es que en este caso se trata
de conceptos del mismo tipo. Sintcticamente se representa mediante una lnea no slida
(similar a la dependencia) con una flecha cerrada dirigida al elemento de nivel ms alto
de abstraccin (como la utilizada en la generalizacin). Semnticamente hablando un
refinamiento se puede tambin leer como una dependencia generalizable. La Figura 3.15
presenta un ejemplo.

Figura 3.15. Ejemplo de una relacin de refinamiento.

66

VOLVER A MEN

Las asociaciones pueden, a su vez, corresponder a alguna de las siguientes categoras:


Normal: es la asociacin ms comn. La Figura 3.16 muestra un par de ejemplos. En
el primer ejemplo la lectura semntica es 1 o ms Personas pueden ser Propietario
de ninguno o varios Carros. Ntese el uso del nombre de la asociacin, en este caso
Propietario y de la multiplicidad a cada extremo de la asociacin 1..* y 0..*. Ntese
tambin la flecha abierta que muestra el sentido de la asociacin e indica claramente que
la Persona es Propietario del Carro y no lo contrario. La lectura semntica del segundo
ejemplo es Empleado usa Computadora o Computadora es usada por Empleado.
Ntese que en este caso no se usa la multiplicidad en ninguno de los extremos y que la
asociacin puede leerse en ambos sentidos.

Figura 3.16. Ejemplos de asociaciones normales.

Nota: El uso de la multiplicidad en las asociaciones es opcional. La multiplicidad *


significa muchos.

Nota: Aunque es posible hacerlo, hay que evitar dejar asociaciones sin nombre o
etiquetas ya que no permite clarificar la lectura semntica correspondiente. En los
ejemplos anteriores, imagine que las etiquetas Propietario y Usa no estuvieran
en el segmento de diagrama, cmo se podran interpretar entonces las respectivas
asociaciones? Cuando una asociacin no contiene nombre entonces sta debera
leerse como est asociado a para el caso en que haya una flecha que
determina el sentido de la asociacin y, estn asociados para el caso en la
cual la asociacin sea bidireccional.

Recursiva: representa la conexin de una clase consigo misma. Por lo tanto, los objetos que
estn conectados semnticamente son de la misma clase. La lectura del ejemplo de la izquierda
de la Figura 3.17 es 0 o ms Nodos se Conectan con otros 0 o ms Nodos. Basado en el uso
de roles, el segmento de diagrama de la parte derecha se lee Persona en el rol de esposa se
67

VOLVER A MEN

casa con otra Persona en el rol de esposo. Ntese que si se hubiera colocado adicionalmente
la multiplicidad 1 a ambos lados de la asociacin, una mejor lectura sera una Persona en el rol
de esposa se casa con una Persona en el rol de esposo.

Figura 3.17. Ejemplos de asociaciones recursivas.

Cualificada: son usadas en asociaciones de uno a muchos o muchos a muchos con


el objeto de reducir la complejidad semntica de la asociacin. El cualificador, que se
representa encerrado en un rectngulo adosado a la clase, indica cmo distinguir de entre
los muchos objetos involucrados en la asociacin, es decir, cul es el que finalmente se
utiliza. Puede verse como un tipo de clave que permite separar todos los objetos en la
asociacin. Permiten reducir en el modelo la multiplicidad de uno a muchos a uno a
uno. En el primer ejemplo de la Figura 3.18, el atributo id figura que forma parte de
la clase Figura, es usado para establecer la asociacin Tiene que ver con entre las
clases Canvas y Figura. En el segundo ejemplo, el nmero de confirmacin de la
Reservacin, permite que sta sea localizada por la Recepcionista.

Figura 3.18. Ejemplos de asociaciones cualificadas.

Disyuntiva (or): representa una restriccin (ver Seccin 3.4) entre dos o ms asociaciones.
Indica que los objetos de una clase pueden participar en, a lo sumo, una de las asociaciones
a la vez. En general, las asociaciones tambin pueden contener restricciones para indicar
que la asociacin debe seguir cierta regla para su cumplimiento. Ntese el uso de la
restriccin {Or} en el ejemplo de la parte superior de la Figura 3.19. Por otro lado, en el
ejemplo de la Figura 3.20, 1 Compaa de Seguros est asociada a 0 o muchas Pliza
de Seguros que tienen que ver con 1 o varias Personas o con 1 o varias Compaas.
Este ejemplo muestra la restriccin {Ordenado} la cual permite leer que 0 o varias
Pliza de Seguros que deben estar ordenadas, ests asociadas a 1 o varios Clientes.

68

VOLVER A MEN

Figura 3.19. Ejemplo de una asociacin disyuntiva.

Figura 3.20. Ejemplo de una asociacin con restriccin.

Ternaria: representa una asociacin en la cual estn involucradas tres clases. Para
ello se hace uso de un smbolo sintctico representado con un diamante (ver Figura
3.21). Las asociaciones calificadas y la agregacin no estn permitidos en este tipo
de asociacin. Su uso no es muy comn y se recomienda no usar, ya que aumenta la
complejidad de interpretacin o lectura del modelo. Su uso puede ser evitado ya que
una asociacin ternaria se puede escribir de manera homloga con dos asociaciones
normales (binarias) (ver Figura 3.22). En el ejemplo de la Figura 3.21, la lectura es como
sigue: 0 o ms Pliza de Seguros, asociado a una Compaa de Seguros, tiene que
ver con 1 o ms instancias de Persona y 0 o ms Clausula.

Figura 3.21. Ejemplo de una asociacin ternaria.

69

VOLVER A MEN

Figura 3.22. Ejemplo de asociaciones binarias equivalente a la Figura 3.21,


en lugar de hacer una asociacin ternaria.

Agregacin: es un caso especial y muy utilizado de asociacin que indica que la relacin
entre las clases es del tipo forma parte de (ver principio de Jerarqua en Seccin 2.2).
Se representa sintcticamente mediante un diamante como se muestra en la Figura
3.23. Los ejemplos representados en la Figura 3.23 tienen la siguiente lectura: Varios
Barco de Guerra forman parte de una Naval, Varios Texto, Botn y Men forman parte
de Ventana. La diferencia entre el diamante relleno y el que no lo est radica en el nivel
de composicin de la relacin. Cuando el diamante est relleno se quiere representar
que el objeto agregado (el que est en el extremo opuesto al diamante) existe porque
el objeto de la clase que agrega (que corresponde a la clase donde est el diamante)
existe. Es decir, la creacin y destruccin del objeto agregado depende del objeto que
agrega. Por el contrario, si no existe esta dependencia en la creacin y destruccin del
objeto agregado entonces se representa mediante un diamante sin rellenar. En este
caso, el objeto que agrega no tiene realmente un objeto como tal sino una referencia del
objeto agregado. En el ejemplo de la Figura 3.23, la destruccin de un objeto de la clase
Naval no implica la destruccin de los objetos de Barco de Guerra asociados; pero,
los textos, botones y mens que forman parte de una ventana, son destruidos cuando la
ventana es destruida.

Figura 3.23. Ejemplos de asociaciones de agregacin y composicin.

70

VOLVER A MEN

Interfaz: Un paquete, componente o clase que tiene una interfaz conectada a l, se dice
que implementa o soporta la interfaz especfica dado que soporta el comportamiento
definido en esa interfaz. Se pueden ver como contratos de colaboracin entre los
diferentes elementos del modelo y slo se describen como operaciones abstractas.
Se representan mediante pequeos crculos asociados (lnea slida) al elemento del
modelo (uno a uno); una clase que usa la interfaz se conecta mediante una relacin de
dependencia (ver un ejemplo en la Figura 3.24).

Figura 3.24. Ejemplo de una asociacin del tipo interfaz.

En la Figura 3.25 se muestran algunos otros ejemplos de uso de multiplicidades en diferentes


asociaciones. Ntese la lectura semntica de cada de uno de los ejemplos presentados.

Figura 3.25. Ejemplos del uso de multiplicidades

71

VOLVER A MEN

La sintaxis de UML contiene, adicionalmente al rectngulo que representa una clase, una serie
de otros smbolos grficos para representar clases especializadas, alguna de las cuales son
utilizadas en lenguajes de programacin especficos. Se trata de los siguientes casos:
Parametrizada: permiten representar lo que en el entorno de programacin se conocen
como Templates (usados para generalizar la definicin de una clase o definir grupos
de clases para que, mediante los parmetros, se puedan crear luego las clases reales).
La Figura 3.26 muestra el smbolo utilizado para este tipo de clases.

Figura 3.26. Smbolo usado para representar una clase parametrizada.

De utilidad: es la respuesta que tiene UML para representar todos aquellos elementos
que estn fuera del contexto de OO y que por lo tanto, no pueden ser representados
bajo ninguna otra forma. El ejemplo ms comn es lo que ocurre con el lenguaje de
programacin C++ y las variables globales y funciones. La clase de utilidad permite
agrupar todos estos elementos y conjugarlo en una clase representados como atributos
y mtodos respectivamente. La idea es darle una connotacin ms formal de OO y no
dejar elementos dispersos que queden fuera de algn objeto. La Figura 3.27 muestra el
smbolo utilizado.

Figura 3.27. Smbolo usado para representar una clase de utilidad.

Por otro lado, en UML los paquetes o agrupaciones de clases se representan haciendo uso
de la sintaxis que se muestra en la Figura 3.28; se trata de un smbolo grfico parecido a una
carpeta. Tal como se muestra en la Figura 3.28, los paquetes se pueden tambin relacionar
entre s por generalizacin y dependencia.

72

VOLVER A MEN

Figura 3.28. Ejemplo de uso de paquetes.

Una clase representada en UML de manera completa est formada por tres partes que, de
arriba hacia abajo son: 1) la identificacin, 2) los atributos y 3) los mtodos. Cada una de estas
partes se separa de la anterior con una lnea slida horizontal que divide el rectngulo en
segmentos. Por otro lado, los atributos y mtodos pueden adicionalmente contener informacin
detallada del elemento como por ejemplo, su interfaz, el tipo de dato del atributo, el tipo de
dato de retorno del mtodo y los posibles parmetros del mtodo (ms adelante se muestra
un ejemplo).
Nota: Mientras ms detalle se coloque en la clase en tiempo de diseo (UML) ms
simple ser el paso a la programacin.
La Figura 3.29 muestra un ejemplo de un diagrama de clases (no completo) relacionado
con el contexto de un plan y los controles grficos requeridos para manejarlo. Ntese el uso
de varias de las relaciones y asociaciones que se explicaron anteriormente. Por ejemplo:
VentanaResumenPlan es un caso particular de Ventana, Banner forma parte de ListaBanner
y PlanMaestro est asociado a CrearVentanaPlan. Ntese tambin que las clases Ventana,
Botn, Lista y Conjunto no slo estn categorizadas o estereotipadas como Referencia
sino que adems son clases abstractas (se diferencian porque el nombre est escrito con
estilo cursiva). Se puede observar tambin la manera en la cual algunas clases tienen definido
parte de los detalles (atributos y mtodos). Ntese el uso del smbolo + para indicar que
el elemento (mtodos por lo general) son de interfaz pblica y el smbolo - para indicar
que el elemento (atributo por lo general) son de interfaz privada. Algunos ejemplos son: El
mtodo pblico ObtCosto de la clase CostoIntervaloTiempo el cual retorna un Real y
no tiene parmetros; el mtodo pblico LiberarPlan de la clase PlanSemanal el cual tiene
un parmetro identificado como ListaBanner; el atributo privado HoraInicio de la clase
CuartoHora el cual es de tipo Integer.
73

VOLVER A MEN

Nota: No existe una sintaxis especfica en UML para representar clases abstractas;
depende de la herramienta grfica que se use.

Nota: No existe una sintaxis especfica en UML para representar la interfaz de los
elementos de una clase. Comnmente se utiliza + para elementos pblicos, - para
elementos privados y # o * para elementos protegidos.

Figura 3.29. Ejemplo de un diagrama de clases relacionado con el problema de controles grficos.

Diagrama de Objetos: Representa la asociacin y relaciones entre objetos. Sigue la misma


notacin que el diagrama de clases con la diferencia de que en lugar de identificar una clase,
identifica una instancia particular u objeto. La sintaxis UML para representar un objeto es la
misma utilizada para representar una clase (un rectngulo). Se diferencian en la manera como
se identifican los elementos y el contenido que se coloca dentro del rectngulo.
A diferencia de la clase, el identificador de un objeto se forma de dos partes separados de
dos puntos :; la primera parte es el identificador propiamente del objeto y la segunda parte
corresponde al identificador de la clase de la cual el objeto es instanciado. La primera parte es
opcional y, de no colocarse su interpretacin semntica es cualquier objeto de la clase. Aun
y cuando no se coloque el identificador del objeto, el separador : es obligatorio ya que es la
74

VOLVER A MEN

nica manera de diferenciar una clase de un objeto genrico de la clase (ver Figura 3.30 con
un ejemplo).
Otra diferencia existente entre la manera en la cual se representa una clase detallada
(identificador, atributos y mtodos) y un objeto, es que el objeto slo consta de dos y
no tres partes. Adems del identificador del objeto, la segunda parte se refiere al estado
actual del objeto que, segn se mencion en la Seccin 2.4, viene dado por los
atributos y valores actuales correspondientes.
Los diagramas de objetos, los cuales forman parte de la Vista Lgica, no son muy comunes
en los modelos pero se pueden usar para ejemplificar un diagrama de clases con instancias
explcitas. Tambin se pueden usar como parte de los diagramas de colaboracin en el modelo
dinmico (ver ms adelante).
Ntese en el ejemplo de la Figura 3.30 que hay tres instancias de la clase CuartoHora
identificados como QH1, QH2 y QH3 cada uno de los cuales con su estado especfico
dado por el valor del atributo HoraInicio; tres objetos de la clase Banner identificados como
Ad1, Ad2 y Ad3 cada uno de los cuales con el estado dado por los valores de los atributos
HoraInicio y HoraFin; una instancia genrica de la clase PlanSemanal y otra de la clase
ListaBanner. Las asociaciones entre los objetos permiten indicar el momento (dado por el
estado de los objetos) en el cual se da el intercambio de mensajes entre ellos.

Figura 3.30. Ejemplo de un diagrama de clases.

Diagrama de cambio de Estados: Forma tambin parte de la Vista Lgica y representa el


comportamiento del sistema en el tiempo, el cual es modelado en trminos del estado en el
75

VOLVER A MEN

cual se encuentran los objetos de una clase especfica y el cambio de estos estados provocado
por la ejecucin de un mtodo. Se indican qu acciones se ejecutan en cada estado y cul
es el nuevo estado al que se llega luego de un determinado evento (mensaje). Los estados
representan condiciones que son vlidas en el objeto en un momento dado; los eventos
representan la causa que origina el cambio desde un estado a otro.

Nota: Por lo general se hace un diagrama de estados por cada una de las clases que
forman parte del modelo.

La sintaxis UML necesaria para realizar un diagrama de estados se muestra en la Figura 3.31.
Ntese que un estado se representa mediante un rectngulo con los bordes curvos excepto
los casos especficos inicial y final. Slo se permite un estado inicial por diagrama y cualquier
cantidad de estados finales. El estado inicial y el estado final estn asociados respectivamente
a la construccin y destruccin del objeto.

Figura 3.31. Smbolos usados en el diagrama de cambio de estados.

La Figura 3.32 muestra el diagrama de estado de la clase detallada Vlvula que se muestra
en la parte izquierda del ejemplo. Ntese que el nico atributo de la clase es el Estado que
determina la manera en la cual la Vlvula est en un momento dado (abierta o cerrada). Los
mtodos Abrir y Cerrar permiten cambiar el estado de una Vlvula. Del diagrama de estados
de la parte derecha del ejemplo se puede leer lo siguiente:
1) El estado inicial del objeto en tiempo de construccin es la vlvula cerrada.
2) Estando la vlvula cerrada y se manda un mensaje de Cerrar, sta permanece cerrada.
3) Estando la vlvula cerrada y se manda un mensaje de Abrir, sta cambia de estado a
vlvula abierta.
4) Estando la vlvula abierta y se manda un mensaje de Abrir, sta permanece abierta.
76

VOLVER A MEN

5) Estando la vlvula abierta y se manda un mensaje de Cerrar, sta cambia de estado a


vlvula cerrada.
6) Estando en cualquiera de los dos estados se puede destruir el objeto.
Ntese la manera en la cual los mtodos Abrir y Cerrar de la clase Vlvula aparecen
como eventos de cambio de estado en el diagrama. El mtodo VerEstado no aparece en
el diagrama ya que no es un mtodo que provoca un cambio en el atributo de la
clase; ste es usado para retornar el valor actual del atributo.

Figura 3.32. Ejemplo de la clase detallada Vlvula y el diagrama de estados correspondiente.

Nota: En la medida de lo posible los eventos de transicin de estado en el diagrama


de cambio de estados deben corresponder fielmente con los mtodos de la clase.

Diagrama de Componentes: Para casos especficos de sistemas de software que deben ser
modelados, este diagrama que forma parte de la Vista de Componentes, permite modelar la
estructura del software, es decir, la manera en la cual el software est construido o estructurado
en componentes, incluyendo dependencias entre componentes en cdigo fuente, componentes
en cdigo binario y componentes ejecutables.
Un componente es visto como un grupo de clases que trabajan estrechamente y que se pueden
clasificar segn su existencia (ver detalles a continuacin) en: tiempo de compilacin, tiempo
de enlace y tiempo de ejecucin.
El diagrama permite, adems, establecer relaciones de dependencia entre componentes y/o
paquetes de componentes. Una dependencia indica que un elemento del modelo (fuente)
depende de otro (objeto), de manera que un cambio en el elemento objeto puede significar
cambiar el elemento fuente.

77

VOLVER A MEN

Desde un punto de vista terico, UML reconoce los siguientes tipos de componentes:
Componentes para trabajar en el producto: tambin llamados componentes fuente,
son tpicamente archivos de cdigo fuente usados para implementar una o ms clases
(archivos de base de datos, cdigo fuente, recursos grficos, entre otros).
Componentes de distribucin: tambin llamados componentes binarios, conforman el
fundamento de los sistemas ejecutables y son tpicamente archivos de cdigo objeto
que se generan a partir de los componentes anteriores (ejecutables, DLL, controles
ActiveX, entre otros).
Componentes de ejecucin: son los creados como resultado de un sistema en ejecucin
(archivos temporales, ndices de bsqueda, entre otros).
Como se muestra en el ejemplo de la Figura 3.33, el smbolo sintctico UML usado para
representar un componente es el rectngulo con las dos barras horizontales colocadas en su
lado izquierdo. Para diferenciar un tipo de componente de otro se usan los estereotipos (ver
Seccin 3.4 para detalles de este mecanismo general de UML).

Figura 3.33. Ejemplo de un diagrama de componentes.

Diagrama de Colaboracin: Forma parte de la Vista de Concurrencia y es uno de los dos


posibles diagramas de UML que permite visualizar la manera en la cual los objetos definidos
en la Vista Lgica intercambian mensajes para satisfacer los requerimientos representados en
la Vista de Casos de Uso.
Se parte de un actor (ver diagrama de casos de uso) y se colocan los objetos que interactan
entre s mediante el intercambio de mensajes. Al igual que ocurre en el diagrama de objetos,
stos pueden ser identificados especficamente o de manera genrica (objeto annimo). Los
enlaces entre el actor y los objetos y entre los objetos entre s se realizan mediante lneas
continuas conectadas entre los elementos. La interaccin que se produce como consecuencia
78

VOLVER A MEN

de un enlace se representa mediante mensajes que se muestran con un texto descriptivo y


una flecha que indica el sentido de la ejecucin desde el origen hacia el que suple el evento.
Cada mensaje tiene, adicionalmente, un consecutivo numrico que indica el orden de ejecucin
que permite satisfacer el caso de uso. La lectura del diagrama empieza cuando el actor
interacta con alguno de los objetos y luego se sigue la secuencia de intercambio de mensajes
entre los objetos hasta completar el caso de uso que origina el proceso de colaboracin.

Figura 3.34. Ejemplo de un diagrama de colaboracin.

La Figura 3.34 presenta un diagrama de colaboracin que inicia con el actor Ingeniero
planificacin que muestra la manera en la cual se hace ComputarBeneficios mediante el
intercambio de mensajes entre un objeto de la clase PlanSemanal, un objeto de la clase
CuartoHora y una instancia de la clase CostoIntervaloTiempo. Ntese la forma como se
indica el orden de ejecucin de los mensajes; tambin la indicacin de retorno en los eventos
o mensajes 2 y 3. Tambin se puede observar que el evento 4 se ejecuta internamente en
el objeto de CuartoHora. Para la lectura de los mensajes que aparecen en el diagrama,
por ejemplo en el caso del mensaje 2, hay que tomar en cuenta que el mtodo identificado
como ObtenerTotalQH pertenece a la clase CuartoHora y el objeto que realiza la llamada
es la instancia de la clase PlanSemanal (esto se determina por el sentido de la flecha de la
colaboracin que va de la clase PlanSemanal a la clase CuartoHora).

Nota: Un modelo completo debera tener un diagrama de colaboracin por cada


caso de uso que se haya representado en los diagramas de casos de uso y que
estn directamente conectados con al menos un actor.
Diagrama de Secuencia: Tambin forma parte de la Vista de Concurrencia y es el otro posible
diagrama de UML para visualizar la manera en la cual los objetos definidos en la Vista Lgica
intercambian mensajes para satisfacer los requerimientos representados en la Vista de Casos
de Uso. Contiene la misma informacin que el diagrama de colaboracin con la diferencia
que en este caso se muestra la interaccin entre objetos estructurada u ordenadamente en
una secuencia de eventos en el tiempo. La manera en la cual se debe leer el diagrama es de
79

VOLVER A MEN

izquierda a derecha y de arriba abajo y, al igual como ocurre con el diagrama de colaboracin,
debe iniciar con un actor que por lo tanto, debe estar en la parte ms izquierda del diagrama.
La Figura 3.35 muestra la sintaxis UML utilizada en este tipo de diagramas. Tanto el actor
como los objetos se organizan en lneas de tiempo que se colocan verticalmente debajo de
cada elemento correspondiente. Debido a esta manera de organizar los elementos y si estn
involucrados muchos objetos en la interaccin, estos diagramas pueden ser excesivamente
grandes, horizontalmente hablando. A diferencia de los diagramas de colaboracin en la cual
los objetos pueden ser colocados en cualquier parte del diagrama.

Figura 3.35. Smbolos usados para representar un diagrama de secuencia.

La Figura 3.36 presenta un ejemplo que corresponde al diagrama de secuencia equivalente al


diagrama de colaboracin de la Figura 3.34. Ntese que el actor Ingeniero planificacin inicia
la secuencia a la izquierda para luego darse la misma colaboracin entre objetos de izquierda
a derecha y de arriba hacia abajo.

Figura 3.36. Ejemplo del diagrama de secuencia equivalente al diagrama de colaboracin de la Figura 3.34.

80

VOLVER A MEN

Diagrama de Distribucin: Es el ltimo de los nueve diagramas que contiene la versin 2.0 de
UML. Forma parte de la Vista de Despliegue y modela la distribucin en tiempo de ejecucin de
los elementos de procesamiento y componentes de software, procesos y objetos asociados.
Tiene que ver con la arquitectura fsica del sistema: dnde estn localizados fsicamente
los programas que implementan las clases y objetos?, en cules computadoras se ejecutan
estos procesos? y cmo se conectan las diferentes computadoras que requiere el sistema
para funcionar?.
El objetivo es modelar los nodos (objetos fsicos o dispositivos) y la comunicacin entre
ellos. Tal como se muestra en la Figura 3.37, los nodos se representan mediante un cubo.
Adicionalmente, cada nodo puede contener instancias de componentes.

Figura 3.37. Smbolos usados para representar un diagrama de distribucin.

Ntese en el ejemplo de la Figura 3.38 la manera en la cual la sintaxis del diagrama de


distribucin se puede combinar con la sintaxis del diagrama de componentes para representar,
no slo cules son los componentes que forman parte del software sino tambin los elementos
de hardware (nodos) donde stos residen fsicamente.

Figura 3.38. Ejemplo de un diagrama de distribucin con el detalle de los componentes.

81

VOLVER A MEN

3.4 Mecanismos Generales


Los mecanismos generales ms utilizados en UML son: los valores etiquetados, las
restricciones y los estereotipos.
Los valores etiquetados son propiedades asociadas a elementos de UML en forma
de etiquetas. Existen en UML una serie de etiquetas ya predefinidas como por
ejemplo: documentacin, ubicacin, persistencia, semntica, responsabilidad,
precondicin, poscondicin, abstracto, entre otras. Estas etiquetas permiten agregar
semntica extra a los elementos que forman parte de los diagramas. Por ejemplo,
informar sobre las precondiciones y poscondiciones de una operacin o mtodo de
una clase. Tambin son usados para agregar informacin administrativa sobre el
progreso o estado de un proyecto. La sintaxis para representar un valor etiquetado
en los diagramas consiste en encerrar entre llaves { y } la etiqueta y su valor
correspondiente, separados ambos por un signo de igualdad =. Por ejemplo: {
Algoritmo = QuickSort }; { Autor = Mauricio Paletta }.
Las restricciones, tambin llamadas condiciones semnticas, son reglas que
restringen la semntica de uno o ms elementos de UML. Pueden estar asociadas
a clases u objetos y tambin a las relaciones entre stos, a fin de restringir la
participacin de estos elementos en la relacin. Al igual que ocurre con los valores
etiquetados, las restricciones tambin se representan en los diagramas encerrados
entre llaves { y }. Y, tambin como ocurre con los valores etiquetados, existen
restricciones predefinidas que pueden ser utilizadas segn convenga al usuario
que est realizando el modelo. Algunos ejemplos de restricciones predefinidas son:
asociacin, local, parmetro, auto, completa, disjunta, incompleta, sobrelapada,
implcita, or, ordenada, voto y difundida.
Gracias a la extensibilidad de UML, un usuario tambin puede definir sus propias
restricciones. Por Ejemplo: {persona.jefe.sueldo >= persona. asistente.sueldo}.
Finalmente, el uso de estereotipos en UML permite representar una semntica
propia asociada a un elemento existente en cualquier diagrama. Se pueden ver
como un tipo de especializacin del elemento. Cuando un estereotipo est asociado
a un elemento, se sobrescribe la semntica definida para ese elemento y ste
puede ser visto como un nuevo elemento basado en otro ya existente. Es decir, se
trata de una extensin de la semntica pero no de la estructura del elemento. Es
uno de las caractersticas ms importantes en referencia a la extensibilidad de UML
o capacidad de disear la semntica propia de interpretacin de los elementos.
82

VOLVER A MEN

Por lo general, los estereotipos son meta-clases para ser asociados a clases o
importaciones para las relaciones de dependencia entre paquetes. Al igual que ocurre
con los mecanismos generales anteriores, pueden existir estereotipos predefinidos
dependiendo de la herramienta automatizada que se utilice. Tambin es posible
definir estereotipos propios. La sintaxis para su representacin es utilizando un
nombre encerrado entre doble corchete angulado << y >>, que por lo general
se coloca delante del identificador del elemento asociado.
Un ejemplo muy comn que aparece en la literatura es el uso de tres tipos particulares
de estereotipos aplicado a las clases: Control, Lmite y Entidad. Estos elementos
estn basados en el concepto model-view-controller (Burbeck, 1987) donde
Entidad es el modelo, Control es el controlador y Lmite es la vista. La notacin
utilizada es la que aparece en la Figura 3.39.

Figura 3.39. Notacin de las clases: control, lmite y entidad.

El estereotipo Lmite especializa el uso de una clase para su presentacin y manipulacin.


Presenta y comunica la informacin entre sistemas (hombre-mquina por ejemplo). Por lo
general son ventanas, cajas de dilogos o formas, clases que representan elementos de
comunicacin (como TCP/IP por ejemplo). El estereotipo Entidad es usado para modelar
objetos del negocio. Y el estereotipo Control es usado para conectar objetos Lmite con sus
objetos Entidad y manejar una secuencia de operaciones dentro del sistema. Estos objetos
tpicamente manejan el procesamiento de la informacin en los objetos Entidad. La Figura 3.40
presenta un ejemplo.

Figura 3.40. Ejemplo de un diagrama de clases expresado en la notacin control, lmite y entidad.

83

VOLVER A MEN

3.5 Resumen de la sintaxis


Haciendo uso de dos tablas, esta seccin presenta un resumen de la sntesis de los elementos
sintcticos que estn inmersos en UML. Hay que recordar que en UML un smbolo se interpreta
de la misma manera sea cual sea el diagrama en la cual el smbolo es utilizado. Se debe tener
cuidado ya que no todos los smbolos son permitidos de usar en algunos diagramas. La Tabla
3.1 muestra el resumen de los smbolos sintcticos y la Tabla 3.2 presenta la lista de tipos de
conectores entre smbolos.
Tabla 3.1. Listado de smbolos sintcticos de UML.
Sintaxis

84

VOLVER A MEN

Semntica

Diagramas

Actor: Un ente externo al sistema que se est


modelando e interacta con este, tanto para iniciar un proceso como para recibir un resultado.

Casos de uso, colaboracin y secuencia.

Caso de uso: Un requerimiento funcional.

Casos de uso.

Actividad/Estado inicial: El inicio de un proceso


algortmico que describe una secuencia de pasos.

Actividad y estados.

Actividad/Estado final: El inicio de un proceso


algortmico que describe una secuencia de pasos.

Actividad y estados.

Actividad: Uno de los pasos del proceso algortmico que describe una secuencia.

Actividad.

Decisin: Colocar una condicin en el algoritmo


que permita separar el flujo de secuencia en dos
caminos diferentes.

Actividad y estados.

Barra de sincronizacin: Para la convergencia de


hilos de ejecucin y/o la generacin concurrente
de ms de un hilo de ejecucin en un proceso
algortmico que describe una secuencia.

Actividad.

Clase/Objeto: Un concepto del modelo o una instancia particular del concepto.

Clase, objeto, colaboracin y secuencia

Paquete: Un grupo de elementos que asociados a


un mismo contexto.

Clase, objeto, componentes, distribucin.

Interfaz: Elemento de interaccin entre conceptos.

Clase, objeto y componentes.

Estado: Situacin actual de un objeto dado por el


valor de sus atributos.

Estados.

Nodo: Elemento de hardware fsico.

Distribucin.

Componente: Elemento de software fsico.

Componentes y distribucin.

Comentario: Informacin adicional asociada a


cualquier elemento.

Todos.

Tabla 3.2. Listado de conectores entre smbolos.


Notacin

Semntica
Asociacin: Describe una conexin entre dos tems que se lee en ambas
direcciones.
Asociacin directa: Describe una conexin entre dos tems que se lee en
el sentido de la flecha.
Dependencia: Indica que el tem que est en el extremo sin flecha depende del otro tem que est en el extremo con flecha.
Generalizacin: El tem del extremo sin la flecha es un caso particular del
tem que est en el extremo con la flecha.
Realizacin: El tem del extremo sin la flecha se implementa haciendo
uso del tem que est en el extremo con la flecha.
Agregacin: El tem del extremo sin el diamante forma parte del tem
que est en el extremo con el diamante. Los tems son independientes.
Composicin: El tem del extremo sin el diamante forma parte del tem
que est en el extremo con el diamante. La existencia del primero depende de la existencia del segundo.
Mensaje: El tem del extremo sin flecha est enviando un evento / mensaje al tem del extremo con flecha.

85

VOLVER A MEN

3.6 Ejercicios
1) Para los siguientes casos que se mencionan a continuacin, realice un modelo conceptual
(diagrama de clases no detallado) que permita representar cada caso.
a) Un punto se representa por un par de coordenadas X, Y. Se requiere un cierto nmero
mnimo de puntos para construir un polgono.
b) Una constante, una variable y una expresin son trminos para construir expresiones
binarias (dos operandos) o unarias (un operando). Los operandos de una expresin
tambin son trminos.
c) En un sistema de transporte areo podemos encontrar los siguientes nombres: ciudad,
aeropuerto, aerolnea, piloto, vuelo, avin, pasajero, puerta de embarque, asiento,
terminal.
d) En un sistema de almacenamiento de informacin se tienen los siguientes elementos:
sistema de archivos, archivo, directorio, nombre del archivo, archivo ASCII, archivo
ejecutable, disco, track, sector, capacidad.
2) Realice el diagrama de casos de uso y el diagrama de clases con tanto nivel de detalle
como sea posible, para los siguientes casos que se mencionan a continuacin.
a) Suponga un programa de computacin para la realizacin de artculos para un peridico.
El software maneja varias pginas que pueden contener, entre otras cosas, columnas
de texto. El usuario puede editar el ancho y alto de una columna de texto, mover una
columna en una pgina o mover una columna entre pginas diferentes. Porciones de
una misma columna pueden aparecer en ms de una pgina.
b) Suponga un sistema para llevar el control de la planificacin y puntuacin de unas
competiciones deportivas como el atletismo. Hay varios eventos y competencias.
Cada competidor puede participar en ms de un evento y cada evento tiene varios
competidores. Cada evento tiene varios jueces que emiten su puntuacin sobre el
desenvolvimiento de los competidores en cada evento. Un mismo juez evala a todos
los competidores de un evento especfico y, en algunas ocasiones, un juez puede estar
asignado a ms de un evento.
c) Representacin de un motor de combustin interna. La fuerza del motor es dada por la
combustin generada por la mezcla de aire y gasolina contra un pistn. El pistn est
pegado a un cigeal por medio de una vara de conexin y se mueve hacia arriba y
hacia abajo dentro de un cilindro haciendo girar un eje. Cuando los pistones se mueven
hacia abajo, unas vlvulas de entrada se abren, permitiendo al pistn tener contacto
con una mezcla de combustible y aire dentro del cilindro. Al llegar al fondo del cilindro,
las vlvulas se cierran. Cuando el pistn se mueve hacia arriba, comprime y calienta la
mezcla. Anillos alrededor del pistn frotan contra las paredes del cilindro proveyendo un
86

VOLVER A MEN

sello necesario para la compresin y lubricacin requeridas. En el tope del cilindro, una
chispa elctrica proveniente de una buja, detona la mezcla que da el poder necesario
para enviar el pistn nuevamente hacia abajo. Una vez en el fondo, una vlvula de
evacuacin es abierta y en el prximo viaje hacia arriba del pistn, los gases de la
detonacin anterior son expulsados. La mezcla del combustible y el aire son hechas en
un carburador. El polvo y suciedad en el aire son removidos mediante un filtro. Unas
vlvulas de admisin controlan la cantidad de mezcla adecuada a ser inducida a los
cilindros. La energa elctrica requerida por la buja es suministrada por un magneto,
una bobina, un condensador.
d) Las Torres de Hanoi es un problema frecuentemente usado para la enseanza de
tcnicas recursivas de programacin. El objeto es mover una pila de discos de una de
las tres clavijas o torres que existen a otra, usando la tercera como ayuda. Cada disco
tiene un tamao diferente. Los discos pueden ser movidos, uno a la vez, desde el tope
de una clavija al tope de otra clavija. Un disco nunca es colocado sobre otro disco menor.
e) Para un editor de texto, un folio es una coleccin de enlaces y cajas. Un enlace es una
secuencia de segmentos que conectan dos cajas. Cada segmento es especificado por
dos puntos. Un punto puede ser compartido por un segmento vertical y un segmento
horizontal en el mismo enlace. Una seleccin es una coleccin de enlaces y cajas que
han sido marcados anticipadamente durante una operacin de edicin. Un buffer es una
coleccin de enlaces y cajas que han sido cortados o copiados del folio.
f) Un sistema de reserva de vuelos permite a un usuario hacer consulta y reserva de
vuelos, adems de poder comprar los boletos areos de forma remota, sin la necesidad
de recurrir a una agencia de viajes. El sistema tiene actualmente un Terminal de
Servicio de Reserva (formado por un ratn Genios, teclado IBM y monitor Sony) en
donde se presenta un mensaje de bienvenida describiendo los servicios ofrecidos junto
con la opcin para registrarse por primera vez, o si ya se est registrado, poder usar el
sistema de reserva de vuelos. Este acceso se da por medio de la insercin de un login
previamente especificado (direccin de correo electrnico del usuario) y una contrasea
previamente escogida y que debe validarse. Una vez registrado el usuario, y despus
de haberse validado el registro y contrasea del usuario, se pueden seleccionar las
siguientes actividades: consulta de vuelo, reserva de vuelo y compra de boletos. La
consulta de vuelos se puede hacer de tres maneras diferentes segn: horarios, tarifas e
informacin de vuelos. La consulta segn horarios muestra los horarios de las diferentes
aerolneas que dan servicio entre dos ciudades. La consulta segn tarifas muestra los
diferentes vuelos entre dos ciudades ordenados por su costo. La informacin de vuelos
se utiliza principalmente para consultar el estado de algn vuelo, incluyendo informacin
de si existen asientos disponibles y, en el caso de un vuelo para el mismo da, si ste est
en hora. Se pueden incluir preferencias en la bsqueda, como fecha y horario deseado,
87

VOLVER A MEN

categora de asiento, aerolnea deseada y si se desean slo vuelos directos. La reserva


de vuelo permite al cliente hacer una reserva para un vuelo particular, especificando la
fecha y horario, bajo una tarifa establecida. Es posible reservar un itinerario compuesto
de mltiples vuelos, para uno o ms pasajeros, adems de poder reservar asientos.
La compra permite al cliente, dada una reserva de vuelo previa y una tarjeta de crdito
vlida, adquirir los boletos areos. Los boletos sern enviados posteriormente al cliente,
o estarn listos para ser recogidos en el mostrador del aeropuerto antes de la salida del
primer vuelo. Es necesario estar previamente registrado con un nmero de tarjeta de
crdito vlida para poder hacer compras de boletos, o bien proveerla en el momento de
la compra. Adems de los servicios de vuelo, el usuario podr en cualquier momento
leer, modificar o cancelar su propio registro, todo esto despus de haber sido un usuario
vlido en el sistema.

g) Una red bancaria computarizada incluye tanto cajeros humanos como automticos
(ATM), stos ltimos compartidos por un consorcio de bancos. Cada banco provee su
propia computadora para mantener sus propias cuentas y procesar las transacciones
contra ellas. Las estaciones de los cajeros son propias de cada banco y se comunican
directamente con la computadora propia del banco. Los cajeros humanos introducen
datos de cuentas y transaccin. Los ATM se comunican con un computador central el
cual transfiere las transacciones con los bancos apropiados. Un ATM acepta una tarjeta
de dbito, interacta con el usuario, se comunica con el sistema central para llevar
a cabo la transaccin, dispensa efectivo e imprime un recibo. El sistema requiere de
mecanismos de seguridad y auditoria adecuados. El sistema puede manejar acceso
concurrente a la misma cuenta. Los bancos proveen su propio software para sus propias
computadoras. El costo del sistema compartido es distribuido por los bancos segn el
nmero de clientes con tarjetas de dbito.
h) Un gestor de servicios de comunicacin Cliente-Servidor debe satisfacer los siguientes
requerimientos: - un equipo al cual llamamos Servidor ejecuta un proceso que
inicialmente escucha un canal de comunicacin segn un puerto especfico; - otro equipo
al que llamamos Cliente abre el canal de comunicacin con el Servidor conociendo el
puerto correspondiente y la direccin IP del Servidor; - una vez establecido el canal
de comunicacin entre el Cliente y el Servidor, ambos procesos pueden optar por los
siguientes Servicios: 1) Servicio de Correo: Cualquiera de los procesos puede enviar/
recibir un mensaje de texto al otro. Cada mensaje viene acompaado por la fecha y
hora de emisin y un pequeo texto descriptivo o asunto. Para administrar los mensajes
salientes y entrantes se requieren un par de buzones: mensajes no enviados y mensajes
recibidos. Se deben implementar las siguientes funciones: - crear mensaje: introducir un
nuevo texto y guardarlo en el buzn de mensajes no enviados; - borrar mensaje: suprimir
88

VOLVER A MEN

uno de los tems del buzn de mensajes no enviados; - enviar mensajes: hacer el envo
de todos los mensajes que se encuentran en el buzn de mensajes no enviados; - leer
mensaje: mostrar el texto de uno de los mensajes recibidos que se encuentran en el
buzn de mensajes recibidos; - borrar mensaje: suprimir uno de los tems del buzn
de mensajes recibidos. 2) Servicio de Envo/Recepcin de archivos: Cualquiera de los
procesos puede enviar/recibir archivos al otro. Se puede enviar cualquier tipo de archivo.
El receptor escoge la ubicacin y el nombre que va a tener en el destino. 3) Servicio de
Conversacin: Cuando uno de los procesos desea entrar en este modo y el otro proceso
acepta, las transcripciones de texto realizadas en cualquiera de los dos equipos se
visualizarn en ambos procesos, de forma tal de establecer una comunicacin en lnea
entre los usuarios de los equipos correspondientes.
3) Basado en el diagrama de clases realizado para cada caso del ejercicio anterior, realice un
diagrama de cambios de estado de cada una de las clases que aparece en cada uno de
los diagramas.
4) Basado en el diagrama de casos de uso y el diagrama de clases de cada uno de los
escenarios del ejercicio 2, realice un diagrama de colaboracin y/o secuencia para cada
uno de los casos de uso directamente asociado con algn actor.

89

VOLVER A MEN

Captulo 4

La programacin Orientada a Objetos


4.1 Introduccin
El objetivo de este captulo es presentar sintcticamente la manera en la cual, los conceptos
derivados de la OO son representados mediante los tres lenguajes de programacin OO
ms utilizados en el momento en la cual fue escrito este libro. Se trata de los lenguajes de
programacin C++, Java y C#. A diferencia de otros libros de texto especializados en alguno
de estos lenguajes de programacin, la estrategia usada aqu es basar el enfoque en los
conceptos OOs explicados en los captulos anteriores, en lugar de enfocar la explicacin en el
lenguaje de programacin propiamente.
Cabe mencionar que la principal intencin de este captulo no es ensear a programar ni a
aprender a escribir programas haciendo uso de C++, Java o C#. La intencin es que sea utilizado
como material de referencia para que, luego de tener un modelo OO (haciendo uso de UML),
ste pueda ser llevado a un enfoque de programacin y as culminar con el proceso de resolver
un problema bajo el paradigma OO (segn se explica en el Captulo 6 de este libro). De haber
debilidades en el rea de programacin y/o en el uso de los lenguajes antes mencionados,
es recomendable que el lector refuerce sus conocimientos con libros especializados en estos
temas.
En otro orden de ideas, desde el punto de vista de lo cerrado que son los lenguajes de
programacin OO en lo que respecta a los conceptos OO, estos pueden ser clasificados como
puros y no puros. Cuando en un lenguaje de programacin es posible representar elementos
que quedan fuera de cualquier definicin OO (la declaracin de variables y funciones globales
y la ejecucin de funciones son los ejemplos ms comunes), entonces se dice que el lenguaje
es no puro. Si, por el contrario, los elementos sintcticos del lenguaje slo contienen aspectos
relacionados con el entorno conceptual de la OO (definicin de clases, objetos, mtodos,
llamada de mensajes, herencia, entre otros) entonces se dice que el lenguaje es puro.
Otro aspecto importante a tener en cuenta es que no se puede afirmar que un programa es OO
tan solo porque haya sido escrito en un lenguaje de programacin OO. La esencia de la OO
radica en los principios que se presentaron en el Captulo 2. Por lo tanto, hacer programacin
OO es programar respetando los principios de la OO. Esto se puede entender ms claramente
una vez que se revise la manera en la cual fueron resueltos los problemas que se presentan
en el Captulo 6 de este libro.

90

VOLVER A MEN

Nota: Usar un lenguaje OO no necesariamente implica escribir programas OO. Un


programa OO es aqul que respeta los principios de la OO haciendo uso de los
conceptos bsicos correspondientes. Escribir un programa OO es hacer abstraccin,
descomposicin y jerarqua haciendo uso de un lenguaje de programacin.

C++ (Stroustrup, 1998) es un lenguaje de programacin compilado (es decir,


permite generar programas ejecutables) no puro desde el punto de vista OO, debido
a que su autor decidi mantener la compatibilidad con su predecesor, el lenguaje
de programacin no OO C. Los programas escritos en C++ poseen bajo nivel de
portabilidad, es decir, tienen una alta dependencia en la plataforma operativa donde
se construye el programa.
Al igual que su predecesor, C++ posee una sintaxis simple (pocos elementos
sintcticos); sin embargo, dado que un mismo smbolo puede ser usado para varios
fines, la semntica puede llegar a ser algo compleja. Desde el punto de vista OO,
C++ es muy rico conceptualmente hablando ya que, adems de los conceptos
bsicos, se manejan tambin los conceptos ms avanzados como por ejemplo la
herencia mltiple y la sobrecarga de operadores. La extensin de los archivos de
cdigo fuente es cpp.
El lenguaje de programacin Java es pseudo-interpretado, es decir, se requiere un motor
o mquina de ejecucin para correr los programas. El pseudo-cdigo que se interpreta se
identifica en la literatura con el nombre de Java bytecode. Esto le permite tener un alto nivel de
portabilidad (los programas pueden correr donde haya una mquina de ejecucin). Desde el
punto de vista OO este lenguaje de programacin es puro.
La sintaxis y semntica del lenguaje es simple. A diferencia de C++, no se permite ni la herencia
mltiple directa ni la sobrecarga de operadores. La extensin de los archivos de cdigo fuente
es java y la extensin de los archivos pseudo-compilados es class.
C# es el lenguaje de programacin ms reciente cuya definicin es una combinacin de 70%
de Java, 10% de C++, 5% de Visual Basic y un 15% de cosas nuevas. Al igual que Java, C# es
pseudo-interpretado, el cual le da caractersticas de portabilidad a los programas construidos
con este lenguaje de programacin. El lenguaje pseudo-interpretado se denomina MSIL (de
sus siglas en ingls Microsoft Intermediate Language).
C# es puro desde el punto de vista de OO con una sintaxis y semntica medianamente
complejas. No se permite la herencia mltiple directa y la extensin de los archivos de cdigo
fuente es cs.
91

VOLVER A MEN

4.2 Definicin de clases


Tanto en C++ como en Java y C# la palabra reservada para declarar una clase es
class. Las palabras reservadas para establecer la declarativa de interfaz (pblico,
protegido y privado) de los elementos de la clase (atributos y mtodos) son: public,
protected y private. La declaracin de esta estructura de datos se abre y cierra
con llaves: { y }.
La Figura 4.1 presenta un ejemplo de la declaracin de la clase Complejo escrita
en C++. En C++, las declarativas de interfaz pueden aparecer ms de una vez y en
cualquier orden. Cualquier elemento definido bajo una declarativa especfica y antes
de que aparezca otra sentencia de este tipo, se le asocia la declarativa de interfaz
correspondiente. En el ejemplo de la Figura 4.1, los atributos Real e Imag son
privados porque estn definidos luego de haber colocado la declarativa private.
Los mtodos ObtReal y ObtImg son pblicos. Por defecto, si no se indica ninguna
declarativa, los elementos son considerados privados. Ntese la diferencia como en la
Figura 4.1 y la Figura 4.2 se indica que los atributos Real e Imag son privados.
Ntese en el ejemplo de la Figura 4.1, que los atributos se declaran de la misma
manera como se declara cualquier variable en este lenguaje. As mismo, la
declaracin o definicin de los mtodos es equivalente a la declaracin o definicin
de funciones. Ntese tambin que, al igual como ocurre con cualquier declaracin en
C++, inmediatamente despus de la declaracin de la estructura es posible declarar
elementos asociados a la misma. En este caso, instanciar objetos de la clase. Es
la razn por la cual se debe colocar obligatoriamente el smbolo de terminacin de
sentencia ; luego de la } que cierra la estructura.
En C++, el cuerpo de los mtodos se puede definir tanto dentro como fuera de la
definicin de la clase. En la literatura se le suele distinguir como inline y outline
respectivamente. Ntese que ambos mtodos del ejemplo de la Figura 4.1 son
inline. La Figura 4.2 muestra el mismo ejemplo pero en este caso los mtodos
son outline. Desde el punto de vista de la compilacin o construccin del cdigo
ejecutable, hay una diferencia en la llamada del mensaje o ejecucin del mtodo
cuando se trata de inline y outline. En el caso inline se hace una copia fiel del cdigo
del cuerpo del mtodo, lo que implica que el cdigo es ms rpido al ejecutarse
porque est a la mano y no se debe ir a buscar en otro sitio; sin embargo, por
cada mensaje presente en el programa hay una copia del cdigo correspondiente
lo que hace que el programa compilado (objeto) sea ms grande. El caso outline es
completamente contrario ya que en la llamada del mensaje se agrega una referencia
donde se encuentra el cdigo del cuerpo del mtodo lo que implica que para su
ejecucin es necesario buscar el cdigo primero donde ste se encuentre teniendo
92

VOLVER A MEN

como consecuencia un cdigo ms lento1. Por otro lado, al no haber copias de un


mismo cdigo, el ejecutable es relativamente ms pequeo.

Nota: Es recomendable hacer inline todos aquellos mtodos cuyo cuerpo sea simple,
como por ejemplo el retorno de una expresin y la inicializacin de atributos. Aquellos
mtodos cuyo desarrollo sea ms complejo, que contengan ciclos o condicionales
mltiples por ejemplo, es recomendable desarrollarlos outline.

Figura 4.1. Ejemplo de la definicin de una clase en C++.

Como se muestra en la Figura 4.2, el operador :: es utilizado para relacionar un elemento


con su clase correspondiente cuando el elemento est fuera del contexto de declaracin de la
clase (en este caso se refiere al desarrollo outline de los mtodos).

Esta lentitud es realmente imperceptible con los avances que existen actualmente en relacin a las velocidades de
procesamiento.

93

VOLVER A MEN

Figura 4.2. Otra manera de escribir el ejemplo mostrado en la Figura 4.1.

Adicionalmente y de ser necesario, el lenguaje permite forzar la declaracin de los mtodos


inline fuera del contexto de declaracin de la clase, como se muestra en la Figura 4.3 al hacer
uso de la declarativa inline precediendo la definicin del mtodo. La nica utilidad de hacerlo
de esta manera es que permite mantener escondido de los usuarios, el cdigo fuente con el
cual se ha desarrollo el mtodo de la clase manteniendo los beneficios de la opcin inline.

Figura 4.3. Ejemplo del uso de la declarativa inline.

Nota: Algunas plataformas de desarrollo que incluyen compiladores en C++ tambin


incluyen la declarativa outline, la cual permite que mtodos que han sido definidos
dentro del contexto de definicin de la clase sean tratados como outline. Es poco
usual ver este tipo de declarativas.

94

VOLVER A MEN

La manera como se declaran las clases en Java y C# es similar. La Figura 4.4


muestra el ejemplo con la clase Complejo para estos otros dos lenguajes de
programacin. Hay dos diferencias notables en relacin a la manera como se hace en
C++. La primera es que cada lnea de declaracin de un elemento de la clase, ya
sea atributo o mtodo, debe poseer obligatoriamente una declarativa de interfaz.
La segunda, ms importante an, es que todo lo que define a la clase incluyendo
los mtodos est dentro de la declaracin de la clase. Es decir, no existe en estos
lenguajes la diferenciacin entre mtodos inline y outline, todo se hace bajo el
criterio inline de C++.

Figura 4.4. Ejemplo de la definicin de una clase en Java / C#.

Otra diferencia existente entre C++ y la pareja Java, C# es que en estos ltimos no es posible
la instanciacin de objetos al final de la declaracin de la clase; por lo tanto, no se requiere
colocar el smbolo de terminacin de sentencia ;.
Tanto en Java como en C# las declarativas de interfaz pueden aparecer ms de una vez y en
cualquier orden. En Java, si no se indica nada se asume una interfaz entre privada y protegida.
Privada porque no permite la visibilidad del elemento para las clases derivadas; protegida
porque permite la visibilidad en todas las clases que se hayan definido en el mismo paquete.
En C#, si no se indica nada, la declarativa de interfaz que se asume es privada.
Por otro lado, C# maneja adicionalmente un concepto especial de atributo que se define como
una combinacin entre atributo y mtodo. En este sentido, un atributo en C# permite esconder
el manejo de los mtodos de lectura (get) y asignacin (set) de cualquier atributo de la clase.
En el ejemplo de la Figura 4.5, el atributo ParteReal contiene el mtodo get que permite
retornar el valor correspondiente asociado al atributo de la clase Real. En el caso del set, el
95

VOLVER A MEN

valor que proviene de la asignacin que realice el usuario se obtiene mediante el uso de la
palabra reservada value.

Figura 4.5. Ejemplo de la definicin de atributos en C#.

Nota: No tiene sentido definir un atributo de C# con interfaz privada. Su utilidad


radica en el hecho de hacerlos pblicos y as proteger los atributos de la clase.

4.3 Instanciacin de objetos


La declaracin o instanciacin de objetos en C++ se puede hacer de dos maneras: de forma
esttica y de forma dinmica. En el ejemplo de la Figura 4.6, los objetos C1 y C2 se estn
instanciando estticamente de la clase Complejo. La instanciacin dinmica se realiza
utilizando una direccin de memoria o apuntador (pC en el ejemplo de la Figura 4.6) y ocurre
al hacer la reserva de espacio de memoria utilizando el operador new. Es importante recordar
que en C++ toda memoria que ha sido reservada dinmicamente debe ser liberada por el
programador; de all el uso del operador delete en el ejemplo de la Figura 4.6.

96

VOLVER A MEN

Figura 4.6. Ejemplo de la instanciacin de objetos en C++.

La instanciacin de objetos en Java se realiza de la manera como se muestra en la Figura


4.7, haciendo uso del operador new. Los objetos son siempre implcitamente instanciados de
manera dinmica. Adicionalmente, el programador no debe preocuparse por hacer liberacin
de memoria ya que el motor de ejecucin de Java se encarga de ello a travs de un proceso
automtico de recoleccin de desperdicio. Desperdicio en este caso est asociado a
memoria ya no ms utilizada.

Figura 4.7. Ejemplo de la instanciacin de objetos en Java.

Nota: En Java no existe explcitamente el concepto de apuntador o direccin de


memoria. Sin embargo, el proceso de instanciacin de objetos es siempre de forma
dinmica. En Java no existe el operador delete.

C# tiene un comportamiento similar al de Java en lo referente a la instanciacin de


objetos. En cuanto al uso de apuntadores o direcciones de memoria slo se permiten
para tipos primitivos (entero, caracter, booleano, entre otros); no se pueden utilizar para
estructuras complejas como es el caso de las clases.
97

VOLVER A MEN

4.4 Acceso a los elementos de la clase


Para acceder a los elementos de una clase, dependiendo de los permisos de acceso
establecidos por la interfaz correspondiente, se hace uso de un operador que une el objeto
con el elemento. Tanto en los objetos instanciados de manera esttica en C++ como en los
objetos instanciados en Java y en C#, el operador de acceso a los elementos de la clase es
el punto .. La Figura 4.8 presenta un ejemplo de la manera como se realiza esta operacin
en C++. Ntese la llamada del mensaje ObtReal haciendo uso del objeto C. Para aquellos
objetos instanciados de manera dinmica en C++ se utiliza el operador de referencia de los
elementos de una estructura cuando se tiene un apuntador, es decir, -> (ntese en el ejemplo
de la Figura 4.8 el acceso al elemento ObtImg utilizando el apuntador pC).

Figura 4.8. Ejemplo del acceso de los elementos de una clase en C++.

Nota: En C++, si pC es un apuntador o direccin de memoria, hacer pC-> es


equivalente a hacer (*pC)..

La Figura 4.9 muestra el ejemplo de la llamada del mensaje o acceso al mtodo ObtReal
haciendo uso del objeto C en los lenguajes de programacin Java y C#. En el ejemplo de la
Figura 4.10 se observa el acceso al atributo ParteReal usando la instanciacin C de la clase
Complejo.

98

VOLVER A MEN

Figura 4.9. Ejemplo del acceso de los elementos de una clase en Java / C#.

Figura 4.10. Ejemplo del acceso de un atributo de una clase en C#.

4.5 Constructor
Basado en el principio de persistencia, el constructor es un mtodo particular que es ejecutado
automticamente durante la instanciacin del objeto, tanto esttica como dinmicamente. Es
til para definir el estado inicial del objeto y realizar otras inicializaciones necesarias. Como
mtodo tiene las siguientes caractersticas:
Es opcional.
Debe ser obligatoriamente pblico.
Su identificador coincide con el nombre de la clase.
No se le especifica tipo de retorno.
Puede tener cualquier nmero de parmetros.

99

VOLVER A MEN

La Figura 4.11 presenta un ejemplo de la definicin de un constructor para la clase Complejo


en C++. Ntese la aplicacin de las reglas antes mencionadas. Es importante tener en cuenta
que en C++ no est permitido asignar un valor inicial a los atributos de una clase en el momento
de la definicin de los mismos (a diferencia de la declaracin de variables en cualquier otro
contexto). Otra caracterstica importante a tener en cuenta en C++ es que toda declaracin,
en particular los atributos de una clase, tiene un valor embasurado (cualquier cosa) hasta
tanto no se les haya asignado uno. Esto, por lo tanto, le da un carcter de importancia a usar
constructores en C++ que permitan inicializar los atributos y, por ende, darle al objeto un
estado inicial en tiempo de instanciacin / construccin.

Figura 4.11. Ejemplo de la definicin de un constructor en C++.

Figura 4.12. Ejemplo de la definicin de un constructor en Java / C#.

100

VOLVER A MEN

En lo que respecta a Java y C#, la Figura 4.12 presenta un ejemplo de la definicin de


un constructor correspondiente a la clase Complejo. Nuevamente ntese el uso de las
caractersticas mencionadas antes y la similitud con C++. Sin embargo, a diferencia de C++
tanto en Java como en C# s se permite la inicializacin de los atributos en el momento en el
cual stos son declarados (ver en el ejemplo de la Figura 4.12). Adicionalmente, cualquier
declaracin que se haga en estos dos lenguajes, estas declaraciones tienen siempre un valor
inicial nulo por defecto. Ntese, en este sentido, que tanto la inicializacin que se hace a los
atributos Real e Imag en el momento de su declaracin como el uso del constructor son
opcionales en el ejemplo. Para ambos casos los atributos hubieran tenido el valor nulo por
defecto.

4.6 Polimorfismo
Como ya se mencion en la Seccin 2.2, el polimorfismo, que est basado en el principio que
lleva el mismo nombre, se refiere a definir dos o ms mtodos con el mismo nombre, tipo de
retorno e interfaz, pero diferente contexto el cual es dado por la descripcin de los parmetros
(cantidad y/o tipo de los mismos).
No hay lmite sobre la cantidad de mtodos que se pueden definir de forma polimrfica. Es
importante tener en cuenta que no se trata de una redefinicin ya que cada mtodo existe y se
trata de forma independiente (declaracin, definicin y llamada).
Dado que el constructor tambin es un mtodo, un caso particular muy comn de hacer
polimorfismo es sobre el constructor. Esto permite dar al usuario diferentes formas de
inicializar un objeto durante la instanciacin del mismo. La Figura 4.13 presenta un ejemplo
de polimorfismo en el constructor de la clase Complejo. Ntese que hay dos constructores
(mtodos que se identifican igual que la clase): el primero de ellos sin parmetros y el segundo
con dos parmetros (R y I). Ntese tambin la manera en la cual los objetos son instanciados
y asociados a los constructores; en este caso y por no tener ningn parmetro, la instanciacin
del objeto C1 est asociada al primer constructor, mientras que la instanciacin del objeto
C2 est asociada al segundo constructor (ya que hay dos argumentos de tipo double).

101

VOLVER A MEN

Figura 4.13. Ejemplo del uso de polimorfismo en C++.

De manera similar, la Figura 4.14 presenta el mismo ejemplo pero esta vez haciendo uso de
los lenguajes Java y C# (es similar para ambos casos). Ntese la manera en la cual se hace la
instanciacin de los objetos C1 y C2 haciendo uso del operador new.

Figura 4.14. Ejemplo del uso de polimorfismo en Java / C#.

Nota: No es posible hacer polimorfismo cambiando tan solo el tipo de dato de retorno
del mtodo. Es necesario o bien cambiar la cantidad de argumentos, o cambiar el
tipo de dato de al menos un argumento.
102

VOLVER A MEN

4.7 Inicializacin por defecto


Este concepto es slo soportado por C++ y permite especificar un valor inicial por defecto a
los parmetros de un mtodo para los casos en los cuales el usuario obvia el parmetro. La
idea es darle al usuario la idea de que los parmetros son opcionales. Cuando hay ms de un
parmetro, el orden de asignacin entre el valor por defecto y el parmetro es de izquierda a
derecha. Es decir, en caso de haber dos argumentos con valores por defecto y el usuario slo
coloca uno en la llamada del mtodo, el valor entregado ser asignado al primer argumento
mientras que el segundo parmetro asumir el valor por defecto. Por lo tanto, no puede haber
saltos en esta relacin de asignacin. Es vlido para cualquier mtodo en general y en particular
para los constructores.
Este concepto es til para reducir el nmero de mtodos de la clase, en otras palabras,
simplificar el uso del polimorfismo. De hecho, dado que Java y C# no soportan este concepto,
la nica posibilidad de hacerle ver al usuario que un mtodo se puede llamar con cantidades
diferentes de argumentos es haciendo un polimorfismo con cada uno de los casos.
Ntese en el ejemplo de la Figura 4.15 la manera en la cual se hace la inicializacin por
defecto en C++, en este caso aplicado sobre el constructor de la clase Complejo. Ntese
tambin en el mismo ejemplo que, con slo tener un constructor, la instanciacin de objetos
se puede hacer de tres maneras diferentes: 1) sin argumentos (caso de C1) en la cual los
parmetros formales R y I toman el valor por defecto; con dos argumentos (caso de C2)
en la cual los parmetros formales R y I asumen el valor de los parmetros reales 1.0 y -1.0
respectivamente; y 3) con un argumento (caso de C3) en la cual el parmetro formal R toma
el valor del parmetro real 1.0 y el parmetro formal I toma el valor por defecto.

Figura 4.15. Ejemplo del uso de inicializacin por defecto en C++.

103

VOLVER A MEN

En el caso particular de los mtodos definidos de manera outline (fuera de la clase), la


inicializacin por defecto se especifica en la declaracin del mtodo (dentro de la clase) y no
en la definicin. La Figura 4.16 muestra un ejemplo de este caso. Ntese tambin en el mismo
ejemplo que, dado que se trata de un constructor definido de manera outline, la manera en la
cual primero se indica el nombre de la clase Complejo, luego viene el operador que relaciona
una clase con un elemento de la misma :: y finalmente el nombre del mtodo que, por ser
un constructor, es el mismo que el de la clase. De all viene el Complejo::Complejo que se
muestra en el ejemplo.

Figura 4.16. Ejemplo del uso de inicializacin por defecto


en C++ cuando el mtodo es outline.

Como se mencion anteriormente Java y C# no soportan el concepto de inicializacin


por defecto en los argumentos de un mtodo. Sin embargo, s est permitido la
inicializacin de los atributos en el momento de su declaracin, tal como se ejemplifica
en la Figura 4.17. Ntese en el mismo ejemplo el error que se est documentando
debido a que se est tratando de instanciar el objeto C sin argumentos y, el
nico constructor que est definido en la clase requiere dos parmetros de uso
obligatorio.

Figura 4.17. Ejemplo de la inicializacin de atributos en Java / C#.

104

VOLVER A MEN

4.8 Destructor
Basado, al igual que el constructor, en el principio de persistencia, se trata de un mtodo
particular que es ejecutado automticamente durante la destruccin o prdida de alcance del
objeto (tanto esttica como dinmicamente). Es til para realizar cdigo necesario cuando el
objeto ya no va a ser ms utilizado, como por ejemplo la liberacin o recuperacin de espacios
de memoria.
Como mtodo u operacin, el destructor tiene las siguientes caractersticas:
Es opcional.
Debe ser pblico.
No se le especifica tipo de retorno.
No tiene parmetros.
La sintaxis C++ para declarar el destructor es anteponiendo al nombre del mtodo, que debe
ser el mismo de la clase, el caracter ~ (ver el ejemplo de la Figura 4.18).

Figura 4.18. Ejemplo del uso del destructor en C++.

Nota: La llamada o ejecucin de un destructor es automtica (no la hace implcitamente


el programador). Ocurre cuando el objeto instanciado estticamente pierde alcance
o, en el caso de la instanciacin dinmica el objeto es destruido implcitamente
(usando el operador delete en C++).
En Java no existe un destructor como tal ya que el lenguaje posee un mecanismo automtico
de liberacin de memoria y, por ende, se asume que no hay nada que destruir. Sin embargo,
existe un concepto similar asociado a un mtodo de finalizacin que es llamado automticamente
cuando la memoria ocupada por el objeto es recuperado por este mecanismo. La sintaxis a
usar es la que se muestra en la Figura 4.19.
105

VOLVER A MEN

Figura 4.19. Ejemplo del uso del mtodo de finalizacin en Java.

En C# ocurre algo similar a Java con el mtodo de finalizacin, el cual puede ser sintcticamente
representado de dos maneras distintas como se muestra en la Figura 4.20. Ntese que una de
las dos opciones es equivalente a la sintaxis de C++ con el uso del caracter ~ antepuesto a
un mtodo que se identifica de la misma manera que la clase.
Nota: Es importante no confundir el concepto de destructor de C++ del mtodo de
finalizacin de Java o C#. Si bien tienen en comn que son ejecutados de manera
automtica, el momento en el cual se hace la llamada es diferente. En el caso de C++
ocurre cuando el objeto pierde alcance o es destruido; en Java y C# ocurre cuando
el mecanismo de recuperacin de memoria libera la memoria del objeto involucrado.

Figura 4.20. Ejemplo del uso del mtodo de finalizacin en C#.

4.9 Elementos estticos


Est basado en el principio de concurrencia y permite que todos los objetos instanciados de
una misma clase y que estn activos en un mismo instante, compartan la misma informacin
asociada a uno o varios de sus atributos y/o compartan el mismo cdigo asociado a uno o
varios de sus mtodos. La idea es forzar a todos los objetos a ver y usar el mismo elemento.
106

VOLVER A MEN

Un ejemplo tpico de Java y C# es el caso particular del mtodo que representa el programa
principal (identificado como main en Java y Main en C#), el cual debe ser esttico porque
no tiene sentido dos instancias de objetos cada uno de los cuales con un programa principal
de ejecucin. Es decir, no tiene sentido que hayan dos mtodos principales para ejecutar el
mismo programa; luego, todas las instancias asociadas al programa principal comparten este
mtodo.
Cuando este concepto se usa para el caso de los atributos, es muy til para compartir informacin
entre objetos de una misma clase. Un ejemplo lo representa el que se quiera llevar un control
sobre la cantidad de objetos instanciados de una misma clase y que ese control se haga
dentro de la clase correspondiente. La nica manera posible de hacer esto es utilizando un
atributo declarado de forma esttica. El ejemplo de la Figura 4.21 presenta un caso particular
de lo antes mencionado escrito en C++. Se est definiendo la clase File para el manejo de
archivos y el atributo NumAbiertos es utilizado para contar la cantidad de archivos abiertos
en un momento dado.
Ntese en el ejemplo de la Figura 4.21 la manera en la cual sintcticamente se dice que
el atributo NumAbiertos es esttico, anteponiendo a la declaracin la palabra reservada y
declarativa static. Ntese adems que el mtodo Abiertos tambin es un elemento esttico
en la clase y esto se debe a que hay una restriccin en el uso de los atributos estticos:
cualquier mtodo que haga uso de un elemento esttico (ya sea atributo o mtodo) debe
tambin ser esttico a excepcin del constructor y el destructor. En el mismo ejemplo tambin
puede verse la diferencia entre la declaracin y la declaracin de una clase. En este caso
String est siendo declarada y File est siendo definida (lo cual tambin implica que se est
declarando). Est claro que la definicin de la clase String debe estar en algn otro lado del
programa.

Figura 4.21. Ejemplo del uso de elementos estticos en C++.

107

VOLVER A MEN

Nota: C++, Java y C# son lenguajes de referencia hacia adelante, lo que significa
que cualquier elemento que se utilice en alguna sentencia debe haber sido declarado
antes de su uso. En varios de los elementos incluidos en C++, es posible hacer una
declaracin del elemento en un contexto separado de su definicin. Este es el caso
del ejemplo de la Figura 4.21. La posibilidad de hacer mtodos outline tambin es
otro ejemplo.

La Figura 4.22 complementa el desarrollo de la Figura 4.21. Ntese que el sitio idneo para
incrementar Abiertos es en el constructor y el sitio idneo para decrementar el atributo es el
destructor. Ahora bien, dado que en C++ no es posible inicializar los atributos en el momento
de su declaracin y dado tambin que el constructor no es el sitio adecuado para hacer una
inicializacin de un atributo esttico ya que cualquier objeto que se instancie y, por ende,
haga que se ejecute el constructor, provocara que el elemento esttico tendra siempre este
valor inicial asignado, hubo que implementar un mecanismo para hacer la inicializacin de los
atributos estticos. Este mecanismo consiste en hacer lo que se muestra en la parte superior
del cdigo de la Figura 4.22. Es como si se estuviera declarando de nuevo el atributo (de all
la colocacin del tipo de dato int), se asocia el atributo con la clase haciendo uso del operador
:: y se coloca el operador de asignacin = con el valor inicial (0 en este caso).

Figura 4.22. Complemento del ejemplo presentado en la Figura 4.21.

La Figura 4.23 muestra un ejemplo en Java en la cual se usa el atributo esttico NumInstancias,
el cual se puede inicializar en el momento de su declaracin ya que este lenguaje lo permite.
Ntese tambin en el ejemplo la declaracin del mtodo principal main que permite la ejecucin
del programa y que, como se explic anteriormente, debe ser esttico.

108

VOLVER A MEN

Figura 4.23. Ejemplo de uso de elementos estticos en Java.

El cdigo de la Figura 4.24 es el equivalente al que se muestra en la Figura 4.23 pero en este
caso escrito en C#. La nica diferencia es que en C# el mtodo principal se identifica como
Main.

Figura 4.24. Ejemplo de uso de elementos estticos en C#.

Nota: En Java y C# la clase a la cual pertenece el mtodo principal (main o Main


segn corresponda), debe ser pblica. Es posible hacer pasaje de parmetros al
programa principal como un conjunto o vector de cadenas de caracteres.

109

VOLVER A MEN

4.10 Operador de asignacin o copia


Al igual que ocurre con los tipos de datos bsicos en la cual un operador de asignacin permite
cambiar el valor de una variable cualquiera, los lenguajes OO manejan internamente el uso
del operador natural de asignacin cuando es aplicado sobre una instancia u objeto. El efecto
es copiar el contenido de la memoria que corresponde a los atributos del objeto que resulta
de la expresin del lado derecho de la asignacin, en el espacio de memoria equivalente del
objeto que recibe el valor en el lado izquierdo de la operacin de asignacin. La Figura 4.25
muestra un ejemplo; la parte superior corresponde a cdigo escrito en C++ y el resto del cdigo
del ejemplo corresponde a la manera como se realiza en Java y C#. Ntese que en todos los
casos el operador de asignacin es =.
Hay que tener cuidado con direcciones de memoria o uso de apuntadores como atributos
(si esto aplica como en el caso del lenguaje C++). Cuando se hace la asignacin se copian
las direcciones de memoria y no los contenidos de lo que hay en esos espacios de memoria.
La Figura 4.26 muestra un ejemplo de este caso. Ntese que la clase String definida en el
ejemplo tiene la direccin de memoria cPointer como atributo cuya reserva de memoria se
hace en el constructor de la clase. Luego de la instanciacin de los objetos (St1(5) y St2(5)) la
distribucin de memoria queda como se indica en el bloque superior de la derecha de la Figura
4.26. Es decir, cada cPointer apunta a un bloque de memoria diferente. Sin embargo, al hacer
la asignacin St2 = St1, la direccin de memoria del cPointer de St2 pasa a ser igual a la
direccin de memoria del cPointer de St1 (los dos apuntadores estn direccionados al mismo
bloque de memoria), tal como se muestra en el bloque inferior de la derecha de la Figura 4.26.
Por otro lado, los lenguajes de programacin que soportan la sobrecarga de operadores
(ver Seccin 4.11) pueden dar su propia definicin del operador de asignacin y as evitar
problemas como el caso anterior, ya que la operacin no sera implcita sino explcitamente
codificada por el programador.
Adicionalmente, cuando el contexto es claro, el operador de asignacin puede dar lugar a
que se realice una llamada indirecta al constructor del objeto de manera tal que se pueda
instanciar temporalmente un objeto para que ste sea utilizado como resultado de la expresin
de la parte derecha de la asignacin. En la Figura 4.25 esto ocurre con la asignacin que se
est haciendo al objeto C3 en la parte superior de la figura. Ntese que al objeto C3 se le
est asignando el nmero real 1.0 que, como es obvio, son de tipos diferentes. Sin embargo,
dado que la clase Complejo tiene un constructor que permite construir un objeto Complejo
teniendo un nmero real como parmetro (ver Figura 4.15), la asignacin puede ejecutarse
una vez que se llame a este constructor y se tenga un objeto Complejo temporal en el lado
derecho de la expresin.

110

VOLVER A MEN

Figura 4.25. Ejemplo de uso del operador de asignacin o copia.

En el ejemplo de la Figura 4.25, especficamente en los dos ltimos segmentos de cdigo


relativos a Java y C#, se puede ver la misma operacin de asignacin C2 = C1 pero realizada
de dos maneras diferentes las cuales no son equivalentes y por ende hay que tener cuidado
con ello. En la primera de ellas ntese que no hay una instanciacin para el objeto C2, que
s est en el ltimo segmento de cdigo al hacer C2 = new Complejo(). En el caso en el cual
no hay un objeto instanciado y por lo tanto no hay un espacio de memoria asociado a l, pero
el objeto declarado est siendo utilizado en la parte izquierda de una asignacin, realmente lo
que se est asignando al objeto declarado es la direccin de memoria del objeto que resulta
de la parte derecha de la asignacin. Es decir, lo que realmente se est haciendo en esa
sentencia de cdigo es que C2 y C1 estn apuntando al mismo espacio de memoria ya que
slo hay un objeto instanciado (en este caso C1).
En el otro caso los dos objetos han sido instanciados y por lo tanto hay dos espacios de
memoria diferentes. Luego la operacin de asignacin realiza la copia del contenido de lo que
est en el espacio de memoria de C1 al espacio de memoria que corresponde a C2.

Figura 4.26. Ejemplo de uso del operador de asignacin o copia


en C++ cuando hay direcciones de memoria involucradas.

111

VOLVER A MEN

Nota: Es importante tener en cuenta las diferencias entre un objeto instanciado de


uno que ha sido solamente declarado. La instanciacin es la operacin mediante el
cual se le asigna al objeto un espacio de memoria correspondiente para que el objeto
sea autnomo e independiente de los dems (principio de persistencia).

4.11 Sobrecarga de operadores


Es un tipo particular de polimorfismo y por ende est basado en este principio. Permite escribir
una implementacin propia sobre el uso de un smbolo u operador predeterminado, usado por
el lenguaje para representar operaciones sobre elementos primitivos (+, -, *, /, =, >, <, entre
otros).
Por ejemplo, para representar la suma de dos objetos C1 y C2 de tipo Complejo y guardar
el resultado en el Complejo C3, es ms intuitivo y claro escribirlo en una sentencia de la
forma C3 = C1 + C2, que hacer algo como C3 = C1.Suma(C2) o C3 = Suma(C1, C2).
El lenguaje de programacin en la cual se tiene una mejor implementacin de la sobrecarga de
operadores es C++. En este lenguaje es posible sobrecargar los siguientes operadores:
Aritmticos: +, -, *, /, %, ++, --, +=, -=, *=, /=, %=.
Asignacin: =.
Relacionales: >, <, >=, <=, ==, !=.
Manejo de bits: &, |, ^, >>, <<, ~, &=, |=, ^=, >>=, <<=.
Lgicos: &&, ||, !.
Conversin explcita de tipos (casting): ( )
Direccionamiento de arreglos: [ ]
Manejo dinmico de memoria: new, delete.
La sintaxis utilizada en C++ para sobrecargar, por ejemplo, el operador de suma +, se puede
visualizar en la Figura 4.27. Ntese el uso de la palabra reservada operator que antecede al
operador que se est sobrecargando, + en este caso. Es importante tener en cuenta que al
sobrecargar el operador hay que obligatoriamente respetar la semntica y asociatividad del
operador. Al hablar de semntica se tiene que tomar en cuenta: 1) el nmero de operandos
especficos (1 para los operadores unarios como por ejemplo ++ y -- y 2 para los operadores
binarios como por ejemplo +, * y >=); 2) el tipo de retorno del operador (por ejemplo, +
112

VOLVER A MEN

debe retornar un elemento del mismo tipo que sus operandos, >= debe retornar un valor
booleano, new debe retornar un apuntador o direccin de memoria).

Figura 4.27. Ejemplo de la sobrecarga del operador + en C++.

Ntese en el cdigo de la Figura 4.27 que, aunque la operacin de suma + es un operador


binario (dos operandos), hay un solo argumento en la definicin del mtodo. En efecto, en C++
los operadores binarios sobrecargados tienen un solo argumento y los operadores unarios
sobrecargados no tienen argumentos. Para el caso de los operadores binarios, el operando
de la izquierda es el objeto que hace la llamada del mensaje mientras que el operando de la
derecha es el argumento que recibe el mtodo. En otras palabras, al hacer C2 + C3, el objeto
C2 es el operando de la izquierda en la expresin y por lo tanto es el objeto involucrado en
la llamada del mensaje +; C3 es el objeto que se pasa como argumento en el mensaje.
Ntese entonces que en C++, C2 + C3 es equivalente a hacer C2.+(C3) lo cual es tambin
sintcticamente vlido para el lenguaje.
Si se observa la implementacin del mtodo de + en el cdigo de la Figura 4.27, ms
especficamente donde se suman las partes reales de los complejos, ntese que uno de los
operandos es Real que corresponde al atributo que pertenece al objeto que est haciendo
la llamada (operando de la izquierda que en el ejemplo es C2) y el segundo operando es
C.Real, siendo C el parmetro formal del mtodo (es el operando de la derecha que, en el
ejemplo, es el parmetro real C3). Ntese que, aun siendo Real un atributo privado, ste
puede ser accedido sin problema ya que su acceso est siendo realizado dentro de la clase.
Dado que la suma es un operador asociativo, es decir, se pueden realizar sumas consecutivas
como por ejemplo C1 + C2 + C3, adems es asociativo de izquierda a derecha ya que primero
se debe resolver C1 + C2 y luego a este resultado se le aplica la operacin de suma con C3;
est claro que para que el componente + C3 se pueda ejecutar como un mensaje, el resultado
de C1 + C2 debe retornar un objeto vlido para as permitir la ejecucin de ese mensaje. Esa
113

VOLVER A MEN

es la razn por la cual la sobrecarga del operador de suma en el ejemplo de la Figura 4.27
retorna un objeto Complejo (el uso de los & en el ejemplo, tanto para el retorno como para
el parmetro formal, corresponde a la sintaxis de pasaje de parmetro por referencia y no es
obligatorio en este caso particular de la sobrecarga de operadores). En general esta regla
relativa a lo que debe retornar el mtodo debe ser as para cualquier operador asociativo que
se est sobrecargando. Escrito en trminos de acceso de los elementos del objeto mediante el
operador ., C1 + C2 + C3 es equivalente a hacer (C1.+(C2)).+(C3).
Nota: La sobrecarga de cualquier operador que es semnticamente asociativo,
implica necesariamente que el mtodo correspondiente debe retornar un objeto
equivalente a la clase en la cual se est agregando la sobrecarga del operador.
El ejemplo de la Figura 4.28 muestra la sobrecarga del operador unario de conversin explcita
de tipo (casting), en este caso para el tipo de dato double. Ntese el uso del operador en
la llamada del mensaje (al final del cdigo mostrado en el ejemplo). Ntese tambin que al
ser un operador unario, como se mencion anteriormente, el mtodo no tiene argumentos.
Adicionalmente y respetando la semntica de uso del operador, el tipo de retorno en este caso
no se coloca porque ya se asume por el identificador del operador que se est sobrecargando
(double). En este caso, el equivalente de hacer (double)C es hacer C.double().

Figura 4.28. Ejemplo de la sobrecarga del operador unario de conversin explcita double en C++.

El lenguaje de programacin Java no soporta el concepto de sobrecarga de operadores.


Para poder por ejemplo, implementar una suma de complejos hay que hacer algo similar a lo
mostrado en el cdigo que aparece en la Figura 4.29.

114

VOLVER A MEN

Figura 4.29. Ejemplo de la implementacin de una suma en Java.

Al igual que C++ y contrario a Java, C# soporta el concepto de sobrecarga de operadores


aunque en menor proporcin que C++. Los operadores que se pueden sobrecargar en C# son
los siguientes:
Aritmticos: +, -, *, /, %, ++, --.
Relacionales: >, <, >=, <=, ==, !=.
Manejo de bits: &, |, ^, >>, <<, ~.
Lgicos: !, true, false.
Conversin explcita de tipos (casting): ( )
Al sobrecargar un operador binario cualquiera O, tambin se sobrecarga automticamente
el operador de asignacin compuesta correspondiente O= siempre y cuando esa operacin
compuesta sea vlida o soportada en el lenguaje. Por ejemplo, si se sobrecarga el
operador de suma +, el operador de asignacin += ya est tambin implcitamente
sobrecargado.
El direccionamiento de arreglos [ ] no se considera en el lenguaje como una sobrecarga,
sino que se maneja haciendo uso del concepto de indexadores (indexer). Ver los detalles
y un ejemplo en la Seccin 4.16.3.3.
La Figura 4.30 Muestra el ejemplo de la sobrecarga del operador de suma escrito en C#.
Ntese las similitudes y diferencias con respecto a la manera como se hace en C++. Se
mantiene la palabra reservada operator en la sintaxis, sin embargo, en C# es obligatorio que
todos los mtodos que corresponden a la sobrecarga de un operador sean estticos, de all
que aparezca en el cdigo la palabra reservada static. Otra diferencia con respecto a C++
es el nmero de argumentos. En C# la sobrecarga de los operadores binarios reciben los
dos operandos como argumento y la sobrecarga de los operadores unarios reciben el nico
operando como parmetro.
115

VOLVER A MEN

Figura 4.30. Ejemplo de la sobrecarga del operador + en C#.

En la Figura 4.31 se encuentra el cdigo relativo a la sobrecarga del operador de conversin


double. Es posible hacer una versin tanto para el caso implcito como para el caso explcito, de
all las palabras reservadas implicit y explicit respectivamente. En la parte final del cdigo que
se muestra en la Figura 4.31 aparece un ejemplo del uso de ambos operadores sobrecargados.
Hay que recordar que en C++ la conversin implcita se hace de manera automtica cuando la
clase contiene un constructor que soporte la conversin que se quiere hacer.

Figura 4.31. Ejemplo de la sobrecarga de los operadores de conversin implcita y explcita en C#.

4.12 Referencia al objeto actual


Como su nombre lo indica, la referencia al objeto actual es una variable predefinida que permite al
programador tener la direccin de memoria o hacer referencia al objeto actualmente instanciado.
Por lo general no se requiere y su uso es opcional, pero en ocasiones es imprescindible para
realizar cierto tipo de operaciones o comportamiento. Tanto para C++ como para Java y C#, la
palabra reservada utilizada para acceder a esta variable es this.
116

VOLVER A MEN

Se puede usar opcionalmente para acceder a todos los elementos de la clase


cuando se est dentro de un objeto instanciado de esa clase. En este caso permite
por ejemplo, diferenciar al objeto actual de cualquier otro posible objeto que se
est utilizando dentro del cuerpo de un mtodo. Es til cuando en el cuerpo de
un mtodo se hace referencia a un elemento de la clase con ms de una instancia
incluyendo la actual y se quiere representar la diferencia (ver ejemplo de la Figura
4.33).
Por otro lado, para los lenguajes que permiten la sobrecarga de operadores, el uso
de this es imprescindible para retornar la instancia actual cuando el operador es
unario y asociativo. Lo anterior se puede observar en el cdigo C++ que se muestra
en la Figura 4.32 en la cuan se est haciendo la sobrecarga del operador unario de
incremento ++ que, adems de modificar los valores del objeto actual, lo retorna
ya que este operador es asociativo (puede ser aplicado varias veces en una misma
expresin). Ntese que al tratarse de una referencia (direccin de memoria), en
C++ el this se comporta como un apuntador y, de all el que se requiere el uso de
los operadores y *.
En Java, this se puede usar dentro de un constructor para hacer la llamada de otro
constructor de la misma clase. La Figura 4.33 presenta un ejemplo de este caso
en la cual se observa que el constructor sin parmetros se resuelve llamando al
constructor con dos argumentos haciendo uso de this. Para este caso es obligatorio
que esta sentencia sea la primera que aparezca en el mtodo.

Figura 4.32. Ejemplo de la sobrecarga del operador ++


en C++ y el uso de this para hacer el retorno.

117

VOLVER A MEN

Figura 4.33. Ejemplo del uso de this en Java.

En la Figura 4.34 se muestra un ejemplo del uso de this en C#. En este caso su uso es
opcional ya que da lo mismo acceder a los elementos de la clase dentro de la clase con o sin
el uso de this.

Figura 4.34. Ejemplo del uso de this en C#.

4.13 Herencia
El mecanismo de herencia o especializacin de una clase a partir de otras se realiza
sintcticamente en C++ como se muestra en el ejemplo de la Figura 4.34. En este caso la
clase base o superclase es Carro y la clase derivada o subclase es Carga. Ntese que la
relacin de herencia se establece con el operador : luego de lo cual se debe indicar la interfaz
de la herencia (ver Seccin 2.4). Hay que tener en cuenta que, como ocurre en el ejemplo de
la Figura 4.35 con la clase Carro, cuando alguna de las clases base tiene un constructor
obligatorio (con argumentos), la clase derivada debe tambin poseer un constructor (con o sin
argumentos) que haga la llamada del constructor de la clase base. Ntese la manera como lo
anterior se realiza en la parte final del cdigo del ejemplo haciendo uso del operador :.
118

VOLVER A MEN

Figura 4.35. Ejemplo del uso de herencia en C++.

La Figura 4.36 presenta un ejemplo en la cual se realiza una herencia con interfaz
privada entre la clase derivada B y la clase base A. La tabla que aparece en la
derecha de la Figura 4.36 muestra los seis elementos que forman parte de B,
tres de los cuales (A1, A2 y A3) resultan como consecuencia de la herencia y
los otros tres (B1, B2 y B3) se definen propiamente dentro de B; se incluye
en la tabla si es posible (indicado con la marca de chequeo ) o no (indicado con
una ) tener acceso a esos elementos dentro de la clase B, en clases derivadas de
B o fuera de B. Ntese que A1 no puede accederse dentro de B porque es
privado para A y por lo tanto slo puede ser accedido dentro de A. Ntese tambin que
la herencia con interfaz privada no permite que el elemento pblico en A (identificado
como A3) pueda ser accedido fuera de B de la misma manera que lo hace el
elemento pblico definido en B (identificado como B3).

Figura 4.36. Herencia con interfaz privada en C++.

De la misma manera que en el caso anterior, el ejemplo de la Figura 4.37 muestra la herencia
con interfaz protegida. Ntese que la nica diferencia entre la tabla que aparece en este caso
comparada con la tabla de la Figura 4.36 es la posibilidad de que A3 pueda ser accedido por
las clases derivadas de C.
119

VOLVER A MEN

Figura 4.37. Herencia con interfaz protegida en C++.

Finalmente, el ltimo caso de interfaz de herencia se ejemplifica en la Figura 4.38 y como


puede notarse, esto hace posible que A3 sea tambin accesible para los usuarios fuera de la
clase derivada D.

Figura 4.38. Herencia con interfaz pblica en C++.

C++ soporta la herencia mltiple la cual se realiza de la manera en la cual aparece en el


ejemplo de la Figura 4.39. Se tienen dos clases base B1 y B2 y la clase derivada D. Luego
del operador : que indica que hay una herencia, aparecen separados por una , las clases
base de las cuales se est heredando, cada una de las cuales con su respectiva interfaz de
herencia. Ntese que, al igual que ocurre en la herencia simple y la llamada de un posible
constructor obligatorio para la clase base, en la herencia mltiple tambin se deben hacer las
llamadas de los constructores de las clases base en el momento de la construccin de la clase
derivada, los cuales se separan por una , luego del operador :.

120

VOLVER A MEN

Figura 4.39. Ejemplo de herencia mltiple en C++.

Nota: Por defecto en C++, cuando no se especifica ninguna interfaz de herencia,


sta se asume que es privada.

En el ejemplo de la Figura 4.39 est claro que como consecuencia de la herencia


mltiple, cualquier objeto de D posee los elementos IB1 de B1 e IB2 de
B2. Ahora bien, cabe preguntarse si es posible que dos elementos de clases base
diferentes tengan el mismo identificador y, de ser esto posible, cmo diferenciar
estos dos elementos que se identifican de la misma manera en la clase derivada. La
respuesta a la primera pregunta es que lo anterior si es posible en C++ y la manera
de diferenciar un elemento de otro es haciendo uso del operador :: relacionando
el elemento especfico con su clase. Un ejemplo de esta situacin se observa en
la Figura 4.40. Ntese que ambas clases B1 y B2 poseen un atributo pblico
identificado como B y que stos pueden ser accesados fuera de la clase derivada D
mediante las operaciones B1::B y B2::B respectivamente.

Figura 4.40. Ejemplo de cmo resolver ambigedades en una herencia mltiple en C++.

121

VOLVER A MEN

Sea el siguiente caso que se presenta a continuacin. Se tiene la clase Equipo que contiene
el atributo serial; se tienen las clases derivadas EquipoAudio y EquipoVideo que, por
herencia simple, son especializaciones de Equipo y que por lo tanto poseen un serial. Se
hace adicionalmente una nueva clase derivada EquipoAudiovisual que combina mediante
una herencia mltiple las caractersticas de EquipoAudio y EquipoVideo. Ntese que
como consecuencia de la herencia mltiple, EquipoAudiovisual va a tener no uno sino dos
atributos serial (ver parte superior del ejemplo de la Figura 4.41), lo cual es incorrecto para
este contexto. Para lidiar con esta situacin, C++ puede realizar una suerte de herencia virtual
indicado con la palabra reservada virtual y siguiendo la sintaxis que se muestra en la parte
inferior del ejemplo de la Figura 4.41. En este caso elementos que se identifican de la misma
manera y que pueden provenir de clases base diferentes son tratados como un solo elemento.
Es decir, un EquipoAudiovisual va a tener un nico atributo identificado como serial.

Figura 4.41. Ejemplo de cmo resolver redundancias en una herencia mltiple en C++.

En Java, la herencia se implementa haciendo uso de la sintaxis que se muestra en la Figura


4.42. La relacin entre la clase derivada y la clase base se establece mediante la palabra
reservada extends. Al igual que ocurre con C++, la obligatoriedad de un constructor en la
clase base obliga a que ste sea llamado en la clase derivada. Esto se hace mediante el uso
de la palabra reservada super (ver su uso en el ejemplo de la Figura 4.42).

122

VOLVER A MEN

Figura 4.42. Ejemplo de uso de la herencia en Java.

En el caso de elementos que se identifiquen igual tanto en la clase base como en la clase
derivada, la manera de diferenciarlos en Java es utilizando el nombre de la clase y el operador
de acceso a los elementos .. En la Figura 4.43 se presenta un ejemplo con la clase base A,
la clase derivada B y el atributo comn I. Ntese la manera en la cual se hace referencia
a la I de A dentro de B mediante la operacin A.I. En el caso de los elementos pblicos
que se identifican igual, como la I en el ejemplo de la Figura 4.43, dado que A.I tambin es
pblico en B, no hay posibilidad fuera de B de diferenciar el acceso entre los dos elementos
que se identifican igual. Se asume siempre que es la I definida localmente en B.

Figura 4.43. Ejemplo de cmo resolver ambigedades en una herencia en Java.

Nota: En Java no existe una manera de discriminar la interfaz de la herencia. En este


caso siempre se asume una interfaz de herencia pblica.

Java no soporta la herencia mltiple; sin embargo, hace uso del concepto de interface para
especializar comportamientos en clases derivadas. A esta especializacin se le denomina
implementacin de la interface. Para ello se hace uso de la sintaxis que aparece en el
ejemplo de la Figura 4.44. La palabra reservada interface se usa para definir la interface y
123

VOLVER A MEN

la palabra reservada implements se usa para implementar la interface en la clase derivada.


Las interfaces slo pueden aplicarse sobre mtodos no definidos (sin cuerpo); la definicin o
cuerpo de los mtodos se realiza durante la implementacin de la interface. En las interfaces
no se permite la definicin de atributos. En el ejemplo de la Figura 4.44 se tiene la interface
VideoClip en la cual los mtodos Play y Stop son implementados en la clase MiClase.

Figura 4.44. Ejemplo de uso de interfaces en Java.

Los creadores de Java aseguran que la herencia mltiple se puede implementar mediante
la herencia simple y la implementacin de una o ms interfaces. La Figura 4.45 presenta un
ejemplo en la cual la clase Subclase hereda de la clase Superclase e implementa de las
interfaces Interface1 e Interface2.

Figura 4.45. Ejemplo del uso combinado de la herencia y la implementacin de interfaces en Java.

Nota: El concepto de interface de Java no es una clase sino ms bien una plantilla
de comportamiento. Por lo tanto no es correcto hablar de herencia en este caso; es
por ello que se habla de implementacin de la interface.

En lo que respeta a C#, la sintaxis para implementar la herencia es una combinacin de lo


que hace C++ y Java. La Figura 4.46 presenta el mismo ejemplo usado anteriormente de
la derivacin entre las clases Carga y Carro. Ntese que al igual que C++, la herencia
se establece con el operador :, aunque en este caso no se especifica interfaz de herencia
124

VOLVER A MEN

ya que, al igual que Java, siempre se asume una interfaz de herencia pblica. Para hacer la
llamada del constructor de la clase base durante la construccin de la clase derivada se hace
uso del operador : (similar a C++) y se utiliza la palabra reservada base. Dado que C# al
igual que Java no soportan la herencia mltiple, no se requiere diferenciar una clase base de
otra ya que slo es posible tener una sola.

Figura 4.46. Ejemplo de uso de la herencia en C#.

A diferencia de C++ y Java, C# hace un manejo ms explcito de la definicin de elementos con


identificadores repetidos entre la clase base y la clase derivada. Para ello se pueden hacer uso
de las palabras reservadas new y override que indican que el segundo elemento es uno
nuevo o remplaza (sobrescribe) el primero. En el ejemplo que aparece en la Figura 4.47 se
muestra el uso de new para definir en la clase B un nuevo elemento I con la intencin de
mantener tambin el elemento I de la clase base A. Ntese que para acceder al elemento
I de A dentro de B se utiliza la palabra reservada base, es decir base.I. Ntese tambin
en el comentario que aparece en el bloque de la parte derecha del ejemplo, que no hay manera
de acceder al elemento pblico I de A haciendo uso de un objeto de B.

Figura 4.47. Ejemplo de cmo resolver ambigedades en una herencia en C#.

125

VOLVER A MEN

Como alternativa para emular una posible herencia mltiple, C# mantiene el mismo concepto
de implementacin de interfaces de Java. El concepto es completamente similar al definido por
Java con algunas diferencias menores. Como se puede ver en el ejemplo de la Figura 4.48 se
hace uso tambin de la palabra reservada interface para definir la interface y se mantiene
la sintaxis de la herencia : para implementar la interface. Por otro lado, dado que C# soporta
el concepto de atributos con las operaciones get y set, stos tambin estn sujetos a ser
incluidos en una interface para ser implementados. Un ejemplo de lo anterior lo representa el
atributo Frames y el mtodo get que aparecen en la Figura 4.48.

Figura 4.48. Ejemplo de uso de interfaces en C#.

La Figura 4.49 muestra un ejemplo en la cual la clase Subclase hereda de la clase Superclase
e implementa de las interfaces Interface1 e Interface2.

Figura 4.49. Ejemplo del uso combinado de la herencia y la implementacin de interfaces en C#.

126

VOLVER A MEN

4.14 Clases abstractas y mtodos virtuales


Como se mencion al final de la Seccin 2.4, las clases abstractas son usadas para servir
como clases base para derivar nuevas clases con ellas. En C++ no existe una manera explcita
de decir que una clase es abstracta o no. Basta con que una clase tenga un mtodo virtual
puro para que la clase sea asumida como abstracta. Para declarar mtodos virtuales en C++
se hace uso de la sintaxis que se presenta en el ejemplo de la Figura 4.50. Se utiliza la palabra
reservada virtual como parte de la declaracin del mtodo y, la diferencia entre un mtodo
virtual puro y un mtodo virtual no puro est en que el no puro contiene un cuerpo (como
DrawL en el ejemplo). Este mtodo puede ser redefinido en las clases derivadas. Un mtodo
virtual puro tiene la sintaxis = 0 indicando que no hay cuerpo y que ste debe ser suministrado
o definido en las clases derivadas (como Area en el ejemplo de la Figura 4.50). Ntese la
diferencia entre redefinir o dar una nueva definicin y definir o dar la definicin.

Figura 4.50. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en C++.

En Java se utiliza la palabra reservada abstract para indicar explcitamente que una clase
es abstracta, como se muestra en el ejemplo de la Figura 4.51. Ntese que esta misma palabra
reservada es la que se utiliza tambin para indicar que un mtodo es virtual puro.

Figura 4.51. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en Java.

C# mantiene la misma interface de Java con la palabra reservada abstract para indicar
que una clase es abstracta y un mtodo es virtual puro. A diferencia de Java, los mtodos
virtuales pueden o no ser redefinidos en clases derivadas al indicarlo explcitamente con las
palabras reservadas override y new respectivamente. La Figura 4.52 presenta el ejemplo
correspondiente a C#.
127

VOLVER A MEN

Figura 4.52. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en C#.

Al igual que ocurre en C++, en C# hay manera de diferenciar un mtodo virtual puro de uno
que no lo es y para ello se hace uso tambin de la palabra reservada virtual. El ejemplo de
la Figura 4.53 muestra los mtodos virtuales no puros F1, F2 y F3 (ntese que hay una
definicin o cuerpo asociado a ellos) definidos en la clase base A. En la clase derivada B
se tiene una redefinicin de F1, una nueva versin de F2 (se tiene as un polimorfismo por
inclusin de F2) y una nueva versin de F3 que adems es virtual y que por lo tanto puede
ser redefinido o no en las posibles clases derivadas de B.

Figura 4.53. Ejemplo de la definicin de mtodos virtuales en C#.

4.15 Paquetes
Los paquetes o agrupacin de elementos (clases por lo general) se manejan en C++ haciendo
uso del concepto de espacio de nombres. La palabra reservada para declarar y definir un
espacio de nombres es namespace (ver ejemplo en la Figura 4.54). Todo lo que se defina
dentro del inicio y fin (dado por los smbolos { y }) del espacio de nombres forma parte de
ese espacio de nombre o paquete. Como se muestra al final del ejemplo de la Figura 4.54,
para acceder a cualquier elemento definido dentro del paquete se hace uso del operador ::.
128

VOLVER A MEN

Figura 4.54. Ejemplo de la definicin de un espacio de nombres en C++.

Por comodidad y a fin de evitar escribir tanto el nombre del paquete y el operador :: antes
de cada ocurrencia de los elementos correspondientes en el cdigo, se puede hacer uso de
la sintaxis que se muestra en la Figura 4.55 con la palabra reservada using namespace.
Quiere decir que desde este punto hasta que finaliza el bloque donde esta sentencia aparezca,
cualquier elemento que no puede ser ubicado en el contexto actual del cdigo, puede ser
buscado en el paquete indicado por el using namespace (identificado como Nuevo en el
ejemplo). Ntese tambin en el comienzo del ejemplo de la Figura 4.55 que es posible declarar
un espacio de nombres haciendo uso de otro identificador con el cual se haya declarado
previamente. En el caso especfico del ejemplo, Nuevo es un paquete que puede ser usado
tambin como nombre alternativo para el paquete MiPaquete.

Figura 4.55. Ejemplo de uso de un espacio de nombres en C++.

Nota: En C++ el uso de espacios de nombres o paquetes es opcional.


package e import son las palabras reservadas utilizadas en el contexto del manejo de
paquetes en Java. La primera permite decir explcitamente a qu paquete pertenece o va a
estar asociada una clase que se va a definir a continuacin. Hay que recordar que en Java
todo archivo de cdigo fuente (extensin java) est asociado a la definicin de una clase. El
uso de la palabra reservada package es obligatorio y la sentencia correspondiente debe ser
colocada al inicio del archivo antes de cualquier otra sentencia. La palabra reservada import
es utilizada para indicar que el cdigo que viene a continuacin hace uso de otros paquetes
previamente definidos.
129

VOLVER A MEN

Es importante tener en cuenta que en Java no slo los archivos de cdigo fuente (uno por cada
clase definida) se identifican de la misma manera que el nombre dado a las clases sino que
adems, el identificador del paquete coincide con el nombre del directorio donde se encuentran
todas las clases asociadas a ese paquete.
La Figura 4.56 muestra un ejemplo del uso de las palabras reservadas package e import
de Java. Para acceder a los elementos o clases del paquete haciendo uso del identificador
del paquete se utiliza el operador . (en el ejemplo se observa cuando se accede a la clase
MiClase que se supone ha sido definida y asociada al paquete MiPaquete). Ahora bien,
ntese que se muestran dos maneras diferentes de usar el import. En el caso de import
MiPaquete.Miclase se est indicando que se va a hacer uso de la clase especfica Miclase
que se supone est asociada al paquete MiPaquete. En el caso import MiPaquete.* se est
indicando que se puede hacer uso de cualquiera de las clases que estn asociadas al paquete
MiPaquete.

Figura 4.56. Sintaxis utilizada en Java para la definicin y uso de paquetes.

La sintaxis utilizada en C# para usar paquetes es similar a la de C++ y el concepto de espacio de


nombres. Como se puede ver en el ejemplo de la Figura 4.57 se mantiene la palabra reservada
namespace para declarar el paquete y, en lugar de utilizar using namespace para indicar
que se va a hacer referencia de un paquete particular en el cdigo que viene a continuacin,
simplemente se utiliza la palabra reservada using. Al igual que Java, en C# se hace uso del
operador . para acceder a los elementos definidos dentro de un paquete especfico.

Figura 4.57. Sintaxis utilizada en C# para la definicin y uso de espacios de nombres.

130

VOLVER A MEN

Nota: El uso de paquetes es til para organizar el modelo conceptual. Permiten


adems resolver cualquier posible problema de ambigedad que bien pudiera existir
al tener dos elementos diferentes que se identifiquen de la misma manera pero que
forman parte de dos paquetes diferentes.

4.16 Otros conceptos


Una vez revisado todos los conceptos inherentes a la OO de los tres lenguajes de programacin
tratados en este texto, el objetivo de esta seccin es describir una serie de otros conceptos
complementarios que pueden ser tiles a la hora de programar con C++, Java y/o C#.

4.16.1 Otros conceptos de C++


4.16.1.1 Funciones y clases amigas
Se trata de un concepto derivado de la no pureza de C++ desde el punto de vista OO y
la necesidad de ir en contra del principio de encapsulamiento. Las funciones amigas son
funciones que estn definidas fuera del contexto de una clase (es decir, no son mtodos) y
tienen permiso de acceder a los elementos privados de esa clase. Por otro lado, una clase
A es amiga de otra clase B cuando dentro de los mtodos de A se puede acceder a los
elementos privados de B.
Para declarar una funcin o clase como amiga de otra clase, se hace uso de la declarativa
friend. Esta relacin de amistad se debe declarar dentro de la clase que permite la amistad.
En el ejemplo de la Figura 4.58, la clase Time declara y permite que la funcin gtime y la
clase Date sean amigas de ella.

Nota: El uso de funciones y clases amigas va en contra del principio de


encapsulamiento. Se puede evitar su uso con poco esfuerzo; basta por ejemplo,
definir mtodos pblicos para acceder a elementos privados.

131

VOLVER A MEN

Figura 4.58. Ejemplo del uso de funciones y clases amigas en C++.

En el ejemplo de la Figura 4.59 se observa la razn de ser de la funcin amiga. Ntese cmo
en la funcin gtime se puede acceder al atributo secs definido como privado en la clase
Time haciendo uso del objeto x. Si gtime no fuera declarada como amiga en Time esto
sera un error de acceso no vlido de un elemento privado fuera de la clase.

Figura 4.59. Complemento del ejemplo de la Figura 4.58.

4.16.1.2 Plantillas de funciones y clases


Las plantillas de funciones permiten definir funciones genricas que admiten cualquier tipo de
dato como parmetros sin necesidad de usar polimorfismo. Las plantillas de clases permiten
definir clases cuyos elementos estn basados en tipos genricos que no deben ser definidos en
el momento de su creacin. Para su implementacin se usa la declarativa y palabra reservada
template y la palabra reservada class typename (no hay diferencia entre una u otra).
Es til para escribir programas genricos con menos cdigo. Cabe destacar que desde el punto
de vista de la OO, es ms elegante el uso del principio de polimorfismo. Como se puede ver
en la Figura 3.26 UML soporta el concepto de plantillas en su sintaxis (se denominan clases
parametrizadas).
132

VOLVER A MEN

En el ejemplo de la Figura 4.60 se est definiendo una plantilla para la funcin GetMax en la
cual se generaliza el tipo de dato del retorno y el tipo de dato de los parmetros. Ntese el uso
de T para representar el tipo de dato genrico. Ntese tambin el uso de los caracteres <
y > en la sintaxis. Se puede observar tambin la llamada de la funcin en dos ocasiones, la
primera haciendo uso de dos argumentos de tipo int y la segunda llamada haciendo uso de
parmetros del tipo long.

Figura 4.60. Ejemplo del uso de plantillas en C++


para la generalizacin de parmetros en un mtodo.

La Figura 4.61 contiene por otro lado, un ejemplo de una plantilla utilizada en la clase Par
para definir los atributos v1 y v2 y el tipo de dato de retorno del mtodo GetMax. Ntese
al final del ejemplo la manera en la cual se hace una instanciacin del objeto MiObjeto de la
clase Par indicando que la plantilla T corresponde al tipo de dato int.

Figura 4.61. Ejemplo del uso de plantillas en C++ para la generalizacin de una clase.

El concepto de plantillas en clases tambin puede ser usado para especializar clases nuevas.
En el ejemplo de la Figura 4.62 se puede observar cmo la clase Par y el uso de la plantilla
T se especializan en la clase Par <int> que se refiere a una versin de la clase Par en la
cual T se asocia al tipo de dato int.
133

VOLVER A MEN

Figura 4.62. Ejemplo del uso de plantillas en C++ para la especializacin de una clase.

Finalmente, el ejemplo de la Figura 4.63 muestra otras maneras sintcticas de utilizar


plantillas: indicando que hay ms de una plantilla; indicando que hay una plantilla y un tipo de
dato primitivo; indicando que la plantilla tiene un valor o tipo de dato asociado por defecto; e
indicando que la plantilla corresponde a un tipo de dato asociado a una funcin.

Figura 4.63. Otros usos de plantillas en C++.

Nota: Las plantillas en C++ son compiladas por demanda, es decir, el cdigo de las
plantillas de funciones no se compila hasta que no se tenga una instancia vlida y el
compilador conozca el tipo de dato que corresponde a esa instancia.

4.16.1.3 Operaciones de conversin de clases


Esta funcionalidad se refiere a la manera en la cual C++ soporta operaciones de asignacin
entre objetos de clases diferentes y que, ameritan cierto tipo de conversin explcita. En este
sentido, C++ contiene varios operadores de conversin de ndole especfico. Uno de ellos se
identifica sintcticamente como reinterpret_cast y es utilizado para convertir un apuntador,
originalmente declarado a apuntar a una direccin de memoria de un tipo de dato particular, a
otro apuntador cuya direccin est asociada a un espacio de memoria que corresponde a otro
134

VOLVER A MEN

tipo de dato. No se hace ningn tipo de chequeo ni transformacin al contenido de la memoria


que se est apuntando. La Figura 4.64 presenta un ejemplo del uso de este operador en la
cual al apuntador b que apunta a un espacio de memoria referido a un objeto del tipo B se
le asigna el apuntador a referido a un espacio de memoria del tipo A.

Figura 4.64. Ejemplo de uso del operador de conversin reinterpret_cast de C++.

La Figura 4.65 muestra la sintaxis y uso del operador static_cast que permite ejecutar cualquier
conversin que puede ser realizada implcitamente de la misma forma que la conversin
inversa (an si no est permitida de forma implcita). Ntese en la Figura 4.65 que lo que se
est tratando de hacer es asignar el apuntador a relativo a una clase base A a un apuntador
relativo a una clase B derivada de A. Implcitamente es posible hacer una conversin de
una clase base a una clase derivada, ya que el espacio de memoria relativo a la clase derivada
incluye ya los elementos relativos a la clase base, ms la inversa no es posible hacerlo de
manera implcita y es por ello que se requiere el operador de conversin para hacerlo de
manera explcita.

Figura 4.65. Ejemplo de uso del operador de conversin static_cast de C++.

La Figura 4.66 muestra otro de los operadores de conversin. Se trata de dynamic_cast el


cual es similar a static_cast con la diferencia que en este caso se hace un chequeo de la
validez de la operacin (es decir, si la conversin retorna un objeto vlido del tipo requerido).
El chequeo es en tiempo de ejecucin y si no es vlido se retorna un valor nulo. En la primera
de las cuatro conversiones que aparecen en el ejemplo, la cual es vlida, se tiene el apuntador
a1 declarado para ser una direccin de memoria al tipo A pero definido como direccin de
memoria de espacio relativo al tipo B (ntese que se asigna a new B). Es vlido porque
el espacio de memoria del apuntador de la expresin de la derecha (lo apuntado por a1) es
equivalente al espacio de memoria apuntado por la variable b1 que recibe la asignacin en
el lado izquierdo.
La segunda conversin no es vlida porque se est tratando de asignar una referencia a la
clase base A a un apuntador relativo a la clase derivada B. La ltima conversin no slo
no es vlida sino que adems genera una excepcin ya que en este caso no se trata de un
apuntador sino del objeto b4 y el espacio de memoria que se supone ocupa este objeto es
mayor que el espacio de memoria relativo al lado derecho de la asignacin.
135

VOLVER A MEN

Figura 4.66. Ejemplo de uso del operador de conversin dynamic_cast de C++.

El operador de conversin const_cast, cuyo ejemplo se puede observar en la


Figura 4.67, manipula los atributos de tipo constante del objeto a ser convertido,
ya sea para ser agregados o removidos en el proceso de conversin. Ntese en el
ejemplo que el apuntador a, el cual es declarado como constante, se est asignando
al apuntador b que no est declarado como constante.

Figura 4.67. Ejemplo de uso del operador de conversin const_cast de C++.

Finalmente, se tiene el operador typeid que permite revisar el tipo resultante de una expresin.
Es til para hacer verificaciones antes de hacer una asignacin que pudiera no ser vlida.

Figura 4.68. Ejemplo de uso del operador de conversin typeid de C++.

4.16.2 Otros conceptos de Java


4.16.2.1 Clases y mtodos finales
Una clase final es aquella que finaliza cualquier lnea de derivacin o cadena de
herencia, es decir, no puede ser usada como clase base para definir otras clases
derivadas. En el ejemplo de la Figura 4.69 ntese que la clase final C1 luego se
intenta utilizar como clase base para derivar la clase C2 lo cual es un error de
compilacin.
Por otro lado, un mtodo final es aqul que no puede ser redefinido en clases
derivadas, incluyendo la definicin de elementos polimrficos. El mtodo Metodo1
136

VOLVER A MEN

definido en C3 est declarado como final y por lotanto cuando se intenta dar una
nueva definicin de este mtodo en la clase derivada C4, se genera un error de
compilacin.
Ntese que tanto para las clases finales como para los mtodos finales se usa la
declarativa o palabra reservada final.

Figura 4.69. Ejemplo de uso de clases y mtodos finales en Java.

4.16.2.2 Clases anidadas


Este concepto permite definir clases auxiliares dentro de otras clases con la intencin de
mantener las primeras dependientes de las segundas. Desde el punto de vista de diseo, se
trata de un caso particular de composicin o agregacin de un concepto dentro de otro, en
la cual se asume que el concepto agregado no formar parte de ninguna otra clase fuera del
contexto del concepto principal que la contiene.
Suponga por ejemplo que se desea definir una clase PC para representar una computadora
personal; se sabe que uno de los componentes de una PC es la tarjeta madre por lo cual
se debera definir la clase TarjetaMadre para representar este concepto; as mismo, se
evidencia la relacin TarjetaMadre forma parte de PC; la pregunta a hacerse es, servir
una tarjeta madre como concepto aislado a ser utilizado para otro contexto que no sea una
computadora personal? Si la respuesta a esta pregunta es no entonces es un indicio de
que la clase TarjetaMadre debera definirse dentro del contexto de la clase PC. La Figura
4.70 presenta la manera sintctica de realizar clases anidadas basado en el ejemplo previo.
La clase TarjetaMadre es esttica porque define internamente el enumerado Modelo y por
restricciones de Java, todos los enumerados se deben definir de manera esttica.
137

VOLVER A MEN

Desde el punto de vista funcional las clases internas pueden acceder a todos los miembros
de la clase ms externa, incluyendo los elementos privados. Las clases internas pueden
hacerse pblicas o privadas dependiendo si se desea o no permitir la instanciacin de objetos
correspondientes fuera del contexto de la clase externa que contiene sus definiciones. Tal
como se observa en la Figura 4.70, el concepto de anidamiento tambin aplica a enumerados
(enum) al igual que con las interfaces (interface).

Figura 4.70. Ejemplo de clases anidadas en Java.

4.16.3 Otros conceptos de C#


4.16.3.1 Clases y mtodos finales
Se trata de la misma caracterstica de Java. Es decir, una clase final es aquella que no puede
ser derivada o dicho de otra manera, no puede ser usada como clase base en una herencia.
Son tiles para permitir terminar una cadena de herencia.
Un mtodo final es un mtodo de una clase derivada que redefine uno equivalente de una clase
base correspondiente y que no se desea sea ms redefinido en otros niveles de herencia, si
es el caso.
Al igual que en Java los errores se identifican en tiempo de compilacin y a diferencia de Java,
se utiliza la palabra reservada sealed. La Figura 4.71 muestra un ejemplo en la cual se est
definiendo a C1 como una clase final (cualquier herencia que use a C1 como clase base
sera un error); en el mismo ejemplo se muestra tambin una herencia entre la clase base C2
con un mtodo virtual f y la clase derivada C3 en la cual no slo se redefine (override)
el mtodo f sino que adems se indica que es la ltima redefinicin que es posible hacer
(sealed).

138

VOLVER A MEN

Figura 4.71. Ejemplo de uso de clases y mtodos finales en C#.

4.16.3.2 Constructor esttico


Se trata de un concepto propio de C# en la cual se permite definir un constructor para la clase
que se invoca una sola vez y slo la primera vez que la clase es usada (es decir, durante la
primera instanciacin de la clase o el primer objeto que se defina de la misma).
Para diferenciarlo de otros constructores se requieren cumplir las siguientes tres reglas: 1)
no se le indica declarativa de interfaz; 2) se usa la declarativa static y 3) no puede tener
parmetros.
Adicionalmente y dado que no permite el polimorfismo (debido a que no acepta parmetros),
solo puede haber un constructor esttico por clase. Sin embargo, su presencia no limita la
existencia o definicin de otros constructores no estticos. Por otro lado, al igual que ocurre
con otros mtodos estticos, solo puede acceder a miembros estticos de la clase.
La Figura 4.72 presenta un ejemplo en la cual se est definiendo la clase Rectangulo que
contiene un constructor esttico que muestra un mensaje de inicializacin de la clase y da
un valor inicial al atributo esttico Edo. Adicionalmente se observa en el mismo ejemplo la
presencia de otros dos constructores, el primero de ellos sin parmetros y el segundo con dos
parmetros. Para el caso del primer objeto instanciado y si hubiere un constructor esttico y
otro no esttico, los dos mtodos son ejecutados siendo el constructor esttico el primero en
ejecutarse.

139

VOLVER A MEN

Figura 4.72. Ejemplo de uso de constructor esttico en C#.

4.16.3.3 Indexadores
Este concepto de C# permite representar la sobrecarga de un operador para indexar un
conjunto de atributos de la clase. Se define como una mezcla entre el manejo de atributos y la
manera como se indexan los arreglos.
Como se puede ver en el ejemplo de la Figura 4.73, adems de la declarativa de interfaz y
el tipo de dato, se usa la palabra reservada this con una indicacin del tipo de dato que
va a representar el ndice con la cual se va a acceder a los elementos, ste encerrado entre
corchetes [ y ] (similar a la forma como de declaran los arreglos). Al igual que ocurre con
el concepto de atributo de C#, a los indexadores se le pueden definir las operaciones get y
set correspondientes.

Figura 4.73. Ejemplo de uso de indexadores en C#.

140

VOLVER A MEN

En el ejemplo de la Figura 4.73 se observa la definicin de dos indexadores. En el primero de


ellos el ndice se maneja como un entero (int) y el retorno es tambin un entero. El segundo
indexador define un ndice como una cadena de caracteres (string) y el retorno es un entero.
En ambos casos slo se est definiendo el mtodo get lo cual implica que estos indexadores
solo permiten la obtencin de informacin. En la Figura 4.74 se muestra un cdigo donde se
ejemplifica el uso del primer indexador y en la que se observa el clculo de los acumulados de
las ventas que corresponden al objeto vm.

Figura 4.74. Ejemplo de uso de indexador definido en ejemplo de Figura 4.73.

4.16.3.4 Clases anidadas


Se trata de la misma idea expresada en la Seccin 4.16.2.2 relativo a Java. En el caso
particular de C# el concepto de anidamiento tambin aplica a estructuras (struct),
enumerados (enum), interfaces (interface) y delegados (delegate). En el
ejemplo de la Figura 4.75 se observa una clase pblica B definida dentro de la clase
A. Ntese que dentro del contexto de B es posible acceder a cualquier elemento
definido en A. Ntese tambin que por ser pblica, fuera del contexto de A
(dentro de C en el ejemplo) es posible instanciar el objeto b de la clase A.B.

Figura 4.75. Ejemplo de uso de clases anidadas en C#.

141

VOLVER A MEN

4.16.3.5 Operaciones de conversin entre clases


De manera similar como existen en C++, C# posee algunas operaciones de conversin
utilizadas cuando se hacen operaciones de asignacin entre objetos de clases diferentes. La
Figura 4.76 muestra algunos ejemplos de los operadores is y as. El primero es utilizado
como expresin condicional para verificar si una posible conversin es o no vlida. El segundo
es usado para hacer la conversin explcitamente. Los comentarios que aparecen en el cdigo
del ejemplo explican el uso adecuado de ambos operadores y los resultados obtenidos cuando
stos son aplicados en diferentes escenarios entre clases base y clases derivadas.

Figura 4.76. Ejemplo de uso de operaciones de conversin en C#.

4.16.3.6 Delegados y eventos


Un delegado en C# es un mtodo para recibir y procesar eventos. Se puede percibir como
un patrn que describe el aspecto que tiene un controlador especfico de eventos y define
adems, lo que debe devolver el controlador de eventos del usuario y lo que debe ser la lista
de parmetros.
Un evento en este contexto es un medio con el cual se puede avisar de una condicin especfica.
Los mtodos que necesitan procesar un evento deben obedecer las convenciones de llamada
del delegado, es decir, no deben devolver ningn dato y deben tener una sola cadena de
caracteres como lista de parmetros.
Por otro lado, las implementaciones de control de eventos pueden tener cualquier nombre de
mtodo vlido, solo se requiere que el tipo de dato retornado y la lista de parmetros recibidos
concuerden con el patrn de delegados previamente definido.
La Figura 4.77 muestra un ejemplo en el cual se puede observar la declaracin del delegado
Notificador como un tipo de dato que luego es utilizado para declarar la instancia Saludos
que se concreta luego como instancia, al aplicarle la operacin new. Sigue en el ejemplo la
142

VOLVER A MEN

manera en la cual se hace la llamada al delegado que provoca, de acuerdo a su definicin, la


llamada al mtodo DiHola. Finalmente se muestra en el ejemplo la declaracin de un evento
asociado al delegado.

Figura 4.77. Ejemplo de uso de delegados y eventos en C#.

4.16.3.7 Atributos
Ese concepto de C#, que no se refiere ni al concepto de atributo de una clase, ni al concepto
particular de atributo que contiene C# con las operaciones get y set, permite incluir directivas
en las clases y en sus miembros para mejorar su declaracin mediante informacin que es
interpretada por otras clases en tiempo real.
Se pueden usar tanto los atributos predefinidos en el lenguaje como tambin aquellos que
son personalizados por el programador. Desde el punto de vista sintctico, los atributos se
escriben entre corchetes [ y ] y deben aparecer inmediatamente antes de la declaracin en
la cual se desea se aplique el atributo (por ejemplo, [MiAtributo]). Los atributos pueden ser
aplicados en cualquier declaracin vlida en C#: clases, miembros de la clase, estructuras,
interfaces, miembros de la interfaz, enumerados, miembros de los enumerados y delegados.
Adicionalmente, al atributo se le puede anteponer un modificador que defina el elemento
al que se aplica el atributo, tambin llamado enlazador de un atributo. Para estos casos el
modificador y el atributo se enlazan con el caracter : ([MiEnlace:MiAtributo] por ejemplo).
Finalmente, algunos atributos tambin tienen la posibilidad de aceptar parmetros (por ejemplo,
[MiAtributo(Parmetros)]).
Como se mencion anteriormente, existe la posibilidad de definir atributos personalizados.
Para ello es necesario escribir clases de atributos propias como resultado de hacer una
derivacin de la clase predefinida Attribute, perteneciente al espacio de nombres System.
La Figura 4.78 muestra un ejemplo donde se est presentando la definicin de la clase de
atributo CodeAuthorAttribute. Ms abajo en el mismo ejemplo aparecen las dos maneras en
las cuales se usa el mismo atributo; ntese que el sufijo Attribute es opcional.
143

VOLVER A MEN

Figura 4.78. Ejemplo de uso de atributos en C#.

En el ejemplo de la Figura 4.79 se tiene el atributo AttributeUsage con el parmetro


AttributeTargets.Method. El atributo AttributeUsage permite personalizar la manera de usar
clases de atributos propios. AttributeTargets es un enumerado que indica cules elementos
especficos de la clase propia deben ser controlados. En el caso especfico del ejemplo se
refiere a Method.

Figura 4.79. Otro ejemplo de uso de atributos en C#.

4.17 Ejercicios
1) Se desea implementar una clase para la representacin y manejo de un reloj. Un objeto
reloj puede ser creado con una hora militar inicial vlida cualquiera expresada con los tres
valores: hora (entre 0 y 23), minuto (entre 0 y 59) y segundo (entre 0 y 59). En caso de no
dar una hora inicial en tiempo de instanciacin del objeto, el reloj tendr una hora inicial
equivalente a 0 horas, 0 minutos y 0 segundos. Adems de retornar los componentes
de la hora actual, el reloj tiene el mtodo tick() que permite aumentar la hora en un
segundo. Tambin est el mtodo cambiar(h, m, s) para modificar la hora actual segn
los parmetros dados.
2) Se desea implementar una clase para la representacin y manejo de una fecha. Un objeto
fecha puede ser creado con una fecha inicial vlida cualquiera expresada con los tres
valores: da (entre 1 y 31), mes (entre 1 y 11) y ao (mayor que 1800). En caso de no dar
una fecha inicial en tiempo de instanciacin del objeto, el objeto tendr una fecha inicial
144

VOLVER A MEN

equivalente al 1 de enero del ao 1800. Adems de retornar los componentes de la fecha


actual, el objeto tiene los mtodos un_dia() y un_mes() que permiten aumentar la fecha
en un da y un mes respectivamente. Tambin est el mtodo cambiar(d, m, a) para
modificar la fecha actual segn los parmetros dados.
3) Asumiendo que la clase Reloj antes descrita puede ser usada para representar una hora;
basado en el uso de esta clase y de la clase Fecha, escriba una clase para representar
un horario que consiste en una Fecha-Hora desde, una Fecha-Hora hasta y los das de
la semana en la cual se aplica ese intervalo de fecha y hora. La clase debe tener los
siguientes mtodos:
a) cambiar_desde(d, m, a, h, mi, s) que permite modificar los datos de la Fecha-Hora
desde.
b) cambiar_hasta(d, m, a, h, mi, s) que permite modificar los datos de la Fecha-Hora
hasta.
c) asignar_dia(d) asigna el da d de la semana (domingo, lunes, martes, mircoles,
jueves, viernes, sbado) al horario.
d) desasignar_dia(d) desasigna el da d de la semana (domingo, lunes, martes, mircoles,
jueves, viernes, sbado) al horario.
e) dentro(F, R, d) indica si la Fecha-Hora representada por los objetos F y R en conjunto
con el da de la semana d, estn dentro del horario representado por el objeto al cual se
hace la llamada del mensaje.
4) De forma genrica un Alimento se puede representar mediante un nmero real que indique
las caloras que este alimento posee, adems de operaciones que permitan calcular el
nivel de protena, el nivel de grasa y el nivel de carbohidratos asociado a cada alimento
especfico. Escriba una clase genrica para modelar un Alimento en base a lo antes descrito
y, de ella escriba una clase especializada para representar la categora de alimento Verdura
asumiendo que, en este caso, los niveles de protena, grasa y carbohidratos coinciden con
el 45%, 2% y 3,1% de las caloras respectivamente.
5) Para un programa que simule la operacin de un banco se deben declarar las clases que
aparecen en el diagrama que se muestra en la Figura 4.80. Se deben tomar en cuenta los
siguientes aspectos:
a) El balance inicial de las cuentas es 0.
b) El mtodo agr_intereses() de la clase Ahorro incrementa en el balance una cantidad
igual al balance actual por el inters.
c) El mtodo conformar(cantidad) de la clase Corriente verifica si el balance actual de
la cuenta tiene fondos suficientes para cubrir un cheque de la cantidad dada como
parmetro manteniendo el saldo mnimo requerido para la cuenta.
145

VOLVER A MEN

d) Los mtodos depositar(cantidad) y retirar(cantidad) deben sumar / restar la cantidad


especfica al balance de la cuenta respectivamente.

Figura 4.80. Diagrama de clases para representar la operacin de un banco.

6) Escriba una clase Esfera que contenga un dato que represente el dimetro de una
esfera. Agregue un constructor para que reciba el valor inicial del dimetro. Incluya tambin
mtodos para consultar y cambiar el dimetro, as como tambin un mtodo que calcule
y retorne el volumen del rea superficial de la esfera (4r2). Incluya tambin el mtodo
toString que retorna una descripcin de la esfera (indicada por su dimetro) como cadena
de caracteres.
7) Escriba una clase Caja que contenga los datos para representar el alto, ancho y profundidad
de una caja. Incluya tambin un atributo de tipo booleano que indique si la caja est o
no llena. El constructor asociado a una caja debe inicializar los datos de altura, ancho y
profundidad e indicar que la caja est vaca. Incluya mtodos para consulta y modificacin
de los datos, as como tambin los mtodos para llenar y vaciar la caja. Agregue tambin
un mtodo para calcular y retornar el volumen de la caja (alto x ancho x profundidad).
8) Defina de forma completa (estructura y cuerpo de los mtodos) la clase Integer25 que
permita representar nmeros enteros grandes de a lo sumo 25 dgitos y las operaciones de
suma, resta, divisin y multiplicacin representada por los smbolos +, -, / y * respectivamente.
9) Escriba las estructuras de una clase para la representacin y manejo de rboles binarios
cuyos nodos contengan objetos del tipo Integer25.
10) Escriba el enunciado de una clase abstracta para la creacin y manejo de elementos
grficos. Un elemento grfico debe tener una posicin (izquierda, superior) en pantalla y
las dimensiones (ancho y alto) en pxeles. Tambin debe tener un nombre que lo identifique
y la referencia a un padre que lo contiene que es tambin un elemento grfico y que debe
ser dado en tiempo de creacin del objeto. La dimensin, tamao y nombre del elemento
se puedan dar opcionalmente en la construccin del objeto, en caso contrario se asumen
valores por defecto. Cada elemento grfico contiene tambin una lista de los elementos hijos
asociados. El comportamiento de un elemento grfico debe ser tal que permita modificar
la posicin y dimensiones del objeto, adems de agregar, suprimir y buscar elementos
hijos de su lista de descendientes a partir del nombre del elemento. El refrescamiento o
visualizacin del objeto depende del tipo de elemento que se defina.
11) Haciendo uso de la definicin anterior, escriba el enunciado de una clase que permita
146

VOLVER A MEN

definir elementos grficos del tipo lista de cadenas de caracteres con el comportamiento
grfico y el correspondiente para el manejo de su lista de textos.
12) A continuacin se ilustra el comportamiento de un objeto para el manejo de nmeros
en notacin romana (usando C++). Defina de forma completa en C++ la clase Romano
para tener el manejo de objetos antes ilustrado. Asuma que existen las funciones char
*IntToRom(int) e int RomToInt(char *) para hacer la conversin entre nmeros enteros y
nmeros romanos. Por defecto se asume un valor igual a 1 (I) para los nmeros romanos.
Romano R1, R2(X), R3 = R2, R4(10), *R5 = (Romano *)new Romano(MMI);
int I = (int)R3;
R1 = R4 + R2 + R3++;






if (R1
if (R1
if (R1
if (R1
if (R1
if (R1
delete

== R2)
> R2)
>= R2)
< R2)
<= R2)
!= R2)
R5;

cout
cout
cout
cout
cout
cout

<<
<<
<<
<<
<<
<<

Iguales;
Mayor que;
Mayor o igual;
Menor que;
Meyor o igual;
Diferentes;

13) Implemente el mismo ejercicio anterior pero esta vez haciendo uso de Java y/o C#.
14) Defina de forma completa (estructura y cuerpo de los mtodos) la clase Boolean que permita
representar nmeros booleanos con las operaciones And, Or, Xor y Not representado por
los smbolos &, |, ^ y ! respectivamente.
15) Escriba una clase para el manejo de un diccionario de datos. Consiste en una estructura
que permite almacenar palabras o conceptos y sus respectivas definiciones, recuperar
las definiciones una vez dada la palabra, eliminar palabras y modificar definiciones de
una palabra dada. Con el fin de agilizar la bsqueda, las palabras se deben organizar
en conjuntos segn su letra inicial, de manera que todas las palabras que comienzan
con la letra A estarn en un mismo conjunto, las que comienzan con la letra B en otro
conjunto y as sucesivamente. La clase debe ser desarrollada completamente, incluyendo
los mtodos.

147

VOLVER A MEN

Captulo 5

Una metodologa Orientada a Objetos


5.1 Introduccin
La mejor manera de conducir a la solucin de un problema partiendo del enunciado del mismo es
utilizando una metodologa que gue paso a paso el camino a seguir. Sobre todo cuando no se
tiene la experticia necesaria en el rea especfica en la cual el problema debe ser resuelto. Por
otro lado, es muy comn el uso de una metodologa para la resolucin de problemas mediante
el desarrollo de software. En este sentido, el objetivo principal de este captulo es presentar
una metodologa para la resolucin de problemas enfocado en el desarrollo de software OO.
La metodologa que se presenta en este captulo est basada en el trabajo realizado por
Oestereich (Oestereich, 2001) y Rumbaugh y compaa cuyos resultados estn presentes en
la referencia (Rumbaugh et al, 1990).
Es importante tener en cuenta que la construccin de software OO tiene que ver
con un mtodo de desarrollo de software basado en mdulos que resultan de los tipos
de objetos que manipula el sistema cuyo software debe ser elaborado; en lugar de
basarse en la funcin o funciones que el sistema debe satisfacer o asegurar. En otras
palabras, se trata de la construccin de sistemas como conjuntos estructurados de
implementaciones de tipos abstractos de datos. En la OO estas implementaciones
de tipos abstractos de datos se convierten en clases cuya agrupacin pasa a ser lo
que se denomina en la literatura una biblioteca de clases. Por lo tanto se trata de
no preguntar primero qu hace o debe hacer el sistema, sino ms bien preguntar
a qu elementos especficos el sistema le hace lo que tiene que hacer. Responder
las preguntas que se mencionan a continuacin sirve de ayuda para lograr lo antes
expresado:

Cmo se encuentran los tipos de objetos relevantes?


Cmo se describen los tipos de objetos?
Cmo se describen las relaciones y analogas entre los tipos de objetos?
Cmo se utilizan los tipos de objetos para estructurar el software?

148

VOLVER A MEN

Nota: Una biblioteca de clases es un conjunto de clases implementadas


adecuadamente (completas, libres de error y tratando se ser lo ms eficiente y eficaz
posible) asociadas a una serie de conceptos de uso comn (por ejemplo: conjunto,
lista, rbol, arreglo, archivo, cadena de carcter, objetos grficos, entre otros) con
miras a su reutilizacin.

Como se observa en la parte izquierda de la Figura 5.1, el proceso metodolgico sugerido para
construir un software OO sigue el mismo conjunto de pasos que comnmente se observan en
una metodologa de desarrollo de software en general. Se tienen las siguientes etapas:
1) Anlisis OO: Tiene que ver con la revisin de los requerimientos del problema y el modelo
conceptual de la solucin. Ver detalles en la Seccin 5.2.
2) Diseo OO: Corresponde a la especificacin de los detalles del modelo y la preparacin
para realizar la codificacin de la solucin. Ver detalles en la Seccin 5.3.
3) Codificacin OO: Se trata de la programacin OO basado en el diseo realizado haciendo
uso de un lenguaje OO (ver Captulo 4).
4) Prueba: Tiene que ver con la verificacin del desarrollo realizado y est basado en la
satisfaccin de los requerimientos del problema.
5) Modificacin: Corresponde al mantenimiento que se debe realizar al desarrollo para
permitir la durabilidad del software realizado.

Figura 5.1. Proceso metodolgico y estimados de tiempo / recursos requeridos.

Ntese que el proceso metodolgico contempla el regreso a las fases anteriores cuando esto
es necesario. Por ejemplo, si una prueba no es satisfecha puede implicar tanto regresar a
149

VOLVER A MEN

la codificacin si es que el software no fue desarrollado acorde a lo indicado en el diseo,


regresar al diseo si es que los detalles del anlisis no fueron realizados correctamente o
incluso regresar al anlisis si es que los requerimientos dados por los usuarios de la solucin
no fueron abordados completa o correctamente.
Por otro lado, en la parte derecha de la Figura 5.1 se muestran las estimaciones de tiempo y
recurso ideales de cada una de las fases del proceso metodolgico. Es de notar como aspecto
curioso que la fase que debera implicar ms tiempo y recursos no es la codificacin como
puede creerse, sino ms bien el diseo. Lo ideal es que se le d mayor dedicacin al diseo
ya que un problema bien diseado con todos los niveles de detalle necesarios facilita la tarea
de codificacin. Otro aspecto curioso es la cantidad de tiempo y recursos que debera darse a
las pruebas.
Nota: Por lo general los procesos de construccin de software le dan ms importancia
a la etapa de codificacin descuidando las actividades de anlisis, diseo y pruebas.
El anlisis y diseo OO estn enfocados en hacer de la codificacin una actividad
no tan complicada y por lo tanto que requiera menos tiempo de lo que se puede
imaginar.

5.2 Anlisis OO
Dicho de una manera muy sintetizada, el anlisis OO corresponde a la descomposicin de un
problema en sus partes. El propsito es modelar un problema basado en conceptos del mundo
real de forma tal que ste se pueda entender y que satisfaga el conjunto de requerimientos
definidos. Lo que se busca como resultado es un problema entendido como una preparacin
para ir al diseo.
Como se mencion anteriormente, el anlisis OO se preocupa ms por identificar los tipos de
objetos que por detallar o identificar los objetos individuales. Adicionalmente, es la primera
oportunidad para identificar la reutilizacin de componentes existentes. Por otro lado, esta
etapa del proceso requiere de la realizacin de dos partes:
1) El anlisis de requerimientos.
2) El anlisis de estructuras o modelado de objetos.

El anlisis de requerimientos tiene que ver con decidir lo que tiene y lo que no tiene que hacer
el sistema por un lado y asegurar que el sistema satisfaga las necesidades de sus usuarios;
para ello es necesario definir los criterios de aceptacin. El principal producto relacionado con
150

VOLVER A MEN

esta parte del anlisis est basado con la identificacin y prueba de los requerimientos lo cual
viene dado principalmente por los diagramas de casos de uso de UML.
El anlisis de estructuras tiene que ver con la descomposicin del problema en sus partes. El
principal producto relacionado con esta parte del anlisis est basado con el modelo de clases
del problema lo cual viene dado principalmente por el diagrama de clases de UML.
Desde el punto de vista de la metodologa presentada en este captulo, el anlisis OO requiere
de cuatro pasos:
1)

Identificar la idea y objetivos bsicos del sistema.

2)

Identificar actores / nombres.

3)

Identificar procesos / requerimientos.

4)

Identificar clases y asociaciones.

5.2.1 Paso 1. Identificar la idea y objetivos


bsicos del sistema.
Es el primer contacto con el problema y por lo tanto se busca entenderlo de forma completa.
Para ello se pueden seguir las siguientes tcnicas:
Escribir entre 5 y 20 oraciones de la informacin que se haya podido recopilar mediante
entrevistas.
Si ya se tiene un enunciado o documento explicativo, extraer las oraciones de all.
Realizar mapas mentales sobre la informacin relativa al problema. Ver un ejemplo en la
Figura 5.2 (realizado en XMind 2012, versin 3.3.0).

Figura 5.2. Ejemplo de mapa mental realizado en XMind 2012, versin 3.3.0.

151

VOLVER A MEN

5.2.2 Paso 2. Identificar actores / nombres.


El objetivo de este paso es identificar los actores y/o nombres que se reconocen luego de
haber terminado el paso anterior. La razn de ello es porque los actores y/o nombres son
posibles conceptos a ser asociados a clases del modelo OO del problema.
Es importante tener en cuenta que todos los nombres deben ser considerados inicialmente.
Luego se har un proceso de seleccin o filtrado que permitir no solo decidir qu conceptos
de la lista se quedan sino tambin cules otros conceptos que no estn en la lista deben
incluirse tambin.
Considerar por ejemplo la siguiente frase: Un sistema de reservacin para la venta de boletos
a varias obras de teatro. La siguiente es la lista de actores y nombres que se extraen de la
frase: sistema de reservacin, venta, boleto, obra y teatro.

5.2.3 Paso 3. Identificar procesos / requerimientos.


Corresponde a la descripcin del sistema desde el punto de vista del usuario, ms
especficamente a los requerimientos funcionales los cuales se conocen como casos de uso.
Los requerimientos se representan mediante una coleccin de situaciones o escenarios
respecto al uso de un sistema. Cada uno de estos escenarios describe una secuencia de
eventos. Cada secuencia se inicia por una entidad que puede ser una persona, otro sistema,
una parte del hardware o por el paso del tiempo. A estas entidades que inician secuencias se
les conoce como actores. El resultado de una secuencia debe ser algo utilizable o funcional,
ya sea por el actor que la inici o por otro actor que se beneficia de ello.
Esta relacin entre actores y los requerimientos funcionales se representa mediante los
diagramas de casos de uso de UML. El detalle de la secuencia de eventos desde que un
actor la inicia hasta que se llega al resultado se puede representar mediante los diagramas de
actividad de UML.

5.2.4 Paso 4. Identificar clases y asociaciones.


Como primer objetivo de este paso se tiene el obtener la lista definitiva de conceptos que van
posteriormente a representar clases dentro del contexto del problema. En este sentido, es
recomendable aplicar criterios para identificar clases incorrectas o innecesarias (ver detalles a
continuacin).

152

VOLVER A MEN

El segundo objetivo de este paso es identificar las relaciones o asociaciones entre los conceptos
y/o clases seleccionados. Para ello tambin es recomendable aplicar criterios para saber si las
asociaciones son o no correctas (ver detalles a continuacin).
La lista de clases y asociaciones permite luego que sean representadas en un diagrama de
clases sin niveles de detalle de UML. Sin nivel de detalle quiere decir que no se especifican
los elementos (atributos y mtodos) de las clases. Es recomendable que las relaciones entre
clases incluyan tambin los posibles roles (breve descripcin de su objetivo) y la multiplicidad
(nmero de elementos involucrados en la relacin).

5.2.4.1 Criterios para identificar clases incorrectas o


innecesarias.
Clases redundantes: ocurre cuando dos o ms clases expresan la misma informacin. En
este caso se debe slo tomar el nombre que sea ms descriptivo para el problema tratado.
Por ejemplo, en el contexto de un problema relativo a transporte, los nombres cliente y
pasajero expresan lo mismo, es decir, una persona que va a utilizar el servicio de transporte.
En este caso particular pasajero es quizs el nombre ms apropiado. En un segundo ejemplo
relacionado con un problema asociado a servicio bancario, cliente y usuario tienen que ver
con una persona que hace uso del servicio; el nombre cliente es tal vez el ms apropiado en
este contexto.
Clases irrelevantes: ocurre cuando una clase tiene poco o nada que ver con el problema
tratado y por lo tanto debe ser removida de la lista. Hay que tener en cuenta que la posible
clase a eliminar puede ser importante en otro contexto o problema. Por ejemplo, el concepto
reservacin es irrelevante cuando el contexto del problema es la ocupacin de un teatro. En
otro ejemplo, venta es irrelevante cuando el contexto del problema est enfocado en el rea
administrativa o financiera.
Clases vagas: ocurre cuando un concepto no es especfico, es decir, no tiene sus lmites
bien definidos con claridad o su alcance es muy amplio. Por ejemplo: sistema, universo y
horizonte.
Clases o atributos: son nombres que describen elementos individuales y que en lugar de
ser considerados como clases deberan ms bien ser considerados como atributos de una
clase ya existente o por identificar. Un concepto debera ser considerado como una clase y
no un atributo si este concepto es importante de manera tal que justifica la existencia de una
propiedad independiente segn el contexto del problema tratado. Por ejemplo, si estudiante
es el contexto del problema entonces nombre, edad, peso y direccin deberan ser
153

VOLVER A MEN

atributos de la clase estudiante. Si, por otro lado, el contexto del problema es el sistema de
correo entonces direccin debera ser tratado como una clase por su importancia conceptual
en este problema.
Clase u operacin: el objetivo de este criterio es no hacer una clase con un nombre que describe
una operacin (mtodo) que es aplicada a objetos o instancias de las clases ya identificadas
o por identificar. Sin embargo, una operacin que tiene caractersticas propias relevantes para
el contexto del problema tratado debe ser modelada como una clase. Por ejemplo, cuando el
contexto del problema es el telfono, la llamada telefnica es un mtodo de la clase telfono
pero, si el contexto del problema es la facturacin telefnica, llamada telefnica debe ser
una clase porque tiene la riqueza conceptual necesaria para ello como por ejemplo atributos
necesarios para representar la duracin de la llamada, la tarifa aplicada, entre otros y tambin
mtodos para iniciar la llamada, culminar la llamada, entre otros.
Clase o asociacin: este criterio establece que el nombre de una clase debe reflejar su
naturaleza intrnseca y no un role que otras posibles clases juegan en una asociacin. En el
contexto de un problema de produccin de carros por ejemplo, propietario no es una clase
sino la asociacin que se da entre las clases persona y carro.
Uso de implementaciones: tiene que ver con implementaciones externas al problema tratado
(seguramente pertenecientes a una biblioteca de clases, basado en la reutilizacin de
elementos). Si bien estos casos son importantes para el modelo del problema, no se deben
agregar en el proceso de anlisis con el objeto de simplificar el modelo en estas primeras
etapas de su elaboracin. Probablemente se requieran agregar luego al modelo en la etapa de
diseo. Algunos ejemplos son: CPU, subrutina, proceso, algoritmo, interrupcin, lista
enlazada, arreglo, rbol, conjunto, tabla y los elementos de interfaz como ventana,
botn, entre otros.

5.2.4.2 Criterios para no tomar en cuenta


asociaciones incorrectas o innecesarias.
Asociaciones entre clases eliminadas: si una de las clases de la asociacin ha sido
eliminada, la asociacin debe ser tambin eliminada o redefinida en trminos de
otra clase.
Asociaciones irrelevantes o de implementaciones: se refiere a eliminar toda
asociacin que est fuera del dominio del problema o tenga que ver con el uso
de implementaciones. Por ejemplo, la asociacin banco mantiene ATM entre las
clases banco y ATM, es irrelevante cuando el contexto del problema es el servicio
154

VOLVER A MEN

que el banco presta a sus clientes. En otro ejemplo asociado al mismo problema,
la asociacin ATM tiene impresora entre las clases ATM e impresora, tiene que
ver con la implementacin del ATM y por ende es irrelevante para el anlisis.
Acciones: el objetivo de este criterio es establecer una diferenciacin entre una
asociacin y un evento o accin; una asociacin debe describir una propiedad
estructural del dominio del sistema a modelar y no un evento representado como
el envo de un mensaje entre objetos. En este sentido, un requerimiento expresado
como una accin puede implicar una relacin estructural y debe ser rescrito
acordemente. Por ejemplo, ATM acepta tarjetas de dbito describe parte de un
ciclo de interaccin entre las clases ATM y cliente probablemente representado
con un mensaje que un objeto de la clase cliente enva haciendo uso de un
mtodo de la clase ATM; no debe ser tratado como una relacin entre ATM y
tarjeta de dbito. En otro ejemplo, computador central transfiere transaccin
con banco describe una accin implcita en la asociacin computador central se
comunica con el banco.
Asociaciones ternarias: muchas asociaciones entre tres o ms clases se pueden
descomponer en asociaciones binarias o rescritas como asociaciones cualificadas. En
este ltimo caso, si un trmino en una asociacin ternaria es puramente descriptivo
y no tiene caractersticas propias, entonces este trmino es un atributo que puede
ser utilizado como enlace en una asociacin binaria cualificada. Por ejemplo,
en la asociacin cajeros introducen transacciones de cuentas en la cual estn
involucradas las clases cajero, transaccin y cuenta, se puede romper en las
siguientes dos asociaciones: cajeros introducen transacciones y transacciones
tienen que ver con cuentas. En un segundo ejemplo, la asociacin compaa
emplea personas con un sueldo que involucra las clases compaa, persona y
sueldo, se puede escribir mediante el siguiente par de asociaciones: compaa
emplea personas y persona tiene que ver con un sueldo.
Asociaciones derivadas: aquellas asociaciones que pueden ser definidas en trminos
de otras asociaciones deben ser omitidas ya que son redundantes. Por ejemplo,
abuelo de puede ser definido usando un par de relaciones del tipo padre de.
Por otro lado, tambin hay que omitir las asociaciones definidas por condiciones
en los atributos. Por ejemplo, ms joven que expresa una condicin en la edad de dos
personas (siendo edad un atributo de la clase persona), no es informacin
adicional al modelo.

155

VOLVER A MEN

Nota: Tanto como sea posible, las clases, los atributos y las asociaciones deben
representar informacin independiente. Muchos caminos entre clases a veces
indican asociaciones derivadas que estn compuestas por asociaciones primitivas.
Por ejemplo, consorcio comparte ATM es una composicin de consorcio tiene
computador central y computador central se comunica con los ATM.

Nota: Hay que tener cuidado porque no todas las asociaciones que forman mltiples
caminos entre clases indican redundancia. Algunas veces la existencia de una
asociacin puede ser derivada de dos o ms asociaciones primitivas. Aunque las
asociaciones derivadas no agregan informacin, son tiles para el diseo.

5.2.4.3 Ejemplos.
Ejemplo 1: Una compaa emplea a muchas personas y es propietaria de muchas computadoras;
a cada empleado se le puede asignar alguna de estas computadoras para su uso personal.
Se identifican los siguientes nombres: compaa, persona, computadora y empleado. Luego de
aplicar los criterios para identificar clases incorrectas o innecesarias, se observa que persona
y empleado son conceptos redundantes siendo empleado el concepto ms adecuado
de utilizar en este contexto de problema especfico. Por lo tanto, un modelo conceptual
OO del enunciado del problema presenta las siguientes clases: compaa, empleado y
computadora.
Por otro lado, se observan las siguientes asociaciones: compaa emplea empleado,
compaa posee computadora y computadora es asignada a empleado. Se obtiene por lo
tanto el diagrama de clases que se muestra en la Figura 5.3. Ntese el uso de las multiplicidades
en las asociaciones.

Figura 5.3. Diagrama de clases correspondiente al Ejemplo 1.

156

VOLVER A MEN

Ejemplo 2 (Rumbaugh et al, 1990): Una red bancaria computarizada incluye tanto cajeros
humanos como automticos (ATM), stos ltimos compartidos por un consorcio de bancos.
Cada banco provee su propia computadora para mantener sus propias cuentas y procesar
las transacciones contra ellas. Las estaciones de los cajeros son propias de cada banco
y se comunican directamente con la computadora propia del banco. Los cajeros humanos
introducen datos de cuentas y transaccin. Los ATM se comunican con un computador central
el cual transfiere las transacciones con los bancos apropiados. Un ATM acepta una tarjeta
de dbito, interacta con el usuario, se comunica con el sistema central para llevar a cabo la
transaccin, dispensa efectivo e imprime un recibo. El sistema requiere de mecanismos de
seguridad y auditoria adecuados. El sistema puede manejar acceso concurrente a la misma
cuenta. Los bancos proveen su propio software para sus propias computadoras. El costo del
sistema compartido es distribuido por los bancos segn el nmero de clientes con tarjetas de
dbito.
Se identifican los siguientes nombres: red bancaria, cajero, ATM, consorcio, banco,
computadora-banco, cuenta, transaccin, estacin-cajero, datos-cuenta, datos-transaccin,
computador-central, tarjeta-dbito, usuario, sistema, efectivo, recibo, mecanismos-seguridad,
mecanismos-auditoria, acceso, software, costo y cliente.
Al aplicar los criterios para identificar clases incorrectas o innecesarias, se tiene lo siguiente:
Clases vagas: sistema, mecanismos-seguridad, mecanismos-auditoria y red-bancaria
Atributos: datos-cuenta, datos-transaccin, recibo y efectivo.
Clases irrelevantes: costo.
Clases redundantes: usuario con cliente.
Implementaciones: acceso y software.
Luego de la aplicacin de los criterios se identifican los siguientes conceptos o clases definitivas:
cuenta, ATM, banco, computadora-banco, tarjeta-dbito, cajero, estacin-cajero, computadorcentral. Como consecuencia y luego de avanzar en el anlisis y algunas etapas del diseo, se
tiene una versin del diagrama de clases incompleto que se muestra en la Figura 5.4. Ntese
el uso de las asociaciones. Se incluyen tambin algunos atributos.

157

VOLVER A MEN

Figura 5.4. Diagrama de clases correspondiente al Ejemplo 2.

5.3 Diseo OO
Tiene que ver con la especificacin de todos los detalles necesarios que deben ser incluidos
en el modelo y que no se hicieron en el anlisis teniendo en cuenta la visin global del
problema. El detalle ms relevante a considerar est dado por las definiciones completas de
las clases y asociaciones incluyendo los algoritmos de los mtodos usados para implementar
las operaciones. El propsito principal es agregar los detalles faltantes y tomar decisiones de
implementacin. El resultado es un producto intermedio entre anlisis y programacin para
facilitar el trabajo de implementacin.
Usualmente el diseo se deriva directamente del anlisis pero a veces es necesario agregar
nuevas clases y/o atributos para mejorar el modelo. Los principales productos asociados a esta
fase del proceso metodolgico estn relacionados con el modelo detallado de clases (modelo
esttico) y los flujos de estados e intercambio de mensajes entre elementos para satisfacer los
requerimientos del problema (modelo dinmico).
Desde el punto de vista de la metodologa presentada en este captulo el diseo OO requiere
de seis pasos:
1) Definir la arquitectura de la solucin.
2) Detallar las clases.
3) Desarrollar los modelos de estado.
158

VOLVER A MEN

4) Elaborar los modelos de colaboracin.


5) Identificar componentes del dominio.
6) Prueba de componentes y clases.

5.3.1 Paso 1. Definir la arquitectura de la solucin.


La arquitectura de la solucin es un diagrama de componentes / elementos que muestra no
solo esta lista de componentes necesarios para el correcto funcionamiento de la solucin,
sino tambin la manera en la cual stos estn interrelacionados. Dilogos, formularios para la
entrada / salida de datos para la interaccin con el usuario, bases de datos, servidores y otros
sistemas con los cuales hay interaccin, son algunos ejemplos.
Cabe mencionar que la realizacin de los controladores de caso de uso y los controladores de
procesos es de ayuda para la elaboracin de la arquitectura de la solucin. Un controlador de
caso de uso es aqul que controla el proceso de un caso de uso y que por lo tanto, dado que
los casos de uso representan funcionalidades, el controlador de caso de uso sirve como prueba
de satisfaccin del caso de uso o requerimiento. Por otro lado, un controlador de proceso es
aqul que inicia, monitorea y controla el procesamiento asociado a las secuencias de eventos
del problema.

5.3.2 Paso 2. Detallar las clases.


En este paso, cada clase identificada en el anlisis debe ser detallada incorporando las
responsabilidades (breve descripcin del objetivo de la clase), restricciones (limitaciones
o comentarios sobre alguno de sus elementos), atributos y mtodos. Para cada uno de los
atributos y mtodos es recomendable indicar tambin el criterio de interfaz asociado (privado,
protegido o pblico).
Adicionalmente, para cada mtodo se puede indicar tambin la descripcin de los parmetros
(cantidad y tipo de datos) y el tipo de dato de retorno de la operacin, si es el caso.

Nota: Mientras ms detallada la clase, menos impacto a la hora de implementar


o programar. Hay que recordar que algunas herramientas automatizadas para
la realizacin de diagramas UML permiten la generacin automtica de cdigo.
Mientras ms detalles se coloquen en los diagramas, ms cdigo automtico se
puede generar.

159

VOLVER A MEN

A continuacin algunas sugerencias para realizar esta actividad especfica de detallar


las clases identificadas en el anlisis:
Todos los conceptos identificados en el anlisis que no fueron considerados como clases
son posibles atributos de alguna de las clases resultantes.
Todos los verbos o acciones identificados en el contexto del problema son posibles
mtodos.
Recordar que las clases estn asociadas a conceptos de la vida real (abstraccin), por
lo que la identificacin de atributos y mtodos debe estar asociado al concepto de la vida
real ms que al aporte que este concepto presenta en el problema a resolver (ayuda a la
reutilizacin de componentes).
En la parte izquierda de la Figura 5.5 se muestra una plantilla de cmo debera verse el detalle
de una clase. La parte derecha muestra un ejemplo en la cual se observa el detalle de la clase
Lavadora.

Figura 5.5. Plantilla y ejemplo del detalle de una clase.

5.3.3 Paso 3. Desarrollar los modelos de estado.


Los modelos de estado permiten describir el comportamiento de los objetos (recordar el
concepto de estado de un objeto, Seccin 2.4). Ayudan a clarificar si los mtodos identificados
en el paso anterior (detalle de la clase) estn completos (faltan algunos) y/o hay de ms (sobran
algunos). Para cumplir con este paso se debe hacer un diagrama de transicin de estados
UML por cada clase que haya sido identificada en el diagrama de clases.
Por otro lado, en esta etapa es prudente tambin realizar el algoritmo detallado de cada mtodo
o al menos de aquellos que son significativos para la resolucin del problema. Para ello se
sugiere el uso de diagramas de actividad UML. Un aspecto importante a tener presente en este
caso es la posible generacin automtica de cdigo a partir de estos diagramas.
160

VOLVER A MEN

5.3.4 Paso 4. Elaborar los modelos de colaboracin.


Los modelos de colaboracin muestran la forma en que los objetos colaboran entre s
(intercambio de mensajes) para satisfacer los objetivos o requerimientos del problema
previamente representados con los diagramas de casos de uso (paso 3 del anlisis). Ayudan
a clarificar si la interfaz asignada para cada uno de los mtodos es o no la correcta ya que no
puede haber intercambio de mensajes entre objetos si los mtodos correspondientes no son
pblicos, privados o protegidos dependiendo del caso.
Estos modelos tambin permiten clarificar la completitud o no del protocolo del objeto:
necesidad de otros mtodos, validacin de los argumentos (en relacin a la cantidad y tipo
de dato) y el retorno del mtodo. Para la satisfaccin de este paso es necesario hacer un
diagrama de colaboracin y/o diagrama de secuencia UML por cada caso de uso que tiene
una interaccin directa con un actor. Hay que recordar que los diagramas de caso de uso
representan requerimientos funcionales de la solucin y que estos requerimientos funcionales
inician y/o terminan siempre con un actor. Los modelos de colaboracin permiten demostrar que
el intercambio de mensajes entre las clases identificadas en el modelo conceptual, permiten
satisfacer los requerimientos funcionales representados en los diagramas de casos de uso.
Esta es la razn por la cual los diagramas de colaboracin y secuencia deben iniciar con un
actor, mismo que est indicado en el diagrama de casos de uso del requerimiento especfico
que se quiere probar.

5.3.5 Paso 5. Identificar componentes del dominio.


Un componente representa el elemento fsico asociado a la abstraccin conceptual o clase.
Comnmente viene dado por una tabla, un archivo de datos, un ejecutable, un archivo binario,
un documento, entre otros. Un mismo componente puede ser la implementacin de ms de
una clase como por ejemplo en una biblioteca de clases.
Desde el punto de visto de la razn de ser del elemento, hay tres tipos de componentes:
1) De distribucin: son ejecutables o archivos binarios como por ejemplo: DLL, ActiveX, de
extensin Obj, de extensin Lib, de extensin Exe, entre otros.
2) Para trabajar en el producto: usados de forma esttica para crear los anteriores, como por
ejemplo: archivos de bases de datos, archivos de cdigo fuente, entre otros.
3) De ejecucin: son elementos creados de forma dinmica y temporalmente durante la ejecucin del sistema.
Un ejemplo en la cual se observan los tres tipos de componentes antes mencionados
es el mecanismo de ayuda en lnea que usan algunas de las versiones del sistema
161

VOLVER A MEN

operativo de Microsoft Windows. Se tienen los archivos de ayuda (extensin Hlp)


que son componentes binarios o de distribucin; los temas de contenido (extensin
Cnt) que son componentes fuente para construir los anteriores (para trabajar en
el producto) y la bsqueda de texto (extensin Fts) e ndice general (extensin Gio)
son componentes de ejecucin ya que se originan por necesidad del usuario cuando
utiliza la ayuda.
Para representar los componentes de dominio en el modelo OO se hacen diagramas de
componentes y diagramas de distribucin de UML.

5.3.6 Paso 6. Prueba de componentes y clases.


El proceso metodolgico finaliza con la prueba de cada caso de uso, con el objeto de determinar la
satisfaccin de los requerimientos del problema. Los diagramas de colaboracin y/o secuencia
son tiles para ello. Las pruebas permiten identificar las posibles operaciones (mtodos) y
propiedades (atributos) faltantes. La prueba del diseo es un factor importante para lograr el
xito en la codificacin, donde xito significa lograr los objetivos en el menor tiempo posible.
Se sugiere tambin incluir en este paso las pruebas individuales o unitarias de cada operacin
o mtodo. Para ello se recomienda definir las pre-condiciones (lo que debe ocurrir antes
de su ejecucin) y pos-condiciones (lo que debe ocurrir despus de su ejecucin) de estas
operaciones de cada clase.

5.4 Resumen
La Tabla 5.1 muestra un resumen del proceso metodolgico con los cuatro pasos del anlisis
y los seis pasos del diseo indicando en cada caso los productos parciales correspondientes.
Tabla 5.1. Resumen del proceso metodolgico.
Anlisis
Paso

Descripcin

Producto

Identificar la idea y objetivos bsicos del si


stema

Oraciones; mapa mental

Identificar actores / nombres

Lista de nombres

Identificar procesos / requerimientos

Diagramas de caso de uso y diagramas


de actividad

Identificar clases y asociaciones

Diagrama no detallado de clases

Paso

Descripcin

Producto

Definir la arquitectura de la solucin

Arquitectura

Detallar las clases

Diagrama detallado de clases y diagramas de actividad

Desarrollar los modelos de estado

Diagramas de estado por cada clase

Elaborar los modelos de colaboracin

Diagramas de colaboracin y/o secuencia por cada caso de uso

Identificar componentes del dominio

Diagramas de componentes y diagramas


de distribucin

Prueba de componentes y clases

Informe de pruebas

Diseo

162

VOLVER A MEN

5.5 Ejemplo
Un reloj digital consta de dos paneles digitales (pantallas), uno para mostrar la hora y el otro
para mostrar los minutos actuales. El primero, por lo tanto, muestra valores entre 0 y 23 y
el segundo entre 0 y 59. Un usuario del reloj debe poder inicializar el reloj (colocar un valor
igual a 0 en cada panel); debe poder incrementar el reloj tanto por minutos como por horas;
poder poner en hora el reloj a un valor cualquiera y observar del reloj el valor u hora actual en
todo momento. La Figura 5.6 muestra una representacin grfica de un reloj digital como se
describe anteriormente.

Figura 5.6. Representacin grfica de un reloj digital.

5.4.1 Anlisis
Paso 1. Identificar la idea y objetivos bsicos del sistema. El enunciado del ejercicio ya es lo
suficientemente explcito para cubrir este paso. Sin embargo, es posible extraer las siguientes
oraciones:
El reloj tiene dos paneles: hora y minuto.
Los paneles son digitales, es decir, muestran dgitos.
La interaccin con el reloj la realiza un usuario.
El usuario puede inicializar el reloj, cambiar la hora y ver en todo momento la hora actual.
Paso 2. Identificar actores / nombres. Al analizar las oraciones del paso anterior se pueden
identificar directamente los siguientes nombres: usuario, reloj y panel. Por otro lado, se menciona
que los paneles son digitales por lo tanto se hace alusin al nombre dgito y si se quiere ser
ms especfico, al observar cada dgito que se presenta en la Figura 5.6, se nota la presencia
de segmentos (en la Figura 5.6 se observan seis segmentos para representar el dgito 0; sin
embargo se requieren siete segmentos en total para representar todos los dgitos, siendo el 8
el que requiere tener los siete segmentos encendidos).
Paso 3. Identificar procesos / requerimientos. Los requerimientos de este problema estn muy
claros. El usuario debe poder: 1) inicializar el reloj; 2) incrementar los minutos; 3) incrementar
las horas; 4) asignar una hora cualquiera y 5) ver la hora actual en todo momento. En este
163

VOLVER A MEN

sentido, se tiene el diagrama de casos de uso que se muestra en la Figura 5.7. Ntese que
los cinco requerimientos antes mencionados tienen interaccin directa con el actor Usuario;
ntese tambin la manera en la cual el sentido de la asociacin entre el actor y estos casos
de uso corresponde con la semntica de interpretacin del requerimiento. Se incluyen tambin
algunos casos de uso que se deben realizar obligatoriamente (asociacin del tipo include)
para realizar los anteriores.

Figura 5.7. Diagrama de casos de uso del reloj digital

Paso 4. Identificar clases y asociaciones. De los nombres identificados en el Paso 2, Usuario


es el actor por lo que no puede ser una clase en este modelo. En este sentido, el resto de
los nombres van a ser clases del modelo OO asociado a este problema: reloj, panel, dgito
y segmento. A fin de identificar las asociaciones hay que tener en cuenta las siguientes
afirmaciones: un reloj tiene dos paneles o lo que es lo mismo, dos instancias de panel
identificados como hora y minuto forman parte del reloj; cada panel del reloj tiene dos dgitos y
cada dgito est constituido por siete segmentos. Se tiene entonces el diagrama no detallado
de clases de la Figura 5.8.

Figura 5.8. Diagrama no detallado de clases del reloj digital.

164

VOLVER A MEN

5.4.2 Diseo
Paso 1. Definir la arquitectura de la solucin. En este caso el problema tiene un bajo nivel
de complejidad y por lo tanto la arquitectura no contempla muchos elementos de interaccin
como por ejemplo bases de datos y otros sistemas. Se tiene tan solo un formulario de interfaz
que permita la interaccin del usuario con la instancia de un reloj, similar por ejemplo a la
representacin grfica de la Figura 5.6.
Paso 2. Detallar las clases. La Figura 5.9 presenta el diagrama detallado de clases relativo a
este problema con la incorporacin de los atributos y mtodos necesarios para completar el
modelo y satisfacer las necesidades requeridas. Ntese que el reloj tiene la interfaz necesaria
para satisfacer los requerimientos representados en el diagrama de casos de uso. Ntese
tambin las dos restricciones asociados al reloj. Observe que todos los atributos son privados
y todos los mtodos son pblicos.

Figura 5.9. Diagrama detallado de clases del reloj digital.

Paso 3. Desarrollar los modelos de estado. La Figura 5.10 muestra el diagrama de estados
asociado a la clase Segmento. Se tienen dos estados: el segmento apagado (Estado = Falso)
y el segmento encendido (Estado = Verdadero); el mtodo Apagar() lleva el objeto al estado
Apagado y el mtodo Encender() lleva el objeto al estado Encendido. Ntese tambin
que el objeto inicia o se construye con el estado Apagado y puede ser destruido estando en
cualquiera de los dos estados.

Figura 5.10. Diagrama de estados de la clase Segmento.

165

VOLVER A MEN

El diagrama de estados de las clases Panel y Dgito es similar y se muestra en la Figura


5.11. Como se observa el estado viene dado por el valor actual del atributo Valor que puede
ser modificado mediante el mtodo AsignarValor(V). Ntese que en el estado inicial del objeto
el valor del atributo es 0. En el diagrama de estado de la clase Dgito debera colocarse la
restriccin {0 <= Valor <= 9} y en la clase Panel debera estar la restriccin {LimiteInferior
<= Valor <= LimiteSuperior}.

Figura 5.11. Diagrama de estados de las clases Panel y Dgito.

Finalmente se tiene el diagrama de estados de la clase Reloj como se muestra en la Figura


5.12. Al ser la clase con ms comportamiento entonces el diagrama de estados es ms
complejo. Se observan cuatro estados donde se generaliza el valor de los atributos PanelHora
y PanelMinuto. De cada estado se puede ir a los otros tres dependiendo de los mtodos que
hacen la transicin: Inicializar(), IncrementarMinutos(), IncrementarHoras() y Asignar(H,
M). En el estado inicial los atributos tienen valor 0 y se puede llegar al estado final desde
cualquiera de los cuatro estados posibles.

Figura 5.12. Diagrama de estados de la clase Reloj.

Paso 4. Elaborar los modelos de colaboracin. Como se observa en el diagrama de casos de


uso de la Figura 5.7, se tienen cinco casos de uso directamente asociados al actor. En este
sentido se deben hacer igual nmero de diagramas de colaboracin / secuencia. En la parte
superior de la Figura 5.13 se muestra el diagrama de colaboracin que corresponde al caso
166

VOLVER A MEN

de uso inicializar el reloj; en la parte inferior est el diagrama de secuencia equivalente.


Ntese el uso especfico de las instancias PanelHora y PanelMinuto de la clase Panel; se
generaliza el objeto en el resto de los casos. Ntese tambin que los mensajes utilizados para
la colaboracin corresponden a los mtodos identificados en el diagrama detallado de clases
de la Figura 5.9. Con esto queda demostrado que el requerimiento Inicializar el reloj puede
ser resuelto con el modelo de clases realizado.

Figura 5.13. Diagrama de colaboracin y secuencia asociado al caso de uso inicializar el reloj.

En la Figura 5.14 se muestran el diagrama de colaboracin y secuencia asociado al requerimiento


Incrementar hora del reloj. Ntese la manera en la cual se representa el retorno (paso 3) como
resultado del mensaje expresado en el paso 2. Los diagramas de colaboracin y secuencia
asociados al requerimiento Incrementar minuto del reloj es similar a los que se muestran en
la Figura 5.14 con la diferencia, adems del mensaje inicial enviado al objeto :reloj que debe
ser IncrementarMinuto(), el objeto instanciado de la clase Panel debe ser PanelMinuto en
lugar de PanelHora.

167

VOLVER A MEN

Figura 5.14. Diagrama de colaboracin y secuencia asociado al caso de uso incrementar hora del reloj.

Los diagramas de colaboracin y secuencia relativos al caso de uso Asignar valor al reloj se
muestran en la Figura 5.15. Finalmente, la Figura 5.16 muestra los diagramas de colaboracin
y secuencia del caso de uso Mostrar hora actual del reloj. Ntese en este ltimo caso, el
retorno representado entre el objeto de la clase Reloj y el Usuario (actor) respetando as
el sentido de la interaccin entre el caso de uso y el actor que se muestra en el diagrama de
casos de uso de la Figura 5.7.

168

VOLVER A MEN

Figura 5.15. Diagrama de colaboracin y secuencia asociado al caso de uso Asignar valor al reloj.

Figura 5.16. Diagrama de colaboracin y secuencia asociado al caso de uso Mostrar hora actual del reloj.

169

VOLVER A MEN

Paso 5. Identificar componentes del dominio. Este paso tiene que ver con la manera en la cual
se desarrolla el modelo y los elementos de software que se originan al respecto. Asumiendo un
desarrollo en Java se tienen cuatro componentes cada uno de los cuales corresponde a cada
clase del modelo, todas ellas asociadas al paquete RelojDigital. La Figura 5.17 muestra el
diagrama de componentes correspondiente.
Por otro lado, para la ejecucin del software que resulta de este modelo se tiene el elemento
de software Reloj.class que resulta luego de la compilacin de Reloj.java; este elemento
debe ser ejecutado en un nodo que tenga la mquina virtual de Java. Esto se puede reflejar en
el diagrama de distribucin que se muestra en la Figura 5.18.

Figura 5.17. Diagrama de componentes del reloj digital.

Figura 5.18. Diagrama de distribucin del reloj digital.

Paso 6. Prueba de componentes y clases. La realizacin de los diagramas de colaboracin /


secuencia (paso 4) permiten demostrar la vialidad del modelo desde el punto de vista terico.
En este paso se debe garantizar la correcta realizacin del desarrollo y que ste haya sido
realizado acorde a lo establecido en los diagramas de componentes (Figura 5.17 en este caso).
La concrecin de los 10 pasos antes presentados lleva del enunciado de un problema a la
correcta implementacin OO del problema dejando, adems, una adecuada documentacin
terica del modelo esttico y dinmico del problema. Esto es quizs lo ms importante de este
proceso.

170

VOLVER A MEN

5.6 Ejercicios
1) Cul es la diferencia entre modelo esttico y modelo dinmico? Qu diagramas UML
corresponden a cada uno de estos modelos?
2) Por qu es necesario para el proceso metodolgico hacer un diagrama de estados por
cada clase identificada en el diagrama de clases?
3) Por qu es necesario para el proceso metodolgico hacer un diagrama de colaboracin
o secuencia por cada caso de uso con asociacin directa a un actor definidos en los
diagramas de casos de uso?
4) Para los ejercicios que se encuentran en la Seccin 3.6, complemente el modelo siguiendo
el proceso metodolgico tal como se mostr en el ejemplo de la Seccin 5.5.

171

VOLVER A MEN

Captulo 6

Modelado y programacin de problemas


6.1 Introduccin
El objetivo de este captulo es poner en prctica todos los conocimientos presentados en
los captulos anteriores. Esto se realiza mediante la resolucin, bajo el paradigma OO, de
nueve problemas desde las etapas de anlisis y diseo utilizando UML (Captulo 3) hasta las
etapas de programacin (Captulo 4). Los problemas fueron seleccionados de una lista de
trabajos prcticos que se han acumulado a travs de varios aos de experiencia acadmica.
Adicionalmente, los problemas estn asociados a diferentes niveles de complejidad partiendo
de una complejidad baja hasta problemas con un nivel alto de complejidad. La lista de casos
prcticos seleccionados es la que se menciona a continuacin; se agrega informacin sobre
elementos tomados en cuenta para la implementacin de la solucin.
1) La calculadora de operaciones bsicas: interfaz modo texto.
2) Simulacin de trfico de un solo carril y sentido : interfaz modo texto.
3) Manejo de conjunto de enteros: manejo de archivos y uso de plantillas.
4) Manejo inteligente de colas: manejo de XML.
5) El generador de energa: interfaz grfica, manejo de imgenes y uso de temporizadores.
6) La fbrica de galletas: interfaz grfica, manejo de imgenes y uso de temporizadores.
7) Grafo de asignacin de recursos: manejo de grafos.
8) La conquista del nuevo mundo: manejo de sonido.
9) Agentes recolectores de desperdicio: manejo de hilos de ejecucin.
A fin de mostrar los resultados presentes en este captulo, los problemas fueron resueltos con
las siguientes herramientas de cdigo abierto:
1) Los mapas mentales fueron realizados haciendo uso de XMind 2012, versin 3.3.0 (http://
www.xmind.net/).
2) Los diagramas UML fueron elaborados con StarUMLTM versin 5.0.2.1570 (http://staruml.
sourceforge.net/en/).
3) La programacin C++ fue realizada en la plataforma de desarrollo NetBeans IDE 7.3
(http://netbeans.org/) haciendo uso del compilador de C/C++ MinGW versin 5.1.4 (http://
mingw.org/). Para la parte grfica se us el kit de desarrollo de Qt versin 4.6.2 (http://
172

VOLVER A MEN

protocols.netlab.uky.edu/~xiwei/cs215_fall2011/qt-sdk-win -opensource-2010.02.1.exe).
4) La programacin Java fue realizada en la plataforma de desarrollo NetBeans IDE 7.3
(http://netbeans.org/) haciendo uso del kit de desarrollo JavaTM SE Development Kit 7u17
(http://www. oracle.com/technetwork/java/javase/downloads/index.html).
5) La programacin en C# fue realizada en la plataforma de desarrollo SharpDevelop versin
4.2.1 (http://www.icsharpcode.net/Open Source/SD/).
Cabe mencionar que, si bien los casos prcticos fueron resueltos haciendo uso de la metodologa
presentada en el Captulo 5, en algunos casos se obvian alguno de los pasos y/o no se realiza
la totalidad de los diagramas UML que se deben hacer. En el caso particular del paso 5 del
diseo referido a los componentes del dominio, dado que tiene una dependencia directa del
lenguaje de programacin utilizado para una implementacin especfica, estos se realizan slo
asumiendo alguno de los tres posibles lenguajes (C++, Java y C#). Adicionalmente, no se
agrega en ninguno de los casos el paso 6 del diseo referido a la prueba de componentes y
clases.

6.2 La calculadora de operaciones bsicas


6.2.1 Enunciado
Se desea hacer una calculadora de operaciones simples sobre nmeros enteros con
procesamiento en lnea. Mediante una interfaz modo texto y haciendo uso de un indicador
como por ejemplo >, el usuario tendr opcin de ejecutar las rdenes o comandos que se
presentan en la Tabla 6.1. Las operaciones posibles y sus respectivos smbolos se muestran
en la Tabla 6.2.

Tabla 6.1. rdenes a satisfacer en el problema de la calculadora de operaciones bsicas.


Comando

173

Descripcin

Muestra la lista de opciones

Cambia a modalidad de representacin base 2 o binaria

Cambia a modalidad de representacin base 10 o decimal

Cambia a modalidad de representacin base 16 o hexadecimal

Finalizar la ejecucin del programa

Muestra el valor actual de la memoria temporal

Muestra el estado actual de la base de representacin

VOLVER A MEN

Tabla 6.2. Operaciones a soportar en el problema de la calculadora de operaciones bsicas.


Smbolo

Operacin

Operandos

Suma o adicin

Resta o sustraccin

Producto o multiplicacin

Divisin entera

Cambio de signo

Mdulo o resto de la divisin entera

&

Conjuncin sobre bits

Disyuncin sobre bits

Disyuncin exclusiva sobre bits

Complemento sobre bits

<

Desplazamiento de bits hacia la izquierda

>

Desplazamiento de bits hacia la derecha

A continuacin las caractersticas y reglas de funcionamiento de la calculadora:


1) Todos los comandos finalizan al presionar la tecla ENTER ( ).
2) La calculadora cuenta con un espacio de memoria temporal cuyo valor puede ser
obtenido mediante el identificador T.
3) Al iniciar la ejecucin, el espacio de memoria temporal tendr un valor inicial igual a 0 y
la base de representacin numrica ser decimal.
4) Slo es posible hacer una operacin por lnea, es decir, una lnea no puede nunca tener
ms de un smbolo que representa operaciones. Es claro que no se requiere y por ende
no es vlido el uso de parntesis.
5) Las operaciones sern escritas en formato pre-orden, es decir, primero el operador y
luego los operandos. Para la separacin se usar un espacio en blanco.
6) Luego de cada operacin el resultado ser mostrado segn la base de representacin
actual y almacenado en el espacio de memoria temporal.
7) Los literales o constantes numricas usadas en las expresiones se deben escribir
haciendo uso de la base de representacin actual.
8) La escala o lmites a usar corresponde a la cantidad de valores que pueden ser
representados en un byte (8 bits) con signo. En la representacin binaria y hexadecimal,
el signo negativo del nmero se representa y almacena con el bit ms significativo. En
este sentido se tiene lo siguiente:
a) No ms de 8 dgitos binarios para base 2; desde 11111111 hasta 1111111.
b) No ms de 3 dgitos decimales para base 10; desde 127 hasta 127.
174

VOLVER A MEN

c) No ms de 2 dgitos hexadecimales para base 16; desde FF hasta 7F.


9) Los caracteres de una lnea de comando deben ser filtrados de forma tal que slo se
mostrarn los que son vlidos. Si al dar el caracter de finalizacin del comando () el
comando est incompleto, no se tomar en cuenta y se mostrar de nuevo el indicador.
10) Cualquier caracter que no cumpla con la sintaxis de los comandos y / o representacin
numrica acorde a la base actual, debe ser rechazado y ser notificado al usuario con un
sonido o mensaje de texto conveniente.
A continuacin algunos ejemplos:

6.2.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): Luego de hacer una revisin del
texto del enunciado se tiene un mapa mental equivalente al que se muestra en la Figura 6.1.

Figura 6.1. Mapa mental relativo al problema de la calculadora.

175

VOLVER A MEN

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: calculadora,


operacin, nmero entero, indicador, comando, smbolo, memoria, operador, operando,
resultado, literal, base de representacin, escala.
Paso 3 (Identificar procesos / requerimientos): En el enunciado no se dice explcitamente sin
embargo, es claro asumir que la calculadora tiene un usuario que interacta con ella y que
espera de ella un resultado. Por otro lado se sabe que la interaccin se hace mediante la
introduccin de comandos que se resumen en: realizar una operacin, cambiar la base de
representacin, mostrar el valor actual de la memoria y de la base de representacin, mostrar
la lista de comandos y finalizar la ejecucin. En este sentido, se tiene el diagrama de casos
de uso de la Figura 6.2. Ntese el sentido de actuacin del Usuario con la Calculadora. Con el
objeto de poder leer mejor el diagrama, el actor se repite dos veces.

Figura 6.2. Diagrama de casos de uso relativo al problema de la calculadora.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Nmero entero, resultado y literal son implementaciones.
Smbolo y operador son redundantes.
Indicador, base de representacin y escala son atributos de calculadora.
Operacin es un caso particular de comando; todo comando tiene un operador y las
operaciones agregan adicionalmente uno o dos operandos.
El operador y los operandos son atributos.
Memoria forma parte de la calculadora.
Luego, se obtiene como propuesta de diagrama no detallado de clases para este problema el
indicado en la Figura 6.3.
176

VOLVER A MEN

Figura 6.3. Diagrama no detallado de clases relativo al problema de la calculadora.

6.2.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Se trata de un problema cuya implementacin
requiere de un programa sin interaccin con ningn otro elemento de software. Se sabe que
la interfaz es modo texto.
Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.3,
resulta el diagrama de clases detallado de la Figura 6.4. Ntese el tipo de dato de los atributos
(a la derecha, despus de :); el tipo de dato de retorno de los mtodos expresados como un
estereotipo (encerrados entre << y >>); el uso de parmetros en los mtodos (encerrados
entre ( y )), si aplica, y los valores iniciales de alguno de los atributos (a la derecha luego del
smbolo de igualdad =).

Figura 6.4. Diagrama detallado de clases relativo al problema de la calculadora.

Paso 3 (Desarrollar los modelos de estado): Sabiendo que los modelos de estados se
determinan revisando el cambio de los atributos de cada una de las clases originado por la
ejecucin de los mensajes correspondientes, es de notar que en la clase Calculadora los
177

VOLVER A MEN

atributos Indicador y NumBytes se asignan inicialmente en tiempo de construccin y pueden


ser luego cambiados por los mtodos AsgIndicador y AsgNumBytes respectivamente; los
atributos T y Base se modifican como consecuencia de la introduccin de alguno de los
comandos (mtodo IntroducirComando). En la clase Memoria es posible modificar el valor
del atributo Valor haciendo uso del mtodo AsgValor (inicialmente es 0). El nico estado de
la clase Comando es aqul dado en tiempo de construccin del objeto cuando se le da valor al
atributo Operacin. Los dos operandos (Operando1 y Operando2) de la clase Operacin
pueden ser modificados por los mtodos AsgOperando1 y AsgOperando2 respectivamente.
Se tienen entonces los diagramas de estado que se muestran en la Figura 6.5.

Figura 6.5. Diagramas de estado relativos al problema de la calculadora.

Paso 4 (Elaborar los modelos de colaboracin): Segn el diagrama de casos de uso de la


Figura 6.2 hay dos requerimientos que deben ser comprobados: Introducir comando y Mostrar
resultado. En este sentido, la Figura 6.6 muestra el diagrama de colaboracin que permite
probar la manera en la cual el modelo conceptual satisface estos requerimientos. El orden de
ejecucin de los mensajes no es exactamente secuencial ya que depende del comando. La
lectura es como sigue: el Usuario ejecuta el mensaje IntroducirComando() el cual retorna el
objeto C que puede ser tanto un Comando como una Operacin; en el caso de ser una
Operacin, se deben ejecutar los mensajes AsgOperando1(O) y AsgOperando2(O); al
Ejecutar() el comando u operacin se tiene un retorno con el resultado; dependiendo del
retorno, ste puede implicar hacer una AsgValor(V) en la Memoria cuya instancia est
identificada como T. Finalmente se muestra el resultado al Usuario.

178

VOLVER A MEN

Figura 6.6. Diagrama de colaboracin relativo al problema de la calculadora.

Paso 5 (Identificar componentes del dominio): Asumiendo el uso de C++ para la implementacin
de este caso prctico, el diagrama de componentes correspondiente al problema se presenta
en la Figura 6.7. Ntese que, dado la simplicidad del ejercicio, solo se contempla un archivo
de cdigo fuente (Calculadora.cpp) con el archivo encabezado asociado (Calculadora.hpp),
cuya compilacin origina el programa (Calculadora.Exe).

Figura 6.7. Diagrama de componentes relativo al problema de la calculadora.

6.2.4 Programacin
A continuacin el cdigo fuente escrito en C++ relativo al problema de la calculadora. Se
muestra primero el archivo encabezado Calculadora.hpp en el cual se encuentran las
declaraciones de las clases identificadas en el diseo (ntese la analoga entre lo previamente
diseado y lo programado). Se utiliz el concepto de plantillas (template) para generalizar la
implementacin de la computadora a un tipo de dato numrico mayor no necesariamente igual
a 1 byte.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.2
* Calculadora.hpp
* Mauricio Paletta
*/

179

VOLVER A MEN

#ifndef CALCULADORA_HPP
#define CALCULADORA_HPP
#include TrimString.hpp
// Tipos posibles de operaciones
//
enum TipoOperacion {
Ayuda, Binario, Decimal, Hexa, Salir, MemoriaActual, BaseActual,
Suma, Resta, Multiplicacion, Division, Signo, Modulo,
Conjuncion, Disyuncion, Exclusivo, Complemento, Izquierda, Derecha
};
// Tipos posibles de base de representacin
//
enum TipoBase {
Base2 = 2, Base10 = 10, Base16 = 16
};
// Representa la memoria de la calculadora
//
template <class TByte>
class Memoria {
private:
TByte Valor;
public:
Memoria() { Valor = 0; }
TByte ObtValor() { return Valor; }
void AsgValor(TByte V) { Valor = V; }
};
// Representa un Comando
//
180

VOLVER A MEN

template <class TByte>


class Comando {
protected:
TipoOperacion Op;
public:
Comando(TipoOperacion O) { Op = O; }
TipoOperacion ObtOperacion() { return Op; }
virtual TByte Ejecutar() { return 0; }
};
// Representa una Operacin
//
template <class TByte>
class Operacion : public Comando<TByte> {
private:
TByte Operando1, Operando2;
TByte BinADec(TrimString B);
TrimString DecABin(TByte D);
TByte HexADec(TrimString H);
TrimString DecAHex(TByte D);
public:
Operacion(TipoOperacion O) : Comando<TByte>(O) { }
void AsgOperando1(TByte O) { Operando1 = O; }
void AsgOperando1(TrimString O, TipoBase B);
void AsgOperando2(TByte O) { Operando2 = O; }
void AsgOperando2(TrimString O, TipoBase B);
TByte Ejecutar();
TrimString Ejecutar(TipoBase B);
TrimString Convertir(TByte N, TipoBase B);
};
181

VOLVER A MEN

// Representa una calculadora bsica de enteros


//
template <class TByte>
class Calculadora {
private:
Memoria<TByte> *T;
TrimString Indicador;
TipoBase Base;
int NumBytes;
public:
Calculadora(TrimString I, int N = 1);
~Calculadora();
void AsgIndicador(TrimString I) { if (I != ) Indicador = I; }
void AsgNumBytes(int N);
void AsgBase(TipoBase B) { Base = B; }
TByte ObtMemoria() { return T.ObtValor(); }
TipoBase ObtBase() { return Base; }
void IntroducirComandos();
};
#endif /* CALCULADORA_HPP */

Lo siguiente es el archivo Calculadora.cpp que contiene el desarrollo de los mtodos outline


y el programa principal que se encuentra al final.

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.2
* Calculadora.cpp
* Mauricio Paletta
*/
#include <iostream>
182

VOLVER A MEN

#include <cstdlib>
#include <math.h>
#include <stdio.h>
#include Calculadora.hpp
using namespace std;
/ Mtodos de la clase Operacion
//
// Convierte una cadena de caracteres que se supone est en binario
// a su equivalente en nmero decimal
//
template <class TByte>
TByte Operacion<TByte>::BinADec(TrimString B) {
TByte N = 0, P = 0, S = 1, T = 0;
if (B.length() == 8 * sizeof(TByte) && B[0] == 1) {
S = -1;
T = 1;
}
for (int i=B.length()-1; i >= T; i--) {
switch (B[i]) {
case 1:
N += (int)pow(2, P);
case 0:
break;
default:
return 0;
}
P++;
}

183

VOLVER A MEN

return S * N;
}
// Convierte un nmero decimal en la cadena de caracteres
// equivalente en binario
//
template <class TByte>
TrimString Operacion<TByte>::DecABin(TByte D) {
char Aux[120];
TrimString S;
S = TrimString(ltoa((long)D, Aux, 2));
if (S.length() > 8 * sizeof(TByte)) S = S.substr(S.length() - (8 * sizeof(TByte)), 8 * sizeof(TByte));
return S;
}
// Convierte una cadena de caracteres que se supone est en hexadecimal
// a su equivalente en nmero decimal
//
template <class TByte>
TByte Operacion<TByte>::HexADec(TrimString H) {
TByte N = 0, P = 0, B, S = 1;
for (int i=H.length()-1; i >= 0; i--) {
switch (H[i]) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
184

VOLVER A MEN

case 9:
B = H[i] - 0;
break;
case A:
case B:
case C:
case D:
case E:
case F:
B = 10 + (H[i] - A);
break;
case a:
case b:
case c:
case d:
case e:
case f:
B = 10 + (H[i] - a);
break;
default:
return 0;
}
if (H.length() == 2 * sizeof(TByte) && i == 0 && B >= 8) {
S = -1;
B -= 8;
}
N += B * (TByte)pow(16, P++);
}
return S * N;
}

185

VOLVER A MEN

// Convierte un nmero decimal en la cadena de caracteres equivalente en hexadecimal


//
template <class TByte>
TrimString Operacion<TByte>::DecAHex(TByte D) {
char Aux[120];
TrimString S;
S = TrimString(ltoa((long)D, Aux, 16));
if (S.length() > 2 * sizeof(TByte)) S = S.substr(S.length() - (2 * sizeof(TByte)), 2 * sizeof(TByte));
return S;
}
// Cambia el valor del primer operando basado en una cadena de
// caracteres O supuestamente representado en base B
//
template <class TByte>
void Operacion<TByte>::AsgOperando1(TrimString O, TipoBase B) {
switch (B) {
case Base2:
Operando1 = BinADec(O);
break;
case Base10:
Operando1 = (TByte)atol(O.c_str());
break;
case Base16:
Operando1 = HexADec(O);
break;
}
}
// Cambia el valor del segundo operando basado en una cadena de
// caracteres O supuestamente representado en base B
//
186

VOLVER A MEN

template <class TByte>


void Operacion<TByte>::AsgOperando2(TrimString O, TipoBase B) {
switch (B) {
case Base2:
Operando2 = BinADec(O);
break;
case Base10:
Operando2 = (TByte)atol(O.c_str());
break;
case Base16:
Operando2 = HexADec(O);
break;
}
}
// Ejecuta la operacin y retorna la respuesta en decimal
//
template <class TByte>
TByte Operacion<TByte>::Ejecutar() {
// Depende de la operacin y del nmero de bytes usados
//
switch (Comando<TByte>::ObtOperacion()) {
case Suma:
return Operando1 + Operando2;
case Resta:
return Operando1 - Operando2;
case Multiplicacion:
return Operando1 * Operando2;
case Division:
return Operando1 / Operando2;
case Signo:
return -Operando1;
case Modulo:
187

VOLVER A MEN

return Operando1 % Operando2;


case Conjuncion:
return Operando1 & Operando2;
case Disyuncion:
return Operando1 | Operando2;
case Exclusivo:
return Operando1 ^ Operando2;
case Complemento:
return ~Operando1;
case Izquierda:
return Operando1 << Operando2;
case Derecha:
return Operando1 >> Operando2;
}
return 0;
}
// Ejecuta la operacin y retorna la respuesta en una cadena de caracteres
// cuya base de representacin es la indicada por B
//
template <class TByte>
TrimString Operacion<TByte>::Ejecutar(TipoBase B) {
int R = Ejecutar();
return Convertir(R, B);
}
template <class TByte>
TrimString Operacion<TByte>::Convertir(TByte N, TipoBase B) {
char Aux[10];
TrimString S = ;
switch (B) {
case Base2:
188

VOLVER A MEN

S = DecABin(N);
if (S.length() > 8 * sizeof(TByte)) S = S.substr(S.length() - (8 * sizeof(TByte)), 8 *
sizeof(TByte));
break;
case Base10:
S = TrimString(ltoa((long)N, Aux, 10));
break;
case Base16:
S = DecAHex(N);
if (S.length() > 2 * sizeof(TByte)) S = S.substr(S.length() - (2 * sizeof(TByte)), 2 *
sizeof(TByte));
break;
}
return S;
}
// Mtodos de la clase Calculadora
//
template <class TByte>
Calculadora<TByte>::Calculadora(TrimString I, int N) {
Indicador = I; NumBytes = N; Base = Base10;
T = new Memoria<TByte>();
}
template <class TByte>
Calculadora<TByte>::~Calculadora() {
delete T;
}
template <class TByte>
void Calculadora<TByte>::AsgNumBytes(int N) {
delete T;
switch (N) {
189

VOLVER A MEN

case 1:
T = new Memoria<char>();
break;
case 2:
T = new Memoria<short>();
break;
case 4:
T = new Memoria<int>();
break;
default:
N = 8;
T = new Memoria<long>();
break;
}
NumBytes = N;
}
template <class TByte>
void Calculadora<TByte>::IntroducirComandos() {
TrimString S, strO, strP1, strP2;
TipoOperacion tOp = Ayuda;
bool Fin = false;
int Aux;
Operacion<TByte> *C;
char Buffer[80];
// Ciclo de lectura y ejecucin de comandos
//
do {
// Primero se muestra el indicador
//
cout << Indicador;

190

VOLVER A MEN

// Se lee una lnea de comando


//
S.clear();
cin.getline(Buffer, 80, \n);
// Se limpian los espacios en blanco a la izquierda y la derecha
//
S = Buffer;
S.trim();
// Se descompone la lnea en cada una de las partes
// Lo primero que debe venir es el tipo de operacin
//
Aux = S.find( , 0);
if (Aux < 0)
strO = S;
else
strO = S.substr(0, Aux);
// Determinar cul es el operador
//
strP1 = ; strP2 = ;
if (strO == ?) tOp = Ayuda;
else
if (strO == B) tOp = Binario;
else
if (strO == D) tOp = Decimal;
else
if (strO == H) tOp = Hexa;
else
if (strO == S) tOp = Salir;
else
if (strO == T) tOp = MemoriaActual;
191

VOLVER A MEN

else
if (strO == V) tOp = BaseActual;
else
if (strO == +) tOp = Suma;
else
if (strO == -) tOp = Resta;
else
if (strO == *) tOp = Multiplicacion;
else
if (strO == /) tOp = Division;
else
if (strO == %) tOp = Modulo;
else
if (strO == &) tOp = Conjuncion;
else
if (strO == |) tOp = Disyuncion;
else
if (strO == !) tOp = Complemento;
else
if (strO == <) tOp = Izquierda;
else
if (strO == >) tOp = Derecha;
else {
// Debe ser un literal vlido
// Se asume una suma a 0
//
tOp = Suma;
strP1 = S;
strP2 = 0;
}
// Verificar si estn los operandos
//
192

VOLVER A MEN

if (strP1 == && Aux >= 0) {


S = S.substr(Aux + 1, S.length() - (Aux + 1));
S.ltrim();
Aux = S.find( , 0);
if (Aux < 0)
strP1 = S;
else
strP1 = S.substr(0, Aux);
if (Aux > 0) {
S = S.substr(Aux + 1, S.length() - (Aux + 1));
S.ltrim();
if (S.length() > 0) {
strP2 = S;
// La operacin debe ser vlida para dos operandos
//
if (tOp < Suma || tOp == Signo || tOp == Complemento) tOp = Ayuda;
}
else {
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if (tOp == Resta) tOp = Signo;
if (tOp != Signo || tOp != Complemento) tOp = Ayuda;
}
}
else {
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if (tOp == Resta) tOp = Signo;
if (tOp != Signo && tOp != Complemento) tOp = Ayuda;
}
193

VOLVER A MEN

}
else {
// Una operacin que requiere operandos y no hay operandos no puede ser
//
if (strP1 == && tOp >= Suma && tOp <= Derecha) tOp = Ayuda;
}
// Se instancia el objecto Comando / Operacin
//
C = new Operacion<TByte>(tOp);
// Se agregan los operandos; pueden ser T
//
if (strP1 != ) {
if (strP1 == T) strP1 = C->Convertir(T->ObtValor(), Base);
C->AsgOperando1(strP1, Base);
if (strP2 != ) {
if (strP2 == T) strP2 = C->Convertir(T->ObtValor(), Base);
C->AsgOperando2(strP2, Base);
}
}
// Se ejecuta el comando / operacin
//
switch (tOp) {
case Binario:
AsgBase(Base2);
break;
case Decimal:
AsgBase(Base10);
break;
case Hexa:
AsgBase(Base16);
194

VOLVER A MEN

break;
case MemoriaActual:
cout << C->Convertir(T->ObtValor(), ObtBase()) << \n;
break;
case BaseActual:
cout << ObtBase() << : ;
switch (ObtBase()) {
case Base2:
cout << { 0, 1 } << \n;
break;
case Base10:
cout << { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } << \n;
break;
case Base16:
cout << { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F } << \n;
break;
}
break;
case Suma:
case Resta:
case Multiplicacion:
case Division:
case Signo:
case Modulo:
case Conjuncion:
case Disyuncion:
case Exclusivo:
case Complemento:
case Izquierda:
case Derecha:
T->AsgValor(C->Ejecutar());
cout << C->Convertir(T->ObtValor(), ObtBase()) << \n;
break;
195

VOLVER A MEN

case Salir:
Fin = true;
break;
default:
cout << ?\tMuestra la lista de opciones\n;
cout << B\tCambia a modalidad de representacin base 2 o binaria\n;
cout << D\tCambia a modalidad de representacin base 10 o decimal\n;
cout << H\tCambia a modalidad de representacin base 16 o hexadecimal\n;
cout << S\tFinalizar la ejecucin del programa\n;
cout << T\tMuestra el valor actual de la memoria temporal\n;
cout << V\tMuestra el estado actual de la base de representacin\n;
break;
}
// Se libera el espacio ocupado por el comando
//
delete C;
} while (!Fin);
}
// Programa principal
//
int main(int argc, char** argv) {
Calculadora<char> Calc(Calcular> , 1);
Calc.IntroducirComandos();
return 0;
}

Ntese que en el ejercicio se utiliza la clase TrimString. A continuacin los archivos TrimString.
hpp y TrimString.cpp relativos a esa clase.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
196

VOLVER A MEN

* Desarrollo en C++; caso prctico Seccin 6.2


* TrimString.hpp
* Mauricio Paletta
*/
#ifndef TRIMSTRING_HPP
#define TRIMSTRING_HPP
#include <string>
class TrimString : public std::string {
public:
TrimString() : std::string() { };
TrimString(const std::string& s) : std::string(s) { };
TrimString(const char *s) : std::string(s) { };
void ltrim() {
erase(begin(), std::find_if(begin(), end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}
void rtrim() {
erase(std::find_if(rbegin(), rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), end());
}
void trim() {
ltrim(); rtrim();
}
};
#endif /* TRIMSTRING_HPP */

197

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.2
* TrimString.cpp
* Mauricio Paletta
*/
#include TrimString.hpp

El mismo programa escrito en Java se puede ver a continuacin. Ntese las diferencias
entre lenguajes para resolver el mismo problema. El archivo fuente Calculadora.java que
define a la clase Calculadora est asociado al paquete que lleva el mismo nombre, es decir,
calculadora.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.2
* Calculadora.java
* Mauricio Paletta
*/
package calculadora;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Calculadora<TByte extends Number> {
// Tipos posibles de operaciones
//
public enum TipoOperacion {
Ayuda, Binario, Decimal, Hexa, Salir, MemoriaActual, BaseActual,
Suma, Resta, Multiplicacion, Division, Signo, Modulo,
198

VOLVER A MEN

Conjuncion, Disyuncion, Exclusivo, Complemento, Izquierda, Derecha


}
// Tipos posibles de base de representacin
//
public enum TipoBase {
Base2, Base10, Base16
}
// Representa la memoria de la calculadora
//
private class Memoria<TByte extends Number> {
private TByte Valor;
public TByte ObtValor() { return Valor; }
public void AsgValor(TByte V) { Valor = V; }
}
// Representa un Comando
//
private abstract class Comando<TByte extends Number> {
protected TipoOperacion Op;
public Comando(TipoOperacion O) { Op = O; }
public TipoOperacion ObtOperacion() { return Op; }
public abstract TByte Ejecutar();
}
// Representa una Operacin
//
private class Operacion<TByte extends Number> extends Comando<TByte> {
private TByte Operando1, Operando2;

199

VOLVER A MEN

private TByte BinADec(String B) {


int S = 1;
if (B.length() == 8 * NumBytes && B.charAt(0) == 1) {
S = -1;
B = B.substring(1);
}
return (TByte)(Number)(S * Integer.parseInt(B, 2));
}
private String DecABin(TByte D) {
String S = Long.toBinaryString((Long)(Number)D);
if (S.length() > 8 * NumBytes)
S = S.substring(S.length() - (8 * NumBytes));
return S;
}
private TByte HexADec(String H) {
long R = Long.parseLong(H, 16);
int S = 1, N = 0;
char C;
if (H.length() == 2 * NumBytes) {
S = -1;
C = H.charAt(0);
if (C >= 0 && C <= 9) N = C - 0;
if (C >= A && C <= F) N = C - A + 10;
if (C >= a && C <= f) N = C - a + 10;
if (N >= 8) {
R -= (N * Math.pow(16, 2 * NumBytes - 1));
R += ((N - 8) * Math.pow(16, 2 * NumBytes - 1));
}
}
200

VOLVER A MEN

return (TByte)(Number)(S * R);


}
private String DecAHex(TByte D) {
String S = Long.toHexString((Long)(Number)D);
if (S.length() > 2 * NumBytes)
S = S.substring(S.length() - (2 * NumBytes));
return S;
}
public Operacion(TipoOperacion O) { super(O); }
public void AsgOperando1(TByte O) { Operando1 = O; }
public void AsgOperando1(String O, TipoBase B) {
switch (B) {
case Base2:
Operando1 = BinADec(O);
break;
case Base10:
Operando1 = (TByte)(Number)Long.parseLong(O);
break;
case Base16:
Operando1 = HexADec(O);
break;
}
}
public void AsgOperando2(TByte O) { Operando2 = O; }
public void AsgOperando2(String O, TipoBase B) {
switch (B) {
case Base2:
Operando2 = BinADec(O);
break;
case Base10:
201

VOLVER A MEN

Operando2 = (TByte)(Number)Long.parseLong(O);
break;
case Base16:
Operando2 = HexADec(O);
break;
}
}
@Override
public TByte Ejecutar() {
long Oper1 = 0, Oper2 = 0;
if (Operando1 != null) Oper1 = Operando1.longValue();
if (Operando2 != null) Oper2 = Operando2.longValue();
// Depende de la operacin y del nmero de bytes usados
//
switch (ObtOperacion()) {
case Suma:
return (TByte)(Number)(Oper1 + Oper2);
case Resta:
return (TByte)(Number)(Oper1 - Oper2);
case Multiplicacion:
return (TByte)(Number)(Oper1 * Oper2);
case Division:
return (TByte)(Number)(Oper1 / Oper2);
case Signo:
return (TByte)(Number)(-Oper1);
case Modulo:
return (TByte)(Number)(Oper1 % Oper2);
case Conjuncion:
return (TByte)(Number)(Oper1 & Oper2);
case Disyuncion:
202

VOLVER A MEN

return (TByte)(Number)(Oper1 | Oper2);


case Exclusivo:
return (TByte)(Number)(Oper1 ^ Oper2);
case Complemento:
return (TByte)(Number)(~Oper1);
case Izquierda:
return (TByte)(Number)(Oper1 << Oper2);
case Derecha:
return (TByte)(Number)(Oper1 >> Oper2);
}
return (TByte)(Number)0;
}
public String Ejecutar(TipoBase B) {
TByte R = Ejecutar();
return Convertir(R, B);
}
public String Convertir(TByte N, TipoBase B) {
String S = ;
switch (B) {
case Base2:
S = DecABin(N);
if (S.length() > 8 * NumBytes) S = S.substring(S.length() - (8 * NumBytes));
break;
case Base10:
S = Long.toString((long)(Number)N);
break;
case Base16:
S = DecAHex(N);
if (S.length() > 2 * NumBytes) S = S.substring(S.length() - (2 * NumBytes), S.length() - 1);
203

VOLVER A MEN

break;
}
return S;
}
}
// Atributos de Calculadora
//
private Memoria<TByte> T;
private String Indicador;
private TipoBase Base;
private int NumBytes;
// Mtodos de la clase Calculadora
//
Calculadora(String I, int N) {
Indicador = I; NumBytes = N; Base = TipoBase.Base10;
T = new Memoria();
}
public<TByte extends Number> void AsgNumBytes() {
TByte Aux = (TByte)(Number)0;
if (Aux.getClass() == Byte.TYPE) NumBytes = 1;
else
if (Aux.getClass() == Short.TYPE) NumBytes = 2;
else
if (Aux.getClass() == Integer.TYPE) NumBytes = 4;
else
if (Aux.getClass() == Long.TYPE) NumBytes = 8;
T = new Memoria();
}

204

VOLVER A MEN

public void AsgIndicador(String I) { if (I.compareTo() != 0) Indicador = I; }


public void AsgBase(TipoBase B) { Base = B; }
public TByte ObtMemoria() { return T.ObtValor(); }
public TipoBase ObtBase() { return Base; }
public void IntroducirComandos() throws IOException {
String S, strO, strP1, strP2;
TipoOperacion tOp;
boolean Fin = false;
int Aux;
Operacion<TByte> C;
BufferedReader Buffer = new BufferedReader(new InputStreamReader(System.in));
// Ciclo de lectura y ejecucin de comandos
//
do {
// Primero se muestra el indicador
//
System.out.print(Indicador);
// Se lee una lnea de comando
//
S = Buffer.readLine();
S.trim();
// Se descompone la lnea en cada una de las partes
// Lo primero que debe venir es el tipo de operacin
//
Aux = S.indexOf( , 0);
if (Aux < 0)
strO = S;
else
strO = S.substring(0, Aux);
205

VOLVER A MEN

// Determinar cul es el operador


//
strP1 = ; strP2 = ;
if (strO.compareTo(?) == 0) tOp = TipoOperacion.Ayuda;
else
if (strO.compareTo(B) == 0) tOp = TipoOperacion.Binario;
else
if (strO.compareTo(D) == 0) tOp = TipoOperacion.Decimal;
else
if (strO.compareTo(H) == 0) tOp = TipoOperacion.Hexa;
else
if (strO.compareTo(S) == 0) tOp = TipoOperacion.Salir;
else
if (strO.compareTo(T) == 0) tOp = TipoOperacion.MemoriaActual;
else
if (strO.compareTo(V) == 0) tOp = TipoOperacion.BaseActual;
else
if (strO.compareTo(+) == 0) tOp = TipoOperacion.Suma;
else
if (strO.compareTo(-) == 0) tOp = TipoOperacion.Resta;
else
if (strO.compareTo(*) == 0) tOp = TipoOperacion.Multiplicacion;
else
if (strO.compareTo(/) == 0) tOp = TipoOperacion.Division;
else
if (strO.compareTo(%) == 0) tOp = TipoOperacion.Modulo;
else
if (strO.compareTo(&) == 0) tOp = TipoOperacion.Conjuncion;
else
if (strO.compareTo(|) == 0) tOp = TipoOperacion.Disyuncion;
else
if (strO.compareTo(!) == 0) tOp = TipoOperacion.Complemento;
else
206

VOLVER A MEN

if (strO.compareTo(<) == 0) tOp = TipoOperacion.Izquierda;


else
if (strO.compareTo(>) == 0) tOp = TipoOperacion.Derecha;
else {
// Debe ser un literal vlido
// Se asume una suma a 0
//
tOp = TipoOperacion.Suma;
strP1 = S;
strP2 = 0;
}
// Verificar si estn los operandos
//
if (strP1.compareTo() == 0 && Aux >= 0) {
S = S.substring(Aux + 1);
S.trim();
Aux = S.indexOf( , 0);
if (Aux < 0)
strP1 = S;
else
strP1 = S.substring(0, Aux);
if (Aux > 0) {
S = S.substring(Aux + 1);
S.trim();
if (S.length() > 0) {
strP2 = S;
// La operacin debe ser vlida para dos operandos
//
switch (tOp) {
207

VOLVER A MEN

case Binario:
case Decimal:
case Hexa:
case MemoriaActual:
case BaseActual:
case Signo:
case Complemento:
case Salir:
tOp = TipoOperacion.Ayuda;
break;
}
}
else {
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if (tOp == TipoOperacion.Resta) tOp = TipoOperacion.Signo;
if (tOp != TipoOperacion.Signo || tOp != TipoOperacion.Complemento)
tOp = TipoOperacion.Ayuda;
}
}
else {
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if (tOp == TipoOperacion.Resta) tOp = TipoOperacion.Signo;
if (tOp != TipoOperacion.Signo && tOp != TipoOperacion.Complemento)
tOp = TipoOperacion.Ayuda;
}
}
else {
// Una operacin que requiere operandos y no hay operandos no puede ser
//
if (strP1.compareTo() == 0 &&
(tOp == TipoOperacion.Suma || tOp == TipoOperacion.Resta ||
208

VOLVER A MEN

tOp == TipoOperacion.Multiplicacion || tOp == TipoOperacion.Division ||


tOp == TipoOperacion.Signo || tOp == TipoOperacion.Modulo ||
tOp == TipoOperacion.Conjuncion || tOp == TipoOperacion.Disyuncion ||
tOp == TipoOperacion.Exclusivo || tOp == TipoOperacion.Complemento ||
tOp == TipoOperacion.Izquierda || tOp == TipoOperacion.Derecha))
tOp = TipoOperacion.Ayuda;
}
// Se instancia el objecto Comando / Operacin
//
C = new Operacion(tOp);
// Se agregan los operandos; pueden ser T
//
if (strP1.compareTo() != 0) {
if (strP1.compareTo(T) == 0) strP1 = C.Convertir(T.ObtValor(), Base);
C.AsgOperando1(strP1, Base);
if (strP2.compareTo() != 0) {
if (strP2.compareTo(T) == 0) strP2 = C.Convertir(T.ObtValor(), Base);
C.AsgOperando2(strP2, Base);
}
}
// Se ejecuta el comando / operacin
//
switch (tOp) {
case Binario:
AsgBase(TipoBase.Base2);
break;
case Decimal:
AsgBase(TipoBase.Base10);
break;
case Hexa:
209

VOLVER A MEN

AsgBase(TipoBase.Base16);
break;
case MemoriaActual:
System.out.println(C.Convertir(T.ObtValor(), ObtBase()));
break;
case BaseActual:
System.out.print(ObtBase());
System.out.print(: );
switch (ObtBase()) {
case Base2:
System.out.println({ 0, 1 });
break;
case Base10:
System.out.println({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
break;
case Base16:
System.out.println({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F });
break;
}
break;
case Suma:
case Resta:
case Multiplicacion:
case Division:
case Signo:
case Modulo:
case Conjuncion:
case Disyuncion:
case Exclusivo:
case Complemento:
case Izquierda:
case Derecha:
T.AsgValor(C.Ejecutar());
210

VOLVER A MEN

System.out.println(C.Convertir(T.ObtValor(), ObtBase()));
break;
case Salir:
Fin = true;
break;
default:
System.out.println(?\tMuestra la lista de opciones);
System.out.println(B\tCambia a modalidad de representacin base 2 o binaria);
System.out.println(D\tCambia a modalidad de representacin base 10 o decimal);
System.out.println(H\tCambia a modalidad de representacin base 16 o hexadecimal);
System.out.println(S\tFinalizar la ejecucin del programa);
System.out.println(T\tMuestra el valor actual de la memoria temporal);
System.out.println(V\tMuestra el estado actual de la base de representacin);
break;
}
} while (!Fin);
}
public static void main(String[] args) throws IOException {
Calculadora<Byte> Calc = new Calculadora<>(Calcular> , 1);
Calc.IntroducirComandos();
}
}

Finalmente, el mismo programa pero esta vez escrito en C# es como sigue a continuacin.
Ntese la similitud con Java aunque hay ciertas diferencias. Se tiene el archivo fuente
Calculadora.cs y el paquete se representa con el espacio de nombres ProyectoCalculadora.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.2
* Calculadora.cs
* Mauricio Paletta
*/
211

VOLVER A MEN

usingSystem;
usingSystem.IO;
namespaceProyectoCalculadora
{
publicclassCalculadora<TByte>
{
// Tipos posibles de operaciones
//
publicenumTipoOperacion{
Ayuda,Binario,Decimal,Hexa,Salir,MemoriaActual,BaseActual,
Suma,Resta,Multiplicacion,Division,Signo,Modulo,
Conjuncion,Disyuncion,Exclusivo,Complemento,Izquierda,Derecha
}
// Tipos posibles de base de representacin
//
publicenumTipoBase{
Base2,Base10,Base16
}
// Representa la memoria de la calculadora
//
publicclassMemoria{
privateTByte Valor;
publicTByteObtValor(){returnValor;}
publicvoidAsgValor(TByte V){Valor = V;}
}
// Representa un Comando
//
publicabstractclassComando{
protectedTipoOperacion Op;

publicComando(TipoOperacion O){Op = O;}


publicTipoOperacionObtOperacion(){returnOp;}
publicabstractTByteEjecutar();
}
// Representa una Operacin
//
publicclassOperacion : Comando{
privateTByte Operando1,Operando2;

privateTByteBinADec(String B){
intS =1;
if(B.Length ==8*NumBytes&&B[0]==1){
S =-1;
212

VOLVER A MEN

B = B.Substring(1);
}
return(TByte)Convert.ChangeType((S*System.Convert.ToInt64(B,2)),typeof(TByte));
}

privateStringDecABin(TByte D){
String S = System.Convert.ToString((long)Convert.ChangeType(D,typeof(long)),2);
if(S.Length>8*NumBytes)
S = S.Substring(S.Length-(8*NumBytes));
returnS;
}

privateTByteHexADec(String H){
longR = System.Convert.ToInt64(H,16);
intS =1,N =0;
charC;

if(H.Length ==2*NumBytes){
S =-1;
C = H[0];
if(C>=0&&C<=9)N = C-0;
if(C>=A&&C<=F)N = C-A+10;
if(C>=a&&C<=f)N = C-a+10;
if(N>=8){
R-=(N*(int)Math.Pow(16,2*NumBytes-1));
R+=((N-8)*(int)Math.Pow(16,2*NumBytes-1));
}
}
return(TByte)Convert.ChangeType(S*R,typeof(TByte));
}

privateStringDecAHex(TByte D){
String S = Convert.ToString((long)Convert.ChangeType(D,typeof(long)),16);
if(S.Length>2*NumBytes)
S = S.Substring(S.Length-(2*NumBytes));
returnS;
}

publicOperacion(TipoOperacion O):base(O){}
publicvoidAsgOperando1(TByte O){Operando1 = O;}
publicvoidAsgOperando1(String O,TipoBase B){
switch(B){
caseTipoBase.Base2:
Operando1 =BinADec(O);
break;
caseTipoBase.Base10:
Operando1 =(TByte)Convert.ChangeType(O,typeof(TByte));
break;
caseTipoBase.Base16:
Operando1 =HexADec(O);
break;
}
}
publicvoidAsgOperando2(TByte O){Operando2 = O;}
213

VOLVER A MEN

publicvoidAsgOperando2(String O,TipoBase B){


switch(B){
caseTipoBase.Base2:
Operando2 =BinADec(O);
break;
caseTipoBase.Base10:
Operando2 =(TByte)Convert.ChangeType(O,typeof(TByte));
break;
caseTipoBase.Base16:
Operando2 =HexADec(O);
break;
}
}

publicoverrideTByteEjecutar(){
longOper1 =0,Oper2 =0;

if(Operando1!=null)Oper1 =(long)Convert.ChangeType(Operando1,typeof(long));
if(Operando2!=null)Oper2 =(long)Convert.ChangeType(Operando2,typeof(long));

// Depende de la operacin y del nmero de bytes usados


//
switch(ObtOperacion()){
caseTipoOperacion.Suma:
return(TByte)Convert.ChangeType(Oper1+Oper2,typeof(TByte));
caseTipoOperacion.Resta:
return(TByte)Convert.ChangeType(Oper1-Oper2,typeof(TByte));
caseTipoOperacion.Multiplicacion:
return(TByte)Convert.ChangeType(Oper1*Oper2,typeof(TByte));
caseTipoOperacion.Division:
return(TByte)Convert.ChangeType(Oper1/Oper2,typeof(TByte));
caseTipoOperacion.Signo:
return(TByte)Convert.ChangeType(-Oper1,typeof(TByte));
caseTipoOperacion.Modulo:
return(TByte)Convert.ChangeType(Oper1%Oper2,typeof(TByte));
caseTipoOperacion.Conjuncion:
return(TByte)Convert.ChangeType(Oper1&Oper2,typeof(TByte));
caseTipoOperacion.Disyuncion:
return(TByte)Convert.ChangeType(Oper1|Oper2,typeof(TByte));
caseTipoOperacion.Exclusivo:
return(TByte)Convert.ChangeType(Oper1^Oper2,typeof(TByte));
caseTipoOperacion.Complemento:
return(TByte)Convert.ChangeType(~Oper1,typeof(TByte));
caseTipoOperacion.Izquierda:
return(TByte)Convert.ChangeType((int)Oper1<<(int)Oper2,typeof(TByte));
caseTipoOperacion.Derecha:
return(TByte)Convert.ChangeType((int)Oper1>>(int)Oper2,typeof(TByte));
}
returndefault(TByte);
}

publicStringEjecutar(TipoBase B){
TByte R =Ejecutar();

returnConvertir(R,B);
}
publicStringConvertir(TByte N,TipoBase B){
String S =;
switch(B){
214

VOLVER A MEN

caseTipoBase.Base2:
S =DecABin(N);
if(S.Length>8*NumBytes)S = S.Substring(S.Length-(8*NumBytes));
break;
caseTipoBase.Base10:
S = System.Convert.ToString((long)Convert.ChangeType(N,typeof(long)));
break;
caseTipoBase.Base16:
S =DecAHex(N);
if(S.Length>2*NumBytes)S = S.Substring(S.Length-(2*NumBytes),S.
Length-1);
break;
}
returnS;
}
}
// Atributos de Calculadora
//
privateMemoria T;
privateString Indicador;
privateTipoBase Base;
staticprotectedintNumBytes;
// Mtodos de la clase Calculadora
//
publicCalculadora(String I,intN){
Indicador = I;NumBytes = N;Base = TipoBase.Base10;
T =newMemoria();
}
publicvoidAsgNumBytes(){
TByte Aux =default(TByte);
byteunByte =0;
shortunShort =0;
intunInt =0;
longunLong =0;

if(unByte.Equals(Aux.GetType()))NumBytes =1;
else
if(unShort.Equals(Aux.GetType()))NumBytes =2;
else
if(unInt.Equals(Aux.GetType()))NumBytes =4;

else

if(unLong.Equals(Aux.GetType()))NumBytes =8;
T =newMemoria();
}
publicvoidAsgIndicador(String I){if(I!=)Indicador = I;}
publicvoidAsgBase(TipoBase B){Base = B;}
publicTByteObtMemoria(){returnT.ObtValor();}
publicTipoBaseObtBase(){returnBase;}
publicvoidIntroducirComandos(){
String S,strO,strP1,strP2;
TipoOperacion tOp;
boolFin =false;
intAux;
Operacion C;

215

VOLVER A MEN

// Ciclo de lectura y ejecucin de comandos


//
do{
// Primero se muestra el indicador
//
Console.Write(Indicador);

// Se lee una lnea de comando


//
S = Console.In.ReadLine();
S.Trim();
// Se descompone la lnea en cada una de las partes
// Lo primero que debe venir es el tipo de operacin
//
Aux = S.IndexOf( ,0);
if(Aux<0)
strO = S;
else
strO = S.Substring(0,Aux);

// Determinar cul es el operador


//
strP1 =;strP2 =;
if(strO ==?)tOp = TipoOperacion.Ayuda;
else
if(strO ==B)tOp = TipoOperacion.Binario;
else
if(strO ==D)tOp = TipoOperacion.Decimal;
else
if(strO ==H)tOp = TipoOperacion.Hexa;
else
if(strO ==S)tOp = TipoOperacion.Salir;
else
if(strO ==T)tOp = TipoOperacion.MemoriaActual;
else
if(strO ==V)tOp = TipoOperacion.BaseActual;
else
if(strO ==+)tOp = TipoOperacion.Suma;
else
if(strO ==-)tOp = TipoOperacion.Resta;
else
if(strO ==*)tOp = TipoOperacion.Multiplicacion;
else
if(strO ==/)tOp = TipoOperacion.Division;
else
if(strO ==%)tOp = TipoOperacion.Modulo;
else
if(strO ==&)tOp = TipoOperacion.Conjuncion;
else
if(strO ==|)tOp = TipoOperacion.Disyuncion;
else
if(strO ==!)tOp = TipoOperacion.Complemento;
else
if(strO ==<)tOp = TipoOperacion.Izquierda;
else
if(strO ==>)tOp = TipoOperacion.Derecha;
else{
// Debe ser un literal vlido
// Se asume una suma a 0
216

VOLVER A MEN

//
tOp = TipoOperacion.Suma;
strP1 = S;
strP2 =0;
}
// Verificar si estn los operandos
//
if(strP1 ==&&Aux>=0){
S = S.Substring(Aux+1);
S.Trim();
Aux = S.IndexOf( ,0);
if(Aux<0)
strP1 = S;
else
strP1 = S.Substring(0,Aux);

if(Aux>0){
S = S.Substring(Aux+1);
S.Trim();
if(S.Length>0){
strP2 = S;
// La operacin debe ser vlida para dos operandos
//
switch(tOp){
caseTipoOperacion.Binario:
caseTipoOperacion.Decimal:
caseTipoOperacion.Hexa:
caseTipoOperacion.MemoriaActual:
caseTipoOperacion.BaseActual:
caseTipoOperacion.Signo:
caseTipoOperacion.Complemento:
caseTipoOperacion.Salir:
tOp = TipoOperacion.Ayuda;
break;
}
}
else{
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if(tOp == TipoOperacion.Resta)tOp = TipoOperacion.Signo;
if(tOp!= TipoOperacion.Signo||tOp!= TipoOperacion.Complemento)
tOp = TipoOperacion.Ayuda;
}
}
else{
// Hay un slo operando; si la operacin requiere dos no puede ser
//
if(tOp == TipoOperacion.Resta)tOp = TipoOperacion.Signo;
if(tOp!= TipoOperacion.Signo&&tOp!= TipoOperacion.Complemento)
tOp = TipoOperacion.Ayuda;
}
}
else{
// Una operacin que requiere operandos y no hay operandos no puede ser
//
217

VOLVER A MEN

if(strP1 ==&&
(tOp == TipoOperacion.Suma||tOp == TipoOperacion.Resta||
tOp == TipoOperacion.Multiplicacion|| tOp == TipoOperacion.Division||
tOp == TipoOperacion.Signo||tOp == TipoOperacion.Modulo||
tOp == TipoOperacion.Conjuncion||tOp == TipoOperacion.Disyuncion||
tOp == TipoOperacion.Exclusivo|| tOp == TipoOperacion.Complemento||
tOp == TipoOperacion.Izquierda||tOp == TipoOperacion.Derecha))
tOp = TipoOperacion.Ayuda;
}

// Se instancia el objecto Comando / Operacin


//
C =newOperacion(tOp);
// Se agregan los operandos; pueden ser T
//
if(strP1!=){
if(strP1 ==T)strP1 = C.Convertir(T.ObtValor(),Base);
C.AsgOperando1(strP1,Base);
if(strP2!=){
if(strP2 ==T)strP2 = C.Convertir(T.ObtValor(),Base);
C.AsgOperando2(strP2,Base);
}
}
// Se ejecuta el comando / operacin
//
switch(tOp){
caseTipoOperacion.Binario:
AsgBase(TipoBase.Base2);
break;
caseTipoOperacion.Decimal:
AsgBase(TipoBase.Base10);
break;
caseTipoOperacion.Hexa:
AsgBase(TipoBase.Base16);
break;
caseTipoOperacion.MemoriaActual:
Console.WriteLine(C.Convertir(T.ObtValor(),ObtBase()));
break;
caseTipoOperacion.BaseActual:
Console.Write(ObtBase());
Console.Write(: );
switch(ObtBase()){
caseTipoBase.Base2:
Console.WriteLine({ 0, 1 });
break;
caseTipoBase.Base10:
Console.WriteLine({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
break;
caseTipoBase.Base16:
Console.WriteLine({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F });
break;
}
218

VOLVER A MEN

break;
caseTipoOperacion.Suma:
caseTipoOperacion.Resta:
caseTipoOperacion.Multiplicacion:
caseTipoOperacion.Division:
caseTipoOperacion.Signo:
caseTipoOperacion.Modulo:
caseTipoOperacion.Conjuncion:
caseTipoOperacion.Disyuncion:
caseTipoOperacion.Exclusivo:
caseTipoOperacion.Complemento:
caseTipoOperacion.Izquierda:
caseTipoOperacion.Derecha:
T.AsgValor(C.Ejecutar());
Console.WriteLine(C.Convertir(T.ObtValor(),ObtBase()));
break;
caseTipoOperacion.Salir:
Fin =true;
break;
default:
Console.WriteLine(?\tMuestra la lista de opciones);
Console.WriteLine(B\tCambia a modalidad de representacin base 2 o binaria);
Console.WriteLine(D\tCambia a modalidad de representacin base 10 o decimal);
Console.WriteLine(H\tCambia a modalidad de representacin base 16 o
hexadecimal);
Console.WriteLine(S\tFinalizar la ejecucin del programa);
Console.WriteLine(T\tMuestra el valor actual de la memoria temporal);
Console.WriteLine(V\tMuestra el estado actual de la base de representacin);
break;
}
}while(!Fin);
}
}

classProgram
{
publicstaticvoidMain(string[]args)
{
Calculadora<byte>Calc =newCalculadora<byte>(Calcular> ,1);
Calc.IntroducirComandos();
}
}
}

6.3 Simulacin de trfico de un solo carril y sentido


6.3.1 Enunciado
Se desea hacer una simulacin del modelo de trfico de un solo carril y de un solo sentido (de
derecha a izquierda) basado en el uso de un autmata celular (von Neumann, 1966). Se trata
de un trabajo inicialmente propuesto por (Cockshott et al, 1993).
Los autmatas celulares fueron introducidos en 1948 de forma independiente por John Von
Neumann y Stanislaw Ulam, ambos del Laboratorio Nacional de Los Alamos, para modelar la
219

VOLVER A MEN

reproduccin biolgica y el crecimiento de cristales respectivamente. Se trata de un conjunto


de autmatas o nodos o celdas organizados en un reticulado d-dimensional, en la cual la
dinmica ocurre sincrnicamente (todos los autmatas al mismo tiempo). Al ser este un sistema
dinmico, cambia de estado en base a una variable de tiempo discreta (un contador). Cada
celda cambia su estado en funcin a una regla local que depende de los estados de los nodos
o autmatas vecinos.
Formalmente hablando, se puede definir un autmata celular como un reticulado d-dimensional
con un autmata de estados finitos residente en cada celda o sitio del reticulado regular. La
regla de transicin de cada uno de los autmatas involucra los estados de los autmatas en
los sitios vecinos.
El algoritmo de ejecucin de la dinmica de un Autmata Celular (AC) es como se muestra a
continuacin.

InicializarAC( )
// Estado inicial del AC en t = 0
t 31
Repetir hasta (criterio terminacin)
Para todos los autmatas si del AC

v = ObtenerVecindad(si)

NuevoEdo = AplicarTransicin(v, si)
ActualizarAC( )
// El AC pasa de t a t+1 con los nuevos estados
t 4 t + 1
// Una nueva iteracin de tiempo
Estado Final del AC

Para el caso especfico de este problema y la simulacin del modelo de trfico, el reticulado
es de dimensin 1 y el alfabeto de los posibles valores discretos de cada autmata es binario,
donde el 0 representa la va sin vehculo y el 1 representa un vehculo circulando en la direccin
derecha a izquierda (se puede tambin abordar el problema en la otra direccin). Las reglas
de transicin del autmata celular entre un estado en tiempo t y el nuevo estado en tiempo
t+1 para este problema, toman en cuenta un vecino a la izquierda y un vecino a la derecha del
autmata al cual se le desea obtener su nuevo estado. Las reglas son como sigue:
Avance: * 0 1 1

01*0
Estable: * 0 0 0

220

11*1

VOLVER A MEN

Donde * representa cualquier cosa, 0 o 1 en este caso.


La primera regla de avance se puede leer como sigue: Si el valor actual del autmata (en tiempo
t) es una va libre (representado por 0) y el vecino de la derecha es un vehculo (representado
por 1), sin importar cul es el vecino de la izquierda, entonces el nuevo valor del autmata (en
tiempo t+1) ser 1 para representar que el vehculo que estaba a la derecha pasar a ocupar
esa posicin. Tanto el vecino izquierdo del autmata que est ms a la izquierda como el
vecino derecho del autmata que est ms a la derecha deben ser considerados como 0.
La segunda regla de avance se puede leer como sigue: Si el valor actual del autmata (en
tiempo t) es un vehculo (representado por 1) y el vecino de la derecha es un espacio libre
(representado por 0), sin importar cul es el vecino de la izquierda, entonces el nuevo valor del
autmata (en tiempo t+1) ser 0 para representar que el vehculo que estaba actualmente en
esa posicin ha avanzado hacia la izquierda.
La Figura 6.8 muestra un ejemplo de la dinmica de un autmata celular de cinco autmatas en
4 tiempos de ejecucin desde un estado inicial cualquiera (t = 0). Ntese la dinmica sincrnica
de cambio de estado de los autmatas (todos a la vez).

Figura 6.8. Ejemplo de una corrida del problema de simulacin de trfico.

El estado inicial del autmata celular se debe obtener de forma aleatoria basado en un porcentaje
de trfico o nmero de vehculos representados en ese estado inicial, donde 0% indica que la
va est completamente libre (todos los autmatas tienen el valor 0) y 100% indica que hay un
trfico pesado en la cual todas las celdas estn ocupadas por un vehculo (todos los autmatas
tienen el valor 1). Adems del nmero de autmatas que se desea tenga la simulacin, en
base a un mnimo y un mximo previamente establecidos, el porcentaje de trfico debe ser
tambin dado antes de iniciar la simulacin. La simulacin debe terminar (condicin de parada
de la dinmica del autmata celular) cuando se llega a un punto fijo, es decir, los estados
del autmata en tiempo t y t+1 son exactamente iguales (ntese que este mismo estado se
mantendr fijo a lo largo de tiempos sucesivos).
221

VOLVER A MEN

Se desea adems que cuente el nmero de vehculos que se representaron en el estado inicial
del autmata y el nmero de vehculos que, siguiendo su recorrido de derecha a izquierda,
salen por el extremo izquierdo del autmata celular. Al final de la simulacin se debe mostrar
un balance sobre la cantidad de vehculos que estaban inicialmente menos la cantidad de
vehculos que salieron. Si la simulacin y el conteo se hicieron correctamente entonces este
balance debe dar cero.

6.3.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): Luego de hacer una revisin del
texto del enunciado se tiene un mapa mental equivalente al que se muestra en la Figura 6.9.

Figura 6.9. Mapa mental relativo al problema de la simulacin de trfico.

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: simulacin,


autmata celular, autmata, reticulado, alfabeto, regla de transicin, trfico, punto fijo, vehculo.
Paso 3 (Identificar procesos / requerimientos): No se dice explcitamente en el enunciado sin
embargo est claro asumir que el simulador tiene un usuario que debe introducir los parmetros
iniciales de la simulacin e iniciar el proceso, as como tambin observar la ejecucin
correspondiente. Tampoco se indica explcitamente el mecanismo de interfaz a utilizar para lo
cual este criterio se deja abierto para la implementacin. Se tiene el diagrama de casos de uso
de la Figura 6.10.

Figura 6.10. Diagrama de casos de uso relativo al problema de la simulacin de trfico.

222

VOLVER A MEN

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Reticulado, alfabeto y regla de transicin son implementaciones o atributos del autmata
celular.
Trfico es un concepto vago y porcentaje de trfico es una condicin inicial del simulador.
Punto fijo es un estado del autmata celular que determina la condicin de parada de la
simulacin.
Una manera ms adecuada para identificar la simulacin es mediante el nombre Simulador Trfico.
El autmata forma parte del autmata celular.
El simulador depende de un autmata celular.
Ntese que las relaciones simulador es un caso particular de autmata celular y autmata
celular forma parte de simulador hubieran podido tambin tomarse en cuenta. Sin embargo, el
hacerlo mediante una dependencia y no mediante una generalizacin o composicin establece
que la misma simulacin se puede realizar sin problemas haciendo uso de cualquier otra tcnica
diferente a la de un autmata celular.
Se propone entonces como diagrama no detallado de clases para este problema el indicado en la
Figura 6.11.

Figura 6.11. Diagrama no detallado de clases relativo al problema de la simulacin de trfico.

6.3.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Se trata de un problema cuya implementacin
requiere de un programa sin interaccin con ningn otro elemento de software. Se puede
hacer una implementacin tanto en interfaz modo texto como modo grfico. En el caso de una
interfaz modo texto se puede buscar una representacin para el vehculo y otra para la va libre
como por ejemplo <- y __ respectivamente. La interfaz grfica se puede realizar con un par
de imgenes como las que se muestran en la Figura 6.8.
223

VOLVER A MEN

Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.11
resulta el diagrama de clases detallado de la Figura 6.12. Ntese el tipo de dato de los atributos
(a la derecha, despus de :); el tipo de dato de retorno de los mtodos expresados como un
estereotipo (encerrados entre << y >>); el uso de parmetros en los mtodos (encerrados
entre ( y )), si aplica.

Figura 6.12. Diagrama detallado de clases relativo al problema de la simulacin de trfico.

Paso 3 (Desarrollar los modelos de estado): Ntese que la clase Simulador Trfico no posee
ningn atributo por lo tanto no es susceptible de sufrir cambios de estado y, en consecuencia,
no tiene un diagrama de cambios de estado asociado. La Figura 6.13 muestra los diagramas
de estado de las otras dos clases presentes en el modelo.

Figura 6.13. Diagramas de estado relativos al problema de la simulacin de trfico.

Paso 4 (Elaborar los modelos de colaboracin): Los casos de uso Inicializar y Configurar
que aparecen en el diagrama de la Figura 6.10 se realizan en un mismo proceso (mtodo). En
este sentido, la Figura 6.14 muestra el diagrama de secuencias que permite probar la manera
en la cual el modelo conceptual satisface estos requerimientos. La lectura es como sigue: el
Usuario ejecuta el mensaje Iniciar() utilizando una instancia cualquiera de Simulador; lo
siguiente es Inicializar() el Autmata Celular el cual provoca una serie de llamadas del
mensaje AsgEstado(Edo) del arreglo de objetos CeldasAct de la clase Autmata; se sigue
224

VOLVER A MEN

con Mostrar() el Autmata Celular el cual requiere Mostrar() los Autmata de CeldasAct.
Se contina con la Transicion() del Autmata Celular cuya ejecucin requiere una serie de
llamadas al mensaje AsgEstado(Edo) de los objetos del arreglo CeldasAnt y luego de los
objetos del arreglo CeldasAct. Cada transicin se muestra al usuario (representado en el
diagrama con el paso 9).

Figura 6.14. Diagrama de secuencias relativo al problema de la simulacin de trfico.

Paso 5 (Identificar componentes del dominio): Asumiendo el uso de Java para la implementacin
de este caso prctico, el diagrama de componentes correspondiente al problema se presenta
en la Figura 6.15. Se tienen dos paquetes Automata y SimulacionTrafico, cada uno de
los cuales con un archivo cdigo fuente de java AutomataCelular.java y Simulador.java
respectivamente.

Figura 6.15. Diagrama de componentes relativo al problema de la simulacin de trfico.

6.3.4 Programacin
La implementacin de este problema haciendo uso de C++ es como sigue. Se realizaron
dos archivos de cdigo fuente, AutomataCelular.cpp y SimulacionTrafico.cpp con sus
respectivos archivos encabezados correspondientes. A continuacin el contenido del archivo
225

VOLVER A MEN

AutomataCelular .hpp.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.3
* AutomataCelular.hpp
* Mauricio Paletta
*/
#ifndef AUTOMATACELULAR_HPP
#define AUTOMATACELULAR_HPP
// Representa un autmata alfabeta binario
//
class Automata {
private:
bool Estado;
public:
Automata() { Estado = false; }
bool ObtEstado() { return Estado; }
void AsgEstado(bool Edo) { Estado = Edo; }
void Mostrar();
};
// Representa un autmata celular
//
class AutomataCelular {
private:
Automata *CeldasAct, *CeldasAnt;
int NumAutomatas;
bool PuntoFijo;

226

VOLVER A MEN

public:
AutomataCelular(int N);
~AutomataCelular();
void Inicializar();
void Inicializar(double P);
bool HayPuntoFijo() { return PuntoFijo; }
void Mostrar();
bool Transicion();
};
#endif /* AUTOMATACELULAR_HPP */

El archivo AutomataCelular.cpp es como sigue.


/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.3
* AutomataCelular.cpp
* Mauricio Paletta
*/
#include <iostream>
#include Random.hpp
#include AutomataCelular.hpp
// Mtodos de la clase Automata
//
void Automata::Mostrar() {
std::cout << (Estado ? <- : __);
}
// Mtodos de la clase AutomataCelular
//
227

VOLVER A MEN

AutomataCelular::AutomataCelular(int N) {
if (N <= 0) N = 1;
CeldasAct = new Automata[N];
CeldasAnt = new Automata[N];
NumAutomatas = N;
PuntoFijo = false;
}
AutomataCelular::~AutomataCelular() {
if (CeldasAct) delete[] CeldasAct;
if (CeldasAnt) delete[] CeldasAnt;
}
void AutomataCelular::Inicializar() {
// Inicializacin aleatoria distribuida uniformemente
//
Inicializar(50.0);
}
void AutomataCelular::Inicializar(double P) {
// Inicializacin aleatoria segn una distribucin basada en la
// probabilidad P de estados activados
//
Random R(time(NULL));
for (int c=0; c < NumAutomatas; c++) {
CeldasAct[c].AsgEstado(R.uniform() * 100.0 <= P);
}
PuntoFijo = false;
}
// Se muestra el autmata celular en base a cada autmata
//
228

VOLVER A MEN

void AutomataCelular::Mostrar() {
for (int c=0; c < NumAutomatas; c++) {
CeldasAct[c].Mostrar();
}
std::cout << std::endl;
}
bool AutomataCelular::Transicion() {
bool Ret = false, Val;
bool VecIzq, VecDer;
int Cambios = 0;
for (int c=0; c < NumAutomatas; c++) {
Val = CeldasAct[c].ObtEstado();
if (!c && Val) Ret = true;
VecIzq = (c > 0 ? CeldasAct[c-1].ObtEstado() : false);
VecDer = (c < NumAutomatas - 1 ? CeldasAct[c+1].ObtEstado() : false);
// Reglas de transicin; dependen del problema
//
if (!Val && VecDer)
Val = true;
else
if (!VecIzq && Val)
Val = false;
CeldasAnt[c].AsgEstado(Val);
}
// Se actualiza el estado del autmata celular en base a las transiciones
// Hay que simular la transicin sincrnica de cada uno de los autmatas
//
for (int c=0; c < NumAutomatas; c++) {
if (CeldasAct[c].ObtEstado() != CeldasAnt[c].ObtEstado()) {
229

VOLVER A MEN

CeldasAct[c].AsgEstado(CeldasAnt[c].ObtEstado());
Cambios++;
}
}
// Al menos un cambio determina que no hay punto fijo
//
PuntoFijo = Cambios == 0;
return Ret;
}

El contenido de los otros dos archivos se muestra a continuacin.


/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.3
* SimulacionTrafico.hpp
* Mauricio Paletta
*/
#ifndef SIMULACIONTRAFICO_HPP
#define SIMULACIONTRAFICO_HPP
// Representa el simulador de trfico
//
class Simulador {
private:
int MinCeldas, MaxCeldas;
public:
Simulador(int Min, int Max);
void Iniciar();
};
#endif /* SIMULACIONTRAFICO_HPP */
230

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.3
* SimulacionTrafico.cpp
* Mauricio Paletta
*/
#include <cstdlib>
#include <iostream>
#include SimulacionTrafico.hpp
#include AutomataCelular.hpp
using namespace std;
// Mtodos de la clase Simulador
//
Simulador::Simulador(int Min, int Max) {
MinCeldas = Min >= 0 ? Min : 0;
MaxCeldas = Max > Min ? Max : Max + 1;
}
void Simulador::Iniciar() {
AutomataCelular *AC;
int N, NumVehiculos, t = 0;
double P;
char O;
cout << Modelado y programacin Orientado a Objetos: Un enfoque prctico.\n;
cout << Simulador de trfico un canal de circulacin, un sentido.\n << endl;
do {
cout << Nmero de autmatas ( << MinCeldas << y << MaxCeldas << ): ;
cin >> N;
} while (N < MinCeldas || N > MaxCeldas);
231

VOLVER A MEN

do {
cout << Indique porcentaje de trfico (entre 0 y 100): ;
cin >> P;
} while (P < 0.0 || P > 100.0);
// Se prepara el autmata celular para la simulacin
//
AC = new AutomataCelular(N);
do {
// Inicializar el autmata celular
//
AC->Inicializar(P);
NumVehiculos = 0;
// Mostrar el estado actual y hacer la transicin mientras no haya un punto fijo
//
while (!AC->HayPuntoFijo()) {
cout << t = << t++ << endl;
AC->Mostrar();
if (AC->Transicion())
NumVehiculos++;
cout << endl;
}
// Si se quiere correr de nuevo la simulacin
//
cout << endl << Se ha llegado a un punto fijo.\n ;
cout << Nmero de vehculos que salieron: << NumVehiculos << .\n ;
cout << endl << Desea ejecutar nuevamente (S/N)? ;
cin >> O;
} while (O == S || O == s);

232

VOLVER A MEN

// Recuperar el espacio del autmata celular


//
delete AC;
}
// Programa principal
//
int main(int argc, char** argv) {
// Se instancia un objeto de la clase Simulador
// Se asumen unos mnimo y mximo especficos
//
Simulador S(1, 40);
S.Iniciar();
return 0;
}

En relacin a la implementacin en Java (correspondiente al diagrama de componentes de la


Figura 6.15), se tienen los archivos AutomataCelular.java y Simulador.java cuyos contenidos
se muestran a continuacin. La clase AutomataCelular est asociada al paquete identificado
como Automata y la clase Simulador est asociada al paquete SimulacionTrafico.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.3
* AutomataCelular.java
* Mauricio Paletta
*/
package Automata;
import java.util.Random;
// Representa un autmata alfabeta binario
//
class Automata {
private boolean Estado;
public Automata() { Estado = false; }
public boolean ObtEstado() { return Estado; }
public void AsgEstado(boolean Edo) { Estado = Edo; }
public void Mostrar() {
System.out.print(Estado ? <- : __);
233

VOLVER A MEN

// Representa un autmata celular


//
public class AutomataCelular {
private Automata[] CeldasAct, CeldasAnt;
private int NumAutomatas;
private boolean PuntoFijo;
public AutomataCelular(int N) {
if (N <= 0) N = 1;
NumAutomatas = N;
CeldasAct = new Automata[NumAutomatas];
CeldasAnt = new Automata[NumAutomatas];
for (int c=0; c < NumAutomatas; c++) {
CeldasAct[c] = new Automata();
CeldasAnt[c] = new Automata();
}
PuntoFijo = false;
}
public void Inicializar() {
// Inicializacin aleatoria distribuida uniformemente
//
Inicializar(50.0);
}
public void Inicializar(double P) {
// Inicializacin aleatoria segn una distribucin basada en la
// probabilidad P de estados activados
//
Random R = new Random();

for (int c=0; c < NumAutomatas; c++) {


CeldasAct[c].AsgEstado(R.nextFloat() * 100.0 <= P);
}
PuntoFijo = false;

public boolean HayPuntoFijo() { return PuntoFijo; }


public void Mostrar() {
for (int c=0; c < NumAutomatas; c++) {
CeldasAct[c].Mostrar();
}
System.out.println();
}
public boolean Transicion() {
boolean Ret = false, Val;
boolean VecIzq, VecDer;
int Cambios = 0;
for (int c=0; c < NumAutomatas; c++) {
Val = CeldasAct[c].ObtEstado();
if (c == 0 && Val) Ret = true;
VecIzq = (c > 0 ? CeldasAct[c-1].ObtEstado() : false);
VecDer = (c < NumAutomatas - 1 ? CeldasAct[c+1].ObtEstado() : false);
234

VOLVER A MEN

// Reglas de transicin; dependen del problema


//
if (!Val && VecDer)
Val = true;
else
if (!VecIzq && Val)
Val = false;
}

CeldasAnt[c].AsgEstado(Val);

// Se actualiza el estado del autmata celular en base a las transiciones


// Hay que simular la transicin sincrnica de cada uno de los autmatas
//
for (int c=0; c < NumAutomatas; c++) {
if (CeldasAct[c].ObtEstado() != CeldasAnt[c].ObtEstado()) {
CeldasAct[c].AsgEstado(CeldasAnt[c].ObtEstado());
Cambios++;
}
}

// Al menos un cambio determina que no hay punto fijo


//
PuntoFijo = Cambios == 0;
return Ret;

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.3
* Simulador.java
* Mauricio Paletta
*/
package SimulacionTrafico;
import Automata.AutomataCelular;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Simulador {
private int MinCeldas, MaxCeldas;
public Simulador(int Min, int Max) {
MinCeldas = Min >= 0 ? Min : 0;
MaxCeldas = Max > Min ? Max : Max + 1;
}
public void Iniciar() throws IOException {
AutomataCelular AC;
int N, NumVehiculos, t = 0;
double P;
char O;
InputStreamReader Entrada = new InputStreamReader(System.in);
BufferedReader Buffer = new BufferedReader(Entrada);
String Lectura;
235

VOLVER A MEN

System.out.println(Modelado y programacin Orientado a Objetos: Un enfoque



prctico.);
System.out.println(Simulador de trfico un canal de circulacin, un sentido.\n);
do {
System.out.printf(Indique nmero de autmatas (entre %d y %d): ,
MinCeldas, MaxCeldas);
Lectura = Buffer.readLine();
N = Integer.parseInt(Lectura);
} while (N < MinCeldas || N > MaxCeldas);
do {
System.out.println(Indique porcentaje de trfico (entre 0 y 100): );
Lectura = Buffer.readLine();
P = Integer.parseInt(Lectura);
} while (P < 0 || P > 100);
// Se prepara el autmata celular para la simulacin
//
AC = new AutomataCelular(N);
do {
// Inicializar el autmata celular
//
AC.Inicializar(P);
NumVehiculos = 0;
// Mostrar el estado actual y hacer la transicin mientras no haya un punto fijo
//
while (!AC.HayPuntoFijo()) {
System.out.printf(t = %d\n, t++);
AC.Mostrar();
if (AC.Transicion())
NumVehiculos++;
System.out.println();
}
// Si se quiere correr de nuevo la simulacin
//
System.out.println(\nSe ha llegado a un punto fijo.) ;
System.out.printf(Nmero de vehculos que salieron: %d.\n, NumVehiculos);
System.out.print(Desea ejecutar nuevamente (S/N)? );
Lectura = Buffer.readLine();
O = (Lectura.length() > 0 ? Lectura.charAt(0) : N);
}

} while (O == S || O == s);

public static void main(String[] args) {


try {
// Se instancia un objeto de la clase Simulador
// Se asumen unos mnimo y mximo especficos
//
Simulador S = new Simulador(1, 40);

}
236

S.Iniciar();
} catch (IOException ex) {
Logger.getLogger(Simulador.class.getName()).log(Level.SEVERE, null, ex);
}

VOLVER A MEN

El contenido de los archivos AutomataCelular.cs y Simulador.cs correspondiente al programa escrito


en C# se muestra a continuacin.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.3
* AutomataCelular.cs
* Mauricio Paletta
*/
using System;
namespace Automata
{
// Representa un autmata alfabeta binario
//
class Automata
{
private bool Estado;

public Automata() { Estado = false; }


public bool ObtEstado() { return Estado; }
public void AsgEstado(bool Edo) { Estado = Edo; }
public void Mostrar()
{
System.Console.Out.Write(Estado ? <- : __);
}

// Representa un autmata celular


//
public class AutomataCelular
{
private Automata[] CeldasAct, CeldasAnt;
private int NumAutomatas;
private bool PuntoFijo;
public AutomataCelular(int N)
{
if (N <= 0) N = 1;
NumAutomatas = N;
CeldasAct = new Automata[NumAutomatas];
CeldasAnt = new Automata[NumAutomatas];
for (int c=0; c < NumAutomatas; c++)
{
CeldasAct[c] = new Automata();
CeldasAnt[c] = new Automata();
}
PuntoFijo = false;
}
public void Inicializar()
{
// Inicializacin aleatoria distribuida uniformemente
//
Inicializar(50.0);
}
public void Inicializar(double P)
{
// Inicializacin aleatoria segn una distribucin basada en la
// probabilidad P de estados activados
237

VOLVER A MEN

//
Random R = new Random();

for (int c=0; c < NumAutomatas; c++)


CeldasAct[c].AsgEstado(R.NextDouble() * 100.0 <= P);
PuntoFijo = false;

public bool HayPuntoFijo() { return PuntoFijo; }


public void Mostrar()
{
for (int c=0; c < NumAutomatas; c++)
CeldasAct[c].Mostrar();
Console.WriteLine();
}
public bool Transicion()
{
bool Ret = false, Val;
bool VecIzq, VecDer;
int Cambios = 0;
for (int c=0; c < NumAutomatas; c++)
{
Val = CeldasAct[c].ObtEstado();
if (c == 0 && Val) Ret = true;
VecIzq = (c > 0 ? CeldasAct[c-1].ObtEstado() : false);
VecDer = (c < NumAutomatas - 1 ? CeldasAct[c+1].ObtEstado() : false);
// Reglas de transicin; dependen del problema
//
if (!Val && VecDer)
Val = true;
else
if (!VecIzq && Val)
Val = false;
}

CeldasAnt[c].AsgEstado(Val);

// Se actualiza el estado del autmata celular en base a las transiciones


// Hay que simular la transicin sincrnica de cada uno de los autmatas
//
for (int c=0; c < NumAutomatas; c++)
{
if (CeldasAct[c].ObtEstado() != CeldasAnt[c].ObtEstado())
{
CeldasAct[c].AsgEstado(CeldasAnt[c].ObtEstado());
Cambios++;
}
}

}
238

// Al menos un cambio determina que no hay punto fijo


//
PuntoFijo = Cambios == 0;
return Ret;

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.3
* Simulador.cs
* Mauricio Paletta
*/
using System;
using Automata;
namespace SimulacionTrafico
{
public class Simulador
{
private int MinCeldas, MaxCeldas;
public Simulador(int Min, int Max)
{
MinCeldas = Min >= 0 ? Min : 0;
MaxCeldas = Max > Min ? Max : Max + 1;
}
public void Iniciar()
{
AutomataCelular AC;
int N, NumVehiculos, t = 0;
double P;
char O;
string Lectura;

Console.WriteLine(Modelado y programacin Orientado a Objetos: Un enfoque


prctico.);
Console.WriteLine(Simulador de trfico un canal de circulacin, un sentido.\n);
do
{
Console.Write(Indique nmero de autmatas (entre {0} y {1}): ,
MinCeldas, MaxCeldas);
Lectura = Console.ReadLine();
N = Convert.ToInt32(Lectura);
} while (N < MinCeldas || N > MaxCeldas);
do
{
Console.Write(Indique porcentaje de trfico (entre 0 y 100): );
Lectura = Console.ReadLine();
P = Convert.ToInt32(Lectura);
} while (P < 0 || P > 100);
// Se prepara el autmata celular para la simulacin
//
AC = new AutomataCelular(N);
do
{
// Inicializar el autmata celular
//
AC.Inicializar(P);
NumVehiculos = 0;
// Mostrar el estado actual y hacer la transicin mientras no haya un punto fijo
//
while (!AC.HayPuntoFijo())
{

239

VOLVER A MEN

Console.WriteLine(t = {0}\n, t++);


AC.Mostrar();
if (AC.Transicion())
NumVehiculos++;
Console.WriteLine();

// Si se quiere correr de nuevo la simulacin


//
Console.WriteLine(\nSe ha llegado a un punto fijo.);
Console.WriteLine(Nmero de vehculos que salieron: {0}., NumVehiculos);
Console.Write(Desea ejecutar nuevamente (S/N)? );
Lectura = Console.ReadLine();
O = Lectura.Length > 0 ? Lectura[0] : N;
} while (O == S || O == s);

public static void Main(string[] args)


{
// Se instancia un objeto de la clase Simulador
// Se asumen unos mnimo y mximo especficos
//
Simulador S = new Simulador(1, 40);

S.Iniciar();

6.4 Manejo de conjuntos de enteros


6.4.1 Enunciado
Se desea implementar un ambiente para el manejo de conjuntos de nmeros enteros positivos.
Cada conjunto ser identificado con una letra en mayscula (A, B, C, D, etc.). El ambiente
deber ser capaz de mantener un nmero no definido de conjuntos en cualquier momento. Al
iniciar la ejecucin no habr conjuntos definidos. El ambiente debe ser capaz de procesar las
siguientes opciones:
Crear y agregar un nuevo conjunto. Este nuevo conjunto estar vaco y se deber introducir
un identificador vlido para l (letra en mayscula no utilizada en algn otro conjunto).
Destruir y suprimir un conjunto del ambiente luego de leer su identificador.
Agregar un elemento a un conjunto. Para ello se debe dar el nmero entero positivo a
agregar y el identificador del conjunto donde se desea agregar el elemento. No es posible
tener nmeros repetidos en un mismo conjunto.
Suprimir un elemento de un conjunto. Para ello se debe dar el nmero entero positivo a
eliminar y el identificador del conjunto donde se desea quitar el elemento.
240

VOLVER A MEN

Indicar si un elemento pertenece o no a un conjunto. Para ello se debe dar el nmero entero positivo a buscar y el identificador del conjunto donde se desea hacer la bsqueda.
Realizar las operaciones de Unin, Interseccin y Diferencia de dos conjuntos existentes
del ambiente, guardando el resultado en un tercer conjunto existente en el ambiente.
Se debe indicar el identificador de los tres conjuntos involucrados en la operacin. En el
conjunto que recibe el resultado se perder cualquier informacin previa que ste pueda
contener.
Determinar si un conjunto est o no vaco.
Mostrar la informacin o contenido de un conjunto del ambiente luego de dar su identificador.
Los elementos deben aparecer en orden usando su valor como clave de ordenamiento.
Mostrar la informacin que se encuentra actualmente en el ambiente, indicando todos
sus conjuntos y los contenidos de stos. La lista de conjuntos se debe mostrar de forma
ordenada usando el identificador de los conjuntos como clave de ordenamiento. Los
elementos de cada conjunto deben aparecer de forma ordenada usando su valor como
clave de ordenamiento.
Guardar el estado o informacin actual del ambiente en un archivo cuyo nombre debe ser
dado. Si el archivo ya existe se deber advertir al usuario si desea o no sustituirlo. Luego
de la operacin el ambiente seguir trabajando normalmente.
Recuperar de un archivo la informacin que debe ser usada en la operacin actual del
ambiente. Cualquier informacin actualmente existente en el ambiente se perder y ser
reemplazada por lo que contenga el archivo.
Ejecutar archivo de comandos. En esta opcin el programa deber, a partir de un archivo
texto, leer e interpretar una serie de comandos de los descritos anteriormente segn la
sintaxis que se presenta en la Tabla 6.3 de manera tal de realizar de forma automtica
estas acciones. Como parte de la sintaxis, los corchetes angulados < y > indican que
en ese lugar debe ir algo vlido segn lo descrito que est encerrado entre ellos. Si por
alguna razn no se puede ejecutar alguno de los comandos, se pasa al siguiente hasta
completar el contenido del archivo.
Finalizar la ejecucin actual del ambiente.

241

VOLVER A MEN

Tabla 6.3. Operaciones a soportar en el problema del manejo de conjuntos.


Comando

Descripcin

N <C>

Agregar al ambiente un conjunto de identificador <C>

C <C>

Suprimir del ambiente el conjunto de identificador <C>

A <N> <C>

Agregar el elemento <N> al conjunto <C> que est actualmente en el


ambiente

D <N> <C>

Suprimir el elemento <N> del conjunto <C> que est actualmente en el


ambiente

+ <C1> <C2> <C3>

Calcular y guardar en <C3> la unin de <C1> con <C2>

* <C1> <C2> <C3>

Calcular y guardar en <C3> la interseccin de <C1> con <C2>

- <C1> <C2> <C3>

Calcular y guardar en <C3> la diferencia entre <C1> y <C2>

S <A>

Guardar el estado del ambiente en el archivo de nombre <A>

L <A>

Cargar o recuperar del archivo de nombre <A> lo que debe estar en el


ambiente

Adicionalmente, se debe guardar un archivo texto de eventos Logger que registre en detalle
(operacin ms parmetros) todas las acciones que el usuario del programa ha realizado desde
el inicio hasta el cierre de una cesin de trabajo. Cada evento ocupar una lnea diferente del
archivo y debe de ir acompaado de la fecha y hora de ocurrencia. El nombre de este archivo
puede ser fijo (Logger.Txt por ejemplo). El formato de cada lnea puede ser como sigue:
<Fecha Actual> <Hora Actual>: <Texto descriptivo del evento>

6.4.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): La Figura 6.16 presenta el mapa
mental que resulta luego de hacer una revisin del texto del enunciado del problema.

Figura 6.16. Mapa mental relativo al problema de la simulacin de trfico.

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Conjunto, nmero,
ambiente, letra, identificador, unin, interseccin, diferencia, resultado, informacin, contenido,
clave, archivo, comando, evento, logger y formato.
Paso 3 (Identificar procesos / requerimientos): Al igual que ocurre con los dos problemas
anteriores, hay un usuario como actor del ambiente para el manejo de conjuntos de nmeros
242

VOLVER A MEN

enteros. Adicionalmente y tal como lo indica el enunciado del problema, los comandos pueden
venir tanto del usuario como de un archivo. Por otro lado, se tiene tambin la generacin del
archivo de datos para guardar la informacin actual del entorno y la posterior recuperacin de
la informacin de estos archivos de datos. Finalmente, se tiene la generacin del archivo de
eventos. Al igual que el usuario, todos estos archivos deben ser representados como actores
externos del ambiente.

Figura 6.17. Diagrama de casos de uso relativo al problema del ambiente para el manejo de conjuntos.

La Figura 6.17 muestra un diagrama de casos de uso apropiado para representar lo que se
desea resolver en este problema. Ntese la presencia de los 4 actores antes mencionados:
usuario, archivo de comandos, archivo de data y archivo de eventos. Tambin ntese el sentido
correcto de flujo de actuacin entre estos actores y los casos de uso correspondientes. La
interaccin con el ambiente se centraliza en un nico caso de uso para Introducir comando y
Mostrar resultado. Cada uno de los comandos expecficos extiende el caso de uso general.
Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se
tienen los siguientes observables:
Nmero, conjunto, evento, comando y archivo son implementaciones.
Letra e identificador son redundantes y adems atributo de conjunto.
Unin, interseccin y diferencia son operaciones.
Informacin, contenido y formato son conceptos vagos.
Resultado y clave son irrelevantes.
Conjunto de enteros es un caso articular de conjunto.
243

VOLVER A MEN

Archivo de eventos (logger) es un caso particular de archivo.


Archivo de eventos forma parte del ambiente.
Se supone la existencia de clases predefinidas para el manejo de conjuntos y archivos genricos.
Este situacin de utilizar clases predefinidas como las antes mencionadas representa uno de
los aspectos ms importantes de la OO relativo a la reusabilidad de elementos.
En consecuencia se proponen las siguientes clases: ambiente para el manejo de conjuntos o
manejador de conjuntos, conjunto de enteros y archivo de eventos. Se tiene el diagrama no
detallado de clases que se muestra en la Figura 6.18. La relacin Conjunto forma parte de
Manejador conjuntos se debe a la necesidad de tener la estructura para albergar todos los
conjuntos de enteros que se vayan agregando al manejador.

Figura 6.18. Diagrama no detallado de clases relativo al problema del ambiente para el manejo de conjuntos.

6.4.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Se trata de un problema cuya implementacin
requiere de un programa que posee interaccin con tres tipos de archivos diferentes: 1) un
archivo de comandos que es modo texto; 2) un archivo de data que puede ser tanto texto como
binario pero preferiblemente binario y 3) un archivo de eventos que es modo texto. Tanto para
el archivo de comandos como para el archivo de eventos se tiene un formato especfico para
leer / escribir la informacin que contienen.
Por otro lado, no se dan detalles especficos de cmo manejar la interfaz. Lo mejor en este
caso es tener una interfaz modo texto con interpretacin de comandos segn lo planteado en
el enunciado del problema que se describe en la Seccin 6.4.1.
Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.18
resulta el diagrama de clases detallado de la Figura 6.19. Ntese el tipo de dato de los atributos
(a la derecha, despus de :); el tipo de dato de retorno de los mtodos expresados como un
estereotipo (encerrados entre << y >>); el uso de parmetros en los mtodos (encerrados
entre ( y )), si aplica.
244

VOLVER A MEN

Figura 6.19. Diagrama detallado de clases relativo al problema del ambiente para el manejo de conjuntos.

Paso 3 (Desarrollar los modelos de estado): Ntese que la clase Archivo Eventos no posee
ningn atributo, los cambios de estados relativos a esta clase estn asociados a la clase base
Archivo y, por ser sta una implementacin, Archivo Eventos no es susceptible de sufrir
cambios de estado. En consecuencia esta clase no tiene un diagrama de cambios de estado
asociado. Algo parecido ocurre con Conjunto Enteros en la cual su nico atributo recibe un
valor inicial en tiempo de construccin del objeto. El resto de los estados estn asociados a
la clase base Conjunto. En este sentido, la Figura 6.20 muestra los diagramas de estado
presentes en el modelo de este problema.

Figura 6.20. Diagramas de estado relativos al problema del ambiente para el manejo de conjuntos.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.21 muestra el diagrama de


colaboracin relativo a este problema y basado en el diagrama de casos de uso de la Figura
6.17 y el diagrama de clases de la Figura 6.19. El orden en el cual se ejecutan los eventos
no est acorde a una secuencia de operacin ya que esto depende de los comandos que se
originen, ya sea tanto del usuario como del archivo de comandos. Ntese la presencia de todos
los actores previamente identificados en el diagrama de casos de uso.
245

VOLVER A MEN

Figura 6.21. Diagrama de colaboracin relativo al problema del ambiente para el manejo de conjuntos.

Paso 5 (Identificar componentes del dominio): Asumiendo el uso de C# para la


implementacin de este caso prctico, el diagrama de componentes correspondiente
al problema se presenta en la Figura 6.22. Se tiene el paquete ManejoConjuntos
al cual estn asociados todos los elementos relativos a este problema definidos en
los archivos de cdigo fuente ManejadorConjutos.Cs y ArchivoEventos.Cs. La compilacin
de estos archivos produce el ejecutable ManejadorConjutos.Exe. Ntese que la ejecucin
genera un archivo texto con los eventos.

Figura 6.22. Diagrama de componentes relativo al problema del ambiente para el manejo de conjuntos.

246

VOLVER A MEN

6.4.4 Programacin
A continuacin el cdigo escrito en C++ relativo al problema. Se tienen los archivos
fuente ManejoConjuntos.hpp y el correspondiente ManejoConjuntos.cpp,
ArchivoEventos.hpp y el correspondiente ArchivoEventos.cpp y el programa
principal en main.cpp. Ntese que la implementacin hace uso de plantillas para la
generalizacin del tipo de dato de los elementos del conjunto.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.4
* ManejoConjuntos.hpp
* Mauricio Paletta
*/
#ifndef MANEJOCONJUNTOS_HPP
#define MANEJOCONJUNTOS_HPP
#include <set>
#include <string>
#include ArchivoEventos.hpp
#include TrimString.hpp
using namespace std;
// Representa un conjunto de enteros
//
class ConjuntoEnteros : public set<int> {
private:
char Identificador;
public:
ConjuntoEnteros() : set<int>() {
247

VOLVER A MEN

Identificador = *;
};
ConjuntoEnteros(char Id) : set<int>() {
if (Id < A || Id > Z) Id = *;
Identificador = Id;
};
char ObtId() const { return Identificador; }
operator char() { return ObtId(); }
bool Pertenece(int E) { return this->find(E) != this->end(); }
bool Agregar(int E);
ConjuntoEnteros operator <<(int E) {
Agregar(E); return *this;
}
bool Suprimir(int E) {
if (!Pertenece(E)) return false;
this->erase(E);
return true;
}
ConjuntoEnteros operator >>(int E) {
Suprimir(E); return *this;
}
void MostrarOrdenado();
void GuardarArchivo(ofstream *MiArchivo);
};
// Representa el manejador de conjunto de enteros
//
struct CompConjuntoEnteros {
bool operator() (const ConjuntoEnteros& C1, const ConjuntoEnteros& C2) const {
return C1.ObtId() < C2.ObtId();
}
};

248

VOLVER A MEN

class ManejadorConjuntos {
private:
set<ConjuntoEnteros, CompConjuntoEnteros> Conjuntos;
ArchivoEventos *Logger;
public:
ManejadorConjuntos() {
Logger = new ArchivoEventos();
Logger->EscribirEvento(Inicio de sesin);
}
~ManejadorConjuntos() {
Logger->EscribirEvento(Fin de sesin);
delete Logger;
}
bool operator() (ConjuntoEnteros C1, ConjuntoEnteros C2) const {
return C1.ObtId() < C2.ObtId();
}
ConjuntoEnteros *BuscarConjunto(char Id);
bool AgregarConjunto(char Id);
ManejadorConjuntos operator <<(char Id) {
AgregarConjunto(Id); return *this;
}
bool SuprimirConjunto(char Id);
ManejadorConjuntos operator >>(char Id) {
SuprimirConjunto(Id); return *this;
}
bool AgregarElemento(int E, char Id);
bool SuprimirElemento(int E, char Id);
bool PerteneceElemento(int E, char Id);
bool HacerUnion(char Id1, char Id2, char Id3);
bool HacerInterseccion(char Id1, char Id2, char Id3);
bool HacerDiferencia(char Id1, char Id2, char Id3);
bool EstaVacio(char Id);
249

VOLVER A MEN

void MostrarOrdenado();
bool Guardar(string Nombre);
bool Cargar(string Nombre);
bool EjecutarComando(TrimString Comando);
bool EjecutarComandos(string Nombre);
};
#endif /* MANEJOCONJUNTOS_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.4
* ManejoConjuntos.cpp
* Mauricio Paletta
*/
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include ManejoConjuntos.hpp
// Mtodos de la clase ConjuntoEnteros
//
bool ConjuntoEnteros::Agregar(int E) {
set<int>::iterator It = this->find(E);
if (It != this->end()) return false;
this->insert(E);
return true;
}
void ConjuntoEnteros::MostrarOrdenado() {
250

VOLVER A MEN

cout << Identificador << : { ;


for (set<int>::iterator It = this->begin(); It != this->end(); It++)
cout << *It << ;
cout << } << endl;
}
void ConjuntoEnteros::GuardarArchivo(ofstream *MiArchivo) {
int *V, S = this->size(), i = 0;
if (!MiArchivo || !MiArchivo->is_open()) return;
MiArchivo->write(&Identificador, 1);
MiArchivo->write((char *)&S, sizeof(int));
if (S) {
V = new int[S];
if (V) {
for (set<int>::iterator It = this->begin(); It != this->end() && i < S; It++)
V[i++] = *It;
MiArchivo->write((char *)V, S * sizeof(int));
delete[] V;
}
}
}
// Mtodos de la clase ManejadorConjuntos
//
ConjuntoEnteros *ManejadorConjuntos::BuscarConjunto(char Id) {
for (set<ConjuntoEnteros>::iterator sIt = Conjuntos.begin(); sIt != Conjuntos.end(); sIt++) {
ConjuntoEnteros *C = (ConjuntoEnteros *)(&(*sIt));
if (C->ObtId() == Id) return C;
}
return NULL;
}
251

VOLVER A MEN

bool ManejadorConjuntos::AgregarConjunto(char Id) {


// Se verifica que el Id es vlido
//
if (Id < A || Id > Z) return false;
// Se verifica que no hay ningn otro conjunto con el mismo Id
//
ConjuntoEnteros *C = BuscarConjunto(Id);
if (C != NULL) return false;
Conjuntos.insert(Id);
return true;
}
bool ManejadorConjuntos::SuprimirConjunto(char Id) {
// Se busca el conjunto
//
ConjuntoEnteros *C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == NULL) return false;
Conjuntos.erase(*C);
return true;
}
bool ManejadorConjuntos::AgregarElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros *C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == NULL) return false;
252

VOLVER A MEN

return C->Agregar(E);
}
bool ManejadorConjuntos::SuprimirElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros *C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == NULL) return false;
return C->Suprimir(E);
}
bool ManejadorConjuntos::PerteneceElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros *C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == NULL) return false;
return C->Pertenece(E);
}
bool ManejadorConjuntos::HacerUnion(char Id1, char Id2, char Id3) {
set<int>::iterator It;
ConjuntoEnteros *C1 = BuscarConjunto(Id1), *C2 = BuscarConjunto(Id2), *C3 =
BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == NULL || C2 == NULL || C3 == NULL) return false;

253

VOLVER A MEN

// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3


// Se elimina cualquier cosa previa que exista en C3
//
C3->clear();
for (It = C1->begin(); It != C1->end(); It++)
C3->Agregar(*It);
for (It = C2->begin(); It != C2->end(); It++)
if (!C3->Pertenece(*It)) C3->Agregar(*It);
return true;
}
bool ManejadorConjuntos::HacerInterseccion(char Id1, char Id2, char Id3) {
set<int>::iterator It;
ConjuntoEnteros *C1 = BuscarConjunto(Id1), *C2 = BuscarConjunto(Id2), *C3 =
BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == NULL || C2 == NULL || C3 == NULL) return false;
// Se procede a hacer la interseccin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3->clear();
for (It = C1->begin(); It != C1->end(); It++)
if (C2->Pertenece(*It)) C3->Agregar(*It);
return true;
}
bool ManejadorConjuntos::HacerDiferencia(char Id1, char Id2, char Id3) {
set<int>::iterator It;
254

VOLVER A MEN

ConjuntoEnteros *C1 = BuscarConjunto(Id1), *C2 = BuscarConjunto(Id2), *C3 =


BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == NULL || C2 == NULL || C3 == NULL) return false;
// Se procede a hacer la diferencia de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3->clear();
for (It = C1->begin(); It != C1->end(); It++)
if (!C2->Pertenece(*It)) C3->Agregar(*It);
return true;
}
bool ManejadorConjuntos::EstaVacio(char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros *C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
// Caso contrario se dice que est vaco
//
if (C == NULL) return false;
return C->empty();
}
void ManejadorConjuntos::MostrarOrdenado() {
for (set<ConjuntoEnteros>::iterator It = Conjuntos.begin(); It != Conjuntos.end(); It++) {
ConjuntoEnteros *C = (ConjuntoEnteros *)(&(*It));
if (C) C->MostrarOrdenado();
255

VOLVER A MEN

}
}
bool ManejadorConjuntos::Guardar(string Nombre) {
ofstream MiArchivo(Nombre.c_str(), ios::out | ios::binary);
int S = Conjuntos.size();
if (!MiArchivo.is_open()) return false;
// Primero se guarda el nmero de conjuntos
//
MiArchivo.write((char *)&S, sizeof(int));
for (set<ConjuntoEnteros>::iterator It = Conjuntos.begin(); It != Conjuntos.end(); It++) {
ConjuntoEnteros *C = (ConjuntoEnteros *)(&(*It));
if (C) C->GuardarArchivo(&MiArchivo);
}
MiArchivo.close();
return true;
}
bool ManejadorConjuntos::Cargar(string Nombre) {
ifstream MiArchivo(Nombre.c_str(), ios::in | ios::binary);
char IdCjto;
int S, N, *V;
// Cualquier cosa previa se suprime
//
Conjuntos.clear();
if (!MiArchivo.is_open()) return false;
// Se lee el nmero de conjuntos
//
256

VOLVER A MEN

MiArchivo.read((char *)&S, sizeof(int));


for (int i=0; i < S; i++) {
// Lo primero que debe venir es el nombre del conjunto
//
MiArchivo.read(&IdCjto, 1);
// Se agrega el conjunto al ambiente
//
AgregarConjunto(IdCjto);
// Luego debe venir el nmero de elementos
//
MiArchivo.read((char *)&N, sizeof(int));
// Si hay elementos vienen a continuacin y se agregan al conjunto
//
if (N) {
V = new int[N];
if (V) {
MiArchivo.read((char *)V, N * sizeof(int));
for (int j=0; j < N; j++)
AgregarElemento(V[j], IdCjto);
delete[] V;
}
}
}
MiArchivo.close();
return true;
}
bool ManejadorConjuntos::EjecutarComando(TrimString Comando) {
int Aux, E;
257

VOLVER A MEN

bool bAux;
TrimString Op, Arg, sAux;
char C[3];
// Se elimina cuaquier espacio antes y despus del comando
//
Comando.trim();
Logger->EscribirEvento(Comando + Comando);
Aux = Comando.find( , 0);
if (Aux < 0) return false;
Op = Comando.substr(0, Aux);
if (Op.size() > 1) return false;
// Las acciones dependen de la operacin
//
if (Op == N || Op == C || Op == M || Op == V) {
// Debe haber un argumento de tres caracteres
//
Arg = Comando.substr(Aux + 1, Comando.length() - (Aux + 1));
Arg.trim();
if (Arg.length() != 3 || Arg[0] != < || Arg[2] != >) return false;
if (Op == N) return AgregarConjunto(Arg[1]);
if (Op == C) return SuprimirConjunto(Arg[1]);
if (Op == M) { MostrarOrdenado(); return true; }
return EstaVacio(Arg[1]);
}
if (Op == A || Op == D || Op == P) {
// Deben haber dos argumentos
//
Arg = Comando.substr(Aux + 1, Comando.length() - (Aux + 1));
Arg.trim();
Aux = Arg.find( , 0);
if (Aux < 0) return false;
258

VOLVER A MEN

sAux = Arg.substr(0, Aux);


sAux.trim();
if (sAux.length() < 3 || sAux[0] != < || sAux[sAux.length()-1] != >) return false;
E = atoi(sAux.substr(1, sAux.length()-2).c_str());
sAux = Arg.substr(Aux + 1, Arg.length() - (Aux + 1));
sAux.trim();
if (sAux.length() != 3 || sAux[0] != < || sAux[2] != >) return false;
if (Op == A) return AgregarElemento(E, sAux[1]);
if (Op == D) return SuprimirElemento(E, sAux[1]);
return PerteneceElemento(E, sAux[1]);
}
if (Op == + || Op == - || Op == *) {
// Deben haber tres argumentos
//
Arg = Comando;
for (int i=0; i < 3; i++) {
Arg = Arg.substr(Aux + 1, Arg.length() - (Aux + 1));
Arg.trim();
Aux = Arg.find( , 0);
if (i < 2) {
if (Aux < 0) return false;
sAux = Arg.substr(0, Aux);
}
else
sAux = Arg;
sAux.trim();
if (sAux.length() != 3 || sAux[0] != < || sAux[2] != >) return false;
C[i] = sAux[1];
}
if (Op == +) return HacerUnion(C[0], C[1], C[2]);
if (Op == -) return HacerDiferencia(C[0], C[1], C[2]);
return HacerInterseccion(C[0], C[1], C[2]);
}
259

VOLVER A MEN

if (Op == S || Op == L) {
// Debe haber un argumento
//
bAux = (Op == S);
Arg = Comando.substr(Aux + 1, Comando.length() - (Aux + 1));
Arg.trim();
if (Arg.length() < 3 || Arg[0] != < || Arg[Arg.length()-1] != >) return false;
return (bAux ? Guardar(Arg.substr(1, Arg.length()-2)) : Cargar(Arg.substr(1, Arg.length()-2)));
}
return false;
}
bool ManejadorConjuntos::EjecutarComandos(string Nombre) {
ifstream MiArchivo(Nombre.c_str(), ios::in);
char Buffer[80];
if (!MiArchivo.is_open()) return false;
Logger->EscribirEvento(Archivo de Comandos + Nombre);
while (!MiArchivo.eof()) {
MiArchivo.getline(Buffer, 80);
EjecutarComando(Buffer);
}
return true;
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.4
* ArchivoEventos.hpp
* Mauricio Paletta
*/

260

VOLVER A MEN

#ifndef ARCHIVOEVENTOS_HPP
#define ARCHIVOEVENTOS_HPP
#include <string>
#include <fstream>
using namespace std;
class ArchivoEventos : public ofstream {
public:
ArchivoEventos(string Nombre) : ofstream(Nombre.c_str()) { };
ArchivoEventos() : ofstream() { open(Logger.Txt); };
~ArchivoEventos() { close(); }
void EscribirEvento(string Ev);
};
#endif /* ARCHIVOEVENTOS_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.4
* ArchivoEventos.cpp
* Mauricio Paletta
*/
#include <ctime>
#include ArchivoEventos.hpp
void ArchivoEventos::EscribirEvento(string Ev) {
time_t tSac = time(NULL);
struct tm* pt = localtime(&tSac);

261

VOLVER A MEN

*this << < << pt->tm_mday << / << pt->tm_mon+1 << / << pt->tm_year+1900 << > ;
*this << < << pt->tm_hour << : << pt->tm_min << : << pt->tm_sec << >: ;
*this << Ev.c_str() << endl;
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.4
* Main.cpp
* Mauricio Paletta
*/
#include <cstdlib>
#include <iostream>
#include ManejoConjuntos.hpp
using namespace std;
// Programa principal
//
int main(int argc, char** argv) {
bool Fin = false;
char Buffer[80];
TrimString S, strO;
int Aux;
ManejadorConjuntos *Manejador = new ManejadorConjuntos();
do {
// Primero se muestra el indicador
//
cout << Conjuntos> ;

262

VOLVER A MEN

// Se lee una lnea de comando


//
S.clear();
cin.getline(Buffer, 80, \n);
S = Buffer;
S.trim();
// Se ubica la operacin
//
Aux = S.find( , 0);
if (Aux < 0)
strO = S;
else
strO = S.substr(0, Aux);
// Proceder segn el operador
//
if (strO == ?) {
cout << ?\t\t\tMuestra la lista de opciones\n;
cout << N <C>\t\t\tAgregar al ambiente un conjunto de identificador <C>\n;
cout << C <C>\t\t\tSuprimir del ambiente el conjunto de identificador <C>\n;
cout << V <C>\t\t\tIndicar si el conjunto de identificador <C> est o no vaco\n;
cout << A <N> <C>\t\tAgregar el elemento <N> al conjunto <C> que est actualmente en el
ambiente\n;
cout << D <N> <C>\t\tSuprimir el elemento <N> del conjunto <C> que est actualmente en el
ambiente\n;
cout << P <N> <C>\t\tIndicar si el elemento <N> pertenece al conjunto <C> que est
actualmente

en el ambiente\n;
cout << + <C1> <C2> <C3>\tCalcular y guardar en <C3> la unin de <C1> con <C2>\n;
cout << * <C1> <C2> <C3>\tCalcular y guardar en <C3> la interseccin de <C1> con <C2>\n;
cout << - <C1> <C2> <C3>\tCalcular y guardar en <C3> la diferencia entre <C1> y <C2>\n;
cout << M <C>\t\t\tMostrar la informacin ordenada del conjunto de identificador <C>\n;

263

VOLVER A MEN

cout << I\t\t\tMostrar la informacin ordenada del ambiente\n;


cout << S <A>\t\t\tGuardar el estado del ambiente en el archivo de nombre <A>\n;
cout << L <A>\t\t\tCargar o recuperar del archivo de nombre <A> lo que debe estar en el
ambiente\n;
cout << F\t\t\tSalir del entorno\n\n;
}
else
if (strO == V) {
if (Manejador->EjecutarComando(S))
cout << El conjunto est vaco\n;
else
cout << El conjunto no est vaco el conjunto no existe\n;
}
else
if (strO == P) {
if (Manejador->EjecutarComando(S))
cout << El elemento SI pertenece al conjunto\n;
else
cout << El elemento NO pertenece al conjunto el conjunto no existe\n;
}
else
if (strO == I) Manejador->MostrarOrdenado();
else
if (strO == F) Fin = true;
else {
if (!Manejador->EjecutarComando(S))
cout << Algn problema ejecutando el comando\n;
}
} while (!Fin);
delete Manejador;
return 0;
}
264

VOLVER A MEN

En relacin a Java, el cdigo es el que se presenta a continuacin basado en los archivos


fuentes ManejadorConjuntos.java y ArchivoEventos.java. Las clases correspondientes
estn definidas dentro del paquete identificado como manejoconjuntos.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.4
* ManejoConjuntos.java
* Mauricio Paletta
*/
package manejoconjuntos;
import java.io.*;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.logging.Level;
// Representa el manejador de conjunto de enteros
//
public class ManejadorConjuntos {
class IntComparator implements Comparator<Integer> {
@Override
public int compare(Integer I1, Integer I2) {
if (I1.intValue() > I2.intValue()) return 1;
if (I1.intValue() < I2.intValue()) return -1;
return 0;
}
}
// Representa un conjunto de enteros
//
class ConjuntoEnteros extends TreeSet<Integer> {
265

VOLVER A MEN

private char Identificador;


public ConjuntoEnteros(char Id) {
super();
if (Id < A || Id > Z) Id = *;
Identificador = Id;
}
public ConjuntoEnteros() {
this(*);
}
public char ObtId() { return Identificador; }
public boolean Pertenece(int E) { return this.contains(E); }
public boolean Agregar(int E) {
return this.add(E);
}
public boolean Suprimir(int E) {
return this.remove(E);
}
public void MostrarOrdenado() {
System.out.printf(%c: { , Identificador);
for (Iterator<Integer> It = this.iterator(); It.hasNext(); )
System.out.printf(%d , It.next().intValue());
System.out.printf(}\n);
}
public void GuardarArchivo(FileOutputStream MiArchivo) throws IOException {
Integer []E;
if (MiArchivo == null) return;
MiArchivo.write(Identificador);
MiArchivo.write(this.size());
if (this.size() > 0) {
E = new Integer[this.size()];
E = this.toArray(E);
for (int i=0; i < this.size(); i++)
266

VOLVER A MEN

MiArchivo.write(E[i]);
}
}
}
class SetComparator implements Comparator<ConjuntoEnteros> {
@Override
public int compare(ConjuntoEnteros C1, ConjuntoEnteros C2) {
if (C1.ObtId() > C2.ObtId()) return 1;
if (C1.ObtId() < C2.ObtId()) return -1;
return 0;
}
}
private TreeSet<ConjuntoEnteros> Conjuntos;
private ArchivoEventos Logger;
public ManejadorConjuntos() {
try {
Conjuntos = new TreeSet(new SetComparator());
Logger = new ArchivoEventos();
Logger.EscribirEvento(Inicio de sesin);
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,

null, ex);

} catch (IOException ex) {


java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);
}
}

267

VOLVER A MEN

@Override
public void finalize() {
try {
Logger.EscribirEvento(Fin de sesin);
Logger.finalize();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);
} catch (Throwable ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);
}
}
public ConjuntoEnteros BuscarConjunto(char Id) {
for (Iterator<ConjuntoEnteros> sIt = Conjuntos.iterator(); sIt.hasNext(); ) {
ConjuntoEnteros C = sIt.next();
if (C.ObtId() == Id) return C;
}
return null;
}
public boolean AgregarConjunto(char Id) {
// Se verifica que el Id es vlido
//
if (Id < A || Id > Z) return false;
// Se verifica que no hay ningn otro conjunto con el mismo Id
//
ConjuntoEnteros C = BuscarConjunto(Id);
if (C != null) return false;
Conjuntos.add(new ConjuntoEnteros(Id));
268

VOLVER A MEN

return true;
}
public boolean SuprimirConjunto(char Id) {
// Se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
Conjuntos.remove(C);
return true;
}
public boolean AgregarElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
return C.Agregar(E);
}
public boolean SuprimirElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
269

VOLVER A MEN

return C.Suprimir(E);
}
public boolean PerteneceElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
return C.Pertenece(E);
}
public boolean HacerUnion(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2), C3 =
BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == null || C2 == null || C3 == null) return false;
// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3.clear();
C3.addAll(C1);
C3.addAll(C2);
return true;
}
public boolean HacerInterseccion(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2), C3 =
BuscarConjunto(Id3);
270

VOLVER A MEN

// Primero se verifica la existencia de los 3 conjuntos


//
if (C1 == null || C2 == null || C3 == null) return false;
// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3.clear();
C3.addAll(C1);
C3.retainAll(C2);
return true;
}
public boolean HacerDiferencia(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2), C3 =
BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == null || C2 == null || C3 == null) return false;
// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3.clear();
C3.addAll(C1);
C3.removeAll(C2);
return true;
}

271

VOLVER A MEN

public boolean EstaVacio(char Id) {


// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
// Caso contrario se dice que est vaco
//
if (C == null) return false;
return C.isEmpty();
}
public void MostrarOrdenado() {
for (Iterator<ConjuntoEnteros> It = Conjuntos.iterator(); It.hasNext(); ) {
ConjuntoEnteros C = It.next();
if (C != null) C.MostrarOrdenado();
}
}
public void MostrarOrdenado(char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
// Caso contrario se dice que est vaco
//
if (C != null) C.MostrarOrdenado();
}
public boolean Guardar(String Nombre) {
FileOutputStream MiArchivo;

272

VOLVER A MEN

try {
MiArchivo = new FileOutputStream(Nombre);
// Primero se guarda el nmero de conjuntos en el entorno
//
MiArchivo.write(Conjuntos.size());
for (Iterator<ConjuntoEnteros> It = Conjuntos.iterator(); It.hasNext(); ) {
ConjuntoEnteros C = It.next();
if (C != null) C.GuardarArchivo(MiArchivo);
}
MiArchivo.close();
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);
return false;
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

return false;
}
return true;
}
public boolean Cargar(String Nombre) {
FileInputStream MiArchivo;
char IdCjto;
int C, N, V;
try {
MiArchivo = new FileInputStream(Nombre);
// Cualquier cosa previa se suprime
//
Conjuntos.clear();
273

VOLVER A MEN

if (MiArchivo == null) return false;


// Primero se lee el nmero de conjuntos en el entorno
//
C = MiArchivo.read();
for (int i=0; i < C; i++) {
// Se lee el Id del conjunto
//
IdCjto = (char)MiArchivo.read();
// Se agrega el conjunto al ambiente
//
AgregarConjunto(IdCjto);
// Luego debe venir el nmero de elementos
//
N = MiArchivo.read();
// Si hay elementos vienen a continuacin y se agregan al conjunto
//
for (int j=0; j < N; j++) {
V = MiArchivo.read();
AgregarElemento(V, IdCjto);
}
}
MiArchivo.close();
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,

null, ex);
return false;
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,


274

null, ex);
VOLVER A MEN

return false;
}
return true;
}
public boolean EjecutarComando(String Comando) {
int Aux, E;
boolean bAux;
String Op, Arg, sAux;
char C[] = new char[3];
// Se elimina cuaquier espacio antes y despus del comando
//
Comando.trim();
try {
Logger.EscribirEvento(Comando + Comando);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

Aux = Comando.indexOf( , 0);


if (Aux < 0) return false;
Op = Comando.substring(0, Aux);
if (Op.length() > 1) return false;
// Las acciones dependen de la operacin
//
if (Op.compareTo(N) == 0 || Op.compareTo(C) == 0 ||
Op.compareTo(M) == 0 || Op.compareTo(V) == 0) {
// Debe haber un argumento de 3 caracteres
//
Arg = Comando.substring(Aux + 1, Comando.length());
275

VOLVER A MEN

Arg.trim();
if (Arg.length() != 3 || Arg.charAt(0) != < || Arg.charAt(2) != >) return false;
if (Op.compareTo(N) == 0) return AgregarConjunto(Arg.charAt(1));
if (Op.compareTo(C) == 0) return SuprimirConjunto(Arg.charAt(1));
if (Op.compareTo(M) == 0) { MostrarOrdenado(Arg.charAt(1)); return true; }
return EstaVacio(Arg.charAt(1));
}
if (Op.compareTo(A) == 0 || Op.compareTo(D) == 0 || Op.compareTo(P) == 0) {
// Deben haber dos argumentos
//
Arg = Comando.substring(Aux + 1, Comando.length());
Arg.trim();
Aux = Arg.indexOf( , 0);
if (Aux < 0) return false;
sAux = Arg.substring(0, Aux);
sAux.trim();
if (sAux.length() < 3 || sAux.charAt(0) != < || sAux.charAt(sAux.length()-1) != >) return false;
try {
E = Integer.parseInt(sAux.substring(1, sAux.length()-1));
} catch (NumberFormatException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

return false;
}
sAux = Arg.substring(Aux + 1, Arg.length());
sAux.trim();
if (sAux.length() != 3 || sAux.charAt(0) != < || sAux.charAt(2) != >) return false;
if (Op.compareTo(A) == 0) return AgregarElemento(E, sAux.charAt(1));
if (Op.compareTo(D) == 0) return SuprimirElemento(E, sAux.charAt(1));
return PerteneceElemento(E, sAux.charAt(1));
}
if (Op.compareTo(+) == 0 || Op.compareTo(-) == 0 || Op.compareTo(*) == 0) {
276

VOLVER A MEN

// Deben haber tres argumentos


//
Arg = Comando;
for (int i=0; i < 3; i++) {
Arg = Arg.substring(Aux + 1, Arg.length());
Arg.trim();
Aux = Arg.indexOf( , 0);
if (i < 2) {
if (Aux < 0) return false;
sAux = Arg.substring(0, Aux);
}
else
sAux = Arg;
sAux.trim();
if (sAux.length() != 3 || sAux.charAt(0) != < || sAux.charAt(2) != >) return false;
C[i] = sAux.charAt(1);
}
if (Op.compareTo(+) == 0) return HacerUnion(C[0], C[1], C[2]);
if (Op.compareTo(-) == 0) return HacerDiferencia(C[0], C[1], C[2]);
return HacerInterseccion(C[0], C[1], C[2]);
}
if (Op.compareTo(S) == 0 || Op.compareTo(L) == 0) {
// Debe haber un argumento
//
bAux = (Op.compareTo(S) == 0);
Arg = Comando.substring(Aux + 1, Comando.length());
Arg.trim();
if (Arg.length() < 3 || Arg.charAt(0) != < || Arg.charAt(Arg.length()-1) != >) return false;
return (bAux ? Guardar(Arg.substring(1, Arg.length()-1)) :
Cargar(Arg.substring(1, Arg.length()-1)));
}
return false;
}
277

VOLVER A MEN

public boolean EjecutarComandos(String Nombre) {


FileReader MiArchivo;
BufferedReader Buffer;
String Linea;
try {
MiArchivo = new FileReader(Nombre);
Buffer = new BufferedReader(MiArchivo);
Logger.EscribirEvento(Archivo de Comandos + Nombre);
while ((Linea = Buffer.readLine()) != null) {
EjecutarComando(Linea);
}
MiArchivo.close();
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

return false;
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

return false;
}
return true;
}
public static void main(String[] args) {
boolean Fin = false;
BufferedReader Buffer = new BufferedReader(new InputStreamReader(System.in));
String S, strO, Arg;
int Aux;
ManejadorConjuntos Manejador = new ManejadorConjuntos();
do {
278

VOLVER A MEN

// Primero se muestra el indicador


//
System.out.print(Conjuntos> );
try {
// Se lee una lnea de comando
//
S = Buffer.readLine();
S.trim();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(ManejadorConjuntos.class.getName()).log(Level.SEVERE,
null, ex);

return;
}
// Se ubica la operacin
//
Aux = S.indexOf( , 0);
if (Aux < 0)
strO = S;
else
strO = S.substring(0, Aux);
// Proceder segn el operador
//
if (strO.compareTo(?) == 0) {
System.out.println(?\t\t\tMuestra la lista de opciones);
System.out.println(N <C>\t\t\tAgregar al ambiente un conjunto de identificador <C>);
System.out.println(C <C>\t\t\tSuprimir del ambiente el conjunto de identificador <C>);
System.out.println(V <C>\t\t\tIndicar si el conjunto de identificador <C> est o no vaco);
System.out.println(A <N> <C>\t\tAgregar el elemento <N> al conjunto <C> que est

actualmente en el ambiente);
System.out.println(D <N> <C>\t\tSuprimir el elemento <N> del conjunto <C> que est


279

actualmente en el ambiente);
VOLVER A MEN

System.out.println(P <N> <C>\t\tIndicar si el elemento <N> pertenece al conjunto <C> que

est

actualmente en el ambiente);
System.out.println(+ <C1> <C2> <C3>\tCalcular y guardar en <C3> la unin de <C1> con

<C2>);
System.out.println(* <C1> <C2> <C3>\tCalcular y guardar en <C3> la interseccin de <C1>

con <C2>);
System.out.println(- <C1> <C2> <C3>\tCalcular y guardar en <C3> la diferencia entre <C1>

<C2>);
System.out.println(M <C>\t\t\tMostrar la informacin ordenada del conjunto de identificador
<C>);
System.out.println(I\t\t\tMostrar la informacin ordenada del ambiente);
System.out.println(S <A>\t\t\tGuardar el estado del ambiente en el archivo de nombre <A>);
System.out.println(L <A>\t\t\tCargar o recuperar del archivo de nombre <A> lo que debe

estar

en el ambiente);
System.out.println(E <A>\t\t\tEjecutar el archivo de comandos de nombre <A>);
System.out.println(F\t\t\tSalir del entorno\n);
}
else
if (strO.compareTo(V) == 0) {
if (Manejador.EjecutarComando(S))
System.out.println(El conjunto est vaco);
else
System.out.println(El conjunto no est vaco el conjunto no existe);
}
else
if (strO.compareTo(P) == 0) {
if (Manejador.EjecutarComando(S))
System.out.println(El elemento SI pertenece al conjunto);
else
System.out.println(El elemento NO pertenece al conjunto el conjunto no existe);
}

280

VOLVER A MEN

else
if (strO.compareTo(I) == 0) Manejador.MostrarOrdenado();
else
if (strO.compareTo(F) == 0) {
Manejador.finalize();
Fin = true;
}
else
if (strO.compareTo(E) == 0) {
// Debe haber un argumento
//
Arg = S.substring(Aux + 1, S.length());
Arg.trim();
Manejador.EjecutarComandos(Arg.substring(1, Arg.length()-1));
}
else {
if (!Manejador.EjecutarComando(S))
System.out.println(Algn problema ejecutando el comando);
}
} while (!Fin);
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.4
* ArchivoEventos.java
* Mauricio Paletta
*/
package manejoconjuntos;
import java.io.FileWriter;
import java.io.IOException;
281

VOLVER A MEN

import java.io.PrintWriter;
import java.util.Calendar;
// Representa un archivo de eventos
//
public class ArchivoEventos extends FileWriter {
private PrintWriter Pw;
public ArchivoEventos(String Nombre) throws IOException{
super(Nombre);
Pw = new PrintWriter(this);
}
public ArchivoEventos() throws IOException {
this(Logger.Txt);
}
@Override
public void finalize() throws Throwable {
super.finalize();
this.close();
}
public void EscribirEvento(String Ev) throws IOException {
Calendar C = Calendar.getInstance();
String Fecha = < + Integer.toString(C.get(Calendar.DAY_OF_MONTH)) + / +

Integer.toString(C.get(Calendar.MONTH) + 1) + / + Integer.toString(C.get(Calendar.YEAR)) +

> + < + Integer.toString(C.get(Calendar.HOUR_OF_DAY)) + : +

Integer.toString(C.get(Calendar.MINUTE)) + : + Integer.toString(C.get(Calendar.SECOND)) +

>: ;
Pw.println(Fecha + Ev);
}

}
282

VOLVER A MEN

Por ltimo, a continuacin el cdigo escrito en C#. Se tienen los archivos fuente
ManejadorConjuntos.cs y ArchivoEventos.cs y todos los elementos definidos
asociados al paquete o espacio de nombres identificado como ManejoConjuntos
(ver Figura 6.22).
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.4
* ManejadorConjuntos.cs
* Mauricio Paletta
*/
using System;
using System.IO;
using System.Collections.Generic;
namespace ManejoConjuntos
{
// Representa un conjunto de enteros
//
class ConjuntoEnteros : SortedSet<int> {
private char Identificador;
public ConjuntoEnteros(char Id) : base() {
if (Id < A || Id > Z) Id = *;
Identificador = Id;
}
public ConjuntoEnteros() : this(*) { }
public char ObtId() { return Identificador; }
public bool Pertenece(int E) { return this.Contains(E); }
public bool Agregar(int E) {
return this.Add(E);
}
public bool Suprimir(int E) {
return this.Remove(E);
283

VOLVER A MEN

}
public void MostrarOrdenado() {
Console.Write(Convert.ToString(Identificador) + : { );
foreach (int E in this)
Console.Write({0} , E);
Console.WriteLine(});
}
public void GuardarArchivo(BinaryWriter MiArchivo) {
int []E;
if (MiArchivo == null) return;
MiArchivo.Write(Identificador);
MiArchivo.Write((Int32)this.Count);
if (this.Count > 0) {
E = new int[this.Count];
this.CopyTo(E);
for (int i=0; i < Count; i++)
MiArchivo.Write((Int32)E[i]);
}
}
}
// Define un comparador para los conjuntos de enteros
//
class ComparadorConjuntoEnteros : IComparer<ConjuntoEnteros> {
public int Compare(ConjuntoEnteros C1, ConjuntoEnteros C2)
{
if (C1.ObtId() < C2.ObtId()) return -1;
if (C1.ObtId() > C2.ObtId()) return 1;
return 0;
}
}

284

VOLVER A MEN

class ManejadorConjuntos
{
private SortedSet<ConjuntoEnteros> Conjuntos;
private ArchivoEventos Logger;
public ManejadorConjuntos() {
Conjuntos = new SortedSet<ConjuntoEnteros>(new ComparadorConjuntoEnteros());
Logger = new ArchivoEventos();
Logger.EscribirEvento(Inicio de sesin);
}
~ManejadorConjuntos() {
Logger.EscribirEvento(Fin de sesin);
Logger.Cerrar();
}
public ConjuntoEnteros BuscarConjunto(char Id) {
foreach (ConjuntoEnteros C in Conjuntos)
if (C.ObtId() == Id) return C;
return null;
}
public bool AgregarConjunto(char Id) {
// Se verifica que el Id es vlido
//
if (Id < A || Id > Z) return false;
// Se verifica que no hay ningn otro conjunto con el mismo Id
//
ConjuntoEnteros C = BuscarConjunto(Id);
if (C != null) return false;
Conjuntos.Add(new ConjuntoEnteros(Id));
return true;
285

VOLVER A MEN

}
public bool SuprimirConjunto(char Id) {
// Se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
Conjuntos.Remove(C);
return true;
}
public bool AgregarElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
return C.Agregar(E);
}
public bool SuprimirElemento(int E, char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
return C.Suprimir(E);
}

286

VOLVER A MEN

public bool PerteneceElemento(int E, char Id) {


// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
//
if (C == null) return false;
return C.Pertenece(E);
}
public bool HacerUnion(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2),

C3 = BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == null || C2 == null || C3 == null) return false;
// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3.Clear();
C3.UnionWith(C1);
C3.UnionWith(C2);
return true;
}
public bool HacerInterseccion(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2),

C3 = BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == null || C2 == null || C3 == null) return false;

287

VOLVER A MEN

// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3


// Se elimina cualquier cosa previa que exista en C3
//
C3.Clear();
C3.UnionWith(C1);
C3.IntersectWith(C2);
return true;
}
public bool HacerDiferencia(char Id1, char Id2, char Id3) {
ConjuntoEnteros C1 = BuscarConjunto(Id1), C2 = BuscarConjunto(Id2),

C3 = BuscarConjunto(Id3);
// Primero se verifica la existencia de los 3 conjuntos
//
if (C1 == null || C2 == null || C3 == null) return false;
// Se procede a hacer la unin de C1 con C2 y guardar el resultado en C3
// Se elimina cualquier cosa previa que exista en C3
//
C3.Clear();
C3.UnionWith(C1);
C3.ExceptWith(C2);
return true;
}
public bool EstaVacio(char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
// Caso contrario se dice que est vaco
//

288

VOLVER A MEN

if (C == null) return false;


return C.Count == 0;
}
public void MostrarOrdenado() {
foreach (ConjuntoEnteros C in Conjuntos)
C.MostrarOrdenado();
}
public void MostrarOrdenado(char Id) {
// Primero se busca el conjunto
//
ConjuntoEnteros C = BuscarConjunto(Id);
// Se verifica si un conjunto con ese Id existe
// Caso contrario se dice que est vaco
//
if (C != null) C.MostrarOrdenado();
}
public bool Guardar(String Nombre) {
FileStream MiArchivo = new FileStream(Nombre, FileMode.Create);
BinaryWriter Bw = new BinaryWriter(MiArchivo);
// Primero se guarda el nmero de conjuntos en el entorno
//
Bw.Write((Int32)Conjuntos.Count);
foreach (ConjuntoEnteros C in Conjuntos)
C.GuardarArchivo(Bw);
MiArchivo.Close();
return true;
}
public bool Cargar(string Nombre) {
FileStream MiArchivo = new FileStream(Nombre, FileMode.Open);
289

VOLVER A MEN

BinaryReader Br = new BinaryReader(MiArchivo);


char IdCjto;
int C, N, V;
// Cualquier cosa previa se suprime
//
Conjuntos.Clear();
if (MiArchivo == null || Br == null) return false;
// Primero se lee el nmero de conjuntos en el entorno
//
C = Br.ReadInt32();
for (int i=0; i < C; i++) {
// Se lee el Id del conjunto
//
IdCjto = Br.ReadChar();
// Se agrega el conjunto al ambiente
//
AgregarConjunto(IdCjto);
// Luego debe venir el nmero de elementos
//
N = Br.ReadInt32();
// Si hay elementos vienen a continuacin y se agregan al conjunto
//
for (int j=0; j < N; j++) {
V = Br.ReadInt32();
AgregarElemento(V, IdCjto);
}
}
MiArchivo.Close();
return true;
}

290

VOLVER A MEN

public bool EjecutarComando(string Comando) {


int Aux, E;
bool bAux;
string Op, Arg, sAux;
char[] C = new char[3];
// Se elimina cuaquier espacio antes y despus del comando
//
Comando.Trim();
Logger.EscribirEvento(Comando + Comando);
Aux = Comando.IndexOf( , 0);
if (Aux < 0) return false;
Op = Comando.Substring(0, Aux);
if (Op.Length > 1) return false;
// Las acciones dependen de la operacin
//
if (Op == N || Op == C || Op == M || Op == V) {
// Debe haber un argumento de 3 caracteres
//
Arg = Comando.Substring(Aux + 1, Comando.Length - (Aux + 1));
Arg.Trim();
if (Arg.Length != 3 || Arg[0] != < || Arg[2] != >) return false;
if (Op == N) return AgregarConjunto(Arg[1]);
if (Op == C) return SuprimirConjunto(Arg[1]);
if (Op == M) { MostrarOrdenado(Arg[1]); return true; }
return EstaVacio(Arg[1]);
}
if (Op == A || Op == D || Op == P) {
// Deben haber dos argumentos
//
Arg = Comando.Substring(Aux + 1, Comando.Length - (Aux + 1));
291

VOLVER A MEN

Arg.Trim();
Aux = Arg.IndexOf( , 0);
if (Aux < 0) return false;
sAux = Arg.Substring(0, Aux);
sAux.Trim();
if (sAux.Length < 3 || sAux[0] != < || sAux[sAux.Length-1] != >) return false;
E = Convert.ToInt32(sAux.Substring(1, sAux.Length-2));
sAux = Arg.Substring(Aux + 1, Arg.Length - (Aux + 1));
sAux.Trim();
if (sAux.Length != 3 || sAux[0] != < || sAux[2] != >) return false;
if (Op == A) return AgregarElemento(E, sAux[1]);
if (Op == D) return SuprimirElemento(E, sAux[1]);
return PerteneceElemento(E, sAux[1]);
}
if (Op == + || Op == - || Op == *) {
// Deben haber tres argumentos
//
Arg = Comando;
for (int i=0; i < 3; i++) {
Arg = Arg.Substring(Aux + 1, Arg.Length - (Aux + 1));
Arg.Trim();
Aux = Arg.IndexOf( , 0);
if (i < 2) {
if (Aux < 0) return false;
sAux = Arg.Substring(0, Aux);
}
else
sAux = Arg;
sAux.Trim();
if (sAux.Length != 3 || sAux[0] != < || sAux[2] != >) return false;
C[i] = sAux[1];
}
292

VOLVER A MEN

if (Op == +) return HacerUnion(C[0], C[1], C[2]);


if (Op == -) return HacerDiferencia(C[0], C[1], C[2]);
return HacerInterseccion(C[0], C[1], C[2]);
}
if (Op == S || Op == L) {
// Debe haber un argumento
//
bAux = (Op == S);
Arg = Comando.Substring(Aux + 1, Comando.Length - (Aux + 1));
Arg.Trim();
if (Arg.Length < 3 || Arg[0] != < || Arg[Arg.Length-1] != >) return false;
return (bAux ? Guardar(Arg.Substring(1, Arg.Length-2)) :

Cargar(Arg.Substring(1, Arg.Length-2)));
}
return false;
}
public bool EjecutarComandos(string Nombre) {
StreamReader MiArchivo = new StreamReader(Nombre);
Logger.EscribirEvento(Archivo de Comandos + Nombre);
while (!MiArchivo.EndOfStream)
EjecutarComando(MiArchivo.ReadLine());
MiArchivo.Close();
return true;
}
public static void Main(string[] args)
{
bool Fin = false;
string S, strO, Arg;
int Aux;
ManejadorConjuntos Manejador = new ManejadorConjuntos();

293

VOLVER A MEN

do {
// Primero se muestra el indicador
//
Console.Write(Conjuntos> );
// Se lee una lnea de comando
//
S = Console.In.ReadLine();
S.Trim();
// Se ubica la operacin
//
Aux = S.IndexOf( , 0);
if (Aux < 0)
strO = S;
else
strO = S.Substring(0, Aux);
// Proceder segn el operador
//
if (strO == ?) {
Console.WriteLine(?\t\t\tMuestra la lista de opciones);
Console.WriteLine(N <C>\t\t\tAgregar al ambiente un conjunto de identificador <C>);
Console.WriteLine(C <C>\t\t\tSuprimir del ambiente el conjunto de identificador <C>);
vaco);

Console.WriteLine(V <C>\t\t\tIndicar si el conjunto de identificador <C> est o no


Console.WriteLine(A <N> <C>\t\tAgregar el elemento <N> al conjunto <C> que

est actualmente en el ambiente);


Console.WriteLine(D <N> <C>\t\tSuprimir el elemento <N> del conjunto <C> que est

actualmente en el ambiente);
Console.WriteLine(P <N> <C>\t\tIndicar si el elemento <N> pertenece al conjunto <C>


con

que est actualmente en el ambiente);


Console.WriteLine(+ <C1> <C2> <C3>\tCalcular y guardar en <C3> la unin de <C1>

<C2>);
Console.WriteLine(* <C1> <C2> <C3>\tCalcular y guardar en <C3> la interseccin de
294

VOLVER A MEN

<C1> con <C2>);


Console.WriteLine(- <C1> <C2> <C3>\tCalcular y guardar en <C3> la diferencia entre

<C1> y <C2>);
Console.WriteLine(M <C>\t\t\tMostrar la informacin ordenada del conjunto de

identificador <C>);
Console.WriteLine(I\t\t\tMostrar la informacin ordenada del ambiente);
Console.WriteLine(S <A>\t\t\tGuardar el estado del ambiente en el archivo de nombre

<A>);
Console.WriteLine(L <A>\t\t\tCargar o recuperar del archivo de nombre <A> lo que debe

estar en el ambiente);
Console.WriteLine(E <A>\t\t\tEjecutar el archivo de comandos de nombre <A>);
Console.WriteLine(F\t\t\tSalir del entorno\n);
}
else
if (strO == V) {
if (Manejador.EjecutarComando(S))
Console.WriteLine(El conjunto est vaco);
else
Console.WriteLine(El conjunto no est vaco el conjunto no existe);
}
else
if (strO == P) {
if (Manejador.EjecutarComando(S))
Console.WriteLine(El elemento SI pertenece al conjunto);
else
Console.WriteLine(El elemento NO pertenece al conjunto el conjunto no existe);
}
else
if (strO == I) Manejador.MostrarOrdenado();
else
if (strO == F) {
Fin = true;
}

295

VOLVER A MEN

else
if (strO == E) {
// Debe haber un argumento
//
Arg = S.Substring(Aux + 1, S.Length - (Aux + 1));
Arg.Trim();
Manejador.EjecutarComandos(Arg.Substring(1, Arg.Length-2));
}
else {
if (!Manejador.EjecutarComando(S))
Console.WriteLine(Algn problema ejecutando el comando);
}
} while (!Fin);
}
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.4
* ArchivoEventos.cs
* Mauricio Paletta
*/
using System;
using System.IO;
namespace ManejoConjuntos
{
// Representa un archivo de eventos
//
public class ArchivoEventos : StreamWriter {
public ArchivoEventos(string Nombre) : base(Nombre) { }
296

VOLVER A MEN

public ArchivoEventos() : this(Logger.Txt) { }


public void Cerrar() {
this.Close();
}
public void EscribirEvento(string Ev) {
DateTime Data = DateTime.Now;
string Fecha = < + Data.Day + / + Data.Month + / + Data.Year + > +
< + Data.Hour + : + Data.Minute + : + Data.Second + >: ;
this.WriteLine(Fecha + Ev);
}
}
}

6.5 Manejo inteligente de colas


6.5.1 Enunciado
Se quiere hacer un simulador para el manejo inteligente de colas de solicitudes de servicio basado
en una serie de criterios y/o requerimientos. Se puede imaginar una compaa de servicios
(banco, electricidad, cable, etc.) con una serie de clientes que desean ser satisfechos en algn
servicio particular (pagar, abrir una cuenta, solicitar informacin, etc.). Los requerimientos son
los siguientes:
La oficina prestataria del servicio contiene varias taquillas de atencin cada una de las
cuales atiende una lista de servicios particulares.
Existen diferentes tipos de clientes (regulares, vip, etc.) cada uno de los cuales tiene una
prioridad de atencin.
Hay tres niveles de prioridad para la atencin de los clientes: Alta (75 %), Media (50 %)
y Baja (25 %). Quiere decir que por cada taquilla desocupada en un momento dado y
que atienda el servicio especfico que el cliente requiera, la probabilidad de atencin del
cliente es la indicada.
Se asume que un cliente slo va a solicitar un servicio por vez.
Se asume que todos los servicios deben ser atendidos por al menos una taquilla de atencin. No puede haber una taquilla que no atienda ningn servicio como tampoco puede
297

VOLVER A MEN

haber un servicio que no sea atendido por ninguna taquilla. Una taquilla puede atender
todos los servicios y un mismo servicio puede ser atendido por todas las taquillas.
La cola de atencin se maneja segn una numeracin consecutiva por tipo de cliente.
Previo al inicio de la simulacin hay que configurar el escenario con la siguiente informacin:
1. Tipos de servicio.
2. Nmero de taquillas de atencin.
3. Servicios que presta cada taquilla de atencin.
Esta informacin se debe almacenar en disco, de manera tal que cada vez que el simulador
abra se cargue la informacin actualizada y cuando el simulador se cierre guarde siempre
su estado actual.
La simulacin consiste en lo siguiente:
1. Viene un cliente e identifica su tipo (prioridad) y el servicio que requiere.
2. El simulador le asigna un nmero.
3. Basado en la prioridad de atencin del tipo de cliente, en el servicio que requiere, en
las taquillas disponibles y la forma como stas estn configuradas, el simulador debe
informar el nmero siguiente a atender y en qu taquilla debe dirigirse el cliente.
4. Se asume que el tiempo que dura un cliente en una taquilla est entre 3 y 6 segundos y
se debe asignar de forma aleatoria una vez que el simulador seleccione el nmero del
cliente para su atencin.
5. Durante la simulacin se pueden habilitar o deshabilitar taquillas de atencin. Tambin
se pueden habilitar o deshabilitar servicios prestados por taquilla.
6. Se debe mostrar en todo momento el nmero de clientes en espera a ser atendidos.
7. Al cerrar la atencin al pblico, es decir, no aceptar ms clientes y una vez que sean
atendidos todos los clientes en espera, se debe emitir un resumen estadstico que
contenga la siguiente informacin:
Total de clientes atendidos en la oficina.
Total de clientes atendidos por taquilla.
Total de clientes atendidos por servicio.
Tiempo promedio de atencin desde que un cliente solicit el nmero de servicio
hasta que fue completamente atendido.
298

VOLVER A MEN

Tiempo mnimo de atencin que corresponde al tiempo ms bajo desde que un


cliente solicit el nmero de servicio hasta que fue completamente atendido.
Tiempo mximo de atencin que corresponde al tiempo ms alto desde que un cliente
solicit el nmero de servicio hasta que fue completamente atendido.

6.5.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): Luego de hacer una revisin del
texto del enunciado del problema descrito en la seccin anterior, un posible mapa mental que
resulta de este anlisis es el que se muestra en la Figura 6.23.

Figura 6.23. Mapa mental relativo al problema del manejo inteligente de colas de atencin.

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Simulador, manejo
de colas, cliente, servicio, prioridad, oficina, taquilla, cola de atencin, archivo y estadstica.
Paso 3 (Identificar procesos / requerimientos): Nuevamente se tiene un usuario como actor del
simulador y al igual que en el problema anterior, se tiene un archivo para guardar / recuperar
la informacin relativa a la configuracin del entorno de simulacin. En este sentido y tomando
en cuenta los requerimientos identificados en la Seccin 6.5.1, se tiene el diagrama de casos
de uso que se muestra en la Figura 6.24.

299

VOLVER A MEN

Figura 6.24. Diagrama de casos de uso relativo al problema del manejo inteligente de colas de atencin.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Simulador y manejo de colas son redundantes.
Prioridad es un atributo del cliente.
Cola de atencin y archivo son implementaciones.
En consecuencia se proponen las siguientes clases: Manejador de colas, cliente, servicio,
oficina, taquilla, cliente en espera y gestor de estadsticas. Ntese que si bien el enunciado
del problema no indica que pudiera haber ms de una oficina prestadora del servicio, esto fue
tomado en cuenta para la resolucin de este problema y de all la presencia de la clase Oficina
y la multiplicidad de 1 a muchos asociada a Manejador Colas. La relacin en este caso se
lee de la siguiente manera: 1 o ms oficinas forman parte del manejador de colas. La clase
Cliente Espera permite representar la asociacin entre todos los elementos involucrados en
la prestacin del servicio: el cliente, el servicio que ste requiere y la taquilla asignada para
satisfacer este servicio. En este sentido se tiene el diagrama no detallado de clases que se
muestra en la Figura 6.25.

300

VOLVER A MEN

Figura 6.25. Diagrama no detallado de clases relativo al problema del manejo inteligente de colas de atencin.

6.5.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Para este problema es conveniente usar una
interfaz grfica con un panel de botones para las diferentes opciones a realizar: configurar,
cargar, guardar, e iniciar / detener la simulacin. Es conveniente tambin tener un par de
formularios (formas) para permitir por un lado la configuracin basado en la edicin de los
parmetros correspondientes y por otro lado para mostrar la simulacin. Adicionalmente se
requiere la interaccin con un archivo para guardar / cargar la configuracin. Para ello se
decidi utilizar el formato XML con el siguiente detalle que se presenta a continuacin con un
ejemplo basado en 6 servicios: S1, S2, S3, S4, S5 y S6; 1 oficina identificada con el
nmero 1 que tiene 4 taquillas identificadas con los nmeros 1, 2, 3 y 4 respectivamente; la
taquilla 1 ofrece los servicios S1, S2, y S5; la taquilla 2 ofrece los servicios S1 y S3; la
taquilla 3 ofrece los servicios S2, S4 y S6 y la taquilla 4 ofrece los servicios S2 y S4.
<?xml version=1.0?>
<ManejoColas>
<Servicios>

301

<Servicio>S1</Servicio>

<Servicio>S2</Servicio>

<Servicio>S3</Servicio>

<Servicio>S4</Servicio>

VOLVER A MEN

<Servicio>S5</Servicio>

<Servicio>S6</Servicio>

</Servicios>
<Oficina Id=1 Taquillas=4>

<Taquilla Id=1>

<Servicio>S1</Servicio>
<Servicio>S2</Servicio>
<Servicio>S5</Servicio>

</Taquilla>

<Taquilla Id=2>

<Servicio>S1</Servicio>

<Servicio>S3</Servicio>

</Taquilla>

<Taquilla Id=3>

<Servicio>S2</Servicio>
<Servicio>S4</Servicio>
<Servicio>S6</Servicio>

</Taquilla>

<Taquilla Id=4>

<Servicio>S2</Servicio>
<Servicio>S4</Servicio>

</Taquilla>

</Oficina>
</ManejoColas>

Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.25
resulta el diagrama de clases detallado de la Figura 6.26.

302

VOLVER A MEN

Figura 6.26. Diagrama detallado de clases relativo al problema del manejo inteligente de colas de atencin.

Paso 3 (Desarrollar los modelos de estado): Ntese que las clases Cliente, ClienteEspera
y Servicio asignan sus atributos en tiempo de construccin del objeto y stos luegos no
son modificados nuevamente por ningn mtodo. De esta manera, la Figura 6.27 muestra los
diagramas de estado de cada una de las clases presentes en el modelo de este problema. A
fin de no complicar la lectura del diagrama se obvian las transiciones recursivas que indican
permanecer en el mismo estado luego de la llamada del mtodo correspondiente.

303

VOLVER A MEN

Figura 6.27. Diagramas de estado relativos al problema del manejo inteligente de colas de atencin.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.28 muestra los diagramas de
secuencia relativos a este problema y basado en el diagrama de casos de uso de la Figura
6.24 y el diagrama de clases de la Figura 6.26. En este sentido se tienen dos diagramas, uno
para los casos de uso relacionados con la configuracin (parte superior) y otro para los casos
de uso relacionados con la simulacin (parte inferior). El orden en el cual se ejecutan los
eventos no est acorde a una secuencia de operacin aunque se trata de reflejar qu acciones
son precedentes de otras.
304

VOLVER A MEN

Figura 6.28. Diagramas de secuencia relativos al problema del manejo inteligente de colas de atencin:
1) configuracin (parte superior); 2) simulacin (parte inferior).

Paso 5 (Identificar componentes del dominio): Asumiendo el uso de C++ para la implementacin
de este caso prctico se tiene el diagrama de componentes correspondiente al problema que
se presenta en la Figura 6.29. Ntese las dependencias entre los archivos de cdigo fuente
extencin cpp y los archivos encabezado xon extensin hpp. Ntese tambin la incorporacin
de los elementos grficos (ui) relativos al kit de desarrollo de Qt.

305

VOLVER A MEN

Figura 6.29. Diagrama de componentes relativo al problema del manejo inteligente de colas de atencin.

6.5.4 Programacin
A continuacin el cdigo escrito en C++ relativo al problema. Para evitar ser tan extenso no
se agrega el manejo de la interfaz grfica (este puede ser consultado en el material digital
entregado junto a este libro). Primero se muestran los archivos Cliente.hpp y Cliente.cpp.
Luego se muestran los archivos Servicio.hpp y Servicio.cpp. Ntese que en estos dos
casos el archivo extensin cpp solo se utiliza para hacer la inclusin del archivo encabezado
hpp correspondiente. Posteriormente se muestran los archivos Oficina.hpp y Oficina.
cpp y finalmente se encuentra el cdigo fuente relativo a los archivos ManejadorCola.hpp y
ManejadorCola.cpp.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Cliente.hpp
* Mauricio Paletta
*/
#ifndef CLIENTE_HPP
#define

306

CLIENTE_HPP

VOLVER A MEN

enum PrioridadAtencion {
Baja, Media, Alta
};
class Cliente {
private:
PrioridadAtencion Prioridad;
int Id;
public:
Cliente(int id, PrioridadAtencion p) {
Id = id; Prioridad = p;
}
int ObtId() { return Id; }
PrioridadAtencion ObtPrioridad() { return Prioridad; }
};
typedef Cliente *pCliente;
#endif /* CLIENTE_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Cliente.cpp
* Mauricio Paletta
*/
#include Cliente.hpp
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Servicio.hpp
307

VOLVER A MEN

* Mauricio Paletta
*/
#ifndef SERVICIO_HPP
#define

SERVICIO_HPP

#include <string>
using namespace std;
class Servicio {
private:
string Nombre;
public:
Servicio(string Str) { Nombre = Str; }
string ObtNombre() { return Nombre; }
string ToString() { return ObtNombre(); }
bool operator ==(Servicio S) { return this->Nombre == S.Nombre; }
};
typedef Servicio *pServicio;
#endif /* SERVICIO_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Servicio.cpp
* Mauricio Paletta
*/
#include Servicio.hpp

308

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Oficina.hpp
* Mauricio Paletta
*/
#ifndef OFICINA_HPP
#define OFICINA_HPP
#include <QtGui>
#include <string>
#include <vector>
#include <sstream>
#include <time.h>
#include Servicio.hpp
#include Cliente.hpp
using namespace std;
// Para convertir nmeros a cadenas de caracteres
//
class Convertir {
public:
static string ToString(int N) {
stringstream R;
R << N;
return R.str() ;
}
static string ToString(double N) {
stringstream R;
R << N;
309

VOLVER A MEN

return R.str();
}
};
// Representa una taquilla de la oficina
//
class Taquilla {
private:
int Id;
vector<pServicio> Servicios;
bool Habilitada;
pServicio BuscarServicio(string nombreS);
public:
Taquilla(int id) {
Id = id; Habilitada = true;
}
~Taquilla();
int ObtId() { return Id; }
string ToString() { return Convertir::ToString(Id); }
int ObtNumeroServicios() { return Servicios.size(); }
pServicio ObtServicio(int Ind) { return Servicios[Ind]; }
void Habilitar() { Habilitada = true; }
void Deshabilitar() { Habilitada = false; }
bool EstaHabilitada() { return Habilitada; }
void AgregarServicio(Servicio S) {
// Primero se verifica si el servicio ya existe
//
if (BuscarServicio(S.ObtNombre()) != NULL) return;
Servicios.push_back(&S);
}
void AgregarServicio(string nombreS) {
310

VOLVER A MEN

// Primero se verifica si el servicio ya existe


//
if (BuscarServicio(nombreS) != NULL) return;
Servicios.push_back(new Servicio(nombreS));
}
void RemoverServicio(Servicio S);
void RemoverServicio(string nombreS) {
pServicio S = BuscarServicio(nombreS);
if (S != NULL) RemoverServicio(*S);
}
bool OfreceServicio(string nombreS) {
return BuscarServicio(nombreS) != NULL;
}
};
typedef Taquilla *pTaquilla;
// Representa un gestor de estadsticas x oficina
//
class ClienteEspera;
class GestorEstadisticas {
private:
int TotalClientesOficina, nT, nS;
int *TotalClientesTaquilla, *TotalClientesServicio;
double PromedioAtencion, MinimoAtencion, MaximoAtencion;
public:
GestorEstadisticas(int N, int S);
~GestorEstadisticas();
void AgregarClienteTaquilla(ClienteEspera *CE);
void ClienteServido(double Tpo);
string ToString();
};
311

VOLVER A MEN

typedef GestorEstadisticas *pGestorEstadisticas;


// Representa una oficina
//
class ClienteEspera;
class Oficina {
private:
int Id, Nt;
pTaquilla *Taquillas;
pGestorEstadisticas Estadisticas;
int nCliente;
vector<ClienteEspera *> ClientesEspera;
public:
Oficina(int id, int nT, int nS);
~Oficina();
int ObtId() { return Id; }
string ToString() { return Convertir::ToString(Id); }
int ObtNumeroTaquillas() { return Nt; }
int ObtNumeroServiciosTaquilla(int idT) {
return (Taquillas != NULL && idT < Nt ? Taquillas[idT]->ObtNumeroServicios() : 0);
}
pServicio ObtServicioTaquilla(int idT, int idS) {
if (Taquillas == NULL || idT < 0 || idT >= Nt) return NULL;
return Taquillas[idT]->ObtServicio(idS);
}
void HabilitarTaquilla(int idT) {
if (idT < 0 || idT >= Nt) return;
Taquillas[idT]->Habilitar();
}
void DeshabilitarTaquilla(int idT) {
312

VOLVER A MEN

if (idT < 0 || idT >= Nt) return;


Taquillas[idT]->Deshabilitar();
}
pTaquilla BuscarTaquilla(int Idx) {
return (Idx >= 0 && Idx < Nt ? Taquillas[Idx] : NULL);
}
int ProximoCliente() { return ++nCliente; }
void LimpiarClientes() { nCliente = 0; }
void AgregarServicioTaquilla(string nombreS, int idT) {
if (idT < 0 || idT >= Nt) return;
Taquillas[idT]->AgregarServicio(nombreS);
}
bool RevisarPrestacionServicios(vector<pServicio> Ss);
void NuevoClienteEspera(pCliente c, pServicio s);
void RevisarEstadoTaquillas();
string ClienteAtendidoTaquilla(pTaquilla T);
string ClienteAtendidoTaquilla(int idxT) {
return idxT >= 0 && idxT < Nt ? ClienteAtendidoTaquilla(Taquillas[idxT]) : ;
}
string ClientesEsperaTaquilla(pTaquilla T, pServicio S);
// Obtener las estadsticas en un formato texto
//
string ObtEstadisticas() {
return Estadisticas != NULL ? Estadisticas->ToString() : ;
}
void ClienteServido(double TiempoServicio) {
if (Estadisticas != NULL) Estadisticas->ClienteServido(TiempoServicio);
}
};
typedef Oficina *pOficina;

313

VOLVER A MEN

// Representa un cliente en espera


//
enum Estado {
Espera, Atendiendo, Servido
};
class ClienteEspera {
private:
pCliente C;
pServicio S;
pTaquilla T;
pOficina O;
int N;
time_t InicioServicio;
int DuracionServicio;
Estado E;
public slots:
void OnTimedEvent();
public:
ClienteEspera(pCliente c, pServicio s, pTaquilla t, pOficina o, int n);
~ClienteEspera() { }
pCliente ObtCliente() { return C; }
pServicio ObtServicio() { return S; }
pTaquilla ObtTaquilla() { return T; }
int ObtNumeroAtencion() { return N; }
Estado ObtEstado() { return E; }
void AsgEstado(Estado e) { E = e; }
void Atender() { E = Atendiendo; time(&InicioServicio); }
};
typedef ClienteEspera *pClienteEspera;
#endif /* OFICINA_HPP */
314

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* Oficina.cpp
* Mauricio Paletta
*/
#include <limits>
#include <cstdlib>
#include ManejadorColas.hpp
#include Oficina.hpp
// Mtodos de la clase Taquilla
//
Servicio *Taquilla::BuscarServicio(string nombreS) {
for (unsigned i=0; i < Servicios.size(); i++) {
pServicio S = Servicios[i];
if (nombreS == S->ObtNombre()) return S;
}
return NULL;
}
void Taquilla::RemoverServicio(Servicio S) {
for(vector<pServicio>::iterator It=Servicios.begin(); It != Servicios.end(); It++) {
pServicio T = (pServicio)*It;
if (T != NULL && *T == S) {
delete T;
Servicios.erase(It);
return;
}
}
}
315

VOLVER A MEN

Taquilla::~Taquilla() {
for(vector<pServicio>::iterator It=Servicios.begin(); It != Servicios.end(); It++) {
pServicio T = (pServicio)*It;
if (T != NULL) delete T;
}
Servicios.clear();
}
// Mtodos de la clase GestorEstadisticas
//
GestorEstadisticas::GestorEstadisticas(int N, int S) {
int i;
if (N <= 0) N = 1;
TotalClientesOficina = 0;
PromedioAtencion = MaximoAtencion = 0.0;
MinimoAtencion = numeric_limits<double>::max();
TotalClientesTaquilla = new int[N];
TotalClientesServicio = new int[S];
for (i=0; i < N; i++)
TotalClientesTaquilla[i] = 0;
for (i=0; i < S; i++)
TotalClientesServicio[i] = 0;
nT = N; nS = S;
}
GestorEstadisticas::~GestorEstadisticas() {
delete[] TotalClientesTaquilla;
delete[] TotalClientesServicio;
}
void GestorEstadisticas::AgregarClienteTaquilla(pClienteEspera CE) {
if (CE == NULL || CE->ObtTaquilla() == NULL || CE->ObtServicio() == NULL) return;
if (CE->ObtTaquilla()->ObtId() <= 0 || CE->ObtTaquilla()->ObtId() > nT) return;
316

VOLVER A MEN

if (ManejadorColas::BuscarIndiceServicio(CE->ObtServicio()->ObtNombre()) < 0) return;


TotalClientesOficina++;
TotalClientesTaquilla[CE->ObtTaquilla()->ObtId()-1]++;
TotalClientesServicio[ManejadorColas::BuscarIndiceServicio(CE->ObtServicio()->ObtNombre())]++;
}
void GestorEstadisticas::ClienteServido(double Tpo) {
PromedioAtencion += Tpo;
if (Tpo < MinimoAtencion) MinimoAtencion = Tpo;
if (Tpo > MaximoAtencion) MaximoAtencion = Tpo;
}
string GestorEstadisticas::ToString() {
string Texto = ;
double Promedio;
Texto += Total Clientes Oficina: + Convertir::ToString(TotalClientesOficina) + \n;
Texto += Total Clientes Taquilla:\n;
for (int t=0; t < nT; t++) {
Texto += \t + Convertir::ToString(t + 1) + : + Convertir::ToString(TotalClientesTaquilla[t]) + \n;
}
Texto += Total Clientes Servicio:\n;
for (int s=0; s < nS; s++) {
Texto += \t + ManejadorColas::BuscarServicio(s)->ObtNombre() + : +

Convertir::ToString(TotalClientesServicio[s]) + \n;

}
Promedio = TotalClientesOficina > 0 ? PromedioAtencion / TotalClientesOficina : 0.0;
Texto += Promedio de atencin: + Convertir::ToString(Promedio) + \n;
Texto += Tiempo mnimo de atencin: + Convertir::ToString(MinimoAtencion) + \n;
Texto += Tiempo mximo de atencin: + Convertir::ToString(MaximoAtencion) + \n;
return Texto;
}

317

VOLVER A MEN

// Mtodos de la clase Oficina


//
Oficina::Oficina(int id, int nT, int nS) {
Id = id;
if (nT <= 0) nT = 1;
Taquillas = new pTaquilla[nT];
for (int i=0; i < nT; i++) {
Taquillas[i] = new Taquilla(i+1);
Taquillas[i]->Habilitar();
}
Estadisticas = new GestorEstadisticas(nT, nS);
nCliente = 0;
Nt = nT;
}
Oficina::~Oficina() {
for (int i=0; i < Nt; i++) delete Taquillas[i];
delete[] Taquillas;
delete Estadisticas;
for(vector<pClienteEspera>::iterator It=ClientesEspera.begin(); It != ClientesEspera.end(); It++) {
pClienteEspera CE = (pClienteEspera)*It;
if (CE != NULL) delete CE;
}
ClientesEspera.clear();
}
// Revisa que todos los servicios S estn garantizados a ser
// prestados en la oficina
//
bool Oficina::RevisarPrestacionServicios(vector<pServicio> Ss) {
bool ServicioPrestado;
// Se verifica primero si hay servicios que revisar
//
318

VOLVER A MEN

if (Ss.size() == 0) return true;


// Para todos los servicios a verificar
//
for (unsigned i=0; i < Ss.size(); i++) {
pServicio S = (pServicio)Ss[i];
ServicioPrestado = false;
// Para todas las taquillas habilitadas de esta oficina
//
for (int t=0; t < Nt; t++) {
// Solo se toman en cuenta taquillas habilitadas
//
if (Taquillas[t]->EstaHabilitada()) {
// La taquilla debe dar al menos un servicio
//
if (Taquillas[t]->ObtNumeroServicios() == 0) return false;
if (Taquillas[t]->OfreceServicio(S->ObtNombre())) {
ServicioPrestado = true;
break;
}
}
}
// Este servicio debe ser dado por al menos una taquilla
//
if (!ServicioPrestado) return false;
}
return true;
}
void Oficina::NuevoClienteEspera(pCliente c, pServicio s) {
if (c == NULL || s == NULL) return;
319

VOLVER A MEN

// Se buscan las taquillas que ofrecen el servicio solicitado por el cliente


//
int nMenor, tMenor;
int *T = new int[Nt];
pClienteEspera CE;
for (int i=0; i < Nt; i++) {
T[i] = (Taquillas[i]->EstaHabilitada() && Taquillas[i]->OfreceServicio(s->ObtNombre()) ? 0 : -1);
}
// Para todos los clientes actualmente en espera
// Hay que buscar la taquilla que ofrezca el servicio solicitado por el cliente
// con el menor nmero de clientes de la misma prioridad
//
for (unsigned i=0; i < ClientesEspera.size(); i++) {
CE = (pClienteEspera)ClientesEspera[i];
if (CE->ObtEstado() != Servido && T[CE->ObtTaquilla()->ObtId()-1] >= 0 &&
T[CE->ObtTaquilla()->ObtId()-1] < CE->ObtNumeroAtencion())
T[CE->ObtTaquilla()->ObtId()-1] = CE->ObtNumeroAtencion();
}
nMenor = numeric_limits<int>::max();
tMenor = -1;
for (int i=0; i < Nt; i++) {
if (T[i] >= 0) {
if (T[i] < nMenor) {
nMenor = T[i];
tMenor = i;
}
}
}
if (tMenor >= 0 && tMenor < Nt) {
CE = new ClienteEspera(c, s, Taquillas[tMenor], this, nMenor+1);
320

VOLVER A MEN

ClientesEspera.push_back(CE);
// Se actualizan las estadsticas
//
Estadisticas->AgregarClienteTaquilla(CE);
}
}
void Oficina::RevisarEstadoTaquillas() {
pClienteEspera CE, *Menores;
bool *TaquillaLibre;
int T;
unsigned c;
// Si no hay clientes en espera nada que hacer
//
if (ClientesEspera.size() == 0) return;
Menores = new pClienteEspera[Nt];
TaquillaLibre = new bool[Nt];
// Para todas las taquillas de la oficina
//
for (T=0; T < Nt; T++) {
Menores[T] = NULL;
TaquillaLibre[T] = true;
}
for (c=0; c < ClientesEspera.size(); ) {
CE = (pClienteEspera)ClientesEspera[c];
T = CE->ObtTaquilla()->ObtId() - 1;

321

VOLVER A MEN

// Se revisa si el cliente ya fue atendido


//
CE->OnTimedEvent();
if (CE->ObtEstado() == Atendiendo) {
// Si el cliente no ha sido servido an, la taquilla no est disponible
//
TaquillaLibre[T] = false;
c++;
}
else
if (CE->ObtEstado() == Servido) {
// Si el cliente ya fue servido se debe sacar de la cola
//
delete CE;
ClientesEspera.erase(ClientesEspera.begin()+c);
}
else {
// Forma parte de la bsqueda de un nuevo cliente a ser atendido
//
if ((Menores[T] == NULL) ||
(CE->ObtCliente()->ObtPrioridad() > Menores[T]->ObtCliente()->ObtPrioridad()) ||
((CE->ObtCliente()->ObtPrioridad() == Menores[T]->ObtCliente()->ObtPrioridad()) &&
CE->ObtNumeroAtencion() < Menores[T]->ObtNumeroAtencion()))
Menores[T] = CE;
c++;
}
}
// Hacer las asignaciones de los clientes en las respectivas taquillas
//
for (T=0; T < Nt; T++) {
if (TaquillaLibre[T] && Menores[T] != NULL) {
322

VOLVER A MEN

Menores[T]->AsgEstado(Atendiendo);
Menores[T]->Atender();
}
}
delete[] Menores;
delete[] TaquillaLibre;
}
// Buscar el cliente que se est atendiendo actualmente en la taquilla
// indicada y retornar una cadena de caracteres con formato para actualizar
// la lista de estado de avance correspondiente
//
string Oficina::ClienteAtendidoTaquilla(pTaquilla T) {
for (unsigned i=0; i < ClientesEspera.size(); i++) {
pClienteEspera CE = (pClienteEspera)ClientesEspera[i];
if (CE->ObtTaquilla() == T && CE->ObtEstado() == Atendiendo)
return Convertir::ToString(CE->ObtCliente()->ObtId()) + ( +

Convertir::ToString(CE->ObtNumeroAtencion()) + );

}
return ;
}
// Armar en una cadena de caracteres la cola de clientes asociados a un servicio
// de una taquilla especfica
//
string Oficina::ClientesEsperaTaquilla(pTaquilla T, pServicio S) {
string Clientes = ;
for (unsigned i=0; i < ClientesEspera.size(); i++) {
pClienteEspera CE = (pClienteEspera)ClientesEspera[i];
if (CE->ObtTaquilla() == T && CE->ObtEstado() != Atendiendo && CE->ObtServicio() == S)
Clientes += Convertir::ToString(CE->ObtCliente()->ObtId()) + ( +

323

VOLVER A MEN

Convertir::ToString(CE->ObtNumeroAtencion()) + ) ;

}
return Clientes;
}
// Mtodos de la clase ClienteEspera
//
ClienteEspera::ClienteEspera(pCliente c, pServicio s, pTaquilla t, pOficina o, int n) {
C = c; S = s; T = t; N = n; O = o; E = Espera;
DuracionServicio = (rand() % 6) + 1;
}
// Para representar el reloj del tiempo de duracin del cliente en espera
//
void ClienteEspera::OnTimedEvent() {
time_t FinServicio;
// Ver si el cliente ya ha sido servido
//
time(&FinServicio);
if (difftime(FinServicio, InicioServicio) >= DuracionServicio) {
E = Servido;
// Se actualiza las estadsticas
//
O->ClienteServido((double)DuracionServicio);
}
}

324

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* ManejadorColas.cpp
* Mauricio Paletta
*/
#ifndef MANEJADORCOLAS_HPP
#define MANEJADORCOLAS_HPP
#include <QtGui>
#include <vector>
#include Servicio.hpp
#include Oficina.hpp
using namespace std;
class ManejadorColas {
private:
static vector<pServicio> Servicios;
vector<pOficina> Oficinas;
QTreeView *TreeSimulacion;
pServicio BuscarServicio(string nombreS);
QXmlStreamReader::TokenType ObtProxToken(QXmlStreamReader *Rxml);
public slots:
void OnTimedEvent();
public:
ManejadorColas() { }
~ManejadorColas() { LimpiarDatos(); }

325

VOLVER A MEN

static pServicio BuscarServicio(int Ind) {


return Ind >= 0 && (unsigned)Ind < Servicios.size() ? (pServicio)Servicios[Ind] : NULL;
}
static int BuscarIndiceServicio(string nombreS);
void AgregarServicio(string nombreS) {
if (BuscarServicio(nombreS) != NULL) return;
Servicios.push_back(new Servicio(nombreS));
}
void RemoverServicio(string nombreS) {
int S = BuscarIndiceServicio(nombreS);
if (S < 0) return;
Servicios.erase(Servicios.begin()+S);
}
int ObtNumeroServicios() { return Servicios.size(); }
pServicio ObtServicio(int Ind) {
return Ind >= 0 && (unsigned)Ind < Servicios.size() ? (pServicio)Servicios[Ind] : NULL;
}
pOficina BuscarOficina(int iD);
int BuscarIndiceOficina(int iD);
void AgregarOficina(int iD, int nT, int nS) {
if (BuscarOficina(iD) != NULL) return;
Oficinas.push_back(new Oficina(iD, nT, nS));
}
void RemoverOficina(int iD) {
int O = BuscarIndiceOficina(iD);
if (O < 0) return;
Oficinas.erase(Oficinas.begin()+O);
}
// Elimina todos los datos existentes en el objeto
//
void LimpiarDatos();

326

VOLVER A MEN

// Revisa que todos los servicios estn garantizados a ser


// prestados en la oficina iD
//
bool RevisarPrestacionServicios(int iD) {
// Primero se ubica la oficina
//
pOficina O = BuscarOficina(iD);
if (O == NULL) return false;
return O->RevisarPrestacionServicios(Servicios);
}
bool IniciarSimulacion(int iD, QTreeView *TreeS);
bool CargarConfiguracion(QString NombreA);
bool GuardarConfiguracion(QString NombreA);
};
#endif /* MANEJADORCOLAS_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.5
* ManejadorColas.hpp
* Mauricio Paletta
*/
#include <cstdlib>
#include <time.h>
#include <QTreeView>
#include <QStandardItemModel>
#include ManejadorColas.hpp
vector<pServicio> ManejadorColas::Servicios;
pServicio ManejadorColas::BuscarServicio(string nombreS) {
for (unsigned i=0; i < Servicios.size(); i++) {
pServicio S = (pServicio)Servicios[i];
327

VOLVER A MEN

if (nombreS == S->ObtNombre()) return S;


}
return NULL;
}
int ManejadorColas::BuscarIndiceServicio(string nombreS) {
for (unsigned i=0; i < Servicios.size(); i++) {
pServicio S = (pServicio)Servicios[i];
if (nombreS == S->ObtNombre()) return (int)i;
}
return -1;
}
pOficina ManejadorColas::BuscarOficina(int iD) {
for (unsigned i=0; i < Oficinas.size(); i++) {
pOficina O = (pOficina)Oficinas[i];
if (O->ObtId() == iD) return O;
}
return NULL;
}
int ManejadorColas::BuscarIndiceOficina(int iD) {
for (unsigned i=0; i < Oficinas.size(); i++) {
pOficina O = (pOficina)Oficinas[i];
if (iD == O->ObtId()) return (int)i;
}
return -1;
}
void ManejadorColas::LimpiarDatos() {
for (vector<pOficina>::iterator It=Oficinas.begin(); It != Oficinas.end(); It++) {
pOficina O = (pOficina)*It;
if (O != NULL) delete O;
328

VOLVER A MEN

}
Oficinas.clear();
for(vector<pServicio>::iterator It=Servicios.begin(); It != Servicios.end(); It++) {
pServicio S = (pServicio)*It;
if (S != NULL) delete S;
}
Servicios.clear();
}
bool ManejadorColas::IniciarSimulacion(int iD, QTreeView *TreeS) {
// La simulacin no puede iniciar si no estn dadas las condiciones
//
time_t segundos;
pOficina O = BuscarOficina(1);
if (!RevisarPrestacionServicios(iD) || O == NULL) return false;
// Iniciar la simulacin
//
time(&segundos);
srand((unsigned)segundos);
O->LimpiarClientes();
TreeSimulacion = TreeS;
// Se carga el rbol de la simulacin
//
QStandardItemModel *Model;
QStandardItem *nOficina = new QStandardItem(Oficina 1);
Model = (QStandardItemModel *)TreeSimulacion->model();
if (Model != NULL)
delete Model;
Model = new QStandardItemModel();

329

VOLVER A MEN

// Se agregan las taquillas


//
for (int t=0; t < O->ObtNumeroTaquillas(); t++) {
QStandardItem *nTaquilla = new QStandardItem(QString(Taquilla %0).arg(t+1));
// Se agregan los servicios de la taquilla
//
for (int s=0; s < O->ObtNumeroServiciosTaquilla(t); s++) {
pServicio S = O->ObtServicioTaquilla(t, s);
if (S != NULL) {
QStandardItem *nServicio = new QStandardItem(QString(S->ObtNombre().c_str()) +
QString(: ));
nTaquilla->appendRow(nServicio);
}
}
nOficina->appendRow(nTaquilla);
}
Model->setItem(0, 0, nOficina);
TreeSimulacion->setModel(Model);
TreeSimulacion->expandAll();
TreeSimulacion->repaint();
return true;
}
void ManejadorColas::OnTimedEvent() {
// Ciclo de simulacin
//
pServicio S;
pOficina O = BuscarOficina(1);
if (O == NULL) return;

330

VOLVER A MEN

// Se revisa primero el estado de las taquillas


//
O->RevisarEstadoTaquillas();
// Comprobar si hay o no un cliente
// Hay un cliente, se decide su prioridad
//
if (rand() > RAND_MAX / 2) {
pCliente C;
PrioridadAtencion P;
double R = (double)rand() / RAND_MAX;
if (R < 0.33) P = Baja;
else
if (R < 0.66) P = Media;
else
P = Alta;
C = new Cliente(O->ProximoCliente(), P);
// Se selecciona al azar el servicio que desea hacer el cliente
//
S = ObtServicio(rand() % Servicios.size() + 1);
O->NuevoClienteEspera(C, S);
}
// Actaulizar el rbol de visualizacin del estado de la simulacin
//
QStandardItem *nOficina, *nTaquilla, *nServicio;
QStandardItemModel *Model = (QStandardItemModel *)TreeSimulacion->model();
string N, Aux;
int A;
if (Model == NULL) return;
nOficina = (QStandardItem *)Model->item(0, 0);
331

VOLVER A MEN

if (nOficina == NULL) return;


for (int t=0; t < nOficina->rowCount(); t++) {
pTaquilla T = O->BuscarTaquilla(t);
Aux = Taquilla + Convertir::ToString(t+1);
nTaquilla = nOficina->child(t, 0);
if (nTaquilla != NULL && T != NULL) {
if (!T->EstaHabilitada()) Aux = Aux + (X);
nTaquilla->setText(QString(Aux.c_str()) + QString(: ) +
QString(O->ClienteAtendidoTaquilla(T).c_str()));
// Para todos los servicios de la taquilla
//
for (int s=0; s < nTaquilla->rowCount(); s++) {
nServicio = nTaquilla->child(s, 0);
if (nServicio != NULL) {
Aux = nServicio->text().toStdString();
A = Aux.find(:, 0);
if (A >= 0) {
N = Aux.substr(0, A);
nServicio->setText(QString(N.c_str()) + QString(: ) +
QString(O->ClientesEsperaTaquilla(O->BuscarTaquilla(t),
BuscarServicio(N)).c_str()));
}
}
}
}
}
TreeSimulacion->expandAll();
TreeSimulacion->repaint();
}
QXmlStreamReader::TokenType ManejadorColas::ObtProxToken(QXmlStreamReader *Rxml) {
QXmlStreamReader::TokenType Token = Rxml->readNext();
332

VOLVER A MEN

if (Token == QXmlStreamReader::Characters)
Token = Rxml->readNext();
return Token;
}
bool ManejadorColas::CargarConfiguracion(QString NombreA) {
int j, nT;
pOficina O;
QFile xmlDoc(NombreA);
QXmlStreamReader Rxml;
QXmlStreamReader::TokenType Token;
if (!xmlDoc.open(QFile::ReadOnly | QFile::Text)) return false;
Rxml.setDevice(&xmlDoc);
Token = Rxml.readNext();
if (Token != QXmlStreamReader::StartDocument) {
xmlDoc.close();
return false;
}
Token = Rxml.readNext();
if (Token != QXmlStreamReader::StartElement || Rxml.name() != ManejoColas) {
xmlDoc.close();
return false;
}
// Primero se limpia cualquier dato existente
//
LimpiarDatos();
// Se cargan los servicios
//
333

VOLVER A MEN

Token = ObtProxToken(&Rxml);
if (Token != QXmlStreamReader::StartElement || Rxml.name() != Servicios) {
xmlDoc.close();
return false;
}
Token = ObtProxToken(&Rxml);
while(!Rxml.atEnd() && !Rxml.hasError() && Token != QXmlStreamReader::EndElement) {
if (Token == QXmlStreamReader::StartElement && Rxml.name() == Servicio) {
string Aux = Rxml.readElementText().toStdString();
AgregarServicio(Aux);
}
Token = ObtProxToken(&Rxml);
}
// Se agrega una oficina
//
Token = ObtProxToken(&Rxml);
if (Token == QXmlStreamReader::StartElement && Rxml.name() == Oficina) {
nT = Rxml.attributes()[1].value().toString().toInt();
AgregarOficina(1, nT, Servicios.size());
O = BuscarOficina(1);
// Se cargan los servicios x taquillas
//
for (j=0; j < nT; j++) {
Token = ObtProxToken(&Rxml);
if (Token == QXmlStreamReader::StartElement && Rxml.name() == Taquilla) {
Token = ObtProxToken(&Rxml);
while (!Rxml.atEnd() && !Rxml.hasError() && Token != QXmlStreamReader::EndElement) {
if (Token == QXmlStreamReader::StartElement && Rxml.name() == Servicio) {
O->AgregarServicioTaquilla(Rxml.readElementText().toStdString(), j);
}
Token = ObtProxToken(&Rxml);
334

VOLVER A MEN

}
}
}
}
Rxml.clear();
xmlDoc.close();
return true;
}
bool ManejadorColas::GuardarConfiguracion(QString NombreA) {
int i, j;
pOficina O;
QFile xmlDoc(NombreA);
xmlDoc.open(QIODevice::WriteOnly);
QXmlStreamWriter xmlWriter(&xmlDoc);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
// Elemento raz
//
xmlWriter.writeStartElement(ManejoColas);
// Servicios
//
xmlWriter.writeStartElement(Servicios);
for (i=0; i < ObtNumeroServicios(); i++) {
pServicio S = ObtServicio(i);
if (S != NULL) {
xmlWriter.writeTextElement(Servicio, S->ObtNombre().c_str());
}
}
335

VOLVER A MEN

xmlWriter.writeEndElement();
// Se agrega la oficina
//
O = BuscarOficina(1);
if (O != NULL) {
xmlWriter.writeStartElement(Oficina);
xmlWriter.writeAttribute(Id, 1);
xmlWriter.writeAttribute(Taquillas, Convertir::ToString(O->ObtNumeroTaquillas()).c_str());
for (i=0; i < O->ObtNumeroTaquillas(); i++) {
xmlWriter.writeStartElement(Taquilla);
xmlWriter.writeAttribute(Id, Convertir::ToString(i+1).c_str());
for (j=0; j < O->ObtNumeroServiciosTaquilla(i); j++) {
pServicio S = O->ObtServicioTaquilla(i, j);
if (S != NULL)
xmlWriter.writeTextElement(Servicio, S->ObtNombre().c_str());
}
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
// Se escribe la data en el archivo XML
//
xmlWriter.writeEndElement();
xmlDoc.close();
return true;
}

336

VOLVER A MEN

En relacin a Java se tiene el cdigo fuente que aparece a continuacin (sin incluir el manejo
de la interfaz grfica). Se tiene el paquete ManejadorColas y los siguientes archivos fuente:
ManejadorColas.java, Servicio.java, Cliente.java y Oficina.java.

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* ManejadorColas.java
* Mauricio Paletta
*/
package ManejadorColas;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
337

VOLVER A MEN

// Representa un manejador de colas para hacer la simulacin


//
public class ManejadorColas {
private static ArrayList<Servicio> Servicios = null;
private ArrayList<Oficina> Oficinas;
private Random Rnd;
private JTree jTreeSimulacion;
// Para representar el reloj de la simulacin
//
private TimerTask timerSimulacion = new TimerTask() {
public void run() {
// Ciclo de simulacin
//
int A;
Servicio S;
String N, Aux;
Oficina O = BuscarOficina(1);
if (O == null) return;
// Se revisa primero el estado de las taquillas
//
O.RevisarEstadoTaquillas();
// Comprobar si hay o no un cliente
// Hay un cliente, se decide su prioridad
//
if (Rnd.nextDouble() > 0.5) {
Cliente C;
Cliente.PrioridadAtencion P;
if (Rnd.nextDouble() < 0.33) P = Cliente.PrioridadAtencion.Baja;
338

VOLVER A MEN

else
if (Rnd.nextDouble() < 0.66) P = Cliente.PrioridadAtencion.Media;
else
P = Cliente.PrioridadAtencion.Alta;
C = new Cliente(O.ProximoCliente(), P);
// Se selecciona al azar el servicio que desea hacer el cliete
//
S = ObtServicio(Rnd.nextInt(Servicios.size()));
O.NuevoClienteEspera(C, S);
}
// Actaulizar el rbol de visualizacin del estado de la simulacin
//
DefaultTreeModel Model = (DefaultTreeModel)jTreeSimulacion.getModel();
DefaultMutableTreeNode nOficina =

(DefaultMutableTreeNode)jTreeSimulacion.getModel().getRoot(), nTaquilla, nServicio;

for (int t=0; t < nOficina.getChildCount(); t++) {


Oficina.Taquilla T = O.BuscarTaquilla(t);
Aux = Taquilla + Integer.toString(t+1);
nTaquilla = (DefaultMutableTreeNode)nOficina.getChildAt(t);
if (nTaquilla != null && T != null) {
if (!T.EstaHabilitada()) Aux = Aux + (X);
nTaquilla.setUserObject(Aux + : + O.ClienteAtendidoTaquilla(T));
// Para todos los servicios de la taquilla
//
for (int s=0; s < nTaquilla.getChildCount(); s++) {
nServicio = (DefaultMutableTreeNode)nTaquilla.getChildAt(s);
if (nServicio != null) {
Aux = nServicio.getUserObject().toString();
339

VOLVER A MEN

A = Aux.indexOf(:);
if (A >= 0) {
N = Aux.substring(0, A);
nServicio.setUserObject(N + : +
O.ClientesEsperaTaquilla(O.BuscarTaquilla(t), BuscarServicio(N)));
}
}
}
}
}
Model.reload(nOficina);
for (int i=0; i < jTreeSimulacion.getRowCount(); i++)
jTreeSimulacion.expandRow(i);
}
};
private Timer timerS;
public ManejadorColas() {
if (Servicios == null)
Servicios = new ArrayList<Servicio>();
Oficinas = new ArrayList<Oficina>();
}
private Servicio BuscarServicio(String nombreS) {
for (Iterator<Servicio> It = Servicios.iterator(); It.hasNext(); ) {
Servicio S = It.next();
if (nombreS.compareTo(S.ObtNombre()) == 0) return S;
}
return null;
}
public static Servicio BuscarServicio(int Ind) {
return Servicios.get(Ind);
340

VOLVER A MEN

}
public static int BuscarIndiceServicio(String nombreS) {
for (int i=0; i < Servicios.size(); i++) {
Servicio S = Servicios.get(i);
if (nombreS.compareTo(S.ObtNombre()) == 0) return i;
}
return -1;
}
public void AgregarServicio(String nombreS) {
if (BuscarServicio(nombreS) != null) return;
Servicios.add(new Servicio(nombreS));
}
public void RemoverServicio(String nombreS) {
Servicio S = BuscarServicio(nombreS);
if (S == null) return;
Servicios.remove(S);
}
public int ObtNumeroServicios() {
return Servicios.size();
}
public Servicio ObtServicio(int Ind) {
return Servicios.get(Ind);
}
public Oficina BuscarOficina(int iD) {
for (Iterator<Oficina> It = Oficinas.iterator(); It.hasNext(); ) {
Oficina O = It.next();
if (O.ObtId() == iD) return O;
341

VOLVER A MEN

}
return null;
}
public void AgregarOficina(int iD, int nT, int nS) {
if (BuscarOficina(iD) != null) return;
Oficinas.add(new Oficina(iD, nT, nS));
}
public void RemoverOficina(int iD) {
Oficina O = BuscarOficina(iD);
if (O == null) return;
Oficinas.remove(O);
}
// Elimina todos los datos existentes en el objeto
//
public void LimpiarDatos() {
Servicios.clear();
Oficinas.clear();
}
// Revisa que todos los servicios estn garantizados a ser
// prestados en la oficina iD
//
public boolean RevisarPrestacionServicios(int iD) {
// Primero se ubica la oficina
//
Oficina O = BuscarOficina(iD);
if (O == null) return false;
return O.RevisarPrestacionServicios(Servicios);
}

342

VOLVER A MEN

public boolean IniciarSimulacion(int iD, JTree jTreeS) {


// La simulacin no puede iniciar si no estn dadas las condiciones
//
Oficina O = BuscarOficina(1);
if (!RevisarPrestacionServicios(iD) || O == null) return false;
// Iniciar la simulacin
//
Rnd = new Random();
timerS = new Timer();
timerS.scheduleAtFixedRate(timerSimulacion, 0, 1000);
O.LimpiarClientes();
jTreeSimulacion = jTreeS;
// Se carga el rbol de la simulacin
//
DefaultMutableTreeNode nOficina = new DefaultMutableTreeNode(Oficina 1);
DefaultTreeModel Arbol = new DefaultTreeModel(nOficina);
jTreeSimulacion.setModel(Arbol);
// Se agregan las taquillas
//
for (int t=0; t < O.ObtNumeroTaquillas(); t++) {
DefaultMutableTreeNode nTaquilla = new DefaultMutableTreeNode(Taquilla +
Integer.toString(t+1));
Arbol.insertNodeInto(nTaquilla, nOficina, t);
// Se agregan los servicios de la taquilla
//
for (int s=0; s < O.ObtNumeroServiciosTaquilla(t); s++) {
Servicio S = O.ObtServicioTaquilla(t, s);
if (S != null) {
343

VOLVER A MEN

DefaultMutableTreeNode nServicio = new DefaultMutableTreeNode(S.ObtNombre() + : );


Arbol.insertNodeInto(nServicio, nTaquilla, s);
}
}
}
for (int i=0; i < jTreeSimulacion.getRowCount(); i++)
jTreeSimulacion.expandRow(i);
return true;
}
public void PararSimulacion(int iD) {
timerS.cancel();
}
public boolean CargarConfiguracion(String NombreA) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
File XmlFile = new File(NombreA);
Document doc;
NodeList nList, nListS, nListT;
Element Elemento;
Oficina O;
int i, j, nT;
doc = dBuilder.parse(XmlFile);
doc.getDocumentElement().normalize();
if (doc.getDocumentElement().getNodeName().compareTo(ManejoColas) == 0) {
// Primero se limpia cualquier dato existente
//
LimpiarDatos();

344

VOLVER A MEN

// Se cargan los servicios


//
Elemento = (Element)doc.getElementsByTagName(Servicios).item(0);
nListS = Elemento.getElementsByTagName(Servicio);
for (i=0; i < nListS.getLength(); i++) {
nList = nListS.item(i).getChildNodes();
AgregarServicio(nList.item(0).getNodeValue());
}
// Se agrega una oficina
//
Elemento = (Element)doc.getElementsByTagName(Oficina).item(0);
nT = Integer.parseInt(Elemento.getAttribute(Taquillas));
AgregarOficina(1, nT, Servicios.size());
O = BuscarOficina(1);
// Se cargan los servicios x taquillas
//
nListT = Elemento.getElementsByTagName(Taquilla);
for (j=0; j < nListT.getLength(); j++) {
Elemento = (Element)nListT.item(j).getChildNodes();
nListS = Elemento.getElementsByTagName(Servicio);
for (i=0; i < nListS.getLength(); i++) {
nList = nListS.item(i).getChildNodes();
O.AgregarServicioTaquilla(nList.item(0).getNodeValue(), j);
}
}
}
return true;
} catch (ParserConfigurationException ex) {
Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);
} catch (SAXException ex) {
Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
345

VOLVER A MEN

Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);


}
return false;
}
public boolean GuardarConfiguracion(String NombreA) {
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
File XmlFile = new File(NombreA);
Document doc;
NodeList nList, nListS, nListT;
Element Elemento, ElementoS, ElementoO, ElementoT, ElementoSo;
Oficina O;
int i, j, nT;
doc = dBuilder.newDocument();
// Elemento raz
//
Elemento = doc.createElement(ManejoColas);
doc.appendChild(Elemento);
// Servicios
//
ElementoS = doc.createElement(Servicios);
Elemento.appendChild(ElementoS);
for (i=0; i < ObtNumeroServicios(); i++) {
Servicio S = ObtServicio(i);
if (S != null) {
ElementoSo = doc.createElement(Servicio);
ElementoSo.appendChild(doc.createTextNode(S.ObtNombre()));
ElementoS.appendChild(ElementoSo);
}
346

VOLVER A MEN

}
// Se agrega la oficina
//
O = BuscarOficina(1);
if (O != null) {
ElementoO = doc.createElement(Oficina);
ElementoO.setAttribute(Id, 1);
ElementoO.setAttribute(Taquillas, Integer.toString(O.ObtNumeroTaquillas()));
Elemento.appendChild(ElementoO);
for (i=0; i < O.ObtNumeroTaquillas(); i++) {
ElementoT = doc.createElement(Taquilla);
ElementoT.setAttribute(Id, Integer.toString(i+1));
ElementoO.appendChild(ElementoT);
for (j=0; j < O.ObtNumeroServiciosTaquilla(i); j++) {
Servicio S = O.ObtServicioTaquilla(i, j);
if (S != null) {
ElementoS = doc.createElement(Servicio);
ElementoS.appendChild(doc.createTextNode(S.ObtNombre()));
ElementoT.appendChild(ElementoS);
}
}
}
}
// Se escribe la data en el archivo XML
//
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(XmlFile);
transformer.transform(source, result);
347

VOLVER A MEN

return true;
} catch (ParserConfigurationException ex) {
Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);
} catch (TransformerConfigurationException ex) {
Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);
} catch (TransformerException ex) {
Logger.getLogger(ManejoColas.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Servicio.java
* Mauricio Paletta
*/
package ManejadorColas;
// Representa un servicio a ser prestado por una taquilla de una oficina
//
public class Servicio {
private String Nombre;
public Servicio(String Str) {
Nombre = Str;
}
public String ObtNombre() {
return Nombre;
}

348

VOLVER A MEN

@Override
public String toString() {
return ObtNombre();
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Cliente.java
* Mauricio Paletta
*/
package ManejadorColas;
// Representa un cliente de la oficina
//
public class Cliente {
public enum PrioridadAtencion {
Baja, Media, Alta
}
private PrioridadAtencion Prioridad;
private int Id;
public Cliente(int id, PrioridadAtencion p) {
Id = id;
Prioridad = p;
}
public int ObtId() {
return Id;
}
349

VOLVER A MEN

public PrioridadAtencion ObtPrioridad() {


return Prioridad;
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Oficina.java
* Mauricio Paletta
*/
package ManejadorColas;
import java.util.*;
// Representa una oficina prestataria de servicios
//
public class Oficina {
// Representa un gestor de estadsticas x oficina
//
class GestorEstadisticas {
protected int TotalClientesOficina;
protected int []TotalClientesTaquilla;
protected int []TotalClientesServicio;
protected double PromedioAtencion, MinimoAtencion, MaximoAtencion;
public GestorEstadisticas(int N, int S) {
int i;
if (N <= 0) N = 1;
TotalClientesOficina = 0;
PromedioAtencion = MaximoAtencion = 0.0;
350

VOLVER A MEN

MinimoAtencion = Double.MAX_VALUE;
TotalClientesTaquilla = new int[N];
TotalClientesServicio = new int[S];
for (i=0; i < N; i++)
TotalClientesTaquilla[i] = 0;
for (i=0; i < S; i++)
TotalClientesServicio[i] = 0;
}
private void AgregarClienteTaquilla(ClienteEspera CE) {
if (CE == null || CE.T == null || CE.S == null) return;
if (CE.T.Id <= 0 || CE.T.Id > TotalClientesTaquilla.length) return;
if (ManejadorColas.BuscarIndiceServicio(CE.S.ObtNombre()) < 0) return;
TotalClientesOficina++;
TotalClientesTaquilla[CE.T.Id-1]++;
TotalClientesServicio[ManejadorColas.BuscarIndiceServicio(CE.S.ObtNombre())]++;
}
private void ClienteServido(double Tpo) {
PromedioAtencion += Tpo;
if (Tpo < MinimoAtencion) MinimoAtencion = Tpo;
if (Tpo > MaximoAtencion) MaximoAtencion = Tpo;
}
@Override
public String toString() {
String Texto = ;
double Promedio;
Texto += Total Clientes Oficina: + Integer.toString(TotalClientesOficina) + \n;
Texto += Total Clientes Taquilla:\n;
for (int t=0; t < TotalClientesTaquilla.length; t++) {
Texto += \t + Integer.toString(t+1) + : + Integer.toString(TotalClientesTaquilla[t]) +\n;
351

VOLVER A MEN

}
Texto += Total Clientes Servicio:\n;
for (int s=0; s < TotalClientesServicio.length; s++) {
Texto += \t + ManejadorColas.BuscarServicio(s) + : +

Integer.toString(TotalClientesServicio[s]) +\n;

}
Promedio = TotalClientesOficina > 0 ? PromedioAtencion / TotalClientesOficina : 0.0;
Texto += Promedio de atencin: + Double.toString(Promedio) + \n;
Texto += Tiempo mnimo de atencin: + Double.toString(MinimoAtencion) + \n;
Texto += Tiempo mximo de atencin: + Double.toString(MaximoAtencion) + \n;
return Texto;
}
}
// Representa una taquilla de la oficina
//
public class Taquilla {
private int Id;
private ArrayList<Servicio> Servicios;
private boolean Habilitada;
public Taquilla(int id) {
Id = id;
Servicios = new ArrayList<Servicio>();
Habilitada = true;
}
public int ObtId() {
return Id;
}
@Override
352

VOLVER A MEN

public String toString() {


return Integer.toString(Id);
}
public int ObtNumeroServicios() {
return Servicios.size();
}
public Servicio ObtServicio(int Ind) {
return Servicios.get(Ind);
}
public void Habilitar() {
Habilitada = true;
}
public void Deshabilitar() {
Habilitada = false;
}
public boolean EstaHabilitada() {
return Habilitada;
}
private Servicio BuscarServicio(String nombreS) {
for (Iterator<Servicio> It = Servicios.iterator(); It.hasNext(); ) {
Servicio S = It.next();
if (nombreS.compareTo(S.ObtNombre()) == 0) return S;
}
return null;
}
public void AgregarServicio(Servicio S) {
// Primero se verifica si el servicio ya existe
353

VOLVER A MEN

//
if (S == null || Servicios.indexOf(S) >= 0 || BuscarServicio(S.ObtNombre()) != null) return;
Servicios.add(S);
}
public void AgregarServicio(String nombreS) {
// Primero se verifica si el servicio ya existe
//
if (BuscarServicio(nombreS) != null) return;
Servicios.add(new Servicio(nombreS));
}
public void RemoverServicio(Servicio S) {
Servicios.remove(S);
}
public void RemoverServicio(String nombreS) {
Servicios.remove(BuscarServicio(nombreS));
}
public boolean OfreceServicio(String nombreS) {
return BuscarServicio(nombreS) != null;
}
}
// Representa un cliente en espera
//
public enum Estado {
Espera, Atendiendo, Servido
}
public class ClienteEspera {
private Cliente C;
private Servicio S;
private Taquilla T;
354

VOLVER A MEN

private int N;
private Estado E;
private long TiempoServicio;
// Para representar el reloj del tiempo de duracin del cliente en espera
//
private TimerTask timerEspera = new TimerTask() {
public void run() {
// Este cliente ya ha sido servido
//
E = Estado.Servido;
timerE.cancel();
// Se actualiza las estadsticas
//
Estadisticas.ClienteServido((double)TiempoServicio);
}
};
private Timer timerE;
public ClienteEspera(Cliente c, Servicio s, Taquilla t, int n) {
C = c; S = s; T = t; N = n; E = Estado.Espera;
}
public Cliente ObtCliente() { return C; }
public Servicio ObtServicio() { return S; }
public Taquilla ObtTaquilla() { return T; }
public int ObtNumeroAtencion() { return N; }
public Estado ObtEstado() { return E; }
public void Atender() {
E = Estado.Atendiendo;
timerE = new Timer();
TiempoServicio = 1000 * (new Random().nextInt(6) + 1);
355

VOLVER A MEN

timerE.scheduleAtFixedRate(timerEspera, 0, TiempoServicio);
}
}
private int Id;
private Taquilla []Taquillas;
protected GestorEstadisticas Estadisticas;
private int nCliente;
private ArrayList<ClienteEspera> ClientesEspera;
public Oficina(int id, int nT, int nS) {
Id = id;
if (nT <= 0) nT = 1;
Taquillas = new Taquilla[nT];
for (int i=0; i < nT; i++) {
Taquillas[i] = new Taquilla(i+1);
Taquillas[i].Habilitar();
}
Estadisticas = new GestorEstadisticas(nT, nS);
nCliente = 0;
ClientesEspera = new ArrayList<ClienteEspera>();
}
public int ObtId() {
return Id;
}
@Override
public String toString() {
return Integer.toString(Id);
}

356

VOLVER A MEN

public int ObtNumeroTaquillas() {


return (Taquillas != null ? Taquillas.length : 0);
}
public int ObtNumeroServiciosTaquilla(int idT) {
return (Taquillas != null && idT < Taquillas.length ? Taquillas[idT].ObtNumeroServicios() : 0);
}
public Servicio ObtServicioTaquilla(int idT, int idS) {
if (Taquillas == null || idT < 0 || idT >= Taquillas.length) return null;
return Taquillas[idT].ObtServicio(idS);
}
public void HabilitarTaquilla(int idT) {
if (idT < 0 || idT >= Taquillas.length) return;
Taquillas[idT].Habilitar();
}
public void DeshabilitarTaquilla(int idT) {
if (idT < 0 || idT >= Taquillas.length) return;
Taquillas[idT].Deshabilitar();
}
public Taquilla BuscarTaquilla(int Idx) {
return (Idx >= 0 && Idx < Taquillas.length ? Taquillas[Idx] : null);
}
public int ProximoCliente() {
return ++nCliente;
}
public void LimpiarClientes() {
nCliente = 0;
}
357

VOLVER A MEN

public void AgregarServicioTaquilla(String nombreS, int idT) {


if (idT < 0 || idT >= Taquillas.length) return;
Taquillas[idT].AgregarServicio(nombreS);
}
// Revisa que todos los servicios S estn garantizados a ser
// prestados en la oficina
//
protected boolean RevisarPrestacionServicios(ArrayList<Servicio> Ss) {
boolean ServicioPrestado;
// Se verifica primero si hay servicios que revisar
//
if (Ss.isEmpty()) return true;
// Para todos los servicios a verificar
//
for (Iterator<Servicio> It = Ss.iterator(); It.hasNext(); ) {
Servicio S = It.next();
ServicioPrestado = false;
// Para todas las taquillas habilitadas de esta oficina
//
for (int t=0; t < Taquillas.length; t++) {
// Solo se toman en cuenta taquillas habilitadas
//
if (Taquillas[t].EstaHabilitada()) {
// La taquilla debe dar al menos un servicio
//
if (Taquillas[t].Servicios.isEmpty()) return false;
if (Taquillas[t].OfreceServicio(S.ObtNombre())) {
ServicioPrestado = true;
break;
358

VOLVER A MEN

}
}
}
// Este servicio debe ser dado por al menos una taquilla
//
if (!ServicioPrestado) return false;
}
return true;
}
public void NuevoClienteEspera(Cliente c, Servicio s) {
if (c == null || s == null) return;
// Se buscan las taquillas que ofrecen el servicio solicitado por el cliente
//
int nMenor, tMenor;
int T[] = new int[Taquillas.length];
ClienteEspera CE;
for (int i=0; i < Taquillas.length; i++) {
T[i] = (Taquillas[i].EstaHabilitada() && Taquillas[i].OfreceServicio(s.ObtNombre()) ? 0 : -1);
}
// Para todos los clientes actualmente en espera
// Hay que buscar la taquilla que ofrezca el servicio solicitado por el cliente
// con el menor nmero de clientes de la misma prioridad
//
for (Iterator<ClienteEspera> It = ClientesEspera.iterator(); It.hasNext(); ) {
CE = It.next();
if (CE.E != Estado.Servido && T[CE.T.Id - 1] >= 0 && T[CE.T.Id - 1] < CE.N)

T[CE.T.Id - 1] = CE.N;

}
359

VOLVER A MEN

nMenor = Integer.MAX_VALUE;
tMenor = -1;
for (int i=0; i < Taquillas.length; i++) {
if (T[i] >= 0) {
if (T[i] < nMenor) {
nMenor = T[i];
tMenor = i;
}
}
}
if (tMenor >= 0 && tMenor < Taquillas.length) {
CE = new ClienteEspera(c, s, Taquillas[tMenor], nMenor+1);
ClientesEspera.add(CE);
// Se actualizan las estadsticas
//
Estadisticas.AgregarClienteTaquilla(CE);
}
}
public void RevisarEstadoTaquillas() {
ClienteEspera CE, Menores[] = new ClienteEspera[Taquillas.length];
boolean TaquillaLibre[] = new boolean[Taquillas.length];
int T, c;
// Si no hay clientes en espera nada que hacer
//
if (ClientesEspera.isEmpty()) return;
// Para todas las taquillas de la oficina
//
for (T=0; T < Taquillas.length; T++) {
360

VOLVER A MEN

Menores[T] = null;
TaquillaLibre[T] = true;
}
for (c=0; c < ClientesEspera.size(); ) {
CE = ClientesEspera.get(c);
T = CE.ObtTaquilla().Id - 1;
if (CE.E == Estado.Atendiendo) {
// Si el cliente no ha sido servido an, la taquilla no est disponible
//
TaquillaLibre[T] = false;
c++;
}
else
if (CE.E == Estado.Servido) {
// Si el cliente ya fue servido se debe sacar de la cola
//
ClientesEspera.remove(CE);
}
else {
// Forma parte de la bsqueda de un nuevo cliente a ser atendido
//
if ((Menores[T] == null) ||
(CE.C.ObtPrioridad().compareTo(Menores[T].C.ObtPrioridad()) > 0) ||
(CE.C.ObtPrioridad().compareTo(Menores[T].C.ObtPrioridad()) > 0 &&
CE.N < Menores[T].N))
Menores[T] = CE;
c++;
}
}

361

VOLVER A MEN

// Hacer las asignaciones de los clientes en las respectivas taquillas


//
for (T=0; T < Taquillas.length; T++) {
if (TaquillaLibre[T] && Menores[T] != null) {
Menores[T].E = Estado.Atendiendo;
Menores[T].Atender();
}
}
}
// Buscar el cliente que se est atendiendo actualmente en la taquilla
// indicada y retornar una cadena de caracteres con formato para actualizar
// la lista de estado de avance correspondiente
//
public String ClienteAtendidoTaquilla(Taquilla T) {
for (Iterator<ClienteEspera> It=ClientesEspera.iterator(); It.hasNext(); ) {
ClienteEspera CE = It.next();
if (CE.T == T && CE.E == Estado.Atendiendo)
return Integer.toString(CE.C.ObtId()) + ( + Integer.toString(CE.N) + );
}
return ;
}
public String ClienteAtendidoTaquilla(int idxT) {
return idxT >= 0 && idxT < Taquillas.length ? ClienteAtendidoTaquilla(Taquillas[idxT]) : ;
}
// Armar en una cadena de caracteres la cola de clientes asociados a un servicio
// de una taquilla especfica
//
public String ClientesEsperaTaquilla(Taquilla T, Servicio S) {
String Clientes = ;
for (Iterator<ClienteEspera> It=ClientesEspera.iterator(); It.hasNext(); ) {
ClienteEspera CE = It.next();
362

VOLVER A MEN

if (CE.T == T && CE.E != Estado.Atendiendo && CE.S == S)


Clientes = Clientes + Integer.toString(CE.C.ObtId()) + ( + Integer.toString(CE.N) + ) ;
}
return Clientes;
}
// Obtener las estadsticas en un formato texto
//
public String ObtEstadisticas() {
return Estadisticas.toString();
}
}

El cdigo fuente relativo a C# aparece a continuacin y, al igual que los casos anteriores, no
se incluye el manejo de la interfaz grfica. De la misma manera como se hizo con Java, se
tiene un paquete (espacio de nombres) identificado como ManejadorColas y los siguientes
archivos fuente: ManejadorColas.cs, Servicio.cs, Cliente.cs y Oficina.cs.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* ManejadorColas.cs
* Mauricio Paletta
*/
using System;
using System.Collections;
using System.Windows.Forms;
using System.Xml;
namespace ManejoColas
{
public class ManejadorColas
{
private static ArrayList Servicios = null;
363

VOLVER A MEN

private ArrayList Oficinas;


private Random Rnd;
private TreeView TreeSimulacion;
private System.Timers.Timer timerS;
public ManejadorColas()
{
if (Servicios == null)
Servicios = new ArrayList();
Oficinas = new ArrayList();
}
// Para representar el reloj de la simulacin
//
private delegate void TreeDelegate();
private void TreeSimulacionDelegate()
{
if (TreeSimulacion.InvokeRequired)
{
TreeDelegate Del = new TreeDelegate(TreeSimulacionDelegate);
TreeSimulacion.Invoke(Del);
return;
}
// Actaulizar el rbol de visualizacin del estado de la simulacin
//
TreeNode nOficina, nTaquilla, nServicio;
Oficina O = BuscarOficina(1);
string N, Aux;
int A;
nOficina = TreeSimulacion.Nodes[0];
if (nOficina == null) return;
364

VOLVER A MEN

for (int t=0; t < nOficina.Nodes.Count; t++) {


Oficina.Taquilla T = O.BuscarTaquilla(t);
Aux = Taquilla + Convert.ToString(t+1);
nTaquilla = nOficina.Nodes[t];
if (nTaquilla != null && T != null) {
if (!T.EstaHabilitada()) Aux = Aux + (X);
nTaquilla.Text = Aux + : + O.ClienteAtendidoTaquilla(T);
// Para todos los servicios de la taquilla
//
for (int s=0; s < nTaquilla.Nodes.Count; s++) {
nServicio = nTaquilla.Nodes[s];
if (nServicio != null) {
Aux = nServicio.Text;
A = Aux.IndexOf(:);
if (A >= 0) {
N = Aux.Substring(0, A);
nServicio.Text = N + : + O.ClientesEsperaTaquilla(O.BuscarTaquilla(t),
BuscarServicio(N));
}
}
}
}
}
TreeSimulacion.ExpandAll();
}
private void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e)
{
// Ciclo de simulacin
//
Servicio S;
Oficina O = BuscarOficina(1);
365

VOLVER A MEN

if (O == null) return;
// Se revisa primero el estado de las taquillas
//
O.RevisarEstadoTaquillas();
// Comprobar si hay o no un cliente
// Hay un cliente, se decide su prioridad
//
if (Rnd.NextDouble() > 0.5) {
Cliente C;
PrioridadAtencion P;
doubleR = Rnd.NextDouble();

if (R < 0.33) P = PrioridadAtencion.Baja;


else
if (R < 0.66) P = PrioridadAtencion.Media;
else
P = PrioridadAtencion.Alta;
C = new Cliente(O.ProximoCliente(), P);
// Se selecciona al azar el servicio que desea hacer el cliente
//
S = ObtServicio(Rnd.Next(1, Servicios.Count+1));
O.NuevoClienteEspera(C, S);
}
TreeSimulacionDelegate();
}
private Servicio BuscarServicio(string nombreS)
{
for (int i=0; i < Servicios.Count; i++)
{
Servicio S = (Servicio)Servicios[i];
366

VOLVER A MEN

if (nombreS == S.ObtNombre()) return S;


}
return null;
}
public static Servicio BuscarServicio(int Ind)
{
return (Servicio)Servicios[Ind];
}
public static int BuscarIndiceServicio(string nombreS)
{
for (int i=0; i < Servicios.Count; i++)
{
Servicio S = (Servicio)Servicios[i];
if (nombreS == S.ObtNombre()) return i;
}
return -1;
}
public void AgregarServicio(string nombreS)
{
if (BuscarServicio(nombreS) != null) return;
Servicios.Add(new Servicio(nombreS));
}
public void RemoverServicio(string nombreS)
{
Servicio S = BuscarServicio(nombreS);
if (S == null) return;
Servicios.Remove(S);
}

367

VOLVER A MEN

public int ObtNumeroServicios()


{
return Servicios.Count;
}
public Servicio ObtServicio(int Ind) {
return (Servicio)Servicios[Ind];
}
public Oficina BuscarOficina(int iD)
{
for (int i=0; i < Oficinas.Count; i++)
{
Oficina O = (Oficina)Oficinas[i];
if (O.ObtId() == iD) return O;
}
return null;
}
public void AgregarOficina(int iD, int nT, int nS)
{
if (BuscarOficina(iD) != null) return;
Oficinas.Add(new Oficina(iD, nT, nS));
}
public void RemoverOficina(int iD)
{
Oficina O = BuscarOficina(iD);
if (O == null) return;
Oficinas.Remove(O);
}
// Elimina todos los datos existentes en el objeto
//
368

VOLVER A MEN

public void LimpiarDatos()


{
Servicios.Clear();
Oficinas.Clear();
}
// Revisa que todos los servicios estn garantizados a ser
// prestados en la oficina iD
//
public bool RevisarPrestacionServicios(int iD)
{
// Primero se ubica la oficina
//
Oficina O = BuscarOficina(iD);
if (O == null) return false;
return O.RevisarPrestacionServicios(Servicios);
}
public bool IniciarSimulacion(int iD, TreeView TreeS)
{
// La simulacin no puede iniciar si no estn dadas las condiciones
//
Oficina O = BuscarOficina(1);
if (!RevisarPrestacionServicios(iD) || O == null) return false;
// Iniciar la simulacin
//
Rnd = new Random();
timerS = new System.Timers.Timer();
timerS.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);
timerS.Interval = 1000;
O.LimpiarClientes();
TreeSimulacion = TreeS;
369

VOLVER A MEN

// Se carga el rbol de la simulacin


//
TreeNode nOficina = new TreeNode(Oficina 1);
TreeS.Nodes.Clear();
TreeS.Nodes.Add(nOficina);
// Se agregan las taquillas
//
for (int t=0; t < O.ObtNumeroTaquillas(); t++) {
TreeNode nTaquilla = new TreeNode(Taquilla + Convert.ToString(t+1));
// Se agregan los servicios de la taquilla
//
for (int s=0; s < O.ObtNumeroServiciosTaquilla(t); s++) {
Servicio S = O.ObtServicioTaquilla(t, s);
if (S != null) {
TreeNode nServicio = new TreeNode(S.ObtNombre() + : );
nTaquilla.Nodes.Add(nServicio);
}
}
nOficina.Nodes.Add(nTaquilla);
}
TreeSimulacion.ExpandAll();
timerS.Start();
return true;
}
public void PararSimulacion(int iD)
{
timerS.Stop();
}
public bool CargarConfiguracion(string NombreA)
{
int i, j, nT;
370

VOLVER A MEN

Oficina O;
XmlDocument XmlDoc = new XmlDocument();
XmlNodeList nList, nListS, nListT;
XmlElement Elemento;
XmlDoc.Load(NombreA);
nList = XmlDoc.GetElementsByTagName(ManejoColas);
if (nList != null)
{
// Primero se limpia cualquier dato existente
//
LimpiarDatos();
// Se cargan los servicios
//
nList = XmlDoc.GetElementsByTagName(Servicios);
if (nList != null)
{
nListS = nList.Item(0).ChildNodes;
for (i=0; i < nListS.Count; i++)

Elemento = (XmlElement)nListS[i];
AgregarServicio(Elemento.InnerText);
}
// Se agrega una oficina
//
Elemento = (XmlElement)XmlDoc.GetElementsByTagName(Oficina).Item(0);
nT = Convert.ToInt32(Elemento.GetAttribute(Taquillas));
AgregarOficina(1, nT, Servicios.Count);
O = BuscarOficina(1);

371

VOLVER A MEN

// Se cargan los servicios x taquillas


//
nListT = Elemento.GetElementsByTagName(Taquilla);
for (j=0; j < nListT.Count; j++) {
nListS = nListT.Item(j).ChildNodes;
for (i=0; i < nListS.Count; i++) {
Elemento = (XmlElement)nListS[i];
O.AgregarServicioTaquilla(Elemento.InnerText, j);
}
}
}
}
return true;
}
public bool GuardarConfiguracion(string NombreA)
{
int i, j;
Oficina O;
XmlDocument xmlDoc = new XmlDocument();
XmlElement Elemento, ElementoS, ElementoO, ElementoT, ElementoSo;
// Elemento raz
//
Elemento = xmlDoc.CreateElement(ManejoColas);
xmlDoc.AppendChild(Elemento);
// Servicios
//
ElementoS = xmlDoc.CreateElement(Servicios);
Elemento.AppendChild(ElementoS);
for (i=0; i < ObtNumeroServicios(); i++) {
Servicio S = ObtServicio(i);
if (S != null) {
372

VOLVER A MEN

ElementoSo = xmlDoc.CreateElement(Servicio);
ElementoSo.AppendChild(xmlDoc.CreateTextNode(S.ObtNombre()));
ElementoS.AppendChild(ElementoSo);
}
}
// Se agrega la oficina
//
O = BuscarOficina(1);
if (O != null) {
ElementoO = xmlDoc.CreateElement(Oficina);
ElementoO.SetAttribute(Id, 1);
ElementoO.SetAttribute(Taquillas, Convert.ToString(O.ObtNumeroTaquillas()));
Elemento.AppendChild(ElementoO);
for (i=0; i < O.ObtNumeroTaquillas(); i++) {
ElementoT = xmlDoc.CreateElement(Taquilla);
ElementoT.SetAttribute(Id, Convert.ToString(i+1));
ElementoO.AppendChild(ElementoT);
for (j=0; j < O.ObtNumeroServiciosTaquilla(i); j++) {
Servicio S = O.ObtServicioTaquilla(i, j);
if (S != null) {
ElementoS = xmlDoc.CreateElement(Servicio);
ElementoS.AppendChild(xmlDoc.CreateTextNode(S.ObtNombre()));
ElementoT.AppendChild(ElementoS);
}
}
}
}
// Se escribe la data en el archivo XML
//
xmlDoc.Save(NombreA);
373

VOLVER A MEN

return true;
}
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Servicio.cs
* Mauricio Paletta
*/
using System;
namespace ManejoColas
{
// Representa un servicio
//
public class Servicio
{
private string Nombre;
public Servicio(string Str) {
Nombre = Str;
}
public string ObtNombre() {
return Nombre;
}
public override string ToString() {
return ObtNombre();
}
}
}
374

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Cliente.cs
* Mauricio Paletta
*/
using System;
namespace ManejoColas
{
public enum PrioridadAtencion {
Baja, Media, Alta
}
// Representa un cliente
//
public class Cliente
{
private PrioridadAtencion Prioridad;
private int Id;
public Cliente(int id, PrioridadAtencion p)
{
Id = id;
Prioridad = p;
}
public int ObtId() {
return Id;
}
public PrioridadAtencion ObtPrioridad() {
return Prioridad;
}
}
}
375

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.5
* Oficina.cs
* Mauricio Paletta
*/
using System;
using System.Collections;
namespace ManejoColas
{
// Representa una oficina
//
public class Oficina
{
// Representa un gestor de estadsticas x oficina
//
protected class GestorEstadisticas {
protected int TotalClientesOficina;
protected int []TotalClientesTaquilla;
protected int []TotalClientesServicio;
protected double PromedioAtencion, MinimoAtencion, MaximoAtencion;
public GestorEstadisticas(int N, int S) {
int i;
if (N <= 0) N = 1;
TotalClientesOficina = 0;
PromedioAtencion = MaximoAtencion = 0.0;
MinimoAtencion = Double.MaxValue;
TotalClientesTaquilla = new int[N];
TotalClientesServicio = new int[S];
for (i=0; i < N; i++)
376

VOLVER A MEN

TotalClientesTaquilla[i] = 0;
for (i=0; i < S; i++)
TotalClientesServicio[i] = 0;
}
public void AgregarClienteTaquilla(ClienteEspera CE) {
if (CE == null || CE.ObtTaquilla() == null || CE.ObtServicio() == null) return;
if (CE.ObtTaquilla().ObtId() <= 0 || CE.ObtTaquilla().ObtId() > TotalClientesTaquilla.Length)

return;

if (ManejadorColas.BuscarIndiceServicio(CE.ObtServicio().ObtNombre()) < 0) return;


TotalClientesOficina++;
TotalClientesTaquilla[CE.ObtTaquilla().ObtId()-1]++;
TotalClientesServicio[
ManejadorColas.BuscarIndiceServicio(CE.ObtServicio().ObtNombre())]++;
}
public void ClienteServido(double Tpo) {
PromedioAtencion += Tpo;
if (Tpo < MinimoAtencion) MinimoAtencion = Tpo;
if (Tpo > MaximoAtencion) MaximoAtencion = Tpo;
}
public override string ToString() {
string Texto = ;
double Promedio;
Texto += Total Clientes Oficina: + Convert.ToString(TotalClientesOficina) + \n;
Texto += Total Clientes Taquilla:\n;
for (int t=0; t < TotalClientesTaquilla.Length; t++) {
Texto += \t + Convert.ToString(t+1) + : +
Convert.ToString(TotalClientesTaquilla[t]) +\n;
}
Texto += Total Clientes Servicio:\n;
377

VOLVER A MEN

for (int s=0; s < TotalClientesServicio.Length; s++) {


Texto += \t + ManejadorColas.BuscarServicio(s) + : +

Convert.ToString(TotalClientesServicio[s]) + \n;

}
Promedio = TotalClientesOficina > 0 ? PromedioAtencion / TotalClientesOficina : 0.0;
Texto += Promedio de atencin: + Convert.ToString(Promedio) + \n;
Texto += Tiempo mnimo de atencin: + Convert.ToString(MinimoAtencion) + \n;
Texto += Tiempo mximo de atencin: + Convert.ToString(MaximoAtencion) + \n;
return Texto;
}
}
// Representa una taquilla de la oficina
//
public class Taquilla {
private int Id;
private ArrayList Servicios;
private bool Habilitada;
public Taquilla(int id) {
Id = id;
Servicios = new ArrayList();
Habilitada = true;
}
public int ObtId() {
return Id;
}
public override string ToString() {
return Convert.ToString(Id);
}
378

VOLVER A MEN

public int ObtNumeroServicios() {


return Servicios.Count;
}
public Servicio ObtServicio(int Ind) {
return (Servicio)Servicios[Ind];
}
public void Habilitar() {
Habilitada = true;
}
public void Deshabilitar() {
Habilitada = false;
}
public bool EstaHabilitada() {
return Habilitada;
}
private Servicio BuscarServicio(string nombreS) {
for (int i=0; i < Servicios.Count; i++) {
Servicio S = (Servicio)Servicios[i];
if (nombreS == S.ObtNombre()) return S;
}
return null;
}
public void AgregarServicio(Servicio S) {
// Primero se verifica si el servicio ya existe
//
if (S == null || Servicios.IndexOf(S) >= 0 || BuscarServicio(S.ObtNombre()) != null) return;
Servicios.Add(S);
}
379

VOLVER A MEN

public void AgregarServicio(string nombreS) {


// Primero se verifica si el servicio ya existe
//
if (BuscarServicio(nombreS) != null) return;
Servicios.Add(new Servicio(nombreS));
}
public void RemoverServicio(Servicio S) {
Servicios.Remove(S);
}
public void RemoverServicio(string nombreS) {
Servicios.Remove(BuscarServicio(nombreS));
}
public bool OfreceServicio(string nombreS) {
return BuscarServicio(nombreS) != null;
}
}
// Representa un cliente en espera
//
public enum Estado {
Espera, Atendiendo, Servido
}
protected class ClienteEspera {
private Cliente C;
private Servicio S;
private Taquilla T;
private Oficina O;
private int N;
private Estado E;
380

VOLVER A MEN

private System.Timers.Timer timerE;


public ClienteEspera(Cliente c, Servicio s, Taquilla t, Oficina o, int n)
{
C = c; S = s; T = t; N = n; O = o; E = Estado.Espera;
timerE = new System.Timers.Timer();
timerE.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent);
timerE.Interval = 1000;
timerE.Enabled = false;
}
// Para representar el reloj del tiempo de duracin del cliente en espera
//
private void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e)
{
// Este cliente ya ha sido servido
//
E = Estado.Servido;
timerE.Stop();
// Se actualiza las estadsticas
//
O.ClienteServido((double)timerE.Interval);
}
public Cliente ObtCliente() { return C; }
public Servicio ObtServicio() { return S; }
public Taquilla ObtTaquilla() { return T; }
public int ObtNumeroAtencion() { return N; }
public Estado ObtEstado() { return E; }
public void AsgEstado(Estado e) { E = e; }
public void Atender()
{
381

VOLVER A MEN

E = Estado.Atendiendo;
timerE.Interval = 1000 * (new Random().Next(1, 7));
timerE.Start();
}
}
private int Id;
private Taquilla []Taquillas;
protected GestorEstadisticas Estadisticas;
private int nCliente;
private ArrayList ClientesEspera;
public Oficina(int id, int nT, int nS) {
Id = id;
if (nT <= 0) nT = 1;
Taquillas = new Taquilla[nT];
for (int i=0; i < nT; i++) {
Taquillas[i] = new Taquilla(i+1);
Taquillas[i].Habilitar();
}
Estadisticas = new GestorEstadisticas(nT, nS);
nCliente = 0;
ClientesEspera = new ArrayList();
}
public int ObtId() {
return Id;
}
public override string ToString() {
return Convert.ToString(Id);
}
public int ObtNumeroTaquillas() {
382

VOLVER A MEN

return (Taquillas != null ? Taquillas.Length : 0);


}
public int ObtNumeroServiciosTaquilla(int idT) {
return (Taquillas != null && idT < Taquillas.Length ? Taquillas[idT].ObtNumeroServicios() :
0);
}
public Servicio ObtServicioTaquilla(int idT, int idS) {
if (Taquillas == null || idT < 0 || idT >= Taquillas.Length) return null;
return Taquillas[idT].ObtServicio(idS);
}
public void HabilitarTaquilla(int idT) {
if (idT < 0 || idT >= Taquillas.Length) return;
Taquillas[idT].Habilitar();
}
public void DeshabilitarTaquilla(int idT) {
if (idT < 0 || idT >= Taquillas.Length) return;
Taquillas[idT].Deshabilitar();
}
public Taquilla BuscarTaquilla(int Idx) {
return (Idx >= 0 && Idx < Taquillas.Length ? Taquillas[Idx] : null);
}
public int ProximoCliente() {
return ++nCliente;
}
public void LimpiarClientes() {
nCliente = 0;
}
public void AgregarServicioTaquilla(string nombreS, int idT) {
383

VOLVER A MEN

if (idT < 0 || idT >= Taquillas.Length) return;


Taquillas[idT].AgregarServicio(nombreS);
}
// Revisa que todos los servicios S estn garantizados a ser
// prestados en la oficina
//
public bool RevisarPrestacionServicios(ArrayList Ss) {
bool ServicioPrestado;
// Se verifica primero si hay servicios que revisar
//
if (Ss.Count == 0) return true;
// Para todos los servicios a verificar
//
for (int i=0; i < Ss.Count; i++) {
Servicio S = (Servicio)Ss[i];
ServicioPrestado = false;
// Para todas las taquillas habilitadas de esta oficina
//
for (int t=0; t < Taquillas.Length; t++) {
// Solo se toman en cuenta taquillas habilitadas
//
if (Taquillas[t].EstaHabilitada()) {
// La taquilla debe dar al menos un servicio
//
if (Taquillas[t].ObtNumeroServicios() == 0) return false;
if (Taquillas[t].OfreceServicio(S.ObtNombre())) {
ServicioPrestado = true;
break;
}
384

VOLVER A MEN

}
}
// Este servicio debe ser dado por al menos una taquilla
//
if (!ServicioPrestado) return false;
}
return true;
}
public void NuevoClienteEspera(Cliente c, Servicio s) {
if (c == null || s == null) return;
// Se buscan las taquillas que ofrecen el servicio solicitado por el cliente
//
int nMenor, tMenor;
int []T = new int[Taquillas.Length];
ClienteEspera CE;
for (int i=0; i < Taquillas.Length; i++) {
T[i] = (Taquillas[i].EstaHabilitada() && Taquillas[i].OfreceServicio(s.ObtNombre()) ? 0 : -1);
}
// Para todos los clientes actualmente en espera
// Hay que buscar la taquilla que ofrezca el servicio solicitado por el cliente
// con el menor nmero de clientes de la misma prioridad
//
for (int i=0; i < ClientesEspera.Count; i++) {
CE = (ClienteEspera)ClientesEspera[i];
if (CE.ObtEstado() != Estado.Servido && T[CE.ObtTaquilla().ObtId() - 1] >= 0 &&
T[CE.ObtTaquilla().ObtId() - 1] < CE.ObtNumeroAtencion())
T[CE.ObtTaquilla().ObtId() - 1] = CE.ObtNumeroAtencion();
}
385

VOLVER A MEN

nMenor = int.MaxValue;
tMenor = -1;
for (int i=0; i < Taquillas.Length; i++) {
if (T[i] >= 0) {
if (T[i] < nMenor) {
nMenor = T[i];
tMenor = i;
}
}
}
if (tMenor >= 0 && tMenor < Taquillas.Length) {
CE = new ClienteEspera(c, s, Taquillas[tMenor], this, nMenor+1);
ClientesEspera.Add(CE);
// Se actualizan las estadsticas
//
Estadisticas.AgregarClienteTaquilla(CE);
}
}
public void RevisarEstadoTaquillas() {
ClienteEspera CE;
ClienteEspera []Menores = new ClienteEspera[Taquillas.Length];
bool []TaquillaLibre = new bool[Taquillas.Length];
int T, c;
// Si no hay clientes en espera nada que hacer
//
if (ClientesEspera.Count == 0) return;
// Para todas las taquillas de la oficina
//
for (T=0; T < Taquillas.Length; T++)
386

VOLVER A MEN

{
Menores[T] = null;
TaquillaLibre[T] = true;
}
for (c=0; c < ClientesEspera.Count; )
{
CE = (ClienteEspera)ClientesEspera[c];
T = CE.ObtTaquilla().ObtId() - 1;
if (CE.ObtEstado() == Estado.Atendiendo)

{
// Si el cliente no ha sido servido an, la taquilla no est disponible
//
TaquillaLibre[T] = false;
c++;
}
else
if (CE.ObtEstado() == Estado.Servido)

{
// Si el cliente ya fue servido se debe sacar de la cola
//
ClientesEspera.Remove(CE);
}
else

{
// Forma parte de la bsqueda de un nuevo cliente a ser atendido
//
if ((Menores[T] == null) ||
(CE.ObtCliente().ObtPrioridad().CompareTo(Menores[T].ObtCliente().ObtPrioridad()) >

0) ||
==
387

(CE.ObtCliente().ObtPrioridad().CompareTo(Menores[T].ObtCliente().ObtPrioridad())

VOLVER A MEN

0 && CE.ObtNumeroAtencion() < Menores[T].ObtNumeroAtencion()))

Menores[T] = CE;
c++;
}
}
// Hacer las asignaciones de los clientes en las respectivas taquillas
//
for (T=0; T < Taquillas.Length; T++)
{
if (TaquillaLibre[T] && Menores[T] != null)

{
Menores[T].AsgEstado(Estado.Atendiendo);
Menores[T].Atender();
}
}

}
// Buscar el cliente que se est atendiendo actualmente en la taquilla
// indicada y retornar una cadena de caracteres con formato para actualizar
// la lista de estado de avance correspondiente
//
public string ClienteAtendidoTaquilla(Taquilla T) {
for (int i=0; i < ClientesEspera.Count; i++) {
ClienteEspera CE = (ClienteEspera)ClientesEspera[i];
if (CE.ObtTaquilla() == T && CE.ObtEstado() == Estado.Atendiendo)
return Convert.ToString(CE.ObtCliente().ObtId()) + ( +

Convert.ToString(CE.ObtNumeroAtencion()) + );

}
return ;
}
public string ClienteAtendidoTaquilla(int idxT) {
return idxT >= 0 && idxT < Taquillas.Length ? ClienteAtendidoTaquilla(Taquillas[idxT]) : ;
388

VOLVER A MEN

}
// Armar en una cadena de caracteres la cola de clientes asociados a un servicio
// de una taquilla especfica
//
public string ClientesEsperaTaquilla(Taquilla T, Servicio S) {
string Clientes = ;
for (int i=0; i < ClientesEspera.Count; i++) {
ClienteEspera CE = (ClienteEspera)ClientesEspera[i];
if (CE.ObtTaquilla() == T && CE.ObtEstado() != Estado.Atendiendo &&

CE.ObtServicio() == S)
Clientes = Clientes + Convert.ToString(CE.ObtCliente().ObtId()) +

( + Convert.ToString(CE.ObtNumeroAtencion()) + ) ;

}
return Clientes;
}
// Obtener las estadsticas en un formato texto
//
public string ObtEstadisticas() {
return Estadisticas.ToString();
}
public void ClienteServido(double TiempoServicio) {
Estadisticas.ClienteServido(TiempoServicio);
}
}
}

389

VOLVER A MEN

6.6 El generador de energa


6.6.1 Enunciado
Se tiene un generador de energa elctrica (ver Figura 6.30) haciendo girar una turbina impulsada
por la presin generada por el vapor resultante de aplicar calor a un recipiente con agua
(una caldera). La temperatura de la caldera es controlada al encender / apagar un mechero
o quemador alimentado por un depsito de combustible y se mide en grados centgrados.
Es necesario controlar que el combustible no se agote (puede ser medido en porcentaje). La
cantidad de agua en la caldera se controla por una vlvula que permite la entrada del lquido
en ella y se mide en porcentaje. Existe tambin una vlvula que abre o cierra el paso del vapor
desde la caldera a la turbina para efectos de control de la presin dentro de la caldera medida
en bares. La velocidad de la turbina, medida en revoluciones por minuto, determina la cantidad
de energa generada en Kvolts.

Figura 6.30. Diagrama esquemtico del generador de energa.

6.6.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): El anlisis del enunciado nos lleva
a un posible mapa mental como el que se muestra en la Figura 6.31.

Figura 6.31. Mapa mental relativo al problema del generador de energa.

390

VOLVER A MEN

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Generador de


energa, turbina, caldera, depsito, mechero, agua, combustible, grado centgrado, porcentaje,
vlvula, vapor, presin, bar, revolucin por minuto, energa.
Paso 3 (Identificar procesos / requerimientos): Debe haber un ente ya sea humano o
automatizado que realice el control del sistema el cual consiste en el monitoreo de los valores
y acciones indicadas en la Figura 6.31. Tambin hay un actor que requiere o usa la energa
elctrica generada. Se propone entonces el diagrama de casos de uso que se muestra en la
Figura 6.32.

Figura 6.32. Diagrama de casos de uso relativo al problema del generador de energa.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Agua, combustible, grado centgrado, presin y energa se representan como atributos de
alguno de los conceptos seleccionados.
Porcentaje, bar, vapor y revolucin por minuto son irrelevantes para este problema.
Tanto el mechero como la caldera tienen un depsito cuya capacidad es medida en
porcentaje.
Se proponen las siguientes clases: Generador de energa, turbina, caldera, depsito, mechero
y vlvula. Luego se tiene el diagrama no detallado de clases que se muestra en la Figura 6.33.

Figura 6.33. Diagrama no detallado de clases relativo al problema del generador de energa.

391

VOLVER A MEN

6.6.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Se propone el uso de una iinterfaz grfica
con control manual. Las condiciones iniciales del sistema en cuanto a cantidad de agua,
combustible y temperatura inicial de la caldera sern tomadas al azar. Inicialmente el sistema
estar apagado (mechero y turbina apagados y vlvulas cerradas). Se asume que el agua y el
combustible se agregan en unidades de porcentaje.
Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.33
resulta el diagrama de clases detallado de la Figura 6.34.

Figura 6.34. Diagrama detallado de clases relativo al problema del generador de energa.

Paso 3 (Desarrollar los modelos de estado): La Figura 6.35 muestra los diagramas de estado
de cada una de las clases presentes en el modelo de este problema. En relacin a la clase
Generador energa, el cambio de estado est asociado al cambio de estado de cada uno de
sus tres componentes: LaCaldera, LaTurbina y ElMechero.

392

VOLVER A MEN

Figura 6.35. Diagramas de estado relativos al problema del generador de energa.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.36 muestra el diagrama de


colaboracin relativo a este problema y basado en el diagrama de casos de uso de la Figura
6.32 y el diagrama de clases de la Figura 6.34. Ntese el uso correcto de la identificacin de
los objetos especficos. Ntese tambin la presencia de los actores que estn en el diagrama
de casos de uso y que cada uno de los eventos que establecen una colaboracin son mtodos
de la clase en la cual va el sentido de la flecha del evento. Por ejemplo, cualquier instancia de
la clase Generador energa dispara el mensaje LaCaldera.AgregarAgua() que corresponde
al evento 9 en el diagrama de colaboracin. Por lo tanto, AgregarAgua() es un mtodo de
Caldera.

Figura 6.36. Diagrama de colaboracin relativo al problema del generador de energa.

393

VOLVER A MEN

Paso 5 (Identificar componentes del dominio): Se tiene el diagrama de componentes que se


muestra en la Figura 6.37 basado en el lenguaje de programacin Java. Todas las clases estn
definidas en el paquete GeneradorEnergia.

Figura 6.37. Diagrama de componentes relativo al problema del generador de energa.

6.6.4 Programacin
A continuacin el cdigo fuente escrito en C++. Las clases estn definidas en el paquete
(espacio de nombres) GeneradorEnergia. Sin tomar en cuenta lo reativo a la interfaz se
tienen los archivos GeneradorEnergia.hpp y GeneradorEnergia.cpp.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.6
* GeneradorEnergia.hpp
* Mauricio Paletta
*/
#ifndef GENERADORENERGIA_HPP
#define GENERADORENERGIA_HPP
#include <time.h>
namespace GeneradorEnergia {

394

VOLVER A MEN

// Representa una vlvula


//
class Valvula {
private:
bool Abierta;
public:
Valvula() { Abierta = false; }
void Abrir() { Abierta = true; }
void Cerrar() { Abierta = false; }
bool EstaAbierta() { return Abierta; }
};

// Representa un depsito de lquido medido en porcentaje


//
class Deposito {
private:
double Porcentaje;
public:
Deposito(double C=0.0) {
if (C > 100.0) C = 100.0;
Porcentaje = C;
}
void Agregar(double C) {
Porcentaje += C;
if (Porcentaje > 100.0) Porcentaje = 100.0;
}
void Consumir(double C) {
Porcentaje -= C;
if (Porcentaje < 0.0) Porcentaje = 0.0;
}
395

VOLVER A MEN

double ObtNivel() {
return Porcentaje;
}
};
// Representa un mechero o quemador
//
class Mechero {
private:
Deposito *DepositoCombustible;
bool Encendido;
time_t InicioEncendido;
public:
Mechero(double C=0.0) {
DepositoCombustible = new Deposito(C);
Encendido = false;
}
~Mechero() {
delete DepositoCombustible;
}
void Encender() { Encendido = true; time(&InicioEncendido); }
void Apagar() { Encendido = false; }
void AgregarCombustible(double C) {
DepositoCombustible->Agregar(C);
}
double ObtNivelCombustible() {
return DepositoCombustible->ObtNivel();
}
void RevisarCombustible();
bool EstaEncendido() { return Encendido; }
};

396

VOLVER A MEN

// Representa una caldera


//
class Caldera {
private:
Deposito *DepositoAgua;
Valvula Agua, Presion;
double Temperatura;
time_t CambioTemperatrura, TiempoEbullicion;
public:
Caldera(double A, double T) {
DepositoAgua = new Deposito(A);
Temperatura = T;
CambioTemperatrura = TiempoEbullicion = (time_t)0;
}
~Caldera() {
delete DepositoAgua;
}
void AgregarAgua(double A) {
Agua.Abrir();
DepositoAgua->Agregar(A);
Agua.Cerrar();
}
void AbrirValvulaPresion() {
Presion.Abrir();
}
void CerrarValvulaPresion() {
Presion.Cerrar();
}
double ObtNivelAgua() {
return DepositoAgua->ObtNivel();
}
double ObtTemperatura() { return Temperatura; }
397

VOLVER A MEN

void Revisar();
bool ValvulaAguaAbierta() { return Agua.EstaAbierta(); }
bool ValvulaPresionAbierta() { return Presion.EstaAbierta(); }
};
// Representa una turbina
//
class Turbina {
private:
double RPM;
public:
Turbina() {
RPM = 0.0;
}
void Parar() { RPM = 0.0; }
void Acelerar(double rpm) { RPM += rpm; }
void Disminuir(double rpm) {
RPM -= rpm;
if (RPM < 0.0) Parar();
}
double ObtRPM() { return RPM; }
};
// Representa un generador de energa
//
class GeneradorEnergia {
private:
Caldera *LaCaldera;
Turbina *LaTurbina;
Mechero *ElMechero;
public:
398

VOLVER A MEN

GeneradorEnergia(double C=0.0, double A=0.0, double T=27.0) {


LaCaldera = new Caldera(A, T);
LaTurbina = new Turbina;
ElMechero = new Mechero(C);
}
~GeneradorEnergia() {
delete LaCaldera;
delete LaTurbina;
delete ElMechero;
}
void Encender() {
ElMechero->Encender();
}
void Apagar() {
ElMechero->Apagar();
LaCaldera->CerrarValvulaPresion();
}
void AgregarCombustible(double C) {
ElMechero->AgregarCombustible(C);
}
void AgregarAgua(double A) {
LaCaldera->AgregarAgua(A);
}
double ObtNivelAgua() {
return LaCaldera->ObtNivelAgua();
}
double ObtNivelCombustible() {
return ElMechero->ObtNivelCombustible();
}
double ObtRPM() {
return LaTurbina->ObtRPM();
}
double ObtTemperatura() {
399

VOLVER A MEN

return LaCaldera->ObtTemperatura();
}
bool EstaEncendido() { return ElMechero->EstaEncendido(); }
void Generar();
void AbrirPresion() { LaCaldera->AbrirValvulaPresion(); }
void CerrarPresion() { LaCaldera->CerrarValvulaPresion(); }
};
}
#endif /* GENERADORENERGIA_HPP */

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.6
* GeneradorEnergia.cpp
* Mauricio Paletta
*/
#include <math.h>
#include GeneradorEnergia.hpp
namespace GeneradorEnergia {
void Mechero::RevisarCombustible() {
time_t TiempoActual;
double Consumido;
// Se asume un consumo de combustible equivalente a 0.33 unidades de % x segundo
//
time(&TiempoActual);
Consumido = 0.33 * difftime(TiempoActual, InicioEncendido);
DepositoCombustible->Consumir(Consumido);
400

VOLVER A MEN

if (DepositoCombustible->ObtNivel() <= 0.0) Apagar();


}
void Caldera::Revisar() {
time_t TiempoActual;
if (CambioTemperatrura == (time_t)0) {
time(&CambioTemperatrura);
return;
}

// La temperatura aumenta exponencialmente hasta alcanzar el nivel de ebullicin


// Mientras menos agua ms rpido el calentamiento
//
time(&TiempoActual);
if (Temperatura < 100.0)
Temperatura += exp(difftime(TiempoActual, CambioTemperatrura)) / DepositoAgua>ObtNivel();
// Se asume el consumo de agua de la caldera dependiendo si la vlvula de
// presin est abierta y se ha llegado a la temperatura de ebullicin
// En ese caso se consumen 3 unidades de % x segundo
//
if (Temperatura >= 100.0 && Presion.EstaAbierta()) {
if (TiempoEbullicion == (time_t)0) {
time(&TiempoEbullicion);
return;
}
DepositoAgua->Consumir(3.0 * difftime(TiempoActual, TiempoEbullicion));
}
}
void GeneradorEnergia::Generar() {
401

VOLVER A MEN

ElMechero->RevisarCombustible();
LaCaldera->Revisar();
// La aceleracin / disminucin de velocidad de la turbina depende de las
// condiciones de la caldera
// Se estima a 1 RPM cada 1/3 segundos
//
if (LaCaldera->ObtTemperatura() >= 100.0 && LaCaldera->ValvulaPresionAbierta())
LaTurbina->Acelerar(1.0);
else
LaTurbina->Disminuir(1.0);
}
}

A continuacin el cdigo escrito en Java. Se tienen los archivos Valvula.java, Turbina.


java, Deposito.java, Mechero.java, Caldera.java y GeneradorEnerga.java (ver Figura
6.37), cada uno de ellos representando la clase correspondiente y formando parte del paquete
GeneradorEnergia.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* Valvula.java
* Mauricio Paletta
*/
package GeneradorEnergia;
public class Valvula {
private boolean Abierta;
public Valvula() { Abierta = false; }
public void Abrir() { Abierta = true; }
public void Cerrar() { Abierta = false; }
public boolean EstaAbierta() { return Abierta; }
}
402

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* Turbina.java
* Mauricio Paletta
*/
package GeneradorEnergia;
public class Turbina {
private double RPM;
public Turbina() { RPM = 0.0; }
public void Parar() { RPM = 0.0; }
public void Acelerar(double rpm) { RPM += rpm; }
public void Disminuir(double rpm) {
RPM -= rpm;
if (RPM < 0.0) Parar();
}
public double ObtRPM() { return RPM; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* Deposito.java
* Mauricio Paletta
*/
package GeneradorEnergia;
public class Deposito {
private double Porcentaje;
public Deposito(double C) {
if (C > 100.0) C = 100.0;
403

VOLVER A MEN

Porcentaje = C;
}
public void Agregar(double C) {
Porcentaje += C;
if (Porcentaje > 100.0) Porcentaje = 100.0;
}
public void Consumir(double C) {
Porcentaje -= C;
if (Porcentaje < 0.0) Porcentaje = 0.0;
}
public double ObtNivel() { return Porcentaje; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* Mechero.java
* Mauricio Paletta
*/
package GeneradorEnergia;
import java.util.Calendar;
import java.util.Date;
public class Mechero {
private Deposito DepositoCombustible;
private boolean Encendido;
private Date InicioEncendido;
public Mechero(double C) {
DepositoCombustible = new Deposito(C);
Encendido = false;
}
404

VOLVER A MEN

public void Encender() {


Encendido = true;
InicioEncendido = Calendar.getInstance().getTime();
}
public void Apagar() { Encendido = false; }
public void AgregarCombustible(double C) {
DepositoCombustible.Agregar(C);
}
public double ObtNivelCombustible() {
return DepositoCombustible.ObtNivel();
}
public void RevisarCombustible() {
Date TiempoActual = Calendar.getInstance().getTime();
double Consumido;
// Se asume un consumo de combustible equivalente a 0.33 unidades de % x segundo
//
Consumido = 0.33 * (TiempoActual.getTime() - InicioEncendido.getTime()) / 1000;
DepositoCombustible.Consumir(Consumido);
if (DepositoCombustible.ObtNivel() <= 0.0) Apagar();
}
public boolean EstaEncendido() { return Encendido; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* Caldera.java
* Mauricio Paletta
*/
package GeneradorEnergia;
import java.util.Calendar;
405

VOLVER A MEN

import java.util.Date;
public class Caldera {
private Deposito DepositoAgua;
private Valvula Agua, Presion;
private double Temperatura;
private long CambioTemperatrura, TiempoEbullicion;
public Caldera(double A, double T) {
DepositoAgua = new Deposito(A);
Agua = new Valvula();
Presion = new Valvula();
Temperatura = T;
CambioTemperatrura = TiempoEbullicion = 0;
}
public void AgregarAgua(double A) {
Agua.Abrir();
DepositoAgua.Agregar(A);
Agua.Cerrar();
}
public void AbrirValvulaPresion() {
Presion.Abrir();
}
public void CerrarValvulaPresion() {
Presion.Cerrar();
}
public double ObtNivelAgua() {
return DepositoAgua.ObtNivel();
}
public double ObtTemperatura() { return Temperatura; }
public void Revisar() {
Date Aux = Calendar.getInstance().getTime();
long TiempoActual = Aux.getTime();
406

VOLVER A MEN

if (CambioTemperatrura == 0) {
CambioTemperatrura = TiempoActual;
return;
}
// La temperatura aumenta exponencialmente hasta alcanzar el nivel de ebullicin
// Mientras menos agua ms rpido el calentamiento
//
if (Temperatura < 100.0)
Temperatura += Math.exp((TiempoActual - CambioTemperatrura) / 1000) /
DepositoAgua.ObtNivel();
// Se asume el consumo de agua de la caldera dependiendo si la vlvula de
// presin est abierta y se ha llegado a la temperatura de ebullicin
// En ese caso se consumen 3 unidades de % x segundo
//
if (Temperatura >= 100.0 && Presion.EstaAbierta()) {
if (TiempoEbullicion == 0) {
TiempoEbullicion = TiempoActual;
return;
}
DepositoAgua.Consumir(3.0 * (TiempoActual - TiempoEbullicion) / 1000);
}
}
public boolean ValvulaAguaAbierta() { return Agua.EstaAbierta(); }
public boolean ValvulaPresionAbierta() { return Presion.EstaAbierta(); }
}

407

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.6
* GeneradorEnergia.java
* Mauricio Paletta
*/
package GeneradorEnergia;
public class GeneradorEnergia {
private Caldera LaCaldera;
private Turbina LaTurbina;
private Mechero ElMechero;
public GeneradorEnergia(double C, double A, double T) {
LaCaldera = new Caldera(A, T);
LaTurbina = new Turbina();
ElMechero = new Mechero(C);
}
public void Encender() { ElMechero.Encender(); }
public void Apagar() {
ElMechero.Apagar();
LaCaldera.CerrarValvulaPresion();
}
public void AgregarCombustible(double C) {
ElMechero.AgregarCombustible(C);
}
public void AgregarAgua(double A) {
LaCaldera.AgregarAgua(A);
}
public double ObtNivelAgua() {
return LaCaldera.ObtNivelAgua();
}
public double ObtNivelCombustible() {
408

VOLVER A MEN

return ElMechero.ObtNivelCombustible();
}
public double ObtRPM() {
return LaTurbina.ObtRPM();
}
public double ObtTemperatura() {
return LaCaldera.ObtTemperatura();
}
public boolean EstaEncendido() { return ElMechero.EstaEncendido(); }
public void Generar() {
ElMechero.RevisarCombustible();
LaCaldera.Revisar();
// La aceleracin / disminucin de velocidad de la turbina depende de las
// condiciones de la caldera
// Se estima a 1 RPM cada 1/3 segundos
//
if (LaCaldera.ObtTemperatura() >= 100.0 && LaCaldera.ValvulaPresionAbierta())
LaTurbina.Acelerar(1.0);
else
LaTurbina.Disminuir(1.0);
}
public void AbrirPresion() { LaCaldera.AbrirValvulaPresion(); }
public void CerrarPresion() { LaCaldera.CerrarValvulaPresion(); }
}

En relacin a C# se tiene el siguiente cdigo fuente. Al igual que en C++ y en Java, todas
las clases involucradas en el problema estn definidas en el paquete o espacio de nombres
GeneradorEnergia.

409

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.6
* GeneradorEnergia.cs
* Mauricio Paletta
*/
using System;
namespace GeneradorEnergia
{
public class Valvula
{
private bool Abierta;
public Valvula() { Abierta = false; }
public void Abrir() { Abierta = true; }
public void Cerrar() { Abierta = false; }
public bool EstaAbierta() { return Abierta; }
}
public class Turbina
{
private double RPM;
public Turbina() { RPM = 0.0; }
public void Parar() { RPM = 0.0; }
public void Acelerar(double rpm) { RPM += rpm; }
public void Disminuir(double rpm) {
RPM -= rpm;
if (RPM < 0.0) Parar();
}
public double ObtRPM() { return RPM; }
}

410

VOLVER A MEN

public class Deposito


{
private double Porcentaje;
public Deposito(double C) {
if (C > 100.0) C = 100.0;
Porcentaje = C;
}
public void Agregar(double C) {
Porcentaje += C;
if (Porcentaje > 100.0) Porcentaje = 100.0;
}
public void Consumir(double C) {
Porcentaje -= C;
if (Porcentaje < 0.0) Porcentaje = 0.0;
}
public double ObtNivel() { return Porcentaje; }
}
public class Mechero
{
private Deposito DepositoCombustible;
private bool Encendido;
private DateTime InicioEncendido;
public Mechero(double C)
{
DepositoCombustible = new Deposito(C);
Encendido = false;
}
public void Encender()
{
Encendido = true;
411

VOLVER A MEN

InicioEncendido = DateTime.Now;
}
public void Apagar() { Encendido = false; }
public void AgregarCombustible(double C)
{
DepositoCombustible.Agregar(C);
}
public double ObtNivelCombustible()
{
return DepositoCombustible.ObtNivel();
}
public void RevisarCombustible()
{
DateTime TiempoActual = DateTime.Now;
double Consumido;
// Se asume un consumo de combustible equivalente a 0.33 unidades de % x segundo
//
Consumido = 0.33 * TiempoActual.Subtract(InicioEncendido).Milliseconds / 1000;
DepositoCombustible.Consumir(Consumido);
if (DepositoCombustible.ObtNivel() <= 0.0) Apagar();
}
public bool EstaEncendido() { return Encendido; }
}
public class Caldera {
private Deposito DepositoAgua;
private Valvula Agua, Presion;
private double Temperatura;
private long CambioTemperatrura, TiempoEbullicion;
public Caldera(double A, double T)
{
412

VOLVER A MEN

DepositoAgua = new Deposito(A);


Agua = new Valvula();
Presion = new Valvula();
Temperatura = T;
CambioTemperatrura = TiempoEbullicion = 0;
}
public void AgregarAgua(double A)
{
Agua.Abrir();
DepositoAgua.Agregar(A);
Agua.Cerrar();
}
public void AbrirValvulaPresion()
{
Presion.Abrir();
}
public void CerrarValvulaPresion()
{
Presion.Cerrar();
}
public double ObtNivelAgua()
{
return DepositoAgua.ObtNivel();
}
public double ObtTemperatura() { return Temperatura; }
public void Revisar()
{
DateTime Aux = DateTime.Now;
long TiempoActual = Aux.Millisecond;
if (CambioTemperatrura == 0)
{
CambioTemperatrura = TiempoActual;
413

VOLVER A MEN

return;
}
// La temperatura aumenta exponencialmente hasta alcanzar el nivel de ebullicin
// Mientras menos agua ms rpido el calentamiento
//
if (Temperatura < 100.0)
Temperatura += Math.Exp((TiempoActual - CambioTemperatrura) / 1000) /
DepositoAgua.ObtNivel();
// Se asume el consumo de agua de la caldera dependiendo si la vlvula de
// presin est abierta y se ha llegado a la temperatura de ebullicin
// En ese caso se consumen 3 unidades de % x segundo
//
if (Temperatura >= 100.0 && Presion.EstaAbierta())
{
if (TiempoEbullicion == 0)
{
TiempoEbullicion = TiempoActual;
return;
}
DepositoAgua.Consumir(3.0 * (TiempoActual - TiempoEbullicion) / 1000);
}
}
public bool ValvulaAguaAbierta() { return Agua.EstaAbierta(); }
public bool ValvulaPresionAbierta() { return Presion.EstaAbierta(); }
}
public class GeneradorEnergia
{
private Caldera LaCaldera;
private Turbina LaTurbina;
private Mechero ElMechero;

414

VOLVER A MEN

public GeneradorEnergia(double C, double A, double T)


{
LaCaldera = new Caldera(A, T);
LaTurbina = new Turbina();
ElMechero = new Mechero(C);
}
public void Encender() { ElMechero.Encender(); }
public void Apagar()
{
ElMechero.Apagar();
LaCaldera.CerrarValvulaPresion();
}
public void AgregarCombustible(double C)
{
ElMechero.AgregarCombustible(C);
}
public void AgregarAgua(double A)
{
LaCaldera.AgregarAgua(A);
}
public double ObtNivelAgua()
{
return LaCaldera.ObtNivelAgua();
}
public double ObtNivelCombustible()
{
return ElMechero.ObtNivelCombustible();
}
public double ObtRPM()
{
return LaTurbina.ObtRPM();
}
public double ObtTemperatura()
415

VOLVER A MEN

{
return LaCaldera.ObtTemperatura();
}
public bool EstaEncendido() { return ElMechero.EstaEncendido(); }
public void Generar()
{
ElMechero.RevisarCombustible();
LaCaldera.Revisar();
// La aceleracin / disminucin de velocidad de la turbina depende de las
// condiciones de la caldera
// Se estima a 1 RPM cada 1/3 segundos
//
if (LaCaldera.ObtTemperatura() >= 100.0 && LaCaldera.ValvulaPresionAbierta())
LaTurbina.Acelerar(1.0);
else
LaTurbina.Disminuir(1.0);
}
public void AbrirPresion() { LaCaldera.AbrirValvulaPresion(); }
public void CerrarPresion() { LaCaldera.CerrarValvulaPresion(); }
}
}

6.7 La fbrica de galletas


6.7.1 Enunciado
En una fbrica automatizada de galletas se controla la produccin de diferentes tipos de
galletas, cada una de las cuales se prepara segn una frmula especfica que indica los tipos
de ingredientes a utilizar, la cantidad o peso de cada uno de ellos y los tiempos de amasado y
horneado requeridos. La fbrica consiste de un conjunto de depsitos de ingredientes diversos
(harina, azcar, avena, sal, etc.), cada uno de los cuales se conecta con una vlvula a un canal
que los lleva hasta una balanza que controla el peso que requiere la frmula con respecto a ese
ingrediente. Una vez que se determine en la balanza que el peso es correcto, otra vlvula hace
caer el ingrediente especfico a un pequeo depsito de almacenamiento temporal. Cuando
todos los ingredientes de la frmula han sido incluidos en este depsito se inicia el proceso
de amasado en la cual, tres vlvulas hacen pasar poco a poco las cantidades de ingredientes,
416

VOLVER A MEN

agua y otros lquidos necesarios para ir realizando la mezcla. Una vez que la masa est
concluida, una ltima vlvula deja caer la mezcla a un molde que a su vez va dejando caer la
preparacin moldeada a una correa transportadora que mueve el producto crudo a un horno
para su coccin. Al terminar la coccin esta misma correa trasporta el producto terminado hacia
su proceso de reposo y posterior almacenamiento. Para que el proceso se haga correctamente
se deben realizar tres controles: sobre la frmula, sobre el amasado de la mezcla y sobre el
horneado. La Figura 6.38 presenta un diagrama esquemtico de la fbrica de galletas.

Figura 6.38. Diagrama esquemtico de la fbrica de galleta.

6.7.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): La Figura 6.39 presenta un mapa
mental relativo al enunciado del problema.

Figura 6.39. Mapa mental relativo al problema de la fbrica de galletas.

417

VOLVER A MEN

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Fbrica, galleta,
frmula, ingrediente, cantidad, peso, tiempo de amasado, tiempo de horneado, depsito,
harina, azcar, avena, sal, vlvula, canal, balanza, agua, mezcla, molde, depsito, correa
transportadora, producto, horno y coccin.
Paso 3 (Identificar procesos / requerimientos): Debe haber un ente ya sea humano o
automatizado que realice el control de la fabricacin de las galletas el cual, como se mencion
en el enunciado, est asociado a tres partes del proceso: frmula, amasado y horneado. Ntese
que se ve conveniente para este problema representar tambin un actor que recibe las galletas
fabricadas y que puede representar un depsito de almacenamiento o almacn. El diagrama
de casos de uso correspondiente se muestra en la Figura 6.40.

Figura 6.40. Diagrama de casos de uso relativo al problema de la fbrica de galletas.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Cantidad, peso, tiempo de amasado y tiempo de horneado son atributos de la frmula.
Galleta, producto, mezcla y frmula son redundantes; se toma frmula por ser ms representativo
del problema.
Harina, azcar, avena, sal y agua son ingredientes especficos.
Canal, molde y correa transportadora son irrelevantes para el problema.
Coccin es una accin realizada por el horno.

Se proponen las siguientes clases: Fbrica, frmula, ingrediente, vlvula, balanza, depsito
y horno. En este sentido, se tiene el diagrama no detallado de clases que se muestra en la
Figura 6.41.
418

VOLVER A MEN

Figura 6.41. Diagrama no detallado de clases relativo al problema de la fbrica de galletas.

6.6.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Para este problema se usar una interfaz grfica
que permite mostrar la simulacin del control de la fbrica de galletas. La implementacin que se
muestra no tomar en cuenta la administracin de las frmulas las cuales sern representadas
en formato XML haciendo uso de la estructura que se muestra a continuacin.
<?xml version=1.0?>
<Frmula Nombre=un nombre Amasado=tiempo Horneado=tiempo Temperatura=grados>

<Ingrediente Nombre=nombre ingrediente 1 Peso=el peso/>

<Ingrediente Nombre=nombre ingrediente 2 Peso=el peso/>

</Frmula>

Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.41
resulta el diagrama de clases detallado de la Figura 6.42.

419

VOLVER A MEN

Figura 6.42. Diagrama detallado de clases relativo al problema de la fbrica de galletas.

Paso 3 (Desarrollar los modelos de estado): La Figura 6.43 muestra los diagramas de estado de
cada una de la mayora de las clases presentes en el modelo de este problema. No se incluye
la clase Vlvula por ser similar al del problema anterior (ver diagrama de estado en la Figura
6.35). Se omite la clase Fbrica por trener un diagrama extenso y depender sus estados de
los diferentes objetos de otras clases que forman parte de la fbrica: balanza, horno, depsito
y vlvulas. Ntese que los cambios de estado asociados al agregar / suprimir frmulas para la
clase Fbrica se representan de manera similar como se hace con el diagrama de estados de
la clase Frmula al agregar / suprimir ingredientes y que se muestra en la Figura 6.42.

420

VOLVER A MEN

Figura 6.43. Diagramas de estado relativos al problema de la fbrica de galletas.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.44 muestra el diagrama de


secuencias relativo a este problema y basado en el diagrama de casos de uso de la Figura
6.40 y el diagrama de clases de la Figura 6.42. Ntese el uso correcto de la identificacin de
los objetos especficos. Ntese tambin que, por la presencia de varios objetos involucrados
en el intercambio de mensajes y dado que este diagrama crece horizontalmente, se hace
complicado la revisin en detalle del mismo.

421

VOLVER A MEN

Figura 6.44. Diagrama de secuencias relativo al problema de la fbrica de galletas.

Paso 5 (Identificar componentes del dominio): Se tiene el diagrama de componentes que se


muestra en la Figura 6.45 basado en el lenguaje de programacin C#. Todas las clases estn
asociadas al paquete identificado como FrabricaGalletas.

Figura 6.45. Diagrama de componentes relativo al problema de la fbrica de galletas.

422

VOLVER A MEN

6.7.4 Programacin
Para el cdigo fuente correspondiente al lenguaje de programacin C++, todas las clases se
definieron en el archivo FabricaGalletas.hpp y parte de la definicin de los mtodos outline
estn en el archivo FabricaGalletas.cpp. A continuacin el contenido de estos archivos.
Ntese que se incorporaron otras clases adicionales a las identificadas en el diagrama de la
Figura 6.42. Estas clases se agregaron por comodidad para combinar un Depsito con una
Vlvula por ejemplo.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* FabricaGalletas.hpp
* Mauricio Paletta
*/
#ifndef FABRICAGALLETAS_HPP
#define FABRICAGALLETAS_HPP
#include <string>
#include <vector>
using namespace std;
class Balanza {
private:
double Peso;
public:
Balanza() { Peso = 0.0; }
void AgregarMasa(double M) { Peso += M; }
double ObtPeso() { return Peso; }
void Normalizar() { Peso = 0.0; }
};
class Deposito {
423

VOLVER A MEN

private:
double Capacidad;
double NivelActual;
string Contenido;
public:
Deposito(double C) {
Capacidad = C; NivelActual = 0.0;
}
void Agregar(double C) {
NivelActual += C;
if (NivelActual > Capacidad) NivelActual = Capacidad;
}
bool Consumir(double C) {
if (C > NivelActual) return false;
NivelActual -= C;
return true;
}
bool Vacio() { return NivelActual == 0.0; }
bool Lleno() { return NivelActual == Capacidad; }
string ObtContenido() { return Contenido; }
void AsgContenido(string N) { Contenido = N; }
double ObtNivelActual() { return NivelActual; }
};
class Valvula {
private:
bool Abierta;
public:
Valvula() { Abierta = false; }
void Abrir() { Abierta = true; }
void Cerrar() { Abierta = false; }
424

VOLVER A MEN

bool EstaAbierta() { return Abierta; }


};
class Horno {
private:
double Temperatura;
public:
Horno() { Temperatura = 0.0; }
double ObtTemperatura() { return Temperatura; }
void AsgTemperatura(double T) {
if (T < 1000.0 && T >= 0.0) Temperatura = T;
}
bool Encendido() { return Temperatura > 0.0; }
};
class Ingrediente {
private:
string Nombre;
public:
Ingrediente(string N) { Nombre = N; }
string ObtNombre() { return Nombre; }
};
typedef Ingrediente *pIngrediente;
class IngredienteFormula : public Ingrediente {
private:
double Peso;
public:
IngredienteFormula(string N, double P) : Ingrediente(N) {
Peso = P;
}
425

VOLVER A MEN

double ObtPeso() { return Peso; }


};
typedef IngredienteFormula *pIngredienteFormula;
class Formula {
private:
string Nombre;
vector<pIngredienteFormula> Ingredientes;
int TpoAmasado, TpoHorneado;
double Grados;
public:
Formula(string N, int TA, int TH, double Gr) {
Nombre = N; TpoAmasado = TA; TpoHorneado = TH; Grados = Gr;
}
~Formula();
string ObtNombre() { return Nombre; }
int ObtTpoAmasado() { return TpoAmasado; }
int ObtTpoHorneado() { return TpoHorneado; }
double ObtTemperatura() { return Grados; }
pIngredienteFormula BuscarIngrediente(string N);
bool AgregarIngrediente(string N, double P);
bool SuprimirIngrediente(string N);
double ObtPesoIngrediente(string N) {
pIngredienteFormula I = BuscarIngrediente(N);
if (I != NULL) return I->ObtPeso();
return 0.0;
}
pIngrediente ObtIngrediente(int Idx) {
if (Idx >= 0 && (unsigned)Idx < Ingredientes.size())
return (pIngrediente)Ingredientes[Idx];
return NULL;
426

VOLVER A MEN

}
int ObtNumeroIngredientes() { return Ingredientes.size(); }
};
typedef Formula *pFormula;
// Estados de la fabricacin
//
enum Etapas { Pesado, Amasado, Moldeado, Horneado };
// Permite representar un depsito para almacenar los
// diferentes ingredientes a usar en las frmulas
// Se asume una capacidad de 100 y lleno del ingrediente
//
class DepositoIngrediente : public Deposito {
public:
Valvula *V;
DepositoIngrediente() : Deposito(100) {
V = new Valvula();
Deposito::Agregar(100);
}
~DepositoIngrediente() { delete V; }
bool Consumir(double C) {
bool Ret;
V->Abrir();
Ret = Deposito::Consumir(C);
V->Cerrar();
return Ret;
}
};
typedef DepositoIngrediente *pDepositoIngrediente;

427

VOLVER A MEN

class BalanzaRegulada : public Balanza {


public:
Valvula *V;
BalanzaRegulada() : Balanza() {
V = new Valvula();
}
~BalanzaRegulada() { delete V; }
void Normalizar() {
V->Abrir();
Balanza::Normalizar();
V->Cerrar();
}
};
typedef BalanzaRegulada *pBalanzaRegulada;
class DepositoTemporal : public Deposito {
public:
Valvula *VIngrediente, *VAgua, *VOtros, *VMolde;
DepositoTemporal() : Deposito(1000) {
VIngrediente = new Valvula();
VAgua = new Valvula();
VOtros = new Valvula();
VMolde = new Valvula();
}
~DepositoTemporal() {
delete VIngrediente;
delete VAgua;
delete VOtros;
delete VMolde;
}

428

VOLVER A MEN

void AgregarAgua() {
VAgua->Abrir();
// ...
VAgua->Cerrar();
}
void AgregarOtrosLiquidos() {
VOtros->Abrir();
// ...
VOtros->Cerrar();
}
void VaciarAlMolde() {
VMolde->Abrir();
// ...
VMolde->Cerrar();
}
};
typedef DepositoTemporal *pDepositoTemporal;
class FabricaGalletas {
private:
vector<pFormula> Formulas;
vector<pDepositoIngrediente> Ingredientes;
pBalanzaRegulada LaBalanza;
pDepositoTemporal ElAmasador;
Horno *ElHorno;
pDepositoIngrediente BuscarIngrediente(string N);
public:
FabricaGalletas() {
LaBalanza = new BalanzaRegulada();
ElAmasador = new DepositoTemporal();
ElHorno = new Horno();
429

VOLVER A MEN

}
~FabricaGalletas();
bool AgregarFormula(pFormula Frm);
bool SuprimirFormula(Formula Frm);
bool SuprimirFormula(string Frm);
void SuprimirFormulas();
pFormula ObtFormula(int Idx) {
if (Idx < 0 || (unsigned)Idx >= Formulas.size())
return NULL;
return Formulas[Idx];
}
bool AgregarIngrediente(string N);
bool SuprimirIngrediente(string N);
void ConsumirIngrediente(string N, double C) {
pDepositoIngrediente D = BuscarIngrediente(N);
if (D == NULL) return;
D->Consumir(C);
}
void AgregarIngrediente(string N, double C) {
pDepositoIngrediente D = BuscarIngrediente(N);
if (D == NULL) return;
D->Agregar(C);
}
double ObtDisponibilidadIngrediente(string N) {
pDepositoIngrediente D = BuscarIngrediente(N);
if (D == NULL) return 0.0;
return D->ObtNivelActual();
}
double ObtPesoBalanza() {
430

VOLVER A MEN

return LaBalanza->ObtPeso();
}
void AgregarMasaBalanza(double M) {
LaBalanza->AgregarMasa(M);
}
void NormalizarBalanza() {
LaBalanza->Normalizar();
}
void AgregarIngredienteAmasador(double I) {
ElAmasador->Agregar(I);
}
void AsgTemperaturaHorno(double G) {
ElHorno->AsgTemperatura(G);
}
void AgregarAgua() { ElAmasador->AgregarAgua(); }
void AgregarOtrosLiquidos() { ElAmasador->AgregarOtrosLiquidos(); }
void Moldear() { ElAmasador->VaciarAlMolde(); }
};
#endif /* FABRICAGALLETAS_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.7
* FabricaGalletas.cpp
* Mauricio Paletta
*/
#include FabricaGalletas.hpp
Formula::~Formula() {
for(vector<pIngredienteFormula>::iterator It=Ingredientes.begin(); It != Ingredientes.end(); It++) {
431

VOLVER A MEN

pIngredienteFormula I = (pIngredienteFormula)*It;
if (I != NULL) delete I;
}
Ingredientes.clear();
}
pIngredienteFormula Formula::BuscarIngrediente(string N) {
pIngredienteFormula I;
for (unsigned i=0; i < Ingredientes.size(); i++) {
I = Ingredientes[i];
if (I->ObtNombre() == N) return I;
}
return NULL;
}
bool Formula::AgregarIngrediente(string N, double P) {
// Se verifica que el ingrediente no est repetido
//
pIngredienteFormula I = BuscarIngrediente(N);
if (I != NULL) return false;
Ingredientes.push_back(new IngredienteFormula(N, P));
return true;
}
bool Formula::SuprimirIngrediente(string N) {
pIngredienteFormula I;
for (unsigned i=0; i < Ingredientes.size(); i++) {
I = Ingredientes[i];
if (I->ObtNombre() == N) {
Ingredientes.erase(Ingredientes.begin() + i);
return true;
}
}
432

VOLVER A MEN

return false;
}
FabricaGalletas::~FabricaGalletas() {
delete LaBalanza;
delete ElAmasador;
delete ElHorno;
for(vector<pDepositoIngrediente>::iterator It=Ingredientes.begin(); It != Ingredientes.end(); It++) {
pDepositoIngrediente I = (pDepositoIngrediente)*It;
if (I != NULL) delete I;
}
Ingredientes.clear();
SuprimirFormulas();
}
pDepositoIngrediente FabricaGalletas::BuscarIngrediente(string N) {
pDepositoIngrediente D;
for (unsigned i=0; i < Ingredientes.size(); i++) {
D = Ingredientes[i];
if (D->ObtContenido() == N) return D;
}
return NULL;
}
bool FabricaGalletas::AgregarFormula(pFormula Frm) {
// Se revisa primero si no existe una frmula con ese nombre
//
for (unsigned i=0; i < Formulas.size(); i++) {
pFormula F = Formulas[i];
if (F == Frm || F->ObtNombre() == Frm->ObtNombre()) return false;
}
Formulas.push_back(Frm);
return true;
433

VOLVER A MEN

}
bool FabricaGalletas::SuprimirFormula(Formula Frm) {
for (unsigned i=0; i < Formulas.size(); i++) {
pFormula F = Formulas[i];
if (F->ObtNombre() == Frm.ObtNombre()) {
Formulas.erase(Formulas.begin() + i);
return true;
}
}
return false;
}
bool FabricaGalletas::SuprimirFormula(string Frm) {
for (unsigned i=0; i < Formulas.size(); i++) {
pFormula F = Formulas[i];
if (F->ObtNombre() == Frm) {
Formulas.erase(Formulas.begin() + i);
return true;
}
}
return false;
}
void FabricaGalletas::SuprimirFormulas() {
for (vector<pFormula>::iterator It=Formulas.begin(); It != Formulas.end(); It++) {
pFormula F = (pFormula)*It;
if (F != NULL) delete F;
}
Formulas.clear();
}
bool FabricaGalletas::AgregarIngrediente(string N) {
434

VOLVER A MEN

// Se revisa primero si no existe ya un ingrediente con ese nombre


//
pDepositoIngrediente D = BuscarIngrediente(N);
if (D != NULL) return false;
D = new DepositoIngrediente();
D->AsgContenido(N);
Ingredientes.push_back(D);
return true;
}
bool FabricaGalletas::SuprimirIngrediente(string N) {
pDepositoIngrediente D;
for (unsigned i=0; i < Ingredientes.size(); i++) {
D = Ingredientes[i];
if (D->ObtContenido() == N) {
Ingredientes.erase(Ingredientes.begin() + i);
return true;
}
}
return false;
}

Con respecto al lenguaje de programacin Java se tiene el siguiente cdigo fuente


correspondiente a los archivos Balanza.java, Deposito.java, Horno.java, Valvula.java,
Ingrediente.java, Formula.java y Fabrica.java. Todas estas clases se organizan en el
paquete FabricaGalletas.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Balanza.java
* Mauricio Paletta
*/
435

VOLVER A MEN

package FabricaGalletas;
public class Balanza {
private double Peso;
public Balanza() { Peso = 0.0; }
public void AgregarMasa(double M) { Peso += M; }
public double ObtPeso() { return Peso; }
public void Normalizar() { Peso = 0.0; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Deposito.java
* Mauricio Paletta
*/
package FabricaGalletas;
public class Deposito {
private double Capacidad;
private double NivelActual;
private String Contenido;
public Deposito(double C) {
Capacidad = C; NivelActual = 0.0;
}
public void Agregar(double C) {
NivelActual += C;
if (NivelActual > Capacidad) NivelActual = Capacidad;
}
public boolean Consumir(double C) {
if (C > NivelActual) return false;
NivelActual -= C;
436

VOLVER A MEN

return true;
}
public boolean Vacio() { return NivelActual == 0.0; }
public boolean Lleno() { return NivelActual == Capacidad; }
public String ObtContenido() { return Contenido; }
public void AsgContenido(String N) { Contenido = N; }
public double ObtNivelActual() { return NivelActual; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Horno.java
* Mauricio Paletta
*/
package FabricaGalletas;
public class Horno {
private double Temperatura;
public Horno() { Temperatura = 0.0; }
public double ObtTemperatura() { return Temperatura; }
public void AsgTemperatura(double T) {
if (T < 1000.0 && T >= 0.0) Temperatura = T;
}
public boolean Encendido() { return Temperatura > 0.0; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Valvula.java
* Mauricio Paletta
*/
437

VOLVER A MEN

package FabricaGalletas;
public class Valvula {
private boolean Abierta;
public Valvula() { Abierta = false; }
public void Abrir() { Abierta = true; }
public void Cerrar() { Abierta = false; }
public boolean EstaAbierta() { return Abierta; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Ingrediente.java
* Mauricio Paletta
*/
package FabricaGalletas;
public class Ingrediente {
private String Nombre;
public Ingrediente(String N) { Nombre = N; }
public String ObtNombre() { return Nombre; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Formula.java
* Mauricio Paletta
*/
package FabricaGalletas;
438

VOLVER A MEN

import java.util.ArrayList;
public class Formula {
class IngredienteFormula extends Ingrediente {
private double Peso;
public IngredienteFormula(String N, double P) {
super(N);
Peso = P;
}
public double ObtPeso() { return Peso; }
}
private String Nombre;
private ArrayList<IngredienteFormula> Ingredientes;
private int TpoAmasado, TpoHorneado;
private double Grados;
public Formula(String N, int TA, int TH, double Gr) {
Ingredientes = new ArrayList<IngredienteFormula>();
Nombre = N; TpoAmasado = TA; TpoHorneado = TH; Grados = Gr;
}
public String ObtNombre() { return Nombre; }
public int ObtTpoAmasado() { return TpoAmasado; }
public int ObtTpoHorneado() { return TpoHorneado; }
public double ObtTemperatura() { return Grados; }
private IngredienteFormula BuscarIngrediente(String N) {
IngredienteFormula I;
for (int i=0; i < Ingredientes.size(); i++) {
I = Ingredientes.get(i);
if (I.ObtNombre().compareTo(N) == 0) return I;
}
439

VOLVER A MEN

return null;
}
public boolean AgregarIngrediente(String N, double P) {
// Se verifica que el ingrediente no est repetido
//
IngredienteFormula I = BuscarIngrediente(N);
if (I != null) return false;
Ingredientes.add(new IngredienteFormula(N, P));
return true;
}
public boolean SuprimirIngrediente(String N) {
IngredienteFormula I = BuscarIngrediente(N);
if (I != null) {
Ingredientes.remove(I);
return true;
}
return false;
}
public double ObtPesoIngrediente(String N) {
IngredienteFormula I = BuscarIngrediente(N);
if (I != null) return I.ObtPeso();
return 0.0;
}
public Ingrediente ObtIngrediente(int Idx) {
if (Idx >= 0 && Idx < Ingredientes.size())
return (Ingrediente)Ingredientes.get(Idx);
return null;
}
public int ObtNumeroIngredientes() { return Ingredientes.size(); }
}

440

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.7
* Fabrica.java
* Mauricio Paletta
*/
package FabricaGalletas;
import java.util.ArrayList;
public class Fabrica {
// Estados de la fabricacin
//
public enum Etapas { Pesado, Amasado, Moldeado, Horneado }
// Permite representar un depsito para almacenar los
// diferentes ingredientes a usar en las frmulas
// Se asume una capacidad de 100 y lleno del ingrediente
//
class DepositoIngrediente extends Deposito {
public Valvula V;
public DepositoIngrediente() {
super(100);
V = new Valvula();
super.Agregar(100);
}
public boolean Consumir(double C) {
boolean Ret;
V.Abrir();
Ret = super.Consumir(C);
V.Cerrar();
return Ret;
}
441

VOLVER A MEN

}
class BalanzaRegulada extends Balanza {
public Valvula V;
public BalanzaRegulada() {
super();
V = new Valvula();
}
public void Normalizar() {
V.Abrir();
super.Normalizar();
V.Cerrar();
}
}
class DepositoTemporal extends Deposito {
public Valvula VIngrediente, VAgua, VOtros, VMolde;
public DepositoTemporal() {
super(1000);
VIngrediente = new Valvula();
VAgua = new Valvula();
VOtros = new Valvula();
VMolde = new Valvula();
}
public void AgregarAgua() {
VAgua.Abrir();
// ...
VAgua.Cerrar();
}
442

VOLVER A MEN

public void AgregarOtrosLiquidos() {


VOtros.Abrir();
// ...
VOtros.Cerrar();
}
public void VaciarAlMolde() {
VMolde.Abrir();
// ...
VMolde.Cerrar();
}
}
private ArrayList<Formula> Formulas;
private ArrayList<DepositoIngrediente> Ingredientes;
private BalanzaRegulada LaBalanza;
private DepositoTemporal ElAmasador;
private Horno ElHorno;
public Fabrica() {
Formulas = new ArrayList<Formula>();
Ingredientes = new ArrayList<DepositoIngrediente>();
LaBalanza = new BalanzaRegulada();
ElAmasador = new DepositoTemporal();
ElHorno = new Horno();
}
public boolean AgregarFormula(Formula Frm) {
// Se revisa primero si no existe una frmula con ese nombre
//
for (int i=0; i < Formulas.size(); i++) {
Formula F = Formulas.get(i);
if (F == Frm || F.ObtNombre().compareTo(Frm.ObtNombre()) == 0) return false;
}
Formulas.add(Frm);
443

VOLVER A MEN

return true;
}
public boolean SuprimirFormula(Formula Frm) {
for (int i=0; i < Formulas.size(); i++) {
Formula F = Formulas.get(i);
if (F == Frm) {
Formulas.remove(Frm);
return true;
}
}
return false;
}
public boolean SuprimirFormula(String Frm) {
for (int i=0; i < Formulas.size(); i++) {
Formula F = Formulas.get(i);
if (F.ObtNombre().compareTo(Frm) == 0) {
Formulas.remove(F);
return true;
}
}
return false;
}
public void SuprimirFormulas() {
Formulas.clear();
}
public Formula ObtFormula(int Idx) {
if (Idx < 0 || Idx >= Formulas.size())
return null;
return Formulas.get(Idx);
}
private DepositoIngrediente BuscarIngrediente(String N) {
DepositoIngrediente D;
444

VOLVER A MEN

for (int i=0; i < Ingredientes.size(); i++) {


D = Ingredientes.get(i);
if (D.ObtContenido().compareTo(N) == 0) return D;
}
return null;
}
public boolean AgregarIngrediente(String N) {
// Se revisa primero si no existe ya un ingrediente con ese nombre
//
DepositoIngrediente D = BuscarIngrediente(N);
if (D != null) return false;
D = new DepositoIngrediente();
D.AsgContenido(N);
Ingredientes.add(D);
return true;
}
public boolean SuprimirIngrediente(String N) {
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return false;
Ingredientes.remove(D);
return true;
}
public void ConsumirIngrediente(String N, double C) {
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return;
D.Consumir(C);
}
public void AgregarIngrediente(String N, double C) {
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return;
445

VOLVER A MEN

D.Agregar(C);
}
public double ObtDisponibilidadIngrediente(String N) {
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return 0.0;
return D.ObtNivelActual();
}
public double ObtPesoBalanza() {
return LaBalanza.ObtPeso();
}
public void AgregarMasaBalanza(double M) {
LaBalanza.AgregarMasa(M);
}
public void NormalizarBalanza() {
LaBalanza.Normalizar();
}
public void AgregarIngredienteAmasador(double I) {
ElAmasador.Agregar(I);
}
public void AsgTemperaturaHorno(double G) {
ElHorno.AsgTemperatura(G);
}
public void AgregarAgua() { ElAmasador.AgregarAgua(); }
public void AgregarOtrosLiquidos() { ElAmasador.AgregarOtrosLiquidos(); }
public void Moldear() { ElAmasador.VaciarAlMolde(); }
}
A continuacin el cdigo fuente relativo al lenguaje de programacin C#. Todas las clases involucradas
en el problema estn definidas en el paquete o espacio de nombres identificado como FabricaGalletas.

446

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.7
* FabricaGalletas.cs
* Mauricio Paletta
*/
using System;
using System.Collections.Generic;
namespace FabricaGalletas
{
public class Valvula
{
private bool Abierta;
public Valvula() { Abierta = false; }
public void Abrir() { Abierta = true; }
public void Cerrar() { Abierta = false; }
public bool EstaAbierta() { return Abierta; }
}
public class Deposito
{
private double Capacidad;
private double NivelActual;
private string Contenido;
public Deposito(double C)
{
Capacidad = C; NivelActual = 0.0;
}
public void Agregar(double C)
{
447

VOLVER A MEN

NivelActual += C;
if (NivelActual > Capacidad) NivelActual = Capacidad;
}
public bool Consumir(double C)
{
if (C > NivelActual) return false;
NivelActual -= C;
return true;
}
public bool Vacio() { return NivelActual == 0.0; }
public bool Lleno() { return NivelActual == Capacidad; }
public string ObtContenido() { return Contenido; }
public void AsgContenido(string N) { Contenido = N; }
public string ElContenido
{
get { return ObtContenido(); }
set { AsgContenido(value); }
}
public double ObtNivelActual() { return NivelActual; }
}

public class Ingrediente


{
private string Nombre;
public Ingrediente(string N) { Nombre = N; }
public string ObtNombre() { return Nombre; }
public string ElNombre
{
get { return ObtNombre(); }
}
}
448

VOLVER A MEN

public class Formula


{
class IngredienteFormula : Ingrediente
{
private double Peso;
public IngredienteFormula(string N, double P)
: base(N)
{
Peso = P;
}
public double ObtPeso() { return Peso; }
public double ElPeso
{
get { return ObtPeso(); }
}
}
private string Nombre;
private List<IngredienteFormula> Ingredientes;
private int TpoAmasado, TpoHorneado;
private double Grados;
public Formula(string N, int TA, int TH, double Gr)
{
Ingredientes = new List<IngredienteFormula>();
Nombre = N; TpoAmasado = TA; TpoHorneado = TH; Grados = Gr;
}
public string ObtNombre() { return Nombre; }
public string ElNombre
{
get { return ObtNombre(); }
}
public int ObtTpoAmasado() { return TpoAmasado; }
449

VOLVER A MEN

public int ElTiempoAmasado


{
get { return ObtTpoAmasado(); }
}
public int ObtTpoHorneado() { return TpoHorneado; }
public int ElTiempoHorneado
{
get { return ObtTpoHorneado(); }
}
public double ObtTemperatura() { return Grados; }
public double LaTemperatura
{
get { return ObtTemperatura(); }
}
private IngredienteFormula BuscarIngrediente(string N)
{
IngredienteFormula I;
for (int i=0; i < Ingredientes.Count; i++)
{
I = Ingredientes[i];
if (I.ElNombre == N) return I;
}
return null;
}
public bool AgregarIngrediente(string N, double P)
{
// Se verifica que el ingrediente no est repetido
//
IngredienteFormula I = BuscarIngrediente(N);
if (I != null) return false;
Ingredientes.Add(new IngredienteFormula(N, P));
return true;
}
450

VOLVER A MEN

public bool SuprimirIngrediente(string N)


{
IngredienteFormula I = BuscarIngrediente(N);
if (I != null)
{
Ingredientes.Remove(I);
return true;
}
return false;
}
public double ObtPesoIngrediente(string N)
{
IngredienteFormula I = BuscarIngrediente(N);
if (I != null) return I.ElPeso;
return 0.0;
}
public Ingrediente ObtIngrediente(int Idx)
{
if (Idx >= 0 && Idx < Ingredientes.Count)
return (Ingrediente)Ingredientes[Idx];
return null;
}
public int ObtNumeroIngredientes() { return Ingredientes.Count; }
}
public class Horno
{
private double Temperatura;
public Horno() { Temperatura = 0.0; }
public double ObtTemperatura() { return Temperatura; }
public void AsgTemperatura(double T)
{
451

VOLVER A MEN

if (T < 1000.0 && T >= 0.0) Temperatura = T;


}
public double LaTemperatura
{
get { return ObtTemperatura(); }
set { AsgTemperatura(value); }
}
public bool Encendido() { return Temperatura > 0.0; }
}
public class Balanza
{
private double Peso;
public Balanza() { Peso = 0.0; }
public void AgregarMasa(double M) { Peso += M; }
public double ObtPeso() { return Peso; }
public double ElPeso
{
get { return ObtPeso(); }
}
public void Normalizar() { Peso = 0.0; }
}
public class Fabrica
{
// Estados de la fabricacin
//
public enum Etapas { Pesado, Amasado, Moldeado, Horneado }
// Permite representar un depsito para almacenar los
// diferentes ingredientes a usar en las frmulas
// Se asume una capacidad de 100 y lleno del ingrediente
452

VOLVER A MEN

//
class DepositoIngrediente : Deposito
{
public Valvula V;
public DepositoIngrediente()
: base(100)
{
V = new Valvula();
base.Agregar(100);
}
public new bool Consumir(double C)
{
bool Ret;
V.Abrir();
Ret = base.Consumir(C);
V.Cerrar();
return Ret;
}
}
class BalanzaRegulada : Balanza
{
public Valvula V;
public BalanzaRegulada()
: base()
{
V = new Valvula();
}
public new void Normalizar()
{
453

VOLVER A MEN

V.Abrir();
base.Normalizar();
V.Cerrar();
}
}
class DepositoTemporal : Deposito
{
public Valvula VIngrediente, VAgua, VOtros, VMolde;
public DepositoTemporal()
: base(1000)
{
VIngrediente = new Valvula();
VAgua = new Valvula();
VOtros = new Valvula();
VMolde = new Valvula();
}
public void AgregarAgua()
{
VAgua.Abrir();
// ...
VAgua.Cerrar();
}
public void AgregarOtrosLiquidos()
{
VOtros.Abrir();
// ...
VOtros.Cerrar();
}
public void VaciarAlMolde()
{
454

VOLVER A MEN

VMolde.Abrir();
// ...
VMolde.Cerrar();
}
}
private List<Formula> Formulas;
private List<DepositoIngrediente> Ingredientes;
private BalanzaRegulada LaBalanza;
private DepositoTemporal ElAmasador;
private Horno ElHorno;
public Fabrica()
{
Formulas = new List<Formula>();
Ingredientes = new List<DepositoIngrediente>();
LaBalanza = new BalanzaRegulada();
ElAmasador = new DepositoTemporal();
ElHorno = new Horno();
}
public bool AgregarFormula(Formula Frm)
{
// Se revisa primero si no existe una frmula con ese nombre
//
for (int i=0; i < Formulas.Count; i++)
{
Formula F = Formulas[i];
if (F == Frm || F.ElNombre == Frm.ElNombre) return false;
}
Formulas.Add(Frm);
return true;
}
455

VOLVER A MEN

public bool SuprimirFormula(Formula Frm)


{
for (int i=0; i < Formulas.Count; i++)
{
Formula F = Formulas[i];
if (F == Frm)
{
Formulas.Remove(Frm);
return true;
}
}
return false;
}
public bool SuprimirFormula(string Frm)
{
for (int i=0; i < Formulas.Count; i++)
{
Formula F = Formulas[i];
if (F.ElNombre == Frm)
{
Formulas.Remove(F);
return true;
}
}
return false;
}
public void SuprimirFormulas()
{
Formulas.Clear();
}
public Formula ObtFormula(int Idx)
{
if (Idx < 0 || Idx >= Formulas.Count)
456

VOLVER A MEN

return null;
return Formulas[Idx];
}
private DepositoIngrediente BuscarIngrediente(string N)
{
DepositoIngrediente D;
for (int i=0; i < Ingredientes.Count; i++)
{
D = Ingredientes[i];
if (D.ElContenido == N) return D;
}
return null;
}
public bool AgregarIngrediente(string N)
{
// Se revisa primero si no existe ya un ingrediente con ese nombre
//
DepositoIngrediente D = BuscarIngrediente(N);
if (D != null) return false;
D = new DepositoIngrediente();
D.ElContenido = N;
Ingredientes.Add(D);
return true;
}
public bool SuprimirIngrediente(string N)
{
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return false;
Ingredientes.Remove(D);
return true;
}
457

VOLVER A MEN

public void ConsumirIngrediente(string N, double C)


{
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return;
D.Consumir(C);
}
public void AgregarIngrediente(string N, double C)
{
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return;
D.Agregar(C);
}
public double ObtDisponibilidadIngrediente(string N)
{
DepositoIngrediente D = BuscarIngrediente(N);
if (D == null) return 0.0;
return D.ObtNivelActual();
}
public double ObtPesoBalanza()
{
return LaBalanza.ElPeso;
}
public void AgregarMasaBalanza(double M)
{
LaBalanza.AgregarMasa(M);
}
public void NormalizarBalanza()
{
LaBalanza.Normalizar();
}
public void AgregarIngredienteAmasador(double I)
{
458

VOLVER A MEN

ElAmasador.Agregar(I);
}
public void AsgTemperaturaHorno(double G) { ElHorno.LaTemperatura = G; }
public void AgregarAgua() { ElAmasador.AgregarAgua(); }
public void AgregarOtrosLiquidos() { ElAmasador.AgregarOtrosLiquidos(); }
public void Moldear() { ElAmasador.VaciarAlMolde(); }
}
}

6.8 Grafo de asignacin de recursos


6.8.1 Enunciado
Una conocida tcnica para representar el estado actual de la asignacin de recursos (memoria,
CPU, dispositivo de I/O, etc.) a procesos (programa, hilo de ejecucin, etc.) en un entorno de
Sistema Operativo, es el llamado Grafo de Asignacin de Recursos (GAR). Fueron introducidos
por Holt (Holt, 1972) como un mecanismo para evitar los lazos mortales (deadlock) y consisten
en un grafo dirigido que representa el estado del sistema en lo que se refiere a los recursos
y los procesos de manera tal que cada proceso (representado por un crculo) y cada recurso
(representado por un cuadrado) corresponde a un nodo en el grafo (ver Figura 6.46). Los
recursos poseen adems tantos puntos en su interior como instancias haya de dicho recurso.
Por ejemplo, si se hace referencia a un puerto USB y el sistema tiene 4 puertos USB entonces
el recurso puerto USB tiene 4 instancias. Las aristas en el grafo representan la manera en la
cual un proceso y un recurso estn relacionados entre s de acuerdo a lo siguiente:
Una arista desde un proceso a un recurso indica peticin: el proceso ha solicitado el
recurso pero no se le ha concedido an.
Una arista desde un punto (instancia) de un recurso hacia un proceso indica asignacin:
se le ha concedido una peticin al proceso.

459

VOLVER A MEN

Figura 6.46. Ejemplo de la representacin grfica de varios GARs (Corner, 2008).


A la izquierda se tiene un GAR sin la presencia de un deadlock; a la izquierda otro GAR con un deadlock.

Se desea hacer una herramienta automatizada para administrar y gestionar la creacin de un


GAR. En este sentido, se deben satisfacer los siguientes requerimientos:
a) Se debe poder administrar la informacin de un GAR: agregar / suprimir procesos, recursos,
peticiones / solicitudes y asignaciones. Hay que tener en cuenta que cuando se ingresa un
recurso se debe indicar el nmero inicial de instancias que este recurso tiene. Por otro lado,
cuando se asigna un recurso a un proceso, la instancia se decide automticamente siendo
la primera instancia disponible (no asignada) del mismo.
b) La herramienta debe ofrecer al usuario opciones para:
1. Crear un nuevo GAR.
2. Agregar un proceso; los procesos tienen un identificador nico que permite que se
diferencien entre s; este identificador es de la forma Pi donde i es un consecutivo
numrico que se va incrementando a medida que se van agregando procesos al sistema.
3. Suprimir un proceso existente; dado que los procesos cuando se suprimen liberan el
ndice de su identificacin, este ndice puede ser usado nuevamente por otros procesos
que son agregados al sistema a posteriori.
4. Agregar un recurso; los recursos tienen un identificador nico que permite que se
diferencien entre s; este identificador es de la forma Rj donde j es un consecutivo
numrico que se va incrementando a medida que se van agregando recursos al sistema.
5. Suprimir un recurso existente; dado que los recursos cuando se suprimen liberan el
ndice de su identificacin, este ndice puede ser usado nuevamente por otros recursos
que son agregados al sistema a posteriori.
6. Agregar una peticin entre un proceso y un recurso ya existentes.
7. Rechazar una peticin previamente agregada.
460

VOLVER A MEN

8. Asignar una peticin previamente agregada. Esto slo es posible si el recurso tiene
una instancia disponible; est claro que al hacer la asignacin del recurso al proceso la
peticin desaparece.
9. Liberar un recurso previamente asignado a un proceso.
10. Guardar en un archivo el GAR actual; el nombre del archivo debe ser dado.
11. Dado el nombre de un archivo, cargar en memoria el GAR que ste contiene.
c) La herramienta debe mostrar en cada momento el estado actual del GAR.

6.8.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): Se tiene un posible mapa mental
del problema segn se muestra en la Figura 6.47.

Figura 6.47. Mapa mental relativo al problema del grafo de asignacin de recursos.

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Tcnica, estado,
recurso, memoria, CPU, dispositivo I/O, proceso, programa, hilo de ejecucin, sistema operativo,
entorno, GAR, mecanismo, lazo mortal, grafo, crculo, cuadrado, nodo, punto, instancia, puerto
USB, arista, peticin y asignacin.
Paso 3 (Identificar procesos / requerimientos): La herramienta automatizada al cual se refiere
este problema es utilizada por un usuario quien es actor de la misma. Por otro lado se pide
que la herramienta permita crear un archivo con la data asociada a un GAR as como tambin
cargar la data de un GAR de un archivo. El diagrama de casos de uso correspondiente se
muestra en la Figura 6.48.

461

VOLVER A MEN

Figura 6.48. Diagrama de casos de uso relativo al problema del grafo de asignacin de recursos.

Paso 4 (Identificar clases y asociaciones): Se tienen los siguientes observables:


Tcnica, programa, hilo de ejecucin, sistema operativo, entorno, instancia, mecanismo y
lazo mortal son irrelevantes y algunos de ellos difusos.
Estado va a ser representado como un atributo.
Memoria, CPU, dispositivo I/O y puerto USB son recursos especficos e irrelevantes para
este problema.
Crculo, cuadrado y punto son irrelevantes.
Grafo es una implementacin o concepto ya definido y disponible de la biblioteca de
clases del entorno de desarrollo; nodo y arista son conceptos asociados.
GAR es un caso particular de grafo; proceso y recurso son casos particulares de nodos y
peticin y asignacin son casos particulares de aristas.
En este sentido, se proponen los siguientes conceptos: GAR, proceso, recurso, peticin
y asignacin. Se tiene entonces el diagrama no detallado de clases que se muestra en la
Figura 6.49. Ntese que, siguiendo la definicin de un grafo como una estructura formada por
un conjunto de nodos y un conjunto de aristas que enlazan estos nodos, en el diagrama se
representa este hecho al leer semnticamente que dos conjuntos forman parte de un grafo.
462

VOLVER A MEN

Ntese tambin que los conceptos proceso y recurso son representados por una sola clase
identificada como NodoGAR y que peticin y asignacin son representados por la clase
AristaGAR. Observe tambin el correcto uso de las relaciones de herencia o generalizacin
entre las clases GAR y Grafo; se lee un GAR es un caso particular de grafo. Tambin
se tiene esta relacin entre las clases AristaGAR y Arista; se lee una arista es un caso
particular de AristaGAR.

Figura 6.49. Diagrama no detallado de clases relativo al problema del grafo de asignacin de recursos.

6.8.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): En este problema se usar una interfaz grfica
para tanto administrar el GAR como para mostrar el estado actual del mismo. Adicionalmente
se tendr interaccin de lectura / escritura con archivos binarios con el objeto de cargar /
guardar la informacin de un GAR.
Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.49
resulta el diagrama de clases detallado de la Figura 6.50. Solo se detallan las clases que no
son de implementacin. Ntese que adicionalmente se agrega el enumerado TipoNodoGAR.

Figura 6.50. Diagrama detallado de clases relativo al problema del grafo de asignacin de recursos.

463

VOLVER A MEN

Paso 3 (Desarrollar los modelos de estado): En este problema la lgica de cambio de estado
de los objetos involucrados depende casi en su totalidad de la implementacin del grafo (en la
clase GAR). Al obviar los cambios de estado inherentes al agregar/suprimir nodos y agregar/
suprimir aristas en el grafo y por lo tanto en el GAR, se tienen los diagramas de estado de
la Figura 6.51. Ntese que no hay ningn atributo propio en la clase GAR ya que todos son
heredados y no hay cambios de estado en los objetos de la clase AristaGAR.

Figura 6.51. Diagrama de estado relativo al problema del grafo de asignacin de recursos.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.52 muestra el diagrama de


colaboracin relativo a este problema. Por simplicidad no se incluye la colaboracin con las
clases de implementacin usadas para representar el grafo.

Figura 6.52. Diagrama de colaboracin relativo al problema del grafo de asignacin de recursos.

Paso 5 (Identificar componentes del dominio): Se tiene el diagrama de componentes que se


muestra en la Figura 6.53 basado en el lenguaje de programacin C++. El grafo se encuentra
definido en el paquete GrafoBib. Ntese las relaciones de dependencia entre los diferentes
elementos.

464

VOLVER A MEN

Cuarto deposito hasta el 30/01/2014

Figura 6.53. Diagrama de componentes relativo al problema del grafo de asignacin de recursos.

6.8.4 Programacin
A continuacin el cdigo fuente relativo al lenguaje de programacin C++. En este caso
particular no se tiene una implementacin de Grafo como parte de la biblioteca de clases del
lenguaje. Se anexa tambin el cdigo fuente relativo a la definicin de esta clase. Se tiene
entonces los archivos fuente Grafo.hpp, Grafo.cpp, GAR.hpp y GAR.cpp.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.8
* Grafo.hpp
* Mauricio Paletta
*/
#ifndef GRAFO_HPP
#define

GRAFO_HPP

#include <vector>
namespace GrafoBib {
using namespace std;
template <class N>
class Nodo {
465

VOLVER A MEN

private:
N Objeto;
public:
Nodo(N Obj) { Objeto = Obj; }
N ObtObjeto() { return Objeto; }
};
template <class N>
class Arista {
private:
Nodo<N> *O, *D;
public:
Arista(Nodo<N> *o, Nodo<N> *d) { O = o; D = d; }
Nodo<N> *ObtOrigen() { return O; }
Nodo<N> *ObtDestino() { return D; }
};
template <class N>
class Grafo {
protected:
bool Orientado;
vector<Nodo<N> *> Nodos;
vector<Arista<N> *> Aristas;
void Limpiar() {
unsigned i;
for (i=0; i < Nodos.size(); i++)
delete Nodos[i];
Nodos.clear();
for (i=0; i < Aristas.size(); i++)
466

VOLVER A MEN

delete Aristas[i];
Aristas.clear();
}
int BuscarNodo(N obj) {
for (unsigned i=0; i < Nodos.size(); i++) {
Nodo<N> *O = Nodos[i];
if (O->ObtObjeto() == obj) return (int)i;
}
return -1;
}
int BuscarArista(Nodo<N> *O, Nodo<N> *D);
public:
Grafo(bool O) { Orientado = O; }
~Grafo() { Limpiar(); }
bool AgregarNodo(N obj) {
if (BuscarNodo(obj) >= 0) return false;
Nodos.push_back(new Nodo<N>(obj));
return true;
}
void SuprimirNodo(N obj) {
SuprimirNodo(BuscarNodo(obj));
}
void SuprimirNodo(int Idx) {
if (Idx >=0 && Idx < (int)Nodos.size()) {
delete Nodos[Idx];
Nodos.erase(Nodos.begin() + Idx);
}
}
bool AgregarArista(Nodo<N> *O, Nodo<N> *D) {
if (BuscarArista(O, D) >= 0) return false;
Aristas.push_back(new Arista<N>(O, D));
return true;
467

VOLVER A MEN

}
void SuprimirArista(Nodo<N> *O, Nodo<N> *D) {
SuprimirArista(BuscarArista(O, D));
}
void SuprimirArista(int Idx) {
if (Idx >= 0 && Idx < Aristas.size()) {
delete Aristas[Idx];
Aristas.erase(Aristas.begin() + Idx);
}
}
bool EsOrientado() { return Orientado; }
int NumNodos() { return Nodos.size(); }
int NumAristas() { return Aristas.size(); }
int Orden() { return NumNodos(); }
bool EsNulo() { return Orden() == 0; }
};
}
#endif /* GRAFO_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.8
* Grafo.cpp
* Mauricio Paletta
*/
#include Grafo.hpp
namespace GrafoBib {
template <class N>
int Grafo<N>::BuscarArista(Nodo<N> *O, Nodo<N> *D) {
for (unsigned i=0; i < Nodos.size(); i++) {
468

VOLVER A MEN

Arista<N> *A = Aristas[i];
if (A->ObtOrigen() == O && A->ObtDestino() == D) return (int)i;
}
return -1;
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.8
* GAR.hpp
* Mauricio Paletta
*/
#ifndef GAR_HPP
#define GAR_HPP
#include <string>
#include <vector>
#include Grafo.hpp
using namespace GrafoBib;
enum TipoNodoGAR { Proceso, Recurso };
class NodoGAR {
private:
unsigned Id, nTotRec, nRecOcu;
bool *I;
TipoNodoGAR tNodo;
public:
NodoGAR(int id, TipoNodoGAR t, int nRec = 0) {
469

VOLVER A MEN

Id = id; tNodo = t; nTotRec = nRec; nRecOcu = 0;


if (nTotRec > 0) {
I = new bool[nTotRec];
for (unsigned i=0; i < nTotRec; I[i++] = false);
}
}
int ObtId() { return Id; }
bool EsRecurso() { return tNodo == Recurso; }
bool EsProceso() { return !EsRecurso(); }
int ObtNumTotalRecursos() {
if (EsRecurso()) return nTotRec;
return 0;
}
int ObtNumRecursosDisponibles() {
if (EsRecurso()) return nTotRec - nRecOcu;
return 0;
}
int ObtNumRecursosOcupados() {
if (EsRecurso()) return nRecOcu;
return 0;
}
bool HayRecursosDisponibles() {
if (EsRecurso()) return ObtNumRecursosDisponibles() > 0;
return false;
}
int AsignarRecurso() {
if (HayRecursosDisponibles()) {
// Se busca la primera instancia libre
//
unsigned i;
for (i=0; i < nTotRec && I[i]; i++);
I[i] = true;
nRecOcu++;
470

VOLVER A MEN

return (int)i;
}
return -1;
}
bool LiberarRecurso(unsigned i) {
if (EsRecurso() && nRecOcu > 0 && i >=0 && i < nTotRec) {
I[i] = false;
nRecOcu--;
return true;
}
return false;
}
};
typedef NodoGAR *pNodoGAR;
class AristaGAR : public Arista<pNodoGAR> {
private:
int InstanciaR;
public:
AristaGAR(Nodo<pNodoGAR> *O, Nodo<pNodoGAR> *D, int I = 0) : Arista<pNodoGAR>(O, D) {
InstanciaR = I;
}
bool EsPeticion() {
return ObtOrigen()->ObtObjeto()->EsProceso() && ObtDestino()->ObtObjeto()->EsRecurso();
}
bool EsAsignacion() {
return ObtOrigen()->ObtObjeto()->EsRecurso() && ObtDestino()->ObtObjeto()->EsProceso();
}
int ObtInstanciaR() {
return InstanciaR;
}
};
471

VOLVER A MEN

typedef AristaGAR *pAristaGAR;


class GAR : public Grafo<pNodoGAR> {
private:
int ObtId(TipoNodoGAR t);
pNodoGAR BuscarNodoGAR(int Id, TipoNodoGAR t);
pAristaGAR BuscarAristaGAR(int IdO, int IdD);
Nodo<pNodoGAR> *BuscarNodo(int Id, TipoNodoGAR t);
bool SuprimirNodoGAR(int Id, TipoNodoGAR t);
bool SuprimirAristaGAR(int IdO, int IdD);
void Limpiar();
public:
GAR() : Grafo<pNodoGAR>(true) { }
~GAR() { Limpiar(); }
pNodoGAR BuscarRecurso(int Id) {
return BuscarNodoGAR(Id, Recurso);
}
pNodoGAR BuscarProceso(int Id) {
return BuscarNodoGAR(Id, Proceso);
}
int AgregarRecurso(int Rs);
bool AgregarRecurso(int Id, int Rs);
bool SuprimirRecurso(int Id) {
return SuprimirNodoGAR(Id, Recurso);
}
int AgregarProceso();
bool AgregarProceso(int Id);
bool SuprimirProceso(int Id) {
return SuprimirNodoGAR(Id, Proceso);
}
bool AgregarPeticion(int IdP, int IdR);
bool SuprimirPeticion(int IdP, int IdR) {
472

VOLVER A MEN

return SuprimirAristaGAR(IdP, IdR);


}
bool AgregarAsignacion(int IdR, int IdP);
bool SuprimirAsignacion(int IdR, int IdP) {
// Primero se libera el recurso
//
pNodoGAR Nr = BuscarRecurso(IdR);
pAristaGAR A = BuscarAristaGAR(IdR, IdP);
if (Nr == NULL || A == NULL) return false;
Nr->LiberarRecurso(A->ObtInstanciaR());
return SuprimirAristaGAR(IdR, IdP);
}
bool Guardar(string Nombre);
bool Cargar(string Nombre);
vector<unsigned> ObtListaProcesos();
vector<unsigned> ObtListaRecursos();
vector<pAristaGAR> ObtAristasGAR();
};
#endif /* GAR_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.8
* GAR.cpp
* Mauricio Paletta
*/
#include <iostream>
#include <fstream>
#include GAR.hpp
// Busca el Id ms pequeo disponible para identificar un nodo
// dependiendo si es proceso o recurso
473

VOLVER A MEN

//
int GAR::ObtId(TipoNodoGAR t) {
unsigned i;
int Min = 1;
bool Enc = true;
while (Enc) {
// Para todos los nodos del grafo
//
for (i=0; i < Nodos.size(); i++) {
Nodo<pNodoGAR> *N = Nodos[i];
pNodoGAR NGAR = N->ObtObjeto();
bool EsTipo = (t == Proceso && NGAR->EsProceso()) || (t == Recurso && NGAR>EsRecurso());
if (EsTipo && NGAR->ObtId() == Min) {
Min++;
break;
}
}
Enc = i < Nodos.size();
}
return Min;
}
pNodoGAR GAR::BuscarNodoGAR(int Id, TipoNodoGAR t) {
// Para todos los nodos del grafo
//
for (unsigned i=0; i < Nodos.size(); i++) {
Nodo<pNodoGAR> *N = Nodos[i];
pNodoGAR NGAR = N->ObtObjeto();
bool EsTipo = (t == Proceso && NGAR->EsProceso()) || (t == Recurso && NGAR->EsRecurso());
if (EsTipo && NGAR->ObtId() == Id) {
return NGAR;
474

VOLVER A MEN

}
}
return NULL;
}
Nodo<pNodoGAR> *GAR::BuscarNodo(int Id, TipoNodoGAR t) {
// Para todos los nodos del grafo
//
for (unsigned i=0; i < Nodos.size(); i++) {
Nodo<pNodoGAR> *N = Nodos[i];
pNodoGAR NGAR = N->ObtObjeto();
bool EsTipo = (t == Proceso && NGAR->EsProceso()) || (t == Recurso && NGAR->EsRecurso());
if (EsTipo && NGAR->ObtId() == Id) {
return N;
}
}
return NULL;
}
bool GAR::SuprimirNodoGAR(int Id, TipoNodoGAR t) {
// Para todos los nodos del grafo
//
for (unsigned i=0; i < Nodos.size(); i++) {
Nodo<pNodoGAR> *N = Nodos[i];
pNodoGAR NGAR = N->ObtObjeto();
bool EsTipo = (t == Proceso && NGAR->EsProceso()) || (t == Recurso && NGAR->EsRecurso());
if (EsTipo && NGAR->ObtId() == Id) {
delete N->ObtObjeto();
delete N;
Nodos.erase(Nodos.begin() + i);
return true;
}
}
return false;
475

VOLVER A MEN

}
pAristaGAR GAR::BuscarAristaGAR(int IdO, int IdD) {
// Para todas las aristas del grafo
//
for (unsigned i=0; i < Aristas.size(); i++) {
pAristaGAR A = (pAristaGAR)Aristas[i];
pNodoGAR O = A->ObtOrigen()->ObtObjeto(), D = A->ObtDestino()->ObtObjeto();
if (O->ObtId() == IdO && D->ObtId() == IdD) {
return A;
}
}
return NULL;
}
bool GAR::SuprimirAristaGAR(int IdO, int IdD) {
// Para todas las aristas del grafo
//
for (unsigned i=0; i < Aristas.size(); i++) {
pAristaGAR A = (pAristaGAR)Aristas[i];
pNodoGAR O = A->ObtOrigen()->ObtObjeto(), D = A->ObtDestino()->ObtObjeto();
if (O->ObtId() == IdO && D->ObtId() == IdD) {
delete A;
Aristas.erase(Aristas.begin() + i);
return true;
}
}
return false;
}
void GAR::Limpiar() {
unsigned i;
for (i=0; i < Nodos.size(); i++) {
delete Nodos[i]->ObtObjeto();
476

VOLVER A MEN

}
Grafo<pNodoGAR>::Limpiar();
}
int GAR::AgregarRecurso(int Rs) {
// Se busca un identificar vlido
//
int Id = ObtId(Recurso);
return AgregarNodo(new NodoGAR(Id, Recurso, Rs)) ? Id : -1;
}
bool GAR::AgregarRecurso(int Id, int Rs) {
// Se verifica si el identificador no est repetido
//
if (BuscarRecurso(Id) != NULL) return false;
return AgregarNodo(new NodoGAR(Id, Recurso, Rs));
}
int GAR::AgregarProceso() {
// Se busca un identificar vlido
//
int Id = ObtId(Proceso);
return AgregarNodo(new NodoGAR(Id, Proceso)) ? Id : -1;
}
bool GAR::AgregarProceso(int Id) {
// Se verifica si el identificador no est repetido
//
if (BuscarProceso(Id) != NULL) return false;
return AgregarNodo(new NodoGAR(Id, Proceso));
}
bool GAR::AgregarPeticion(int IdP, int IdR) {
// Se buscan los nodos asociados al proceso y al recurso
//
Nodo<pNodoGAR> *Np = BuscarNodo(IdP, Proceso),
*Nr = BuscarNodo(IdR, Recurso);
477

VOLVER A MEN

if (Np == NULL || Nr == NULL) return false;


Aristas.push_back(new AristaGAR(Np, Nr));
return true;
}
bool GAR::AgregarAsignacion(int IdR, int IdP) {
// Se buscan los nodos asociados al proceso y al recurso
//
Nodo<pNodoGAR> *Np = BuscarNodo(IdP, Proceso),
*Nr = BuscarNodo(IdR, Recurso);
if (Np == NULL || Nr == NULL) return false;
// Se verifica si hay un recurso disponible
//
if (Nr->ObtObjeto()->HayRecursosDisponibles()) {
int I = Nr->ObtObjeto()->AsignarRecurso();
if (I >= 0) {
Aristas.push_back(new AristaGAR(Nr, Np, I));
return true;
}
}
return false;
}
// Guarda el contenido del GAR en un archivo cuyo nombre es dado
//
bool GAR::Guardar(string Nombre) {
try {
ofstream MiArchivo(Nombre.c_str(), ios::out | ios::binary);
// Primero se guarda el nmero de nodos y luego los nodos
//
int i, Aux = Nodos.size();
478

VOLVER A MEN

MiArchivo.write((char *)&Aux, sizeof(int));


for (i=0; i < Aux; i++) {
pNodoGAR N = Nodos[i]->ObtObjeto();
Aux = N->EsRecurso() ? 1 : 0;
MiArchivo.write((char *)&Aux, sizeof(int));
Aux = N->ObtId();
MiArchivo.write((char *)&Aux, sizeof(int));
if (N->EsRecurso()) {
Aux = N->ObtNumTotalRecursos();
MiArchivo.write((char *)&Aux, sizeof(int));
}
}
// Se guardan ahora las aristas
//
Aux = Aristas.size();
MiArchivo.write((char *)&Aux, sizeof(int));
for (i=0; i < Aux; i++) {
pAristaGAR A = (pAristaGAR)Aristas[i];
Aux = A->EsAsignacion() ? 1 : 0;
MiArchivo.write((char *)&Aux, sizeof(int));
Aux = A->ObtOrigen()->ObtObjeto()->ObtId();
MiArchivo.write((char *)&Aux, sizeof(int));
Aux = A->ObtDestino()->ObtObjeto()->ObtId();
MiArchivo.write((char *)&Aux, sizeof(int));
}
MiArchivo.close();
} catch(...) {
return false;
}
return true;
}
479

VOLVER A MEN

// Cargar el GAR con el contenido de un archivo cuyo nombre es dado


//
bool GAR::Cargar(string Nombre) {
try {
ifstream MiArchivo(Nombre.c_str(), ios::in | ios::binary);
int i, N, R, Id, nR;
// Cualquier cosa previa se suprime
//
Limpiar();
if (!MiArchivo.is_open()) return false;
// Se lee el nmero de nodos y los nodos
//
MiArchivo.read((char *)&N, sizeof(int));
for (i=0; i < N; i++) {
MiArchivo.read((char *)&R, sizeof(int));
MiArchivo.read((char *)&Id, sizeof(int));
if (R == 1) {
MiArchivo.read((char *)&nR, sizeof(int));
AgregarRecurso(Id, nR);
}
else {
AgregarProceso(Id);
}
}
// Se lee el nmero de aristas y las aristas
//
MiArchivo.read((char *)&N, sizeof(int));
for (i=0; i < N; i++) {
MiArchivo.read((char *)&R, sizeof(int));
MiArchivo.read((char *)&Id, sizeof(int));
MiArchivo.read((char *)&nR, sizeof(int));
480

VOLVER A MEN

if (R == 1)
AgregarAsignacion(Id, nR);
else
AgregarPeticion(Id, nR);
}
MiArchivo.close();
} catch (...) {
return false;
}
return true;
}
// Obtener el listado de los identificadores de procesos
//
vector<unsigned> GAR::ObtListaProcesos() {
vector<unsigned> V;
for (unsigned i=0; i < Nodos.size(); i++) {
pNodoGAR N = Nodos[i]->ObtObjeto();
if (N->EsProceso()) V.push_back(N->ObtId());
}
return V;
}
// Obtener el listado de los identificadores de recursos
//
vector<unsigned> GAR::ObtListaRecursos() {
vector<unsigned> V;
for (unsigned i=0; i < Nodos.size(); i++) {
pNodoGAR N = Nodos[i]->ObtObjeto();
if (N->EsRecurso()) V.push_back(N->ObtId());
}
481

VOLVER A MEN

return V;
}
// Obtener el listado de las aristas del GAR
//
vector<pAristaGAR> GAR::ObtAristasGAR() {
vector<pAristaGAR> V;
for (unsigned i=0; i < Aristas.size(); i++) {
V.push_back((pAristaGAR)Aristas[i]);
}
return V;
}

El cdigo fuente escrito en Java es el que se muestra a continuacin. Al igual que ocurre en el
caso anterior se tiene el paquete GrafoBib con la definicin de las clases para la representacin
del grafo y los archivos fuente Nodo.java, Arista.java y Grafo.java. Por otro lado se tiene
el paquete GAR y el archivo fuente GAR.java con el conjunto de clases necesarias para
representar un GAR.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.8
* Nodo.java
* Mauricio Paletta
*/
package GrafoBib;
public class Nodo<N> {
private N Objeto;
public Nodo(N Obj) { Objeto = Obj; }
public N ObtObjeto() { return Objeto; }
}

482

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.8
* Arista.java
* Mauricio Paletta
*/
package GrafoBib;
public class Arista<N> {
private Nodo<N> O, D;
public Arista(Nodo<N> o, Nodo<N> d) { O = o; D = d; }
public Nodo<N> ObtOrigen() { return O; }
public Nodo<N> ObtDestino() { return D; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.8
* Grafo.java
* Mauricio Paletta
*/
package GrafoBib;
import java.util.ArrayList;
public class Grafo<N> {
protected boolean Orientado;
protected ArrayList<Nodo<N>> Nodos;
protected ArrayList<Arista<N>> Aristas;
protected void Limpiar() {
Nodos.clear();
Aristas.clear();
}
483

VOLVER A MEN

protected int BuscarNodo(N obj) {


for (int i=0; i < Nodos.size(); i++) {
Nodo<N> O = Nodos.get(i);
if (O.ObtObjeto() == obj) return i;
}
return -1;
}
protected int BuscarArista(Nodo<N> O, Nodo<N> D) {
for (int i=0; i < Nodos.size(); i++) {
Arista<N> A = Aristas.get(i);
if (A.ObtOrigen() == O && A.ObtDestino() == D) return i;
}
return -1;
}
public Grafo(boolean O) {
Orientado = O;
Nodos = new ArrayList();
Aristas = new ArrayList();
}
public boolean AgregarNodo(N obj) {
if (BuscarNodo(obj) >= 0) return false;
Nodos.add(new Nodo(obj));
return true;
}
public void SuprimirNodo(N obj) {
SuprimirNodo(BuscarNodo(obj));
}
public void SuprimirNodo(int Idx) {
if (Idx >=0 && Idx < Nodos.size()) {
Nodos.remove(Idx);
}
}
484

VOLVER A MEN

public boolean AgregarArista(Nodo<N> O, Nodo<N> D) {


if (BuscarArista(O, D) >= 0) return false;
Aristas.add(new Arista(O, D));
return true;
}
public void SuprimirArista(Nodo<N> O, Nodo<N> D) {
SuprimirArista(BuscarArista(O, D));
}
public void SuprimirArista(int Idx) {
if (Idx >= 0 && Idx < Aristas.size()) {
Aristas.remove(Idx);
}
}
public boolean EsOrientado() { return Orientado; }
public int NumNodos() { return Nodos.size(); }
public int NumAristas() { return Aristas.size(); }
public int Orden() { return NumNodos(); }
public boolean EsNulo() { return Orden() == 0; }
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.8
* GAR.java
* Mauricio Paletta
*/
package GAR;
import GrafoBib.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
485

VOLVER A MEN

import java.util.logging.Level;
public class GAR extends Grafo {
public enum TipoNodoGAR {
Proceso, Recurso
}
public class NodoGAR {
private int Id, nTotRec, nRecOcu;
boolean I[];
TipoNodoGAR tNodo;
public NodoGAR(int id, TipoNodoGAR t, int nRec) {
Id = id; tNodo = t; nTotRec = nRec; nRecOcu = 0;
if (nTotRec > 0) {
I = new boolean[nTotRec];
for (int i=0; i < nTotRec; I[i++] = false);
}
}
public int ObtId() { return Id; }
public boolean EsRecurso() { return tNodo == TipoNodoGAR.Recurso; }
public boolean EsProceso() { return !EsRecurso(); }
public int ObtNumTotalRecursos() {
if (EsRecurso()) return nTotRec;
return 0;
}
public int ObtNumRecursosDisponibles() {
if (EsRecurso()) return nTotRec - nRecOcu;
return 0;
}
public int ObtNumRecursosOcupados() {
if (EsRecurso()) return nRecOcu;
486

VOLVER A MEN

return 0;
}
public boolean HayRecursosDisponibles() {
if (EsRecurso()) return ObtNumRecursosDisponibles() > 0;
return false;
}
public int AsignarRecurso() {
if (HayRecursosDisponibles()) {
// Se busca la primera instancia libre
//
int i;
for (i=0; i < nTotRec && I[i]; i++);
I[i] = true;
nRecOcu++;
return i;
}
return -1;
}
public boolean LiberarRecurso(int i) {
if (EsRecurso() && nRecOcu > 0 && i >=0 && i < nTotRec) {
I[i] = false;
nRecOcu--;
return true;
}
return false;
}
}
public class AristaGAR extends Arista {
private int InstanciaR;
public AristaGAR(Nodo O, Nodo D, int I) {
487

VOLVER A MEN

super(O, D);
InstanciaR = I;
}
public boolean EsPeticion() {
return ((NodoGAR)(ObtOrigen().ObtObjeto())).EsProceso() &&
((NodoGAR)(ObtDestino().ObtObjeto())).EsRecurso();
}
public boolean EsAsignacion() {
return ((NodoGAR)(ObtOrigen().ObtObjeto())).EsRecurso() &&
((NodoGAR)(ObtDestino().ObtObjeto())).EsProceso();
}
public int ObtInstanciaR() {
return InstanciaR;
}
}
public GAR() { super(true); }
private int ObtId(TipoNodoGAR t) {
int i, Min = 1;
boolean Enc = true;
while (Enc) {
// Para todos los nodos del grafo
//
for (i=0; i < Nodos.size(); i++) {
Nodo N = (Nodo)Nodos.get(i);
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
boolean EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Min) {
Min++;
break;
488

VOLVER A MEN

}
}
Enc = i < Nodos.size();
}
return Min;
}
private NodoGAR BuscarNodoGAR(int Id, TipoNodoGAR t) {
// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.size(); i++) {
Nodo N = (Nodo)Nodos.get(i);
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
boolean EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) {
return NGAR;
}
}
return null;
}
private AristaGAR BuscarAristaGAR(int IdO, int IdD) {
// Para todas las aristas del grafo
//
for (int i=0; i < Aristas.size(); i++) {
AristaGAR A = (AristaGAR)Aristas.get(i);
NodoGAR O = (NodoGAR)A.ObtOrigen().ObtObjeto(),
D = (NodoGAR)A.ObtDestino().ObtObjeto();
if (O.ObtId() == IdO && D.ObtId() == IdD) {
return A;
}
}
return null;
}
489

VOLVER A MEN

private Nodo BuscarNodo(int Id, TipoNodoGAR t) {


// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.size(); i++) {
Nodo N = (Nodo)Nodos.get(i);
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
boolean EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) {
return N;
}
}
return null;
}
private boolean SuprimirNodoGAR(int Id, TipoNodoGAR t) {
// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.size(); i++) {
Nodo N = (Nodo)Nodos.get(i);
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
boolean EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) {
Nodos.remove(i);
return true;
}
}
return false;
}
private boolean SuprimirAristaGAR(int IdO, int IdD) {
// Para todas las aristas del grafo
//
for (int i=0; i < Aristas.size(); i++) {
490

VOLVER A MEN

AristaGAR A = (AristaGAR)Aristas.get(i);
NodoGAR O = (NodoGAR)A.ObtOrigen().ObtObjeto(),
D = (NodoGAR)A.ObtDestino().ObtObjeto();
if (O.ObtId() == IdO && D.ObtId() == IdD) {
Aristas.remove(i);
return true;
}
}
return false;
}
public NodoGAR BuscarRecurso(int Id) {
return BuscarNodoGAR(Id, TipoNodoGAR.Recurso);
}
public NodoGAR BuscarProceso(int Id) {
return BuscarNodoGAR(Id, TipoNodoGAR.Proceso);
}
public int AgregarRecurso(int Rs) {
// Se busca un identificar vlido
//
int Id = ObtId(TipoNodoGAR.Recurso);
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Recurso, Rs)) ? Id : -1;
}
public boolean AgregarRecurso(int Id, int Rs) {
// Se verifica si el identificador no est repetido
//
if (BuscarRecurso(Id) != null) return false;
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Recurso, Rs));
}
public boolean SuprimirRecurso(int Id) {
return SuprimirNodoGAR(Id, TipoNodoGAR.Recurso);
}
public int AgregarProceso() {
491

VOLVER A MEN

// Se busca un identificar vlido


//
int Id = ObtId(TipoNodoGAR.Proceso);
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Proceso, 0)) ? Id : -1;
}
public boolean AgregarProceso(int Id) {
// Se verifica si el identificador no est repetido
//
if (BuscarProceso(Id) != null) return false;
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Proceso, 0));
}
public boolean SuprimirProceso(int Id) {
return SuprimirNodoGAR(Id, TipoNodoGAR.Proceso);
}
public boolean AgregarPeticion(int IdP, int IdR) {
// Se buscan los nodos asociados al proceso y al recurso
//
Nodo Np = BuscarNodo(IdP, TipoNodoGAR.Proceso),
Nr = BuscarNodo(IdR, TipoNodoGAR.Recurso);
if (Np == null || Nr == null) return false;
Aristas.add(new AristaGAR(Np, Nr, 0));
return true;
}
public boolean SuprimirPeticion(int IdP, int IdR) {
return SuprimirAristaGAR(IdP, IdR);
}
public boolean AgregarAsignacion(int IdR, int IdP) {
// Se buscan los nodos asociados al proceso y al recurso
//
Nodo Np = BuscarNodo(IdP, TipoNodoGAR.Proceso),
Nr = BuscarNodo(IdR, TipoNodoGAR.Recurso);
if (Np == null || Nr == null) return false;

492

VOLVER A MEN

// Se verifica si hay un recurso disponible


//
NodoGAR Ng = (NodoGAR)(Nr.ObtObjeto());
if (Ng.HayRecursosDisponibles()) {
int I = Ng.AsignarRecurso();
if (I >= 0) {
Aristas.add(new AristaGAR(Nr, Np, I));
return true;
}
}
return false;
}
public boolean SuprimirAsignacion(int IdR, int IdP) {
// Primero se libera el recurso
//
NodoGAR Nr = BuscarRecurso(IdR);
AristaGAR A = BuscarAristaGAR(IdR, IdP);
if (Nr == null || A == null) return false;
Nr.LiberarRecurso(A.ObtInstanciaR());
return SuprimirAristaGAR(IdR, IdP);
}
public boolean Guardar(String Nombre) {
FileOutputStream MiArchivo;
try {
MiArchivo = new FileOutputStream(Nombre);
// Primero se guarda el nmero de nodos y luego los nodos
//
int i, Aux = Nodos.size();
MiArchivo.write(Aux);
for (i=0; i < Aux; i++) {
NodoGAR N = (NodoGAR)(((Nodo)(Nodos.get(i))).ObtObjeto());
493

VOLVER A MEN

MiArchivo.write(N.EsRecurso() ? 1 : 0);
MiArchivo.write(N.ObtId());
if (N.EsRecurso()) {
MiArchivo.write(N.ObtNumTotalRecursos());
}
}
// Se guardan ahora las aristas
//
Aux = Aristas.size();
MiArchivo.write(Aux);
for (i=0; i < Aux; i++) {
AristaGAR A = (AristaGAR)Aristas.get(i);
MiArchivo.write(A.EsAsignacion() ? 1 : 0);
MiArchivo.write(((NodoGAR)(A.ObtOrigen().ObtObjeto())).ObtId());
MiArchivo.write(((NodoGAR)(A.ObtDestino().ObtObjeto())).ObtId());
}
MiArchivo.close();
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(GAR.class.getName()).log(Level.SEVERE, null, ex);
return false;
} catch (IOException ex) {
java.util.logging.Logger.getLogger(GAR.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
return true;
}
public boolean Cargar(String Nombre) {
FileInputStream MiArchivo;
int i, N, R, Id, nR;
try {
MiArchivo = new FileInputStream(Nombre);
494

VOLVER A MEN

// Cualquier cosa previa se suprime


//
Limpiar();
// Se lee el nmero de nodos y los nodos
//
N = MiArchivo.read();
for (i=0; i < N; i++) {
R = MiArchivo.read();
Id = MiArchivo.read();
if (R == 1) {
nR = MiArchivo.read();
AgregarRecurso(Id, nR);
}
else {
AgregarProceso(Id);
}
}
// Se lee el nmero de aristas y las aristas
//
N = MiArchivo.read();
for (i=0; i < N; i++) {
R = MiArchivo.read();
Id = MiArchivo.read();
nR = MiArchivo.read();
if (R == 1)
AgregarAsignacion(Id, nR);
else
AgregarPeticion(Id, nR);
}
MiArchivo.close();
} catch (FileNotFoundException ex) {
java.util.logging.Logger.getLogger(GAR.class.getName()).log(Level.SEVERE, null, ex);
495

VOLVER A MEN

return false;
} catch (IOException ex) {
java.util.logging.Logger.getLogger(GAR.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
return true;
}
public ArrayList<Integer> ObtListaProcesos() {
ArrayList<Integer> V = new ArrayList();
for (int i=0; i < Nodos.size(); i++) {
NodoGAR N = (NodoGAR)(((Nodo)(Nodos.get(i))).ObtObjeto());
if (N.EsProceso()) V.add(new Integer(N.ObtId()));
}
return V;
}
public ArrayList<Integer> ObtListaRecursos() {
ArrayList<Integer> V = new ArrayList();
for (int i=0; i < Nodos.size(); i++) {
NodoGAR N = (NodoGAR)(((Nodo)(Nodos.get(i))).ObtObjeto());
if (N.EsRecurso()) V.add(new Integer(N.ObtId()));
}
return V;
}
public ArrayList<AristaGAR> ObtAristasGAR() {
ArrayList<AristaGAR> V = new ArrayList();
for (int i=0; i < Aristas.size(); i++) {
V.add((AristaGAR)Aristas.get(i));
}
return V;
}
}
496

VOLVER A MEN

A continuacin el cdigo relativo a C#. Similar a los casos anteriores, se tiene el paquete GrafoBib con
la implementacin del grafo en el archivo fuente Grafo.cs y el paquete GAR con la implementacin
del GAR en el archivo fuente GAR.cs.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.8
* Grafo.cs
* Mauricio Paletta
*/
using System;
using System.Collections.Generic;
namespace GrafoBib
{
public class Nodo<N>
{
private N Objeto;
public Nodo(N Obj) { Objeto = Obj; }
public N ObtObjeto() { return Objeto; }
}
public class Arista<N>
{
private Nodo<N> O, D;
public Arista(Nodo<N> o, Nodo<N> d) { O = o; D = d; }
public Nodo<N> ObtOrigen() { return O; }
public Nodo<N> ObtDestino() { return D; }
}
public class Grafo<N>
{
497

VOLVER A MEN

protected bool Orientado;


protected List<Nodo<N>> Nodos;
protected List<Arista<N>> Aristas;
protected void Limpiar()
{
Nodos.Clear();
Aristas.Clear();
}
protected int BuscarNodo(N obj)
{
for (int i=0; i < Nodos.Count; i++)
{
Nodo<N> O = Nodos[i];
if (O.ObtObjeto().Equals(obj)) return i;
}
return -1;
}
protected int BuscarArista(Nodo<N> O, Nodo<N> D)
{
for (int i=0; i < Nodos.Count; i++)
{
Arista<N> A = Aristas[i];
if (A.ObtOrigen() == O && A.ObtDestino() == D) return i;
}
return -1;
}
public Grafo(bool O)
{
Orientado = O;
Nodos = new List<Nodo<N>>();
Aristas = new List<Arista<N>>();
498

VOLVER A MEN

}
public bool AgregarNodo(N obj)
{
if (BuscarNodo(obj) >= 0) return false;
Nodos.Add(new Nodo<N>(obj));
return true;
}
public void SuprimirNodo(N obj)
{
SuprimirNodo(BuscarNodo(obj));
}
public void SuprimirNodo(int Idx)
{
if (Idx >=0 && Idx < Nodos.Count)
Nodos.Remove(Nodos[Idx]);
}
public bool AgregarArista(Nodo<N> O, Nodo<N> D)
{
if (BuscarArista(O, D) >= 0) return false;
Aristas.Add(new Arista<N>(O, D));
return true;
}
public void SuprimirArista(Nodo<N> O, Nodo<N> D)
{
SuprimirArista(BuscarArista(O, D));
}
public void SuprimirArista(int Idx)
{
if (Idx >= 0 && Idx < Aristas.Count)
Aristas.Remove(Aristas[Idx]);
}
public bool EsOrientado() { return Orientado; }
public int NumNodos() { return Nodos.Count; }
499

VOLVER A MEN

public int NumAristas() { return Aristas.Count; }


public int Orden() { return NumNodos(); }
public bool EsNulo() { return Orden() == 0; }
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C#; caso prctico Seccin 6.8
* GAR.cs
* Mauricio Paletta
*/
using System;
using System.IO;
using System.Collections.Generic;
using GrafoBib;
namespace GAR
{
public enum TipoNodoGAR
{
Proceso, Recurso
}
public class NodoGAR
{
private int Id, nTotRec, nRecOcu;
private bool []I;
private TipoNodoGAR tNodo;
public NodoGAR(int id, TipoNodoGAR t, int nRec)
{
Id = id; tNodo = t; nTotRec = nRec; nRecOcu = 0;
if (nTotRec > 0)
500

VOLVER A MEN

{
I = new bool[nTotRec];
for (int i=0; i < nTotRec; I[i++] = false);
}
}
public int ObtId() { return Id; }
public bool EsRecurso() { return tNodo == TipoNodoGAR.Recurso; }
public bool EsProceso() { return !EsRecurso(); }
public int ObtNumTotalRecursos()
{
if (EsRecurso()) return nTotRec;
return 0;
}
public int ObtNumRecursosDisponibles()
{
if (EsRecurso()) return nTotRec - nRecOcu;
return 0;
}
public int ObtNumRecursosOcupados()
{
if (EsRecurso()) return nRecOcu;
return 0;
}
public bool HayRecursosDisponibles()
{
if (EsRecurso()) return ObtNumRecursosDisponibles() > 0;
return false;
}
public int AsignarRecurso()
{
if (HayRecursosDisponibles())
{

501

VOLVER A MEN

// Se busca la primera instancia libre


//
int i;
for (i=0; i < nTotRec && I[i]; i++);
I[i] = true;
nRecOcu++;
return i;
}
return -1;
}
public bool LiberarRecurso(int i)
{
if (EsRecurso() && nRecOcu > 0 && i >=0 && i < nTotRec)
{
I[i] = false;
nRecOcu--;
return true;
}
return false;
}
}
public class AristaGAR : Arista<NodoGAR>
{
private int InstanciaR;
public AristaGAR(Nodo<NodoGAR> O, Nodo<NodoGAR> D, int I)
: base(O, D)
{
InstanciaR = I;
}
public bool EsPeticion()
502

VOLVER A MEN

{
return ((NodoGAR)(ObtOrigen().ObtObjeto())).EsProceso() &&
((NodoGAR)(ObtDestino().ObtObjeto())).EsRecurso();
}
public bool EsAsignacion()
{
return ((NodoGAR)(ObtOrigen().ObtObjeto())).EsRecurso() &&
((NodoGAR)(ObtDestino().ObtObjeto())).EsProceso();
}
public int ObtInstanciaR()
{
return InstanciaR;
}
}
public class GAR : Grafo<NodoGAR>
{
public GAR() : base(true) { }
private int ObtId(TipoNodoGAR t)
{
int i, Min = 1;
bool Enc = true;

while (Enc) {
// Para todos los nodos del grafo
//
for (i=0; i < Nodos.Count; i++) {
Nodo<NodoGAR> N = (Nodo<NodoGAR>)Nodos[i];
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
503

VOLVER A MEN

bool EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||


(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Min) {
Min++;
break;
}
}
Enc = i < Nodos.Count;
}
return Min;
}
private NodoGAR BuscarNodoGAR(int Id, TipoNodoGAR t)
{
// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.Count; i++)
{
Nodo<NodoGAR> N = (Nodo<NodoGAR>)Nodos[i];
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
bool EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) {
return NGAR;
}
}
return null;
}
private AristaGAR BuscarAristaGAR(int IdO, int IdD)
{
// Para todas las aristas del grafo
//
for (int i=0; i < Aristas.Count; i++) {
AristaGAR A = (AristaGAR)Aristas[i];
504

VOLVER A MEN

NodoGAR O = (NodoGAR)A.ObtOrigen().ObtObjeto(),
D = (NodoGAR)A.ObtDestino().ObtObjeto();
if (O.ObtId() == IdO && D.ObtId() == IdD) return A;
}
return null;
}
private Nodo<NodoGAR> BuscarNodo(int Id, TipoNodoGAR t)
{
// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.Count; i++) {
Nodo<NodoGAR> N = (Nodo<NodoGAR>)Nodos[i];
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
bool EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) return N;
}
return null;
}
private bool SuprimirNodoGAR(int Id, TipoNodoGAR t)
{
// Para todos los nodos del grafo
//
for (int i=0; i < Nodos.Count; i++) {
Nodo<NodoGAR> N = (Nodo<NodoGAR>)Nodos[i];
NodoGAR NGAR = (NodoGAR)N.ObtObjeto();
bool EsTipo = (t == TipoNodoGAR.Proceso && NGAR.EsProceso()) ||
(t == TipoNodoGAR.Recurso && NGAR.EsRecurso());
if (EsTipo && NGAR.ObtId() == Id) {
Nodos.Remove(N);
return true;
}
}
505

VOLVER A MEN

return false;
}
private bool SuprimirAristaGAR(int IdO, int IdD)
{
// Para todas las aristas del grafo
//
for (int i=0; i < Aristas.Count; i++) {
AristaGAR A = (AristaGAR)Aristas[i];
NodoGAR O = (NodoGAR)A.ObtOrigen().ObtObjeto(),
D = (NodoGAR)A.ObtDestino().ObtObjeto();
if (O.ObtId() == IdO && D.ObtId() == IdD)
{
Aristas.Remove(A);
return true;
}
}
return false;
}
public NodoGAR BuscarRecurso(int Id)
{
return BuscarNodoGAR(Id, TipoNodoGAR.Recurso);
}
public NodoGAR BuscarProceso(int Id)
{
return BuscarNodoGAR(Id, TipoNodoGAR.Proceso);
}
public int AgregarRecurso(int Rs)
{
// Se busca un identificar vlido
//
int Id = ObtId(TipoNodoGAR.Recurso);
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Recurso, Rs)) ? Id : -1;
506

VOLVER A MEN

}
public bool AgregarRecurso(int Id, int Rs)
{
// Se verifica si el identificador no est repetido
//
if (BuscarRecurso(Id) != null) return false;
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Recurso, Rs));
}
public bool SuprimirRecurso(int Id)
{
return SuprimirNodoGAR(Id, TipoNodoGAR.Recurso);
}
public int AgregarProceso()
{
// Se busca un identificar vlido
//
int Id = ObtId(TipoNodoGAR.Proceso);
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Proceso, 0)) ? Id : -1;
}
public bool AgregarProceso(int Id)
{
// Se verifica si el identificador no est repetido
//
if (BuscarProceso(Id) != null) return false;
return AgregarNodo(new NodoGAR(Id, TipoNodoGAR.Proceso, 0));
}
public bool SuprimirProceso(int Id)
{
return SuprimirNodoGAR(Id, TipoNodoGAR.Proceso);
}
public bool AgregarPeticion(int IdP, int IdR)
{

507

VOLVER A MEN

// Se buscan los nodos asociados al proceso y al recurso


//
Nodo<NodoGAR> Np = BuscarNodo(IdP, TipoNodoGAR.Proceso),
Nr = BuscarNodo(IdR, TipoNodoGAR.Recurso);
if (Np == null || Nr == null) return false;
Aristas.Add(new AristaGAR(Np, Nr, 0));
return true;
}
public bool SuprimirPeticion(int IdP, int IdR)
{
return SuprimirAristaGAR(IdP, IdR);
}
public bool AgregarAsignacion(int IdR, int IdP)
{
// Se buscan los nodos asociados al proceso y al recurso
//
Nodo<NodoGAR> Np = BuscarNodo(IdP, TipoNodoGAR.Proceso),
Nr = BuscarNodo(IdR, TipoNodoGAR.Recurso);
if (Np == null || Nr == null) return false;
// Se verifica si hay un recurso disponible
//
NodoGAR Ng = (NodoGAR)(Nr.ObtObjeto());
if (Ng.HayRecursosDisponibles()) {
int I = Ng.AsignarRecurso();
if (I >= 0) {
Aristas.Add(new AristaGAR(Nr, Np, I));
return true;
}
}
return false;
}
public bool SuprimirAsignacion(int IdR, int IdP)
508

VOLVER A MEN

{
// Primero se libera el recurso
//
NodoGAR Nr = BuscarRecurso(IdR);
AristaGAR A = BuscarAristaGAR(IdR, IdP);
if (Nr == null || A == null) return false;
Nr.LiberarRecurso(A.ObtInstanciaR());
return SuprimirAristaGAR(IdR, IdP);
}
public bool Guardar(string Nombre)
{
FileStream MiArchivo = new FileStream(Nombre, FileMode.Create);
BinaryWriter Bw = new BinaryWriter(MiArchivo);
try {
// Primero se guarda el nmero de nodos y luego los nodos
//
int i, Aux = Nodos.Count;
Bw.Write((Int32)Aux);
for (i=0; i < Aux; i++)
{
NodoGAR N = (NodoGAR)(((Nodo<NodoGAR>)(Nodos[i])).ObtObjeto());
Bw.Write((Int32)(N.EsRecurso() ? 1 : 0));
Bw.Write((Int32)(N.ObtId()));
if (N.EsRecurso()) Bw.Write((Int32)(N.ObtNumTotalRecursos()));
}
// Se guardan ahora las aristas
//
Aux = Aristas.Count;
Bw.Write((Int32)Aux);
for (i=0; i < Aux; i++)
{
AristaGAR A = (AristaGAR)Aristas[i];
509

VOLVER A MEN

Bw.Write((Int32)(A.EsAsignacion() ? 1 : 0));
Bw.Write((Int32)(((NodoGAR)(A.ObtOrigen().ObtObjeto())).ObtId()));
Bw.Write((Int32)(((NodoGAR)(A.ObtDestino().ObtObjeto())).ObtId()));
}
MiArchivo.Close();
}
catch (Exception)
{
return false;
}
return true;
}
public bool Cargar(string Nombre)
{
FileStream MiArchivo = new FileStream(Nombre, FileMode.Open);
BinaryReader Br = new BinaryReader(MiArchivo);
int i, N, R, Id, nR;
try {
// Cualquier cosa previa se suprime
//
Limpiar();
// Se lee el nmero de nodos y los nodos
//
N = Br.ReadInt32();
for (i=0; i < N; i++)
{
R = Br.ReadInt32();
Id = Br.ReadInt32();
if (R == 1)
{
nR = Br.ReadInt32();
AgregarRecurso(Id, nR);
510

VOLVER A MEN

}
else
{
AgregarProceso(Id);
}
}
// Se lee el nmero de aristas y las aristas
//
N = Br.ReadInt32();
for (i=0; i < N; i++)
{
R = Br.ReadInt32();
Id = Br.ReadInt32();
nR = Br.ReadInt32();
if (R == 1)
AgregarAsignacion(Id, nR);
else
AgregarPeticion(Id, nR);
}
MiArchivo.Close();
}
catch (Exception)
{
return false;
}
return true;
}
public List<int> ObtListaProcesos()
{
List<int> V = new List<int>();
for (int i=0; i < Nodos.Count; i++)
{
511

VOLVER A MEN

NodoGAR N = (NodoGAR)(((Nodo<NodoGAR>)(Nodos[i])).ObtObjeto());
if (N.EsProceso()) V.Add(N.ObtId());
}
return V;
}
public List<int> ObtListaRecursos()
{
List<int> V = new List<int>();
for (int i=0; i < Nodos.Count; i++)
{
NodoGAR N = (NodoGAR)(((Nodo<NodoGAR>)(Nodos[i])).ObtObjeto());
if (N.EsRecurso()) V.Add(N.ObtId());
}
return V;
}
public List<AristaGAR> ObtAristasGAR() {
List<AristaGAR> V = new List<AristaGAR>();
for (int i=0; i < Aristas.Count; i++)
{
V.Add((AristaGAR)Aristas[i]);
}
return V;
}
}
}

512

VOLVER A MEN

6.9 La conquista del nuevo mundo


6.9.1 Enunciado
La motivacin principal de este problema es simular una batalla para la conquista de territorio
entre los conquistadores y los nativos. El objetivo de la batalla es capturar la bandera del
oponente, eliminar todos los oponentes o forzar la total retirada de los oponentes.
El escenario o campo de batalla est representado por una matriz de M x N cuadros. Los
grupos de contrincantes con sus respectivas banderas se encuentran en el extremo izquierdo
y derecho del campo de batalla. En la Figura 6.54 se muestra un ejemplo para un campo de
batalla de 4 x 3 cuadros.

Figura 6.54. Ejemplo de un campo de batalla para el problema de la conquista del nuevo mundo.

Las unidades o entidades que intervienen en la simulacin slo se pueden mover de forma
ortogonal (hacia adelante para el avance, hacia atrs para la retirada y a los lados para cubrir
territorio), no de forma diagonal, a menos que el cuadro inmediato siguiente est ocupado
por una unidad enemiga. El nmero de cuadros con los cuales una unidad se puede mover
depende del tipo de unidad. Inicialmente todas las unidades estn ubicadas en el territorio de
sus banderas. Una unidad nunca puede ocupar un cuadro que est siendo ocupado por una
unidad del enemigo.
Cada unidad posee un valor discreto que determina su nivel de vida y cuando este valor llega a
cero implica la muerte de la unidad. Es importante que este valor se muestre en todo momento
durante la simulacin de forma tal de observar el estado actual de cada unidad.
Cada unidad posee su propia puntera y alcance de tiro que les son asignados en el momento
de la creacin. La puntera viene dada por una probabilidad de acertar al enemigo y el alcance
viene dado por el nmero de cuadros que lo separan del objetivo. La visin de tiro es ortogonal
(hacia adelante y a los lados).
A continuacin se presentan los tipos de unidades que se pueden tener:
Infantes: Su capacidad de movimiento es de un cuadro por turno. Su nivel de vida es igual
a 4. Su puntera viene dada por una probabilidad de 0.6. Su alcance es de un cuadro.
Caballera: Su capacidad de movimiento es de dos cuadros por turno. Su nivel de vida es
de 5. Su puntera viene dada por una probabilidad de 0.75. Su alcance es de un cuadro.
513

VOLVER A MEN

Artillera: Su capacidad de movimiento es de un cuadro por turno. Su nivel de vida es de


6. Su alcance es de dos cuadros y siempre en lnea recta. Su puntera viene dada por una
probabilidad de 0.95 cuando el objetivo est en el cuadro inmediatamente siguiente y 0.50
cuando el objetivo est a una distancia de dos cuadros. Slo los conquistadores poseen
este tipo de unidad.
Las reglas de la simulacin son las siguientes:
1. Cada cuadro puede contener el equivalente a seis infantes. Un caballo y un artillero
equivalen cada uno a dos infantes.
2. El simulador debe dirigir las unidades de uno de los dos grupos y recibir las rdenes de
un usuario que dirige las unidades del otro grupo.
3. Habr un turno para cada oponente en el cual se ubicarn las unidades y se les dar la
orden de ataque. Solo es posible hacer una operacin a la vez (mover o atacar) y slo una
vez por cada turno (mover una sola vez o atacar una sola vez a un objetivo especfico).
La interfaz del simulador debe contar con opciones para que el usuario pueda informar
que ha terminado su turno.
4. En todo momento se debe distinguir el tipo de unidad de cada bando, su ubicacin y su
nivel de vida.
5. El tamao del campo de batalla y el nmero inicial de unidades de cada tipo para cada
oponente debe ser parte de la configuracin para iniciar el simulador. Valores razonables
son los siguientes:
o 4 infantes y 5 caballos para los nativos.
o 4 infantes, 3 caballos y 1 artillero para los conquistadores.
6. Se considera una retirada cuando todas las unidades de un bando regresa a su sitio de
partida (ubicacin de la bandera o base) luego de iniciada la simulacin.
7. La simulacin termina cuando alguno de los dos bandos gana la batalla: se captura la
bandera del oponente, se eliminan todas las unidades del oponente o se fuerza a la
retirada del oponente.

6.9.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): La revisin del enunciado del
problema lleva a un mapa mental segn se muestra en la Figura 6.55.

514

VOLVER A MEN

Figura 6.55. Mapa mental relativo al problema de la conquista del nuevo mundo.

Paso 2 (Identificar actores / nombres): Se tiene la siguiente lista de nombres: Batalla, territorio,
conquistador, nativo, bandera, oponente, campo de batalla, matriz, grupo, contrincante, unidad,
enemigo, nivel de vida, puntera, alcance de tiro, infante, caballera, artillera, cuadro y turno.
Paso 3 (Identificar procesos / requerimientos): En este problema se busca tener un juego en la
cual un usuario o jugador debe enfrentarse a un algoritmo como contrincante. Se tiene entonces
un actor representado por el jugador quin, adems de jugar, tambin realiza la configuracin
necesaria. Se tiene el diagrama de casos de uso que se muestra en la Figura 6.56. Ntese
que se representa tanto el juego realizado por el actor como el realizado automticamente y en
ambos casos se tiene opcionalmente la posibilidad de mover unidades y atacar.

Figura 6.56. Diagrama de casos de uso relativo al problema de la conquista del nuevo mundo.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2 se


tienen los siguientes observables:
Batalla es redundante con el nombre del juego: conquista del nuevo mundo. Se toma
conquista como el identificador de la clase principal que representa el juego.
515

VOLVER A MEN

Territorio y campo de batalla son redundantes. Se toma campo como identificador el


cual adems, se implementa mediante una matriz. Se tiene la relacin campo forma
parte de conquista. Adicionalmente, el campo est formado por cuadros y por dos bases.
En este caso es conveniente la definicin de una clase base abstracta para representar el
Espacio donde se ubican las unidades.
Conquistador y nativo es la manera de identificar cada uno de los grupos que participan
en el juego. Una identificacin apropiada para este concepto es grupo y los dos objetos
que hay que instanciar en el juego pueden ser identificados como conquistadores y
nativos. Se tiene la relacin dos grupos forman parte de conquista.
Oponente, contrincante, unidad, enemigo son redundantes. Unidad es un identificador
apropiado en este caso. Se tiene la relacin un nmero determinado de unidades forman
parte de grupo.
Infante, caballera y artillera son tipos particulares de unidades que se representan con
un atributo para la clase que va a representar una unidad.
Nivel de vida, puntera y alcance de tiro son atributos de la unidad.
Turno es una variable de control o atributo del juego representado por la clase Conquista.
La bandera est asociada directamente al concepto de grupo y dado que no se pide
una descripcin detallada de la bandera (colores, escudos, etc.), es irrelevante para este
problema.
Se proponen entonces los siguientes conceptos: Conquista, campo, espacio, cuadro, base,
grupo y unidad. Resulta el diagrama no detallado de clases que se muestra en la Figura 6.57.

Figura 6.57. Diagrama no detallado de clases relativo al problema de la conquista del nuevo mundo.

516

VOLVER A MEN

6.9.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Lo ms adecuado para este problema es tener
una interfaz grfica y un par de formularios, uno para hacer la configuracin y el otro para jugar.
Con respecto al juego debe haber una manera de discriminar los turnos: jugador y algoritmo.
La interfaz debe adems, mostrar el estado actual del juego en todo momento. Esto implica
que hay que mostrar las unidades, el grupo al cual pertenecen, su tipo, sus niveles de vida y
su ubicacin en el campo de batalla.
Paso 2 (Detallar las clases): Al agregar los atributos y mtodos a las clases de la Figura 6.57
resulta el diagrama de clases detallado de la Figura 6.58. Por la complejidad del problema es
de esperarse que el detalle de las clases est conformado por un nmero mayor de elementos
comparado con los problemas previos. Ntese que se agrega el tipo de dato enumerado
TipoUnidad para representar en tres literales los tipos posibles de unidades. Ntese tambin
que la clase Espacio es abstracta (representado por el identificador escrito en letra cursiva) y
los mtodos UbicarUnidad, SacarUnidad y ObtPosicion son virtuales.

Figura 6.58. Diagrama detallado de clases relativo al problema de la conquista del nuevo mundo.

517

VOLVER A MEN

Paso 3 (Desarrollar los modelos de estado): La Figura 6.59 muestra los diagramas de estado de
las clases relativas a este problema. Las clases Cuadro y Base no aparecen porque carecen
de atributos propios; los cambios de estado se representan con la clase genrica Espacio.
A fin de simplificar el diagrama se estn obviendo algunas transiciones entre estados. Ntese
que con respecto a las clases Campo y Grupo el nico estado posible es dado en tiempo
de construccin del objeto, es decir, da a entender que ni el campo ni los grupos pueden ser
alterados durante el juego.

Figura 6.59. Diagramas de estado relativos al problema de la conquista del nuevo mundo.

Paso 4 (Elaborar los modelos de colaboracin): La Figura 6.60 muestra una simplificacin del
diagrama de secuencias relativo a este problema. Slo se representa lo ms relevante del
problema a fin de hacer el diagrama no tan complejo. Se muestra parte de la secuencia de
colaboracin usada para jugar el usuario; esta misma secuncia se debera repetir en el juego
automatizado. Ntese que se generaliza el uso de los objetos que representan las bases y los
cuadros del campo con la clase base genrica Espacio.
518

VOLVER A MEN

Figura 6.60. Diagrama de secuencias relativo al problema de la conquista del nuevo mundo.

Paso 5 (Identificar componentes del dominio): Se tiene el diagrama de componentes que se


muestra en la Figura 6.61 basado en el lenguaje de programacin Java. Todas las clases se
agrupan en el paquete identificado como ConquistaNuevoMundo.

Figura 6.61. Diagrama de componentes relativo al problema de la conquista del nuevo mundo.

519

VOLVER A MEN

6.9.4 Programacin
Para la implementacin de C++ se realizaron los siguientes archivos fuente: Campo.hpp
y Campo.cpp, Grupo.hpp y Grupo.cpp y la pareja Conquista.hpp y Conquista.cpp.
Adicionalmente y con el objeto de resolver conflictos en la compilacin de los diferentes
elementos cuyas definiciones estn cruzadas entre ellas, se tiene el archivo encabezado
DeclaracionesBase.hpp. A continuacin los listados correspondientes. Se utiliz un espacio
de nombres identificado como ConquistaNM.

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Campo.hpp
* Mauricio Paletta
*/
#ifndef CAMPO_HPP
#define CAMPO_HPP
#include <QtGui>
#include <QPoint>
#include <string>
#include DeclaracionesBase.hpp
namespace ConquistaNM {
// Representa un espacio del campo donde van las unidades
//
class Espacio : public QFrame {
protected:
int Disponibilidad;
pConquista refJuego;
QFrame *panelCol1, *panelCol2;
void mousePressEvent(QMouseEvent *M);
520

VOLVER A MEN

public:
Espacio(pConquista J, std::string n, int x, int y, int u, int f);
~Espacio();
void Limpiar();
int ObtDisponibilidad() { return Disponibilidad; }
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
virtual bool UbicarUnidad(pUnidad U) = 0;
virtual bool SacarUnidad(pUnidad U) = 0;
virtual QPoint *ObtPosicion() = 0;
QFrame *EstaUnidad(pUnidad U);
int ObtIdBase();
bool Vacio() {
return panelCol1->children().count() == 0 && panelCol2->children().count() == 0;
}
int ObtNumeroUnidades() {
return panelCol1->children().count() + panelCol2->children().count();
}
int ObtNumeroUnidades(TipoUnidad T);
pUnidad ObtMasDebil();
};
typedef Espacio *pEspacio;
// Representa una base
//
class Base : public Espacio {
public:
Base(pConquista J, std::string n, int x, int y, int u, int f) :
Espacio(J, n, x, y, u, f) { }

521

VOLVER A MEN

// Ubicar una unidad en la base


//
bool UbicarUnidad(pUnidad U);
bool SacarUnidad(pUnidad U);
QPoint *ObtPosicion() {
return new QPoint(this->x(), this->y());
}
bool HayUnidadEnemiga();
};
typedef Base *pBase;
// Representa un cuadro/celda del campo
//
class Cuadro : public Espacio {
public:
// Puede albergar hasta un mximo de 6 unidades (infantes)
//
Cuadro(pConquista J, std::string n, int x, int y) :
Espacio(J, n, x, y, 6, 1) { }
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
bool UbicarUnidad(pUnidad U);
bool SacarUnidad(pUnidad U);
QPoint *ObtPosicion() {
return new QPoint(x() / width(), y() / height());
}
bool HayUnidad(int IdBase);
};
typedef Cuadro *pCuadro;
class Campo : public QFrame {
522

VOLVER A MEN

private:
// Tamao del campo de batalla
//
int F, C;
pCuadro *Cuadros;
pBase *Bases;
QFrame *gridPanel;
pConquista refJuego;
public:
Campo(pConquista J, int f, int c, int uB1, int uB2);
~Campo();
bool UbicarUnidad(pUnidad U, int f, int c);
bool SacarUnidad(pUnidad U, int f, int c);
bool UbicarUnidad(pUnidad U, int b);
bool SacarUnidad(pUnidad U, int b);
QPoint *ObtPosicionUnidad(pUnidad U);
void Limpiar();
pEspacio BuscarUnidad(pUnidad U);
bool HayUnidad(int IdBase, QPoint *P);
pBase ObtBase(int IdBase) {
return (IdBase >= 1 && IdBase <= 2) ? Bases[IdBase-1] : NULL;
}
pCuadro ObtCuadro(int f, int c) {
return (f >= 0 && f < F && c >= 0 && c < C) ? Cuadros[f * F + c] : NULL;
}
int ObtNumFilas() { return F; }
int ObtNumColumnas() { return C; }
bool HayConquistadoresBaseNativos() {
return Bases[0]->HayUnidadEnemiga();
}
bool HayNativosBaseConquistadores() {
return Bases[1]->HayUnidadEnemiga();
523

VOLVER A MEN

}
};
}
#endif /* CAMPO_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Campo.cpp
* Mauricio Paletta
*/
#include Grupo.hpp
#include Conquista.hpp
#include Campo.hpp
namespace ConquistaNM {
// Mtodos de la clase Espacio
//
Espacio::Espacio(pConquista J, std::string n, int x, int y, int u, int f)
: QFrame(J->ObtFormulario()) {
refJuego = J;
if (u <= 0) u = 1;
Disponibilidad = u;
this->setGeometry(x, y, 90, 135 * f);
this->setObjectName(tr(n.c_str()));
this->setFrameShape(Panel);
this->setFrameShadow(Plain);
this->setLineWidth(2);
this->setCursor(Qt::PointingHandCursor);

524

VOLVER A MEN

panelCol1 = new QFrame(this);


panelCol1->setObjectName(tr((n + -Col1).c_str()));
panelCol1->setGeometry(0, 0, 45, 135 * f);
panelCol1->setCursor(Qt::PointingHandCursor);
panelCol1->setVisible(true);
panelCol2 = new QFrame(this);
panelCol2->setObjectName(tr((n + -Col2).c_str()));
panelCol2->setGeometry(45, 0, 45, 135 * f);
panelCol2->setCursor(Qt::PointingHandCursor);
panelCol2->setVisible(true);
// Para seleccionar el espacio
//
this->setVisible(true);
this->repaint();
}
Espacio::~Espacio() {
if (panelCol1 != NULL) delete panelCol1;
if (panelCol2 != NULL) delete panelCol2;
}
void Espacio::mousePressEvent(QMouseEvent *M) {
// Seleccionado para intentar ubicar una unidad
// Slo cuando se tratan de unidades manejadas por el jugador humano
//
if (refJuego->ValidarMovimientoManual()) refJuego->SeleccionarEspacioMover(this);
}
void Espacio::Limpiar() {
QObjectList L;
if (panelCol1 != NULL) {
525

VOLVER A MEN

L = panelCol1->children();
L.clear();
}
if (panelCol2 != NULL) {
L = panelCol2->children();
L.clear();
}
}
QFrame *Espacio::EstaUnidad(pUnidad U) {
int i;
if (U == NULL) return NULL;
for (i=0; i < panelCol1->children().count(); i++) {
if (panelCol1->children()[i] == U) return panelCol1;
}
for (i=0; i < panelCol2->children().count(); i++) {
if (panelCol2->children()[i] == U) return panelCol2;
}
return NULL;
}
int Espacio::ObtIdBase() {
if (panelCol1->children().count() != 0)
return ((pUnidad)(panelCol1->children()[0]))->ObtIdBase();
if (panelCol2->children().count() != 0)
return ((pUnidad)(panelCol2->children()[0]))->ObtIdBase();
return 0;
}
int Espacio::ObtNumeroUnidades(TipoUnidad T) {
int i, S = 0;
pUnidad U;
for (i=0; i < panelCol1->children().count(); i++) {
526

VOLVER A MEN

U = dynamic_cast<pUnidad>(panelCol1->children()[i]);
if (U->ObtTipo() == T) S++;
}
for (i=0; i < panelCol2->children().count(); i++) {
U = dynamic_cast<pUnidad>(panelCol2->children()[i]);
if (U->ObtTipo() == T) S++;
}
return S;
}
pUnidad Espacio::ObtMasDebil() {
pUnidad U, MasDebil = NULL;
int i;
for (i=0; i < panelCol1->children().count(); i++) {
U = dynamic_cast<pUnidad>(panelCol1->children()[i]);
if (MasDebil == NULL || U->ObtNivelVida() < MasDebil->ObtNivelVida())
MasDebil = U;
}
for (i=0; i < panelCol2->children().count(); i++) {
U = dynamic_cast<pUnidad>(panelCol2->children()[i]);
if (MasDebil == NULL || U->ObtNivelVida() < MasDebil->ObtNivelVida())
MasDebil = U;
}
return MasDebil;
}
// Mtodos de la clase Base
//
bool Base::UbicarUnidad(pUnidad U) {
int i, u;
if (U == NULL || Disponibilidad == 0 || U->HizoOperacion()) return false;
i = panelCol1->children().count();
527

VOLVER A MEN

u = panelCol1->height() / 45;
if (i < u) {
U->setParent(panelCol1);
panelCol1->repaint();
}
else {
i = panelCol2->children().count();
u = panelCol2->height() / 45;
if (i < u) {
U->setParent(panelCol2);
panelCol2->repaint();
}
else
return false;
}
U->show();
U->setGeometry(0, 45 * i, 45, 45);
U->EntrarBase();
U->Refrescar();
Disponibilidad--;
this->repaint();
return true;
}
bool Base::SacarUnidad(pUnidad U) {
QFrame *P = EstaUnidad(U);
QObjectList L;
if (U == NULL || P == NULL || U->HizoOperacion()) return false;
U->SalirBase();
U->setVisible(false);
U->setParent(refJuego->ObtFormulario());
L = P->children();
528

VOLVER A MEN

Disponibilidad++;
for (int i=0; i < L.size(); i++)
dynamic_cast<pUnidad>(L[i])->setGeometry(0, 45*i, U->width(), U->height());
P->repaint();
repaint();
return true;
}
bool Base::HayUnidadEnemiga() {
pUnidad U;
int i, EstaBase = this->x() <= 1 ? 1 : 2;
for (i=0; i < panelCol1->children().count(); i++) {
U = dynamic_cast<pUnidad>(panelCol1->children()[i]));
if (U->ObtIdBase() != EstaBase) return true;
}
for (i=0; i < panelCol2->children().count(); i++) {
U = dynamic_cast<pUnidad>(panelCol2->children()[i]));
if (U->ObtIdBase() != EstaBase) return true;
}
return false;
}
// Mtodos de la clase Cuadro
//
bool Cuadro::UbicarUnidad(pUnidad U) {
int i, Requerido;
if (U == NULL || U->HizoOperacion()) return false;
Requerido = U->ObtTipo() == Infante ? 1 : 2;
if (Disponibilidad - Requerido < 0) return false;
i = panelCol1->children().count();
if (i < 3) {
529

VOLVER A MEN

U->setParent(panelCol1);
panelCol1->repaint();
}
else {
i = panelCol2->children().count();
if (i < 3) {
U->setParent(panelCol2);
panelCol2->repaint();
}
else
return false;
}
U->show();
U->setGeometry(0, i * 45, 45, 45);
U->repaint();
Disponibilidad -= Requerido;
repaint();
return true;
}
bool Cuadro::SacarUnidad(pUnidad U) {
int Ocupado;
QFrame *P = EstaUnidad(U);
QObjectList L;
if (U == NULL || P == NULL || U->HizoOperacion()) return false;
Ocupado = U->ObtTipo() == Infante ? 1 : 2;
U->setVisible(false);
U->setParent(refJuego->ObtFormulario());
Disponibilidad += Ocupado;
L = P->children();
for (int i=0; i < L.size(); i++)
dynamic_cast<pUnidad>(L[i])->setGeometry(0, 45*i, U->width(), U->height());
530

VOLVER A MEN

P->repaint();
repaint();
return true;
}
bool Cuadro::HayUnidad(int IdBase) {
Unidad *U;
if (IdBase < 1 || IdBase > 2) return false;
if (panelCol1->children().count() > 0) {
U = dynamic_cast<pUnidad>(panelCol1->children()[0]);
return U->ObtIdBase() == IdBase;
}
if (panelCol2->children().count() > 0) {
U = dynamic_cast<pUnidad>(panelCol2->children()[0]);
return U->ObtIdBase() == IdBase;
}
return false;
}
// Mtodos de la clase Campo
//
Campo::Campo(pConquista J, int f, int c, int uB1, int uB2)
: QFrame(J->ObtFormulario()) {
if (f <= 0) f = 1;
if (c <= 0) c = 1;
F = f; C = c;
refJuego = J;
this->setGeometry(10, 10, 90 * (C + 2) + 1, 135 * F + (F - 3));
this->setObjectName(tr(PanelCampo));
this->setVisible(true);
531

VOLVER A MEN

this->setFrameShape(Panel);
this->setFrameShadow(Plain);
this->setLineWidth(2);
Bases = new pBase[2];
Bases[0] = new Base(refJuego, Base0, 0, 0, uB1, F);
Bases[0]->setParent(this);
Bases[1] = new Base(refJuego, Base1, 90 * (C + 1), 0, uB2, F);
Bases[1]->setParent(this);
gridPanel = new QFrame(this);
gridPanel->setGeometry(Bases[0]->width(), 0, 90 * C, 135 * F + 10);
Cuadros = new pCuadro[F * C];
for (int i=0; i < F; i++) {
for (int j=0; j < C; j++) {
QString S = QString(Cuadro) + QString::number(i) + QString::number(j);
Cuadros[i * C + j] = new Cuadro(refJuego, S.toStdString(), j * 90, i * 135 + 10);
Cuadros[i * C + j]->setParent(gridPanel);
Cuadros[i * C + j]->setGeometry(j * 90, i * 135, 90, 135);
}
}
}
Campo::~Campo() {
int i, j;
if (Cuadros != NULL) {
for (i=0; i < F; i++) {
for (j=0; j < C; j++) {
if (Cuadros[i * C + j] != NULL) delete Cuadros[i * C + j];
}
}
532

VOLVER A MEN

delete Cuadros;
Cuadros = NULL;
}
if (Bases[0] != NULL) delete Bases[0];
if (Bases[1] != NULL) delete Bases[1];
if (gridPanel != NULL) delete gridPanel;
}
bool Campo::UbicarUnidad(pUnidad U, int f, int c) {
if (U == NULL || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f * F + c]->UbicarUnidad(U);
}
bool Campo::SacarUnidad(pUnidad U, int f, int c) {
if (U == NULL || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f * F + c]->SacarUnidad(U);
}
bool Campo::UbicarUnidad(pUnidad U, int b) {
if (U == NULL || b < 1 || b > 2) return false;
return Bases[b-1]->UbicarUnidad(U);
}
bool Campo::SacarUnidad(pUnidad U, int b) {
if (U == NULL || b < 1 || b > 2) return false;
return Bases[b-1]->SacarUnidad(U);
}
QPoint *Campo::ObtPosicionUnidad(pUnidad U) {
for (int i=0; i < F; i++) {
for (int j=0; j < C; j++) {
if (Cuadros[i * C + j]->EstaUnidad(U) != NULL)
return new QPoint(i, j);
}
533

VOLVER A MEN

}
return NULL;
}
void Campo::Limpiar() {
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
Cuadros[i * C + j]->Limpiar();
Bases[0]->Limpiar();
Bases[1]->Limpiar();
}
pEspacio Campo::BuscarUnidad(pUnidad U) {
int IdBase;
if (U == NULL) return NULL;
IdBase = U->ObtIdBase();
if (IdBase < 1 || IdBase > 2) return NULL;
// Primero se busca en alguna de las bases
//
if (Bases[IdBase - 1]->EstaUnidad(U) != NULL) return Bases[IdBase - 1];
// Si no est en las bases se busca en alguno de los cuadros
//
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
if (Cuadros[i * C + j]->EstaUnidad(U) != NULL) return Cuadros[i * C + j];
return NULL;
}
bool Campo::HayUnidad(int IdBase, QPoint *P) {
if (IdBase < 1 || IdBase > 2) return false;
534

VOLVER A MEN

if (P->x() < 0 || P->x() >= C || P->y() < 0 || P->y() >= F) return false;
return Cuadros[(int)(P->y() * F + P->x())]->HayUnidad(IdBase);
}
}

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Grupo.hpp
* Mauricio Paletta
*/
#ifndef GRUPO_HPP
#define GRUPO_HPP
#include <QtGui>
#include <cstdlib>
#include <time.h>
#include <vector>
#include DeclaracionesBase.hpp
namespace ConquistaNM {
class Unidad : public QFrame {
private:
TipoUnidad Tipo;
int Vida, IdBase;
bool EnBase, HizoOp;
pConquista refJuego;
QGraphicsView *graphicsView;
535

VOLVER A MEN

QGraphicsScene *ImagenUnidad;
QLabel *labelVida;
protected:
void mousePressEvent(QMouseEvent *M);
public:
Unidad(pConquista J, TipoUnidad T, int Id);
~Unidad();
TipoUnidad ObtTipo() { return Tipo; }
int ObtNivelVida() { return Vida; }
bool ObtPunteria(int A) {
double p;
switch (Tipo) {
case Infante:
p = 0.6;
break;
case Caballeria:
p = 0.75;
break;
default:
p = A == 2 ? 0.5 : 0.95;
break;
}
return (rand() / (double)RAND_MAX) <= p;
}
// En el caso de la caballera se asume primer nivel de
// visibilidad o alcance
//
int ObtAlcance() {
536

VOLVER A MEN

return Tipo == Caballeria ? 2 : 1;


}
bool EstaVivo() { return Vida > 0; }
void Mover();
void Herir();
bool Atacar(int A);
bool HizoOperacion() { return HizoOp; }
bool EnSuBase() { return EnBase; }
void SalirBase() { EnBase = false; }
void EntrarBase() { EnBase = true; Mover(); }
void Normalizar() {
HizoOp = false;
NormalizarSeleccion();
}
void NormalizarSeleccion() {
QPalette *palette = new QPalette();
palette->setColor(QPalette::Foreground, Qt::black);
this->setPalette(*palette);
this->repaint();
}
int ObtIdBase() { return IdBase; }
void RefrescarImagen();
void Refrescar() {
RefrescarImagen();
this->setVisible(true);
}
};
typedef Unidad *pUnidad;
class Grupo : public std::vector<pUnidad> {
private:
int Id;
537

VOLVER A MEN

pConquista refJuego;
public:
Grupo(pConquista J, int id) : std::vector<pUnidad>() {
refJuego = J;
Id = id;
}
void AgregarUnidad(TipoUnidad T) {
this->push_back(new Unidad(refJuego, T, Id));
}
void AgregarUnidades(int C, TipoUnidad T) {
for (int i=0; i < C; i++) AgregarUnidad(T);
}
int ObtCantidad() { return this->size(); }
int ObtCantidadVivos();
void ColocarEnBase();
int ObtId() { return Id; }
void Refrescar();
void Normalizar();
bool TodosEnBase();
void Jugar();
};
typedef Grupo *pGrupo;
}
#endif /* GRUPO_HPP */

538

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Grupo.cpp
* Mauricio Paletta
*/
#include <typeinfo>
#include Grupo.hpp
#include Campo.hpp
#include Conquista.hpp
namespace ConquistaNM {
// Mtodos de la clase Unidad
//
Unidad::Unidad(pConquista J, TipoUnidad T, int Id)
: QFrame(J->ObtFormulario()) {
QPalette *palette = new QPalette();
refJuego = J;
Tipo = T;
// Para la generacin de nmeros aleatorios
//
time_t segundos;
time(&segundos);
srand((unsigned)segundos);
EnBase = true;
if (Tipo != Infante) Vida = 4;
else
if (Tipo != Caballeria) Vida = 5;
539

VOLVER A MEN

else
Vida = 6;
Normalizar();
IdBase = Id;
graphicsView = new QGraphicsView(this);
graphicsView->setGeometry(0, 0, 45, 45);
graphicsView->setVisible(true);
graphicsView->setCursor(Qt::PointingHandCursor);
ImagenUnidad = new QGraphicsScene();
graphicsView->setScene(ImagenUnidad);
graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
labelVida = new QLabel(this);
labelVida->setGeometry(2, 2, 10, 12);
labelVida->setText(QString(%1).arg(Vida));
palette->setColor(QPalette::Background, Qt::white);
labelVida->setPalette(*palette);
labelVida->setLayoutDirection(Qt::RightToLeft);
labelVida->setVisible(true);
this->setGeometry(0, 0, 45, 45);
this->setFrameShape(Panel);
this->setFrameShadow(Plain);
this->setLineWidth(1);
this->setVisible(true);
}
Unidad::~Unidad() {
if (labelVida != NULL) delete labelVida;
if (ImagenUnidad != NULL) delete ImagenUnidad;
if (graphicsView != NULL) delete graphicsView;
540

VOLVER A MEN

}
void Unidad::Mover() {
refJuego->ReproducirSonido(5);
HizoOp = true;
}
// Para seleccionar la unidad
// Solo es vlido para las unidades que se manejan manualmente
//
void Unidad::mousePressEvent(QMouseEvent *M) {
QPalette *palette = new QPalette();
// Si ya hizo una operacin, nada que hacer
//
if (HizoOp || !refJuego->JuegoIniciado()) return;
pUnidad U, S = this;
if (M->button() == Qt::LeftButton) {
// Si hay alguien seleccionado para atacar y se trata de
// bandos diferentes, entonces se concreta el ataque;
// si son del mismo bando o ya hay
// alguien seleccionado para mover,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
U = refJuego->ObtUnidadSeleccionadaAtacar();
if (U != NULL) {
if (U->ObtIdBase() != S->ObtIdBase()) {
refJuego->ConcretarAtaque(S);
return;
}
U->Normalizar();
541

VOLVER A MEN

refJuego->SeleccionarUnidadAtacar(NULL);
}
U = refJuego->ObtUnidadSeleccionadaMover();
if (U != NULL) {
if (U->ObtIdBase() != S->ObtIdBase()) return;
U->Normalizar();
refJuego->SeleccionarUnidadMover(NULL);
}
if (U != S) {
if (!refJuego->ValidarMovimientoManual(S)) return;
palette->setColor(QPalette::Foreground, Qt::blue);
S->setPalette(*palette);
refJuego->SeleccionarUnidadMover(S);
}
}
else
if (M->button() == Qt::RightButton) {
// Seleccionado para atacar
//
// Si hay alguien seleccionado para mover o ya hay
// alguien seleccionado para atacar,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
U = refJuego->ObtUnidadSeleccionadaMover();
if (U != NULL) {
U->Normalizar();
refJuego->SeleccionarUnidadMover(NULL);
}
U = refJuego->ObtUnidadSeleccionadaAtacar();
if (U != NULL) {
U->Normalizar();
refJuego->SeleccionarUnidadAtacar(NULL);
542

VOLVER A MEN

}
if (U != S) {
palette->setColor(QPalette::Foreground, Qt::red);
S->setPalette(*palette);
refJuego->SeleccionarUnidadAtacar(S);
}
}
}
void Unidad::Herir() {
if (EstaVivo()) Vida--;
labelVida->setText(QString(%1).arg(Vida));
if (!EstaVivo()) {
refJuego->ReproducirSonido(1);
this->setVisible(false);
}
else {
refJuego->ReproducirSonido(2);
}
labelVida->repaint();
repaint();
}
bool Unidad::Atacar(int A) {
if (HizoOp) return false;
HizoOp = true;
if (Tipo == Artilleria)
refJuego->ReproducirSonido(3);
else
refJuego->ReproducirSonido(4);
return ObtPunteria(A);
}

543

VOLVER A MEN

void Unidad::RefrescarImagen() {
std::string strImagen;
if (Tipo == Infante) strImagen = Infante;
else
if (Tipo == Caballeria) strImagen = Caballeria;
else
strImagen = Artilleria;
if (IdBase == 2) strImagen = strImagen + C.jpg;
else strImagen = strImagen + N.jpg;
ImagenUnidad->clear();
ImagenUnidad->addPixmap(QPixmap(strImagen.c_str()));
repaint();
}
// Mtodos de la clase Grupo
//
int Grupo::ObtCantidadVivos() {
int S = 0;
for (unsigned i=0; i < this->size(); i++) {
pUnidad U = (*this)[i];
if (U->EstaVivo() && U->isVisible()) S++;
}
return S;
}
void Grupo::ColocarEnBase() {
for (unsigned i=0; i < this->size(); i++)
(*this)[i]->EntrarBase();
}
void Grupo::Refrescar() {
for (unsigned i=0; i < this->size(); i++)
544

VOLVER A MEN

(*this)[i]->RefrescarImagen();
}
void Grupo::Normalizar() {
for (unsigned i=0; i < this->size(); i++)
(*this)[i]->Normalizar();
}
bool Grupo::TodosEnBase() {
for (unsigned i=0; i < this->size(); i++)
if (!((*this)[i]->EnSuBase())) return false;
return true;
}
void Grupo::Jugar() {
// El juego automtico se fundamente en las siguientes acciones:
// 1 - ganar ocupando la base del enemigo
// 2 - evitar que el enemigo gane abarcando territorio
// 3 - retirada si estn muy heridos
// 4 - abarcar territorio
//
// Para todas las unidades de este grupo
//
for (unsigned i=0; i < this->size(); i++) {
pUnidad U = (*this)[i], Ue;
pEspacio E;
if (U->isVisible() && U->EstaVivo()) {
E = refJuego->AbarcarTerritorio(U);
if (E != NULL && dynamic_cast<pBase>(E)) {
refJuego->SeleccionarUnidadMover(U);
refJuego->SeleccionarEspacioMover(E);
continue;
}
Ue = refJuego->DefenderTerritorio(U);
if (Ue != NULL) {
545

VOLVER A MEN

refJuego->SeleccionarUnidadAtacar(U);
refJuego->ConcretarAtaque(Ue);
continue;
}
if (U->ObtNivelVida() == 1) {
E = refJuego->Retirada(U);
if (E != NULL) {
refJuego->SeleccionarUnidadMover(U);
refJuego->SeleccionarEspacioMover(E);
continue;
}
}
E = refJuego->AbarcarTerritorio(U);
if (E != NULL) {
// Se tiene previsin de dejar un infante en la base
//
Base *B = refJuego->ObtBase(U->ObtIdBase());
if (!(U->EnSuBase() && U->ObtTipo() == Infante &&
B->ObtNumeroUnidades(Infante) == 1)) {
refJuego->SeleccionarUnidadMover(U);
refJuego->SeleccionarEspacioMover(E);
}
}
}
}
Normalizar();
}
}

546

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Conquista.hpp
* Mauricio Paletta
*/
#ifndef CONQUISTA_HPP
#define CONQUISTA_HPP
#include <QSound>
#include DeclaracionesBase.hpp
namespace ConquistaNM {
class Sonidos {
public:
void ReproducirSonidoMorir() {
QSound::play(Morir.wav);
}
void ReproducirSonidoHerido() {
QSound::play(Herido.wav);
}
void ReproducirSonidoDisparo1() {
QSound::play(Can.wav);
}
void ReproducirSonidoDisparo2() {
QSound::play(Disparo.wav);
}
void ReproducirSonidoAvance() {
QSound::play(Avance.wav);
}
};
547

VOLVER A MEN

class Conquista {
private:
pGrupo Conquistadores, Nativos;
bool LosNativos, Iniciado;
int Ganador;
pConquistaForm Frame;
pUnidad USeleccionadaMover, USeleccionadaAtacar;
Sonidos LosSonidos;
pCampo ElCampo;
public:
Conquista(pConquistaForm);
~Conquista();
QWidget *ObtFormulario();
void ConfigurarJuego();
void ConfigurarCampo(int f, int c);
void AsignarJuegoNativos() { LosNativos = true; }
void AsignarJuegoConquistadores() { LosNativos = false; }
bool EsJuegoNativos() { return LosNativos; }
bool EsJuegoConquistadores() { return !LosNativos; }
void AgregarConquistador(TipoUnidad T) {
Conquistadores->AgregarUnidad(T);
}
void AgregarConquistadores(int C, TipoUnidad T) {
Conquistadores->AgregarUnidades(C, T);
}
void AgregarNativo(TipoUnidad T) {
if (T == Artilleria) return;
Nativos->AgregarUnidad(T);
}
void AgregarNativos(int C, TipoUnidad T) {
if (T == Artilleria) return;
Nativos->AgregarUnidades(C, T);
548

VOLVER A MEN

}
int ObtNumeroConquistadores() { return Conquistadores->ObtCantidad(); }
int ObtNumeroNativos() { return Nativos->ObtCantidad(); }
Base *ObtBase(int IdBase);
void Limpiar();
void IniciarJuego() { Iniciado = true; }
bool JuegoIniciado() { return Iniciado; }
void SeleccionarUnidadMover(pUnidad U) {
if (Iniciado) USeleccionadaMover = U;
}
Unidad *ObtUnidadSeleccionadaMover() {
return USeleccionadaMover;
}
void SeleccionarUnidadAtacar(pUnidad U) {
if (Iniciado) USeleccionadaAtacar = U;
}
pUnidad ObtUnidadSeleccionadaAtacar() {
return USeleccionadaAtacar;
}
bool ValidarMovimientoManual() {
if (USeleccionadaMover == NULL) return false;
return (USeleccionadaMover->ObtIdBase() == 1 && !LosNativos) ||
(USeleccionadaMover->ObtIdBase() == 2 && LosNativos);
}
bool ValidarMovimientoManual(pUnidad U) {
return (U->ObtIdBase() == 1 && !LosNativos) ||
(U->ObtIdBase() == 2 && LosNativos);
}
bool ValidarAtaqueManual(pUnidad U) {
return (U->ObtIdBase() == 1 && !LosNativos) ||
(U->ObtIdBase() == 2 && LosNativos);
}
void SeleccionarEspacioMover(pEspacio E);
549

VOLVER A MEN

// Concretar ataque (si es el caso)


//
void ConcretarAtaque(pUnidad U);
// Normalizar los grupos
//
void NormalizarConquistadores() { Conquistadores->Normalizar(); }
void NormalizarNativos() { Nativos->Normalizar(); }
void NormalizarGrupos() {
NormalizarConquistadores();
NormalizarNativos();
}
void NormalizarBase(int IdB) {
if (IdB == 1) NormalizarNativos();
else NormalizarConquistadores();
}
void VerificarFinalizacion();
int ObtGanador() {
VerificarFinalizacion();
return Ganador;
}
// Para el juego automtico
//
void Jugar() {
if (!Iniciado) return;
if (LosNativos) Nativos->Jugar();
else Conquistadores->Jugar();
}
pEspacio AbarcarTerritorio(pUnidad U);
pUnidad DefenderTerritorio(pUnidad U);
pEspacio Retirada(pUnidad U);
void ReproducirSonido(int S);
};
550

VOLVER A MEN

typedef Conquista *pConquista;


}
#endif /* CONQUISTA_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Conquista.cpp
* Mauricio Paletta
*/
#include <QPoint>
#include Grupo.hpp
#include Campo.hpp
#include ConquistaForm.hpp
#include Conquista.hpp
namespace ConquistaNM {
// Mtodos de la clase Conquista
//
Conquista::Conquista(pConquistaForm F) {
ElCampo = NULL;
Conquistadores = new Grupo(this, 2);
Nativos = new Grupo(this, 1);
Frame = F;
USeleccionadaMover = USeleccionadaAtacar = NULL;
// Inicialmente el juego automtico est asociado a los nativos y no hay ganador
//
LosNativos = true;
551

VOLVER A MEN

Ganador = 0;
Iniciado = false;
}
Conquista::~Conquista() {
if (Conquistadores != NULL) delete Conquistadores;
if (Nativos != NULL) delete Nativos;
if (ElCampo != NULL) delete ElCampo;
}
QWidget *Conquista::ObtFormulario() {
return Frame->centralWidget();
}
void Conquista::ConfigurarJuego() {
// Las unidades se colocan en la base
//
int i;
// Se limpia el campo
//
if (ElCampo != NULL) ElCampo->Limpiar();
// Se ubican las unidades en sus bases respectivas
// Nativos a la izquierda (base 1) y conquistadores a la derecha (base 2)
//
for (i=0; i < Nativos->ObtCantidad(); i++)
ElCampo->UbicarUnidad((*Nativos)[i], 1);
Nativos->ColocarEnBase();
for (i=0; i < Conquistadores->ObtCantidad(); i++)
ElCampo->UbicarUnidad((*Conquistadores)[i], 2);
Conquistadores->ColocarEnBase();
NormalizarGrupos();
552

VOLVER A MEN

Ganador = 0;
Frame->resize(ElCampo->width() + 20, ElCampo->height() + 110);
}
void Conquista::ConfigurarCampo(int f, int c) {
if (f <= 0) f = 1;
if (c <= 0) c = 1;
ElCampo = new Campo(this, f, c, ObtNumeroNativos(), ObtNumeroConquistadores());
Frame->resize(ElCampo->width() + 36, ElCampo->height() + 150);
ConfigurarJuego();
}
Base *Conquista::ObtBase(int IdBase) {
return dynamic_cast<pBase>(ElCampo->ObtBase(IdBase));
}
void Conquista::Limpiar() {
QObjectList L = Frame->children();
Conquistadores->clear();
Nativos->clear();
if (ElCampo != NULL) ElCampo->Limpiar();
L.removeAt(L.indexOf(ElCampo, 0));
USeleccionadaMover = USeleccionadaAtacar = NULL;
}
void Conquista::SeleccionarEspacioMover(pEspacio E) {
int IdBase, NCuadros;
bool Valido;
QPoint *Pact, *Pnue;
if (E == NULL || !Iniciado) return;

553

VOLVER A MEN

IdBase = E->ObtIdBase();
// Solo tiene sentido si actualmente hay una unidad para moverse
//
if (USeleccionadaMover == NULL || USeleccionadaMover->HizoOperacion()) return;
// El movimiento es posible si el espacio seleccionado no tiene una unidad
// enemiga y hay disponibilidad
//
if (IdBase > 0 && IdBase != USeleccionadaMover->ObtIdBase()) return;
// Se busca el espacio actual de la unidad seleccionada
//
pEspacio EUn = ElCampo->BuscarUnidad(USeleccionadaMover);
if (EUn == NULL || EUn == E) return;
// Se valida si el movimiento es posible; depende del tipo de unidad
//
NCuadros = (USeleccionadaMover->ObtTipo() == Infante) ? 1 : 2;
if (NCuadros > E->ObtDisponibilidad()) return;
if (USeleccionadaMover->EnSuBase()) {
Pnue = dynamic_cast<pCuadro>(E)->ObtPosicion();
if (Pnue == NULL) return;
Valido = (USeleccionadaMover->ObtIdBase() == 1 && Pnue->x() <= NCuadros - 1) ||
(USeleccionadaMover->ObtIdBase() == 2 && Pnue->x() >=
ElCampo->ObtNumColumnas() - NCuadros);
delete Pnue;
}
else {
if (E == ElCampo->ObtBase(1)) {
Pact = dynamic_cast<pCuadro>(EUn)->ObtPosicion();
if (Pact == NULL) return;
Valido = Pact->x() <= NCuadros - 1;
554

VOLVER A MEN

delete Pact;
}
else
if (E == ElCampo->ObtBase(2)) {
Pact = dynamic_cast<pCuadro>(EUn)->ObtPosicion();
if (Pact == NULL) return;
Valido = Pact->x() >= ElCampo->ObtNumColumnas() - NCuadros;
delete Pact;
}
else {
// Hay cuatro movimientos posibles: >, ^, v y <
//
Pact = dynamic_cast<pCuadro>(EUn)->ObtPosicion();
Pnue = dynamic_cast<pCuadro>(E)->ObtPosicion();
Valido = (Pact->y() == Pnue->y() && abs(Pact->x() - Pnue->x()) <= NCuadros) ||
(Pact->x() == Pnue->x() && abs(Pact->y() - Pnue->y()) <= NCuadros);

// El caso diagonal es posible siempre y cuando el cuadro inmediato siguiente


// est ocupado por una unidad del bando contrario
//
if (!Valido) {
if ((Pnue->y() == Pact->y() - 1 && Pnue->x() == Pact->x() + 1) ||
(Pnue->y() == Pact->y() + 1 && Pnue->x() == Pact->x() + 1)) {
Valido = ElCampo->HayUnidad(USeleccionadaMover->ObtIdBase() == 1 ? 2 : 1,
new QPoint(Pact->x() + 1, Pact->y()));
}
else
if ((Pnue->y() == Pact->y() - 1 && Pnue->x() == Pact->x() - 1) ||
(Pnue->y() == Pact->y() + 1 && Pnue->x() == Pact->x() - 1)) {
Valido = ElCampo->HayUnidad(USeleccionadaMover->ObtIdBase() == 1 ? 2 : 1,
new QPoint(Pact->x() - 1, Pact->y()));
555

VOLVER A MEN

}
}
delete Pact;
delete Pnue;
}
}
// Si el movimiento es vlido se procede
//
if (Valido) {
// Se saca la unidad del espacio en la cual est actualmente
//
EUn->SacarUnidad(USeleccionadaMover);
// Se agrega la unidad al nuevo espacio
//
E->UbicarUnidad(USeleccionadaMover);
// Se normaliza la unidad movida
//
USeleccionadaMover->NormalizarSeleccion();
USeleccionadaMover = NULL;
}
}
// Concretar ataque (si es el caso)
//
void Conquista::ConcretarAtaque(pUnidad U) {
if (USeleccionadaAtacar == NULL || U == NULL || !Iniciado) return;
// Se valida que el ataque es posible
//
pEspacio E1 = ElCampo->BuscarUnidad(USeleccionadaAtacar),
E2 = ElCampo->BuscarUnidad(U);
556

VOLVER A MEN

QPoint *P1 = E1->ObtPosicion(), *P2 = E2->ObtPosicion();


bool Valido;
if (USeleccionadaAtacar->EnSuBase()) {
Valido = (USeleccionadaAtacar->ObtIdBase() == 1 && P2->x() == 0) ||
(USeleccionadaAtacar->ObtIdBase() == 1 &&
USeleccionadaAtacar->ObtTipo() == Artilleria && P2->x() <= 1) ||
(USeleccionadaAtacar->ObtIdBase() == 2 &&
P2->x() == ElCampo->ObtNumColumnas() - 1) ||
(USeleccionadaAtacar->ObtIdBase() == 2 &&
USeleccionadaAtacar->ObtTipo() == Artilleria &&
P2->x() >= ElCampo->ObtNumColumnas() - 2);
}
else
if (U->EnSuBase()) {
Valido = (U->ObtIdBase() == 1 && P1->x() == 0) || (U->ObtIdBase() == 1 &&
USeleccionadaAtacar->ObtTipo() == Artilleria && P1->x() <= 1) ||
(U->ObtIdBase() == 2 && P1->x() == ElCampo->ObtNumColumnas() - 1) ||
(U->ObtIdBase() == 2 && USeleccionadaAtacar->ObtTipo() == Artilleria &&
P1->x() >= ElCampo->ObtNumColumnas() - 2);
}
else {
Valido = (P1->y() == P2->y() && abs(P1->x() - P2->x()) == 1) || (P1->y() == P2->y() &&
USeleccionadaAtacar->ObtTipo() == Artilleria && abs(P1->x() - P2->x()) == 2) ||
(P1->x() == P2->x() && USeleccionadaAtacar->ObtTipo() != Artilleria &&
abs(P1->y() - P2->y()) == 1);
}
if (Valido) {
int A = 1;
if (P1->y() == P2->y() && USeleccionadaAtacar->ObtTipo() == Artilleria &&
abs(P1->x() - P2->x()) == 2) A = 2;
if (USeleccionadaAtacar->Atacar(A)) {
U->Herir();
557

VOLVER A MEN

if (!U->EstaVivo()) E2->SacarUnidad(U);
}
USeleccionadaAtacar->NormalizarSeleccion();
}
delete P1;
delete P2;
}
void Conquista::VerificarFinalizacion() {
if (!Iniciado) return;
// Ganan los nativos
//
if (Conquistadores->ObtCantidadVivos() == 0 || Conquistadores->TodosEnBase() ||
ElCampo->HayNativosBaseConquistadores()) Ganador = 1;
else
// Ganan los conquistadores
//
if (Nativos->ObtCantidadVivos() == 0 || Nativos->TodosEnBase() ||
ElCampo->HayConquistadoresBaseNativos()) Ganador = 2;
if (Ganador > 0) Iniciado = false;
}
pEspacio Conquista::AbarcarTerritorio(pUnidad U) {
// Buscar un espacio con la cual la unidad dada puede abarcar territorio
//
pEspacio E, Eaux, Emin = NULL;
QPoint *P;
int Max = 0;
bool Asignar;
if (U == NULL) return NULL;
E = ElCampo->BuscarUnidad(U);
558

VOLVER A MEN

if (E == NULL) return NULL;


if (U->EnSuBase()) {
for (int i=0; i < ElCampo->ObtNumFilas(); i++) {
Asignar = false;
Eaux = NULL;
if (U->ObtTipo() == Caballeria) {
Eaux = ElCampo->ObtCuadro(i, U->ObtIdBase() == 1 ? 1 :

ElCampo->ObtNumColumnas()-2);
if (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase()) {
if (Eaux->ObtDisponibilidad() > Max) Asignar = true;
else
if (Eaux->ObtDisponibilidad() == Max) Asignar = rand() >= RAND_MAX / 2;
}
}
if (!Asignar) {
Eaux = ElCampo->ObtCuadro(i, U->ObtIdBase() == 1 ? 0 :
ElCampo->ObtNumColumnas()-1);
if (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase()) {
if (Eaux->ObtDisponibilidad() > Max) Asignar = true;
else
if (Eaux->ObtDisponibilidad() == Max) Asignar = rand() >= RAND_MAX / 2;
}
}
if (Asignar && Eaux != NULL) {
Max = Eaux->ObtDisponibilidad();
Emin = Eaux;
}
}
if (Emin != NULL) return Emin;
}
P = E->ObtPosicion();
if (U->ObtIdBase() == 1) {

559

VOLVER A MEN

if (ElCampo->ObtBase(2)->Vacio() &&
((P->x() == ElCampo->ObtNumColumnas() - 1) ||
(P->x() == ElCampo->ObtNumColumnas() - 2 && U->ObtTipo() == Caballeria))) {
delete P;
return ElCampo->ObtBase(2);
}
if (U->ObtTipo() == Caballeria) {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()+2);
if (Eaux != NULL && Eaux->Vacio()) {
delete P;
return Eaux;
}
}
Eaux = ElCampo->ObtCuadro(P->y(), P->x()+1);
if (Eaux != NULL && (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase())) {
delete P;
return Eaux;
}
}
else {
if (ElCampo->ObtBase(1)->Vacio() &&
((P->x() == 0) ||
(P->x() == 1 && U->ObtTipo() == Caballeria)))
return ElCampo->ObtBase(1);
if (U->ObtTipo() == Caballeria) {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()-2);
if (Eaux != NULL && Eaux->Vacio()) {
delete P;
return Eaux;
}
}
Eaux = ElCampo->ObtCuadro(P->y(), P->x()-1);
if (Eaux != NULL && (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase())) {
560

VOLVER A MEN

delete P;
return Eaux;
}
}
delete P;
return NULL;
}
pUnidad Conquista::DefenderTerritorio(pUnidad U) {
// Verificar si los espacios adyacentes a la unidad dada hay un enemigo
// a fin de atacarlo. Se ataca el ms dbil
//
pEspacio E, Eaux;
QPoint *P;
if (U == NULL) return NULL;
E = ElCampo->BuscarUnidad(U);
if (E == NULL) return NULL;
P = E->ObtPosicion();
if (U->ObtIdBase() == 1) {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()-1);
if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
}
else {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()+1);
if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
}
Eaux = ElCampo->ObtCuadro(P->y()-1, P->x());
if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
561

VOLVER A MEN

Eaux = ElCampo->ObtCuadro(P->y()+1, P->x());


if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
if (U->ObtIdBase() == 1) {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()+1);
if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
}
else {
Eaux = ElCampo->ObtCuadro(P->y(), P->x()-1);
if (Eaux != NULL && !Eaux->Vacio() && Eaux->ObtIdBase() != U->ObtIdBase())
return Eaux->ObtMasDebil();
}
delete P;
return NULL;
}
pEspacio Conquista::Retirada(pUnidad U) {
// Provocar la retirada de la unidad
//
if (U == NULL || U->EnSuBase()) return NULL;
pEspacio E, Eaux;
QPoint *P;
E = ElCampo->BuscarUnidad(U);
if (E == NULL) return NULL;
P = E->ObtPosicion();
if (U->ObtIdBase() == 1) {
if (P->x() == 0) return ElCampo->ObtBase(1);
Eaux = ElCampo->ObtCuadro(P->y(), P->x()-1);
if (Eaux != NULL && (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase()))
562

VOLVER A MEN

return Eaux;
}
else {
if (P->x() == ElCampo->ObtNumColumnas()-1) return ElCampo->ObtBase(2);
Eaux = ElCampo->ObtCuadro(P->y(), P->x()+1);
if (Eaux != NULL && (Eaux->Vacio() || Eaux->ObtIdBase() == U->ObtIdBase()))
return Eaux;
}
delete P;
return NULL;
}
void Conquista::ReproducirSonido(int S) {
switch (S) {
case 1:
LosSonidos.ReproducirSonidoMorir();
break;
case 2:
LosSonidos.ReproducirSonidoHerido();
break;
case 3:
LosSonidos.ReproducirSonidoDisparo1();
break;
case 4:
LosSonidos.ReproducirSonidoDisparo2();
break;
case 5:
LosSonidos.ReproducirSonidoAvance();
break;
}
}
}

563

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* DeclaracionesBase.hpp
* Mauricio Paletta
*/
#ifndef DECLARACIONESBASE_HPP
#define DECLARACIONESBASE_HPP
namespace ConquistaNM {
enum TipoUnidad {
Infante, Caballeria, Artilleria
};
class Unidad;
typedef Unidad *pUnidad;
class Grupo;
typedef Grupo *pGrupo;
class Espacio;
typedef Espacio *pEspacio;
class Base;
typedef Base *pBase;
class Cuadro;
typedef Cuadro *pCuadro;
class Campo;
typedef Campo *pCampo;
class Conquista;
564

VOLVER A MEN

typedef Conquista *pConquista;


}
class ConquistaForm;
typedef ConquistaForm *pConquistaForm;
#endif /* DECLARACIONESBASE_HPP */

El cdigo Java elaborado para este problema se muestra a continuacin. Tal como se indica
en la Figura 6.61, se tienen los archivos fuente Campo.java, Grupo.java y Conquista.java.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Campo.java
* Mauricio Paletta
*/
package ConquistaNuevoMundo;
import java.awt.*;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
public class Campo extends JPanel {
// Representa un espacio del campo donde van las unidades
//
protected abstract class Espacio extends JPanel {
private class EspacioMouseListener implements java.awt.event.MouseListener {
EspacioMouseListener() {
super();
565

VOLVER A MEN

}
@Override
public void mouseClicked(MouseEvent e) {
// Seleccionado para intentar ubicar una unidad
// Slo cuando se tratan de unidades manejadas por el jugador humano
//
if (e.getButton() == 1) {
JPanel P = (JPanel)e.getComponent();
Espacio E = P != null ? (Espacio)P.getParent() : null;
if (E != null &&
E.refJuego.ValidarMovimientoManual()) E.refJuego.SeleccionarEspacioMover(E);
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
protected int Disponibilidad;
protected Conquista refJuego;
protected JPanel panelCol1, panelCol2;
public Espacio(Conquista J, String n, int x, int y, int u, int f) {
566

VOLVER A MEN

super();
refJuego = J;
Dimension D = new Dimension(90, 135 * f);
if (u <= 0) u = 1;
Disponibilidad = u;
setPreferredSize(D);
setMinimumSize(D);
setMaximumSize(D);
setSize(D);
setName(n);
setLocation(x, y);
setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new GridLayout(1, 2));
setCursor(new Cursor(Cursor.HAND_CURSOR));
D = new Dimension(45, 135 * f);
panelCol1 = new JPanel();
panelCol1.setName(n + -Col1);
panelCol1.setPreferredSize(D);
panelCol1.setMinimumSize(D);
panelCol1.setMaximumSize(D);
panelCol1.setSize(D);
panelCol1.setLocation(0, 0);
panelCol1.setLayout(new GridLayout(3 * f, 1));
panelCol1.addMouseListener(new EspacioMouseListener());
panelCol1.setCursor(new Cursor(Cursor.HAND_CURSOR));
panelCol1.setVisible(true);
add(panelCol1);
panelCol2 = new JPanel();
panelCol2.setName(n + -Col2);
panelCol2.setPreferredSize(D);
panelCol2.setMinimumSize(D);
567

VOLVER A MEN

panelCol2.setMaximumSize(D);
panelCol2.setSize(D);
panelCol2.setLocation(45, 0);
panelCol2.setLayout(new GridLayout(3 * f, 1));
panelCol2.addMouseListener(new EspacioMouseListener());
panelCol2.setCursor(new Cursor(Cursor.HAND_CURSOR));
panelCol2.setVisible(true);
add(panelCol2);
// Para seleccionar el espacio
//
addMouseListener(new EspacioMouseListener());
setVisible(true);
repaint();
}
public final void Limpiar() {
if (panelCol1 != null) panelCol1.removeAll();
if (panelCol2 != null) panelCol2.removeAll();
}
public int ObtDisponibilidad() { return Disponibilidad; }
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
public abstract boolean UbicarUnidad(Grupo.Unidad U);
public abstract boolean SacarUnidad(Grupo.Unidad U);
public abstract Point ObtPosicion();
public JPanel EstaUnidad(Grupo.Unidad U) {
int i;
Component []C;

568

VOLVER A MEN

if (U == null) return null;


C = panelCol1.getComponents();
for (i=0; i < panelCol1.getComponentCount(); i++) {
if (C[i] == U) {
return panelCol1;
}
}
C = panelCol2.getComponents();
for (i=0; i < panelCol2.getComponentCount(); i++) {
if (C[i] == U) {
return panelCol2;
}
}
return null;
}
public int ObtIdBase() {
if (panelCol1.getComponentCount() != 0)
return ((Grupo.Unidad)(panelCol1.getComponents()[0])).ObtIdBase();
if (panelCol2.getComponentCount() != 0)
return ((Grupo.Unidad)(panelCol2.getComponents()[0])).ObtIdBase();
return 0;
}
public boolean Vacio() {
return panelCol1.getComponentCount() == 0 && panelCol2.getComponentCount() == 0;
}
public int ObtNumeroUnidades() {
return panelCol1.getComponentCount() + panelCol2.getComponentCount();
}
public int ObtNumeroUnidades(Grupo.TipoUnidad T) {
int i, S = 0;
Grupo.Unidad U;
Component []C;

569

VOLVER A MEN

C = panelCol1.getComponents();
for (i=0; i < panelCol1.getComponentCount(); i++) {
U = (Grupo.Unidad)C[i];
if (U.ObtTipo().compareTo(T) == 0) S++;
}
C = panelCol2.getComponents();
for (i=0; i < panelCol2.getComponentCount(); i++) {
U = (Grupo.Unidad)C[i];
if (U.ObtTipo().compareTo(T) == 0) S++;
}
return S;
}
protected Grupo.Unidad ObtMasDebil() {
Grupo.Unidad U, MasDebil = null;
int i;
Component []C;
C = panelCol1.getComponents();
for (i=0; i < panelCol1.getComponentCount(); i++) {
U = (Grupo.Unidad)C[i];
if (MasDebil == null || U.ObtNivelVida() < MasDebil.ObtNivelVida())
MasDebil = U;
}
C = panelCol2.getComponents();
for (i=0; i < panelCol2.getComponentCount(); i++) {
U = (Grupo.Unidad)C[i];
if (MasDebil == null || U.ObtNivelVida() < MasDebil.ObtNivelVida())
MasDebil = U;
}
return MasDebil;
}
}

570

VOLVER A MEN

// Representa una base


//
class Base extends Espacio {
public Base(Conquista J, String n, int x, int y, int u, int f) {
super(J, n, x, y, u, f);
}
// Ubicar una unidad en la base
//
public boolean UbicarUnidad(Grupo.Unidad U) {
int i, u;
if (U == null || Disponibilidad == 0 || U.HizoOperacion()) return false;
i = panelCol1.getComponentCount();
u = panelCol1.getHeight() / 45;
if (i < u) {
panelCol1.add(U);
panelCol1.repaint();
}
else {
i = panelCol2.getComponentCount();
u = panelCol2.getHeight() / 45;
if (i < u) {
panelCol2.add(U);
panelCol2.repaint();
}
else
return false;
}
U.EntrarBase();
U.setLocation(0, (i % u) * 45);
U.Refrescar();
571

VOLVER A MEN

Disponibilidad--;
repaint();
return true;
}
public boolean SacarUnidad(Grupo.Unidad U) {
JPanel P = EstaUnidad(U);
if (U == null || P == null || U.HizoOperacion()) return false;
U.SalirBase();
P.remove(U);
Disponibilidad++;
P.repaint();
repaint();
return true;
}
public Point ObtPosicion() {
return new Point(getX(), getY());
}
public boolean HayUnidadEnemiga() {
Grupo.Unidad U;
int i, EstaBase = getX() <= 1 ? 1 : 2;
for (i=0; i < panelCol1.getComponentCount(); i++) {
U = (Grupo.Unidad)(panelCol1.getComponents()[i]);
if (U.ObtIdBase() != EstaBase) return true;
}
for (i=0; i < panelCol2.getComponentCount(); i++) {
U = (Grupo.Unidad)(panelCol2.getComponents()[i]);
if (U.ObtIdBase() != EstaBase) return true;
}
return false;
}
}
// Representa un cuadro/celda del campo
//
572

VOLVER A MEN

class Cuadro extends Espacio {


// Puede albergar hasta un mximo de 6 unidades (infantes)
//
public Cuadro(Conquista J, String n, int x, int y) {
super(J, n, x, y, 6, 1);
}
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
public boolean UbicarUnidad(Grupo.Unidad U) {
int i, Requerido;
if (U == null || U.HizoOperacion()) return false;
Requerido = U.ObtTipo().compareTo(Grupo.TipoUnidad.Infante) == 0 ? 1 : 2;
if (Disponibilidad - Requerido < 0) return false;
i = panelCol1.getComponentCount();
if (i < 3) {
panelCol1.add(U);
panelCol1.repaint();
}
else {
i = panelCol2.getComponentCount();
if (i < 3) {
panelCol2.add(U);
panelCol2.repaint();
}
else
return false;
}
U.setLocation(0, (i % 3) * 45);
U.Mover();
573

VOLVER A MEN

U.Refrescar();
Disponibilidad -= Requerido;
repaint();
return true;
}
public boolean SacarUnidad(Grupo.Unidad U) {
int Ocupado;
JPanel P = EstaUnidad(U);
if (U == null || P == null || U.HizoOperacion()) return false;
Ocupado = U.ObtTipo().compareTo(Grupo.TipoUnidad.Infante) == 0 ? 1 : 2;
P.remove(U);
Disponibilidad += Ocupado;
P.repaint();
repaint();
return true;
}
public Point ObtPosicion() {
return new Point(getX() / getWidth(), getY() / getHeight());
}
public boolean HayUnidad(int IdBase) {
Grupo.Unidad U;
if (IdBase < 1 || IdBase > 2) return false;
if (panelCol1.getComponentCount() > 0) {
U = (Grupo.Unidad)panelCol1.getComponents()[0];
return U.ObtIdBase() == IdBase;
}
if (panelCol2.getComponentCount() > 0) {
U = (Grupo.Unidad)panelCol2.getComponents()[0];
return U.ObtIdBase() == IdBase;
}
return false;
574

VOLVER A MEN

}
}
// Tamao del campo de batalla
//
private int F, C;
private Cuadro Cuadros[][];
private Base Bases[];
private JPanel gridPanel;
private Conquista refJuego;
public Campo(Conquista J, int f, int c, int uB1, int uB2) {
super();
if (f <= 0) f = 1;
if (c <= 0) c = 1;
F = f; C = c;
refJuego = J;
setSize(90 * (C + 2), 135 * F);
setLocation(10, 10);
setName(jPanelCampo);
setVisible(true);
setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new BorderLayout());
gridPanel = new JPanel(new GridLayout(F, C));
gridPanel.setSize(90 * C, 135 * F);
Bases = new Base[2];
Bases[0] = new Base(refJuego, Base0, 0, 0, uB1, F);
Bases[1] = new Base(refJuego, Base1, 90 * (C + 1), 0, uB2, F);
add(Bases[0], BorderLayout.WEST);
add(Bases[1], BorderLayout.EAST);
575

VOLVER A MEN

add(gridPanel, FlowLayout.CENTER);
Cuadros = new Cuadro[F][C];
for (int i=0; i < F; i++) {
for (int j=0; j < C; j++) {
Cuadros[i][j] = new Cuadro(refJuego, Cuadro + Integer.toString(i) + Integer.toString(j),
j * 90, i * 135);
gridPanel.add(Cuadros[i][j]);
}
}
}
public boolean UbicarUnidad(Grupo.Unidad U, int f, int c) {
if (U == null || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f][c].UbicarUnidad(U);
}
public boolean SacarUnidad(Grupo.Unidad U, int f, int c) {
if (U == null || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f][c].SacarUnidad(U);
}
public boolean UbicarUnidad(Grupo.Unidad U, int b) {
if (U == null || b < 1 || b > 2) return false;
return Bases[b-1].UbicarUnidad(U);
}
public boolean SacarUnidad(Grupo.Unidad U, int b) {
if (U == null || b < 1 || b > 2) return false;
return Bases[b-1].SacarUnidad(U);
}
public Point ObtPosicionUnidad(Grupo.Unidad U) {
for (int i=0; i < F; i++) {
for (int j=0; j < C; j++) {
if (Cuadros[i][j].EstaUnidad(U) != null)
return new Point(i, j);
}
576

VOLVER A MEN

}
return null;
}
public void Limpiar() {
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
Cuadros[i][j].Limpiar();
Bases[0].Limpiar();
Bases[1].Limpiar();
}
public Espacio BuscarUnidad(Grupo.Unidad U) {
int IdBase;
if (U == null) return null;
IdBase = U.ObtIdBase();
if (IdBase < 1 || IdBase > 2) return null;
// Primero se busca en alguna de las bases
//
if (Bases[IdBase - 1].EstaUnidad(U) != null) return Bases[IdBase - 1];
// Si no est en las bases se busca en alguno de los cuadros
//
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
if (Cuadros[i][j].EstaUnidad(U) != null) return Cuadros[i][j];
return null;
}
public boolean HayUnidad(int IdBase, Point P) {
if (IdBase < 1 || IdBase > 2) return false;
if (P.X < 0 || P.X >= C || P.Y < 0 || P.Y >= F) return false;
return Cuadros[P.Y][P.X].HayUnidad(IdBase);
}
577

VOLVER A MEN

public Espacio ObtBase(int IdBase) {


return (IdBase >= 1 && IdBase <= 2) ? Bases[IdBase-1] : null;
}
public Espacio ObtCuadro(int f, int c) {
return (f >= 0 && f < F && c >= 0 && c < C) ? Cuadros[f][c] : null;
}
public int ObtNumFilas() { return F; }
public int ObtNumColumnas() { return C; }
public boolean HayConquistadoresBaseNativos() {
return Bases[0].HayUnidadEnemiga();
}
public boolean HayNativosBaseConquistadores() {
return Bases[1].HayUnidadEnemiga();
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Grupo.java
* Mauricio Paletta
*/
package ConquistaNuevoMundo;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
578

VOLVER A MEN

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Grupo extends ArrayList<Grupo.Unidad> {
public enum TipoUnidad {
Infante, Caballeria, Artilleria
}
protected class Unidad extends JPanel {
private TipoUnidad Tipo;
private int Vida, IdBase;
private Random R;
private boolean EnBase, HizoOp;
private Conquista refJuego;
private BufferedImage Imagen;
private JLabel labelImagen, labelVida;
public Unidad(Conquista J, TipoUnidad T, int Id) {
super();
refJuego = J;
Dimension D;
Tipo = T;
R = new Random();
579

VOLVER A MEN

R.setSeed((new Date()).getTime());
EnBase = true;
if (Tipo.compareTo(TipoUnidad.Infante) != 0) Vida = 4;
else
if (Tipo.compareTo(TipoUnidad.Caballeria) != 0) Vida = 5;
else
Vida = 6;
Normalizar();
IdBase = Id;
labelImagen = new JLabel();
D = new Dimension(45, 45);
labelImagen.setSize(D);
labelImagen.setPreferredSize(D);
labelImagen.setMinimumSize(D);
labelImagen.setMaximumSize(D);
labelImagen.setBorder(BorderFactory.createLineBorder(Color.black));
labelImagen.setVisible(true);
labelVida = new JLabel();
D = new Dimension(14, 14);
labelVida.setText(Integer.toString(Vida));
labelVida.setSize(D);
labelVida.setPreferredSize(D);
labelVida.setMinimumSize(D);
labelVida.setMaximumSize(D);
labelVida.setVisible(true);
D = new Dimension(labelImagen.getWidth(), labelVida.getHeight() + labelImagen.getHeight());
setSize(D);
setPreferredSize(D);
setMinimumSize(D);
setMaximumSize(D);
setLayout(new BorderLayout());
580

VOLVER A MEN

setBorder(BorderFactory.createLineBorder(Color.black));
add(labelVida, BorderLayout.PAGE_START);
add(labelImagen, BorderLayout.PAGE_END);
setCursor(new Cursor(Cursor.HAND_CURSOR));
// Para seleccionar la unidad
// Solo es vlido para las unidades que se manejan manualmente
//
addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
// Si ya hizo una operacin, nada que hacer
//
if (HizoOp || !refJuego.JuegoIniciado()) return;

Unidad U, S = (Unidad)e.getComponent();
switch (e.getButton()) {
// Seleccionado para moverse
//
case 1:
// Si hay alguien seleccionado para atacar y se trata de
// bandos diferentes, entonces se concreta el ataque;
// si son del mismo bando o ya hay
// alguien seleccionado para mover,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
U = refJuego.ObtUnidadSeleccionadaAtacar();
if (U != null) {
if (U.ObtIdBase() != S.ObtIdBase()) {
refJuego.ConcretarAtaque(S);
return;
581

VOLVER A MEN

}
U.Normalizar();
refJuego.SeleccionarUnidadAtacar(null);
}
U = refJuego.ObtUnidadSeleccionadaMover();
if (U != null) {
if (U.ObtIdBase() != S.ObtIdBase()) {
return;
}
U.Normalizar();
refJuego.SeleccionarUnidadMover(null);
}
if (U != S) {
if (!refJuego.ValidarMovimientoManual(S)) return;
setBorder(BorderFactory.createLineBorder(Color.blue));
refJuego.SeleccionarUnidadMover(S);
}
break;
// Seleccionado para atacar
//
case 3:
// Si hay alguien seleccionado para mover o ya hay
// alguien seleccionado para atacar,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
U = refJuego.ObtUnidadSeleccionadaMover();
if (U != null) {
U.Normalizar();
refJuego.SeleccionarUnidadMover(null);
}
U = refJuego.ObtUnidadSeleccionadaAtacar();
if (U != null) {
582

VOLVER A MEN

U.Normalizar();
refJuego.SeleccionarUnidadAtacar(null);
}
if (U != S) {
setBorder(BorderFactory.createLineBorder(Color.red));
refJuego.SeleccionarUnidadAtacar(S);
}
break;
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
setVisible(false);
}
public TipoUnidad ObtTipo() { return Tipo; }
public int ObtNivelVida() { return Vida; }
public boolean ObtPunteria(int A) {
double p;
switch (Tipo) {
case Infante:
583

VOLVER A MEN

p = 0.6;
break;
case Caballeria:
p = 0.75;
break;
default:
p = A == 2 ? 0.5 : 0.95;
break;
}
return R.nextDouble() <= p;
}
// En el caso de la caballera se asume primer nivel de
// visibilidad o alcance
//
public int ObtAlcance() {
return Tipo.compareTo(TipoUnidad.Caballeria) == 0 ? 2 : 1;
}
public boolean EstaVivo() { return Vida > 0; }
public void Herir() {
if (EstaVivo()) Vida--;
labelVida.setText(Integer.toString(Vida));
if (!EstaVivo()) {
refJuego.ReproducirSonido(1);
setVisible(false);
}
else {
refJuego.ReproducirSonido(2);
}
labelVida.repaint();
repaint();
}
public boolean Atacar(int A) {
584

VOLVER A MEN

if (HizoOp) return false;


HizoOp = true;
if (Tipo.compareTo(TipoUnidad.Artilleria) == 0)
refJuego.ReproducirSonido(3);
else
refJuego.ReproducirSonido(4);
return ObtPunteria(A);
}
public void Mover() {
refJuego.ReproducirSonido(5);
HizoOp = true;
}
public boolean HizoOperacion() { return HizoOp; }
public boolean EnSuBase() { return EnBase; }
public void SalirBase() { EnBase = false; }
public void EntrarBase() { EnBase = true; Mover(); }
public final void Normalizar() {
HizoOp = false;
NormalizarSeleccion();
}
public final void NormalizarSeleccion() {
setBorder(BorderFactory.createLineBorder(Color.black));
}
public int ObtIdBase() { return IdBase; }
private void RefrescarImagen() {
try {
String strImagen;
if (Tipo.compareTo(TipoUnidad.Infante) == 0) strImagen = Infante;
else
if (Tipo.compareTo(TipoUnidad.Caballeria) == 0) strImagen = Caballeria;
585

VOLVER A MEN

else
strImagen = Artilleria;
if (IdBase == 2) strImagen = strImagen + C.jpg;
else strImagen = strImagen + N.jpg;
Imagen = ImageIO.read(new File(strImagen));
labelImagen.setIcon(new ImageIcon(Imagen));
repaint();
} catch (IOException ex) { }
}
public void Refrescar() {
RefrescarImagen();
setVisible(true);
}
}
private int Id;
private Conquista refJuego;
public Grupo(Conquista J, int id) {
super();
refJuego = J;
Id = id;
}
public void AgregarUnidad(TipoUnidad T) {
add(new Unidad(refJuego, T, Id));
}
public void AgregarUnidades(int C, TipoUnidad T) {
for (int i=0; i < C; i++) AgregarUnidad(T);
}
public int ObtCantidad() { return size(); }
public int ObtCantidadVivos() {
586

VOLVER A MEN

int S = 0;
for (int i=0; i < size(); i++) {
Unidad U = get(i);
if (U.EstaVivo() && U.isVisible()) S++;
}
return S;
}
public void ColocarEnBase() {
for (int i=0; i < size(); i++) {
get(i).EntrarBase();
}
}
public int ObtId() { return Id; }
public void Refrescar() {
for (int i=0; i < size(); i++) {
get(i).RefrescarImagen();
}
}
public void Normalizar() {
for (int i=0; i < size(); i++) {
get(i).Normalizar();
}
}
public boolean TodosEnBase() {
for (int i=0; i < size(); i++) {
if (!(get(i).EnSuBase())) return false;
}
return true;
}
protected void Jugar() {
// El juego automtico se fundamente en las siguientes acciones:
// 1 - ganar ocupando la base del enemigo
587

VOLVER A MEN

// 2 - evitar que el enemigo gane abarcando territorio


// 3 - retirada si estn muy heridos
// 4 - abarcar territorio
//
// Para todas las unidades de este grupo
//
for (int i=0; i < size(); i++) {
Unidad U = get(i), Ue;
Campo.Espacio E;
if (U.isVisible() && U.EstaVivo()) {
E = refJuego.AbarcarTerritorio(U);
if (E != null && E instanceof Campo.Base) {
refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
continue;
}
Ue = refJuego.DefenderTerritorio(U);
if (Ue != null) {
refJuego.SeleccionarUnidadAtacar(U);
refJuego.ConcretarAtaque(Ue);
continue;
}
if (U.ObtNivelVida() == 1) {
E = refJuego.Retirada(U);
if (E != null) {
refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
continue;
}
}
E = refJuego.AbarcarTerritorio(U);
if (E != null) {
588

VOLVER A MEN

// Se tiene previsin de dejar un infante en la base


//
if (!(U.EnSuBase() && U.ObtTipo().compareTo(TipoUnidad.Infante) == 0 &&
refJuego.ElCampo.ObtBase(U.IdBase).ObtNumeroUnidades(TipoUnidad.Infante) == 1)) {
refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
}
}
}
}
Normalizar();
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Conquista.java
* Mauricio Paletta
*/
package ConquistaNuevoMundo;
import ConquistaNuevoMundo.Grupo.TipoUnidad;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
589

VOLVER A MEN

public class Conquista {


private class Sonidos {
private Clip SonidoMorir, SonidoHerido, SonidoDisparo1, SonidoDisparo2, SonidoAvance;
private int Retardo = 500;
public Sonidos() {
try {
SonidoMorir = AudioSystem.getClip();
SonidoHerido = AudioSystem.getClip();
SonidoDisparo1 = AudioSystem.getClip();
SonidoDisparo2 = AudioSystem.getClip();
SonidoAvance = AudioSystem.getClip();
SonidoMorir.open(AudioSystem.getAudioInputStream(new File(Morir.wav)));
SonidoHerido.open(AudioSystem.getAudioInputStream(new File(Herido.wav)));
SonidoDisparo1.open(AudioSystem.getAudioInputStream(new File(Can.wav)));
SonidoDisparo2.open(AudioSystem.getAudioInputStream(new File(Disparo.wav)));
SonidoAvance.open(AudioSystem.getAudioInputStream(new File(Avance.wav)));
} catch (LineUnavailableException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
} catch (UnsupportedAudioFileException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void ReproducirSonidoMorir() {
try {
SonidoMorir.setFramePosition(0);
SonidoMorir.start();
while (SonidoMorir.isRunning()) Thread.sleep(Retardo);
} catch (InterruptedException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
590

VOLVER A MEN

}
}
public void ReproducirSonidoHerido() {
try {
SonidoHerido.setFramePosition(0);
SonidoHerido.start();
while (SonidoHerido.isRunning()) Thread.sleep(Retardo);
} catch (InterruptedException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void ReproducirSonidoDisparo1() {
try {
SonidoDisparo1.setFramePosition(0);
SonidoDisparo1.start();
while (SonidoDisparo1.isRunning()) Thread.sleep(Retardo);
} catch (InterruptedException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void ReproducirSonidoDisparo2() {
try {
SonidoDisparo2.setFramePosition(0);
SonidoDisparo2.start();
while (SonidoDisparo2.isRunning()) Thread.sleep(Retardo);
} catch (InterruptedException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void ReproducirSonidoAvance() {
try {
SonidoAvance.setFramePosition(0);
SonidoAvance.start();
591

VOLVER A MEN

while (SonidoAvance.isRunning()) Thread.sleep(Retardo);


} catch (InterruptedException ex) {
Logger.getLogger(Grupo.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
protected Campo ElCampo;
private Grupo Conquistadores, Nativos;
private boolean LosNativos, Iniciado;
private int Ganador;
private javax.swing.JFrame Frame;
private Grupo.Unidad USeleccionadaMover, USeleccionadaAtacar;
private Sonidos LosSonidos;
public Conquista(javax.swing.JFrame F) {
LosSonidos = new Sonidos();
ElCampo = null;
Conquistadores = new Grupo(this, 2);
Nativos = new Grupo(this, 1);
Frame = F;
USeleccionadaMover = USeleccionadaAtacar = null;
// Inicialmente el juego automtico est asociado a los nativos y no hay ganador
//
LosNativos = true;
Ganador = 0;
Iniciado = false;
}
private void ConfigurarJuego() {
// Las unidades se colocan en la base
//
int i;

592

VOLVER A MEN

// Se limpia el campo
//
if (ElCampo != null) ElCampo.Limpiar();
// Se ubican las unidades en sus bases respectivas
// Nativos a la izquierda (base 1) y conquistadores a la derecha (base 2)
//
for (i=0; i < Nativos.ObtCantidad(); i++)
ElCampo.UbicarUnidad(Nativos.get(i), 1);
Nativos.ColocarEnBase();
for (i=0; i < Conquistadores.ObtCantidad(); i++)
ElCampo.UbicarUnidad(Conquistadores.get(i), 2);
Conquistadores.ColocarEnBase();
NormalizarGrupos();
Ganador = 0;
}
public void ConfigurarCampo(int f, int c) {
if (f <= 0) f = 1;
if (c <= 0) c = 1;
ElCampo = new Campo(this, f, c, ObtNumeroNativos(), ObtNumeroConquistadores());
Frame.add(ElCampo);
Frame.setSize(ElCampo.getWidth() + 36, ElCampo.getHeight() + 150);
ConfigurarJuego();
}
public void AsignarJuegoNativos() { LosNativos = true; }
public void AsignarJuegoConquistadores() { LosNativos = false; }
public boolean EsJuegoNativos() { return LosNativos; }
public boolean EsJuegoConquistadores() { return !LosNativos; }
public void AgregarConquistador(Grupo.TipoUnidad T) {
Conquistadores.AgregarUnidad(T);
593

VOLVER A MEN

}
public void AgregarConquistadores(int C, Grupo.TipoUnidad T) {
Conquistadores.AgregarUnidades(C, T);
}
public void AgregarNativo(Grupo.TipoUnidad T) {
if (T.compareTo(TipoUnidad.Artilleria) == 0) return;
Nativos.AgregarUnidad(T);
}
public void AgregarNativos(int C, Grupo.TipoUnidad T) {
if (T.compareTo(TipoUnidad.Artilleria) == 0) return;
Nativos.AgregarUnidades(C, T);
}
public int ObtNumeroConquistadores() { return Conquistadores.ObtCantidad(); }
public int ObtNumeroNativos() { return Nativos.ObtCantidad(); }
public void Limpiar() {
Conquistadores.clear();
Nativos.clear();
if (ElCampo != null) ElCampo.Limpiar();
Frame.remove(ElCampo);
USeleccionadaMover = USeleccionadaAtacar = null;
}
public void IniciarJuego() { Iniciado = true; }
public boolean JuegoIniciado() { return Iniciado; }
protected void SeleccionarUnidadMover(Grupo.Unidad U) {
if (Iniciado) USeleccionadaMover = U;
}
protected Grupo.Unidad ObtUnidadSeleccionadaMover() {
return USeleccionadaMover;
}
protected void SeleccionarUnidadAtacar(Grupo.Unidad U) {
if (Iniciado) USeleccionadaAtacar = U;
594

VOLVER A MEN

}
protected Grupo.Unidad ObtUnidadSeleccionadaAtacar() {
return USeleccionadaAtacar;
}
public boolean ValidarMovimientoManual() {
if (USeleccionadaMover == null) return false;
return (USeleccionadaMover.ObtIdBase() == 1 && !LosNativos) ||
(USeleccionadaMover.ObtIdBase() == 2 && LosNativos);
}
public boolean ValidarMovimientoManual(Grupo.Unidad U) {
return (U.ObtIdBase() == 1 && !LosNativos) ||
(U.ObtIdBase() == 2 && LosNativos);
}
public boolean ValidarAtaqueManual(Grupo.Unidad U) {
return (U.ObtIdBase() == 1 && !LosNativos) ||
(U.ObtIdBase() == 2 && LosNativos);
}
protected void SeleccionarEspacioMover(Campo.Espacio E) {
int IdBase, NCuadros;
boolean Valido;
Point Pact, Pnue;
if (E == null || !Iniciado) return;
IdBase = E.ObtIdBase();
// Solo tiene sentido si actualmente hay una unidad para moverse
//
if (USeleccionadaMover == null || USeleccionadaMover.HizoOperacion()) return;
// El movimiento es posible si el espacio seleccionado no tiene una unidad
// enemiga y hay disponibilidad
//
595

VOLVER A MEN

if (IdBase > 0 && IdBase != USeleccionadaMover.ObtIdBase()) return;


// Se busca el espacio actual de la unidad seleccionada
//
Campo.Espacio EUn = ElCampo.BuscarUnidad(USeleccionadaMover);
if (EUn == null || EUn == E) return;
// Se valida si el movimiento es posible; depende del tipo de unidad
//
2;

NCuadros = (USeleccionadaMover.ObtTipo().compareTo(Grupo.TipoUnidad.Infante) == 0) ? 1 :

if (NCuadros > E.ObtDisponibilidad()) return;


if (USeleccionadaMover.EnSuBase()) {
Pnue = ((Campo.Cuadro)E).ObtPosicion();
if (Pnue == null) return;
Valido = (USeleccionadaMover.ObtIdBase() == 1 && Pnue.x <= NCuadros - 1) ||
(USeleccionadaMover.ObtIdBase() == 2 &&
Pnue.x >= ElCampo.ObtNumColumnas() - NCuadros);
}
else {
if (E == ElCampo.ObtBase(1)) {
Pact = ((Campo.Cuadro)EUn).ObtPosicion();
if (Pact == null) return;
Valido = Pact.x <= NCuadros - 1;
}
else
if (E == ElCampo.ObtBase(2)) {
Pact = ((Campo.Cuadro)EUn).ObtPosicion();
if (Pact == null) return;
Valido = Pact.x >= ElCampo.ObtNumColumnas() - NCuadros;
}
else {
596

VOLVER A MEN

// Hay cuatro movimientos posibles: >, ^, v y <


//
Pact = ((Campo.Cuadro)EUn).ObtPosicion();
Pnue = ((Campo.Cuadro)E).ObtPosicion();
Valido = (Pact.y == Pnue.y &&
Math.abs(Pact.x - Pnue.x) <= NCuadros) ||
(Pact.x == Pnue.x &&
Math.abs(Pact.y - Pnue.y) <= NCuadros);
// El caso diagonal es posible siempre y cuando el cuadro inmediato siguiente
// est ocupado por una unidad del bando contrario
//
if (!Valido) {
if ((Pnue.y == Pact.y - 1 && Pnue.x == Pact.x + 1) ||
(Pnue.y == Pact.y + 1 && Pnue.x == Pact.x + 1)) {
Valido = ElCampo.HayUnidad(USeleccionadaMover.ObtIdBase() == 1 ? 2 : 1,
ElCampo.new Point(Pact.x + 1, Pact.y));
}
else
if ((Pnue.y == Pact.y - 1 && Pnue.x == Pact.x - 1) ||
(Pnue.y == Pact.y + 1 && Pnue.x == Pact.x - 1)) {
Valido = ElCampo.HayUnidad(USeleccionadaMover.ObtIdBase() == 1 ? 2 : 1,
ElCampo.new Point(Pact.x - 1, Pact.y));
}
}
}
}
// Si el movimiento es vlido se procede
//
if (Valido) {
// Se saca la unidad del espacio en la cual est actualmente
//
597

VOLVER A MEN

EUn.SacarUnidad(USeleccionadaMover);
// Se agrega la unidad al nuevo espacio
//
E.UbicarUnidad(USeleccionadaMover);
// Se normaliza la unidad movida
//
USeleccionadaMover.NormalizarSeleccion();
USeleccionadaMover = null;
}
}
// Concretar ataque (si es el caso)
//
protected void ConcretarAtaque(Grupo.Unidad U) {
if (USeleccionadaAtacar == null || U == null || !Iniciado) return;
Campo.Espacio E1 = ElCampo.BuscarUnidad(USeleccionadaAtacar),
E2 = ElCampo.BuscarUnidad(U);
Point P1 = E1.ObtPosicion(), P2 = E2.ObtPosicion();
boolean Valido;
if (USeleccionadaAtacar.EnSuBase()) {
Valido = (USeleccionadaAtacar.ObtIdBase() == 1 && P2.x == 0) ||
(USeleccionadaAtacar.ObtIdBase() == 1 &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
P2.x <= 1) ||
(USeleccionadaAtacar.ObtIdBase() == 2 &&
P2.x == ElCampo.ObtNumColumnas() - 1) ||
(USeleccionadaAtacar.ObtIdBase() == 2 &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
P2.x >= ElCampo.ObtNumColumnas() - 2);
}
598

VOLVER A MEN

else
if (U.EnSuBase()) {
Valido = (U.ObtIdBase() == 1 && P1.x == 0) || (U.ObtIdBase() == 1 &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
P1.x <= 1) ||
(U.ObtIdBase() == 2 && P1.x == ElCampo.ObtNumColumnas() - 1) ||
(U.ObtIdBase() == 2 &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
P1.x >= ElCampo.ObtNumColumnas() - 2);
}
else {
Valido = (P1.y == P2.y && Math.abs(P1.x - P2.x) == 1) ||
(P1.y == P2.y &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
Math.abs(P1.x - P2.x) == 2) ||
(P1.x == P2.x &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) != 0 &&
Math.abs(P1.y - P2.y) == 1);
}
if (Valido) {
int A = 1;
if (P1.y == P2.y &&
USeleccionadaAtacar.ObtTipo().compareTo(Grupo.TipoUnidad.Artilleria) == 0 &&
Math.abs(P1.x - P2.x) == 2) A = 2;
if (USeleccionadaAtacar.Atacar(A)) {
U.Herir();
if (!U.EstaVivo()) E2.SacarUnidad(U);
}
USeleccionadaAtacar.NormalizarSeleccion();
}
}
// Normalizar los grupos
//
599

VOLVER A MEN

public void NormalizarConquistadores() { Conquistadores.Normalizar(); }


public void NormalizarNativos() { Nativos.Normalizar(); }
public void NormalizarGrupos() {
NormalizarConquistadores();
NormalizarNativos();
}
public void NormalizarBase(int IdB) {
if (IdB == 1) NormalizarNativos();
else NormalizarConquistadores();
}
private void VerificarFinalizacion() {
if (!Iniciado) return;
// Ganan los nativos
//
if (Conquistadores.ObtCantidadVivos() == 0 ||
Conquistadores.TodosEnBase() ||
ElCampo.HayNativosBaseConquistadores()) Ganador = 1;
else
// Ganan los conquistadores
//
if (Nativos.ObtCantidadVivos() == 0 ||
Nativos.TodosEnBase() ||
ElCampo.HayConquistadoresBaseNativos()) Ganador = 2;
if (Ganador > 0) Iniciado = false;
}
public int ObtGanador() {
VerificarFinalizacion();
return Ganador;
}
// Para el juego automtico
//
600

VOLVER A MEN

public void Jugar() {


if (!Iniciado) return;
if (LosNativos) Nativos.Jugar();
else Conquistadores.Jugar();
}
protected Campo.Espacio AbarcarTerritorio(Grupo.Unidad U) {
// Buscar un espacio con la cual la unidad dada puede abarcar territorio
//
Campo.Espacio E, Eaux, Emin = null;
Point P;
int Max = 0;
boolean Asignar;
Random R = new Random();
if (U == null) return null;
E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
if (U.EnSuBase()) {
for (int i=0; i < ElCampo.ObtNumFilas(); i++) {
Asignar = false;
Eaux = null;
if (U.ObtTipo().compareTo(Grupo.TipoUnidad.Caballeria) == 0) {
Eaux = ElCampo.ObtCuadro(i, U.ObtIdBase() == 1 ? 1 : ElCampo.ObtNumColumnas()-2);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) {
if (Eaux.ObtDisponibilidad() > Max) Asignar = true;
else
if (Eaux.ObtDisponibilidad() == Max) Asignar = R.nextDouble() >= 0.5;
}
}
if (!Asignar) {
Eaux = ElCampo.ObtCuadro(i, U.ObtIdBase() == 1 ? 0 : ElCampo.ObtNumColumnas()-1);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) {
if (Eaux.ObtDisponibilidad() > Max) Asignar = true;
else
601

VOLVER A MEN

if (Eaux.ObtDisponibilidad() == Max) Asignar = R.nextDouble() >= 0.5;


}
}
if (Asignar && Eaux != null) {
Max = Eaux.ObtDisponibilidad();
Emin = Eaux;
}
}
if (Emin != null) return Emin;
}
P = E.ObtPosicion();
if (U.ObtIdBase() == 1) {
if (ElCampo.ObtBase(2).Vacio() &&
(P.x == ElCampo.ObtNumColumnas() - 1) ||
(P.x == ElCampo.ObtNumColumnas() - 2 &&
U.ObtTipo().compareTo(Grupo.TipoUnidad.Caballeria) == 0))
return ElCampo.ObtBase(2);
if (U.ObtTipo().compareTo(Grupo.TipoUnidad.Caballeria) == 0) {
Eaux = ElCampo.ObtCuadro(P.y, P.x+2);
if (Eaux.Vacio()) return Eaux;
}
Eaux = ElCampo.ObtCuadro(P.y, P.x+1);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) return Eaux;
}
else {
if (ElCampo.ObtBase(1).Vacio() &&
(P.x == 0) ||
(P.x == 1 && U.ObtTipo().compareTo(Grupo.TipoUnidad.Caballeria) == 0))
return ElCampo.ObtBase(1);
if (U.ObtTipo().compareTo(Grupo.TipoUnidad.Caballeria) == 0) {
Eaux = ElCampo.ObtCuadro(P.y, P.x-2);
if (Eaux.Vacio()) return Eaux;
}
602

VOLVER A MEN

Eaux = ElCampo.ObtCuadro(P.y, P.x-1);


if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) return Eaux;
}
return null;
}
protected Grupo.Unidad DefenderTerritorio(Grupo.Unidad U) {
// Verificar si los espacios adyacentes a la unidad dada hay un enemigo
// a fin de atacarlo. Se ataca el ms dbil
//
Campo.Espacio E, Eaux;
Campo.Posicion P;
if (U == null) return null;
E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
P = E.ObtPosicion();
if (U.ObtIdBase() == 1) {
Eaux = ElCampo.ObtCuadro(P.y, P.x-1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
else {
Eaux = ElCampo.ObtCuadro(P.y, P.x+1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
Eaux = ElCampo.ObtCuadro(P.y-1, P.x);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
Eaux = ElCampo.ObtCuadro(P.y+1, P.x);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
603

VOLVER A MEN

if (U.ObtIdBase() == 1) {
Eaux = ElCampo.ObtCuadro(P.y, P.x+1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
else {
Eaux = ElCampo.ObtCuadro(P.y, P.x-1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
return null;
}
protected Campo.Espacio Retirada(Grupo.Unidad U) {
// Provocar la retirada de la unidad
//
if (U == null || U.EnSuBase()) return null;
Campo.Espacio E, Eaux;
Point P;
E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
P = E.ObtPosicion();
if (U.ObtIdBase() == 1) {
if (P.x == 0) return ElCampo.ObtBase(1);
Eaux = ElCampo.ObtCuadro(P.y, P.x-1);
if (Eaux != null && (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()))
return Eaux;
}
else {
if (P.x == ElCampo.ObtNumColumnas()-1) return ElCampo.ObtBase(2);
Eaux = ElCampo.ObtCuadro(P.y, P.x+1);
if (Eaux != null && (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()))
604

VOLVER A MEN

return Eaux;
}
return null;
}
protected void ReproducirSonido(int S) {
switch (S) {
case 1:
LosSonidos.ReproducirSonidoMorir();
break;
case 2:
LosSonidos.ReproducirSonidoHerido();
break;
case 3:
LosSonidos.ReproducirSonidoDisparo1();
break;
case 4:
LosSonidos.ReproducirSonidoDisparo2();
break;
case 5:
LosSonidos.ReproducirSonidoAvance();
break;
}
}
}

A continuacin el cdigo que corresponde a la implementacin rrealizada en C#. Se tienen los


archivos fuente Campo.cs, Grupo.cs y Cpnquista.cs. Todas las clases se agrupan en el
paquete o espacio de nombres identificado como Conquista.

605

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Campo.cs
* Mauricio Paletta
*/
using System;
using System.Windows.Forms;
using System.Drawing;
namespace Conquista
{
// Representa un espacio del campo donde van las unidades
//
public abstract class Espacio : Panel
{
protected int Disponibilidad;
protected Conquista refJuego;
protected Panel panelCol1, panelCol2;
public Espacio(Conquista J, string n, int x, int y, int u, int f) : base()
{
refJuego = J;
if (u <= 0) u = 1;
Disponibilidad = u;
this.Width = 90;
this.Height = 135 * f;
this.Name = n;
this.Location = new Point(x, y);
this.BorderStyle = BorderStyle.FixedSingle;
this.Cursor = Cursors.Hand;
606

VOLVER A MEN

panelCol1 = new Panel();


panelCol1.Name = n + -Col1;
panelCol1.Width = 45;
panelCol1.Height = 135 * f;
panelCol1.Location = new Point(0, 0);
panelCol1.Cursor = Cursors.Hand;
panelCol1.Visible = true;
panelCol1.Dock = DockStyle.Left;
panelCol1.MouseClick += EspacioMouseClick;
Controls.Add(panelCol1);

panelCol2 = new Panel();


panelCol2.Name = n + -Col2;
panelCol1.Width = 45;
panelCol1.Height = 135 * f;
panelCol2.Location = new Point(45, 0);
panelCol2.Cursor = Cursors.Hand;
panelCol2.Visible = true;
panelCol2.Dock = DockStyle.Fill;
panelCol2.MouseClick += EspacioMouseClick;
Controls.Add(panelCol2);
// Para seleccionar el espacio
//
this.MouseClick += EspacioMouseClick;
Visible = true;
this.Refresh();
}
void EspacioMouseClick(object sender, MouseEventArgs e)
{
// Seleccionado para intentar ubicar una unidad
607

VOLVER A MEN

// Slo cuando se tratan de unidades manejadas por el jugador humano


//
Panel P = (Panel)sender;
if (refJuego.ValidarMovimientoManual())
refJuego.SeleccionarEspacioMover(P.Parent as Espacio);
}
public void Limpiar() {
if (panelCol1 != null) panelCol1.Controls.Clear();
if (panelCol2 != null) panelCol2.Controls.Clear();
}
public int ObtDisponibilidad() { return Disponibilidad; }
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
public abstract bool UbicarUnidad(Unidad U);
public abstract bool SacarUnidad(Unidad U);
public abstract Point ObtPosicion();
public Panel EstaUnidad(Unidad U)
{
int i;
if (U == null) return null;
for (i=0; i < panelCol1.Controls.Count; i++)
{
if (panelCol1.Controls[i] == U) return panelCol1;
}
for (i=0; i < panelCol2.Controls.Count; i++)
{
if (panelCol2.Controls[i] == U) return panelCol2;
}
608

VOLVER A MEN

return null;
}
public int ObtIdBase()
{
if (panelCol1.Controls.Count != 0)
return ((Unidad)(panelCol1.Controls[0])).ObtIdBase();
if (panelCol2.Controls.Count != 0)
return ((Unidad)(panelCol2.Controls[0])).ObtIdBase();
return 0;
}
public bool Vacio()
{
return panelCol1.Controls.Count == 0 && panelCol2.Controls.Count == 0;
}
public int ObtNumeroUnidades()
{
return panelCol1.Controls.Count + panelCol2.Controls.Count;
}
public int ObtNumeroUnidades(TipoUnidad T)
{
int i, S = 0;
Unidad U;
for (i=0; i < panelCol1.Controls.Count; i++)
{
U = (Unidad)panelCol1.Controls[i];
if (U.ObtTipo() == T) S++;
}
for (i=0; i < panelCol2.Controls.Count; i++)
{
U = (Unidad)panelCol2.Controls[i];
if (U.ObtTipo() == T) S++;
}
609

VOLVER A MEN

return S;
}
public Unidad ObtMasDebil()
{
Unidad U, MasDebil = null;
int i;
for (i=0; i < panelCol1.Controls.Count; i++)
{
U = (Unidad)panelCol1.Controls[i];
if (MasDebil == null || U.ObtNivelVida() < MasDebil.ObtNivelVida())
MasDebil = U;
}
for (i=0; i < panelCol2.Controls.Count; i++)
{
U = (Unidad)panelCol2.Controls[i];
if (MasDebil == null || U.ObtNivelVida() < MasDebil.ObtNivelVida())
MasDebil = U;
}
return MasDebil;
}
}
// Representa una base
//
public class Base : Espacio
{
public Base(Conquista J, String n, int x, int y, int u, int f) :
base(J, n, x, y, u, f) { }
// Ubicar una unidad en la base
//
public override bool UbicarUnidad(Unidad U)
610

VOLVER A MEN

{
int i, u;
if (U == null || Disponibilidad == 0 || U.HizoOperacion()) return false;
i = panelCol1.Controls.Count;
u = panelCol1.Height / 45;
if (i < u)
{
panelCol1.Controls.Add(U);
panelCol1.Refresh();
}
else
{
i = panelCol2.Controls.Count;
u = panelCol2.Height / 45;
if (i < u)
{
panelCol2.Controls.Add(U);
panelCol2.Refresh();
}
else
return false;
}
U.EntrarBase();
U.Refrescar();
Disponibilidad--;
Refresh();
return true;
}
public override bool SacarUnidad(Unidad U)
{
Panel P = EstaUnidad(U);
if (U == null || P == null || U.HizoOperacion()) return false;
611

VOLVER A MEN

U.SalirBase();
P.Controls.Remove(U);
Disponibilidad++;
P.Refresh();
Refresh();
return true;
}
public override Point ObtPosicion()
{
return new Point(this.Location.X, this.Location.Y);
}
public bool HayUnidadEnemiga()
{
Unidad U;
int i, EstaBase = Location.X <= 1 ? 1 : 2;
for (i=0; i < panelCol1.Controls.Count; i++)
{
U = (Unidad)(panelCol1.Controls[i]);
if (U.ObtIdBase() != EstaBase) return true;
}
for (i=0; i < panelCol2.Controls.Count; i++)
{
U = (Unidad)(panelCol2.Controls[i]);
if (U.ObtIdBase() != EstaBase) return true;
}
return false;
}
}
// Representa un cuadro/celda del campo
//
public class Cuadro : Espacio
{
612

VOLVER A MEN

// Puede albergar hasta un mximo de 6 unidades (infantes)


//
public Cuadro(Conquista J, String n, int x, int y) :
base(J, n, x, y, 6, 1) { }
// Ubicar una unidad en el cuadro siempre y cuando no supere
// el mximo permitido: equivalente a 6 infantes
//
public override bool UbicarUnidad(Unidad U) {
int i, Requerido;
if (U == null || U.HizoOperacion()) return false;
Requerido = U.ObtTipo() == TipoUnidad.Infante ? 1 : 2;
if (Disponibilidad - Requerido < 0) return false;
i = panelCol1.Controls.Count;
if (i < 3)
{
panelCol1.Controls.Add(U);
panelCol1.Refresh();
}
else
{
i = panelCol2.Controls.Count;
if (i < 3)
{
panelCol2.Controls.Add(U);
panelCol2.Refresh();
}
else
return false;
}
U.Location = new Point(0, (i % 3) * 45);
U.Mover();
613

VOLVER A MEN

U.Refrescar();
Disponibilidad -= Requerido;
Refresh();
return true;
}
public override bool SacarUnidad(Unidad U)
{
int Ocupado;
Panel P = EstaUnidad(U);
if (U == null || P == null || U.HizoOperacion()) return false;
Ocupado = U.ObtTipo() == TipoUnidad.Infante ? 1 : 2;
P.Controls.Remove(U);
Disponibilidad += Ocupado;
P.Refresh();
Refresh();
return true;
}
public override Point ObtPosicion()
{
return new Point(Location.X / Width, Location.Y / Height);
}
public bool HayUnidad(int IdBase)
{
Unidad U;
if (IdBase < 1 || IdBase > 2) return false;
if (panelCol1.Controls.Count > 0)
{
U = (Unidad)panelCol1.Controls[0];
return U.ObtIdBase() == IdBase;
}
if (panelCol2.Controls.Count > 0)
614

VOLVER A MEN

{
U = (Unidad)panelCol2.Controls[0];
return U.ObtIdBase() == IdBase;
}
return false;
}
}
public class Campo : Panel
{
// Tamao del campo de batalla
//
private int F, C;
private Cuadro [,]Cuadros;
private Base []Bases;
private Panel gridPanel;
private Conquista refJuego;
public Campo(Conquista J, int f, int c, int uB1, int uB2) : base()
{
if (f <= 0) f = 1;
if (c <= 0) c = 1;
F = f; C = c;
refJuego = J;
Width = 90 * (C + 2) + 1;
Height = 135 * F + F;
Location = new Point(20, 40);
Name = PanelCampo;
Visible = true;
BorderStyle = BorderStyle.FixedSingle;
gridPanel = new Panel();
615

VOLVER A MEN

gridPanel.Width = 90 * C;
gridPanel.Height = 135 * F + 10;
gridPanel.Dock = DockStyle.Left;
Controls.Add(gridPanel);
Bases = new Base[2];
Bases[0] = new Base(refJuego, Base0, 0, 0, uB1, F);
Bases[0].Dock = DockStyle.Left;
Bases[1] = new Base(refJuego, Base1, 90 * (C + 1), 0, uB2, F);
Bases[1].Dock = DockStyle.Right;
Controls.Add(Bases[0]);
Controls.Add(Bases[1]);
Cuadros = new Cuadro[F, C];
for (int i=0; i < F; i++) {
for (int j=0; j < C; j++) {
Cuadros[i, j] = new Cuadro(refJuego, Cuadro + Convert.ToString(i) +
Convert.ToString(j), j * 90, i * 135);
gridPanel.Controls.Add(Cuadros[i, j]);
}
}
}
public bool UbicarUnidad(Unidad U, int f, int c)
{
if (U == null || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f, c].UbicarUnidad(U);
}
public bool SacarUnidad(Unidad U, int f, int c)
{
if (U == null || f < 0 || f >= F || c < 0 || c >= C) return false;
return Cuadros[f, c].SacarUnidad(U);
}
public bool UbicarUnidad(Unidad U, int b)
616

VOLVER A MEN

{
if (U == null || b < 1 || b > 2) return false;
return Bases[b-1].UbicarUnidad(U);
}
public bool SacarUnidad(Unidad U, int b)
{
if (U == null || b < 1 || b > 2) return false;
return Bases[b-1].SacarUnidad(U);
}
public Point ObtPosicionUnidad(Unidad U)
{
for (int i=0; i < F; i++)
{
for (int j=0; j < C; j++)
{
if (Cuadros[i, j].EstaUnidad(U) != null)
return new Point(i, j);
}
}
return new Point(-1, -1);
}
public void Limpiar()
{
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
Cuadros[i, j].Limpiar();
Bases[0].Limpiar();
Bases[1].Limpiar();
}
public Espacio BuscarUnidad(Unidad U)
{
int IdBase;

617

VOLVER A MEN

if (U == null) return null;


IdBase = U.ObtIdBase();
if (IdBase < 1 || IdBase > 2) return null;
// Primero se busca en alguna de las bases
//
if (Bases[IdBase - 1].EstaUnidad(U) != null) return Bases[IdBase - 1];
// Si no est en las bases se busca en alguno de los cuadros
//
for (int i=0; i < F; i++)
for (int j=0; j < C; j++)
if (Cuadros[i, j].EstaUnidad(U) != null) return Cuadros[i, j];
return null;
}
public bool HayUnidad(int IdBase, Point P)
{
if (IdBase < 1 || IdBase > 2) return false;
if (P.X < 0 || P.X >= C || P.Y < 0 || P.Y >= F) return false;
return Cuadros[P.Y, P.X].HayUnidad(IdBase);
}
public Espacio ObtBase(int IdBase)
{
return (IdBase >= 1 && IdBase <= 2) ? Bases[IdBase-1] : null;
}
public Espacio ObtCuadro(int f, int c)
{
return (f >= 0 && f < F && c >= 0 && c < C) ? Cuadros[f, c] : null;
}
public int ObtNumFilas() { return F; }
public int ObtNumColumnas() { return C; }
618

VOLVER A MEN

public bool HayConquistadoresBaseNativos()


{
return Bases[0].HayUnidadEnemiga();
}
public bool HayNativosBaseConquistadores()
{
return Bases[1].HayUnidadEnemiga();
}
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Grupo.cs
* Mauricio Paletta
*/
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections.Generic;
namespace Conquista
{
public enum TipoUnidad
{
Infante, Caballeria, Artilleria
}
public class Unidad : Panel {
private TipoUnidad Tipo;
private int Vida, IdBase;
private Random R;
619

VOLVER A MEN

private bool EnBase, HizoOp;


private Conquista refJuego;
private PictureBox Imagen;
private Label labelVida;
public Unidad(Conquista J, TipoUnidad T, int Id) : base()
{
refJuego = J;
Tipo = T;
R = new Random();
EnBase = true;
if (Tipo != TipoUnidad.Infante) Vida = 4;
else
if (Tipo != TipoUnidad.Caballeria) Vida = 5;
else
Vida = 6;
Normalizar();
IdBase = Id;
Imagen = new PictureBox();
Imagen.Size = new Size(45, 45);
Imagen.Visible = true;
Imagen.Dock = DockStyle.Fill;
Imagen.Cursor = Cursors.Hand;
Imagen.MouseClick += UnidadMouseClick;
labelVida = new Label();
labelVida.Size = new Size(10, 12);
labelVida.Location = new Point(2, 2);
labelVida.Text = Convert.ToString(Vida);
labelVida.BackColor = Color.White;
labelVida.TextAlign = ContentAlignment.MiddleCenter;
labelVida.Visible = true;
620

VOLVER A MEN

this.Size = new Size(Imagen.Size.Width, Imagen.Size.Height);


this.BorderStyle = BorderStyle.FixedSingle;
this.BackColor = Color.Black;
this.Dock = DockStyle.Top;
this.Controls.Add(Imagen);
this.Controls.Add(labelVida);
labelVida.BringToFront();
this.Visible = false;
this.Padding = new Padding(2);
}
// Para seleccionar la unidad
// Solo es vlido para las unidades que se manejan manualmente
//
void UnidadMouseClick(object sender, MouseEventArgs e)
{
// Si ya hizo una operacin, nada que hacer
//
if (HizoOp || !refJuego.JuegoIniciado()) return;
Unidad U, S = (Unidad) ((Control)sender).Parent;
switch (e.Button)
{
// Seleccionado para moverse
//
case MouseButtons.Left:
// Si hay alguien seleccionado para atacar y se trata de
// bandos diferentes, entonces se concreta el ataque;
// si son del mismo bando o ya hay
// alguien seleccionado para mover,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
621

VOLVER A MEN

U = refJuego.ObtUnidadSeleccionadaAtacar();
if (U != null)
{
if (U.ObtIdBase() != S.ObtIdBase())
{
refJuego.ConcretarAtaque(S);
return;
}
U.Normalizar();
refJuego.SeleccionarUnidadAtacar(null);
}
U = refJuego.ObtUnidadSeleccionadaMover();
if (U != null)
{
if (U.ObtIdBase() != S.ObtIdBase()) return;
U.Normalizar();
refJuego.SeleccionarUnidadMover(null);
}
if (U != S)
{
if (!refJuego.ValidarMovimientoManual(S)) return;
S.BackColor = Color.Blue;
refJuego.SeleccionarUnidadMover(S);
}
break;
// Seleccionado para atacar
//
case MouseButtons.Right:
// Si hay alguien seleccionado para mover o ya hay
// alguien seleccionado para atacar,
// se cancela esta seleccin; si se trata de la misma
// unidad se cancela el inicio de la operacin
//
622

VOLVER A MEN

U = refJuego.ObtUnidadSeleccionadaMover();
if (U != null)
{
U.Normalizar();
refJuego.SeleccionarUnidadMover(null);
}
U = refJuego.ObtUnidadSeleccionadaAtacar();
if (U != null)
{
U.Normalizar();
refJuego.SeleccionarUnidadAtacar(null);
}
if (U != S)
{
S.BackColor = Color.Red;
refJuego.SeleccionarUnidadAtacar(S);
}
break;
}
}
public TipoUnidad ObtTipo() { return Tipo; }
public int ObtNivelVida() { return Vida; }
public bool ObtPunteria(int A)
{
double p;
switch (Tipo) {
case TipoUnidad.Infante:
p = 0.6;
break;
case TipoUnidad.Caballeria:
p = 0.75;
623

VOLVER A MEN

break;
default:
p = A == 2 ? 0.5 : 0.95;
break;
}
return R.NextDouble() <= p;
}
// En el caso de la caballera se asume primer nivel de
// visibilidad o alcance
//
public int ObtAlcance()
{
return Tipo == TipoUnidad.Caballeria ? 2 : 1;
}
public bool EstaVivo() { return Vida > 0; }
public void Herir()
{
if (EstaVivo()) Vida--;
labelVida.Text = Convert.ToString(Vida);
if (!EstaVivo())
{
refJuego.ReproducirSonido(1);
Visible = false;
}
else
{
refJuego.ReproducirSonido(2);
}
labelVida.Refresh();
Refresh();
}
public bool Atacar(int A)
624

VOLVER A MEN

{
if (HizoOp) return false;
HizoOp = true;
if (Tipo == TipoUnidad.Artilleria)
refJuego.ReproducirSonido(3);
else
refJuego.ReproducirSonido(4);
return ObtPunteria(A);
}
public void Mover()
{
refJuego.ReproducirSonido(5);
HizoOp = true;
}
public bool HizoOperacion() { return HizoOp; }
public bool EnSuBase() { return EnBase; }
public void SalirBase() { EnBase = false; }
public void EntrarBase() { EnBase = true; Mover(); }
public void Normalizar()
{
HizoOp = false;
NormalizarSeleccion();
}
public void NormalizarSeleccion()
{
BackColor = Color.Black;
}
public int ObtIdBase() { return IdBase; }
public void RefrescarImagen()
{
string strImagen;
625

VOLVER A MEN

if (Tipo == TipoUnidad.Infante) strImagen = Infante;


else
if (Tipo == TipoUnidad.Caballeria) strImagen = Caballeria;
else
strImagen = Artilleria;
if (IdBase == 2) strImagen = strImagen + C.jpg;
else strImagen = strImagen + N.jpg;
Imagen.Load(strImagen);
Refresh();
}
public void Refrescar()
{
RefrescarImagen();
Visible = true;
}
}
public class Grupo : List<Unidad>
{
private int Id;
private Conquista refJuego;
public Grupo(Conquista J, int id) : base()
{
refJuego = J;
Id = id;
}
public void AgregarUnidad(TipoUnidad T)
{
Add(new Unidad(refJuego, T, Id));
}
public void AgregarUnidades(int C, TipoUnidad T)
626

VOLVER A MEN

{
for (int i=0; i < C; i++) AgregarUnidad(T);
}
public int ObtCantidad() { return Count; }
public int ObtCantidadVivos()
{
int S = 0;
for (int i=0; i < Count; i++)
{
Unidad U = this[i];
if (U.EstaVivo() && U.Visible) S++;
}
return S;
}
public void ColocarEnBase()
{
for (int i=0; i < Count; i++)
this[i].EntrarBase();
}
public int ObtId() { return Id; }
public void Refrescar()
{
for (int i=0; i < Count; i++)
this[i].RefrescarImagen();
}
public void Normalizar()
{
for (int i=0; i < Count; i++)
this[i].Normalizar();
}
public bool TodosEnBase()
{
627

VOLVER A MEN

for (int i=0; i < Count; i++)


if (!(this[i].EnSuBase())) return false;
return true;
}
public void Jugar()
{
// El juego automtico se fundamente en las siguientes acciones:
// 1 - ganar ocupando la base del enemigo
// 2 - evitar que el enemigo gane abarcando territorio
// 3 - retirada si estn muy heridos
// 4 - abarcar territorio
//
// Para todas las unidades de este grupo
//
for (int i=0; i < Count; i++)
{
Unidad U = this[i], Ue;
Espacio E;
if (U.Visible && U.EstaVivo())
{
E = refJuego.AbarcarTerritorio(U);
if (E != null && E is Base)
{
refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
continue;
}
Ue = refJuego.DefenderTerritorio(U);
if (Ue != null)
{
refJuego.SeleccionarUnidadAtacar(U);
628

VOLVER A MEN

refJuego.ConcretarAtaque(Ue);
continue;
}
if (U.ObtNivelVida() == 1)
{
E = refJuego.Retirada(U);
if (E != null)
{
refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
continue;
}
}
E = refJuego.AbarcarTerritorio(U);
if (E != null)
{
// Se tiene previsin de dejar un infante en la base
//
if (!(U.EnSuBase() && U.ObtTipo() == TipoUnidad.Infante &&
refJuego.ObtBase(U.ObtIdBase()).ObtNumeroUnidades(TipoUnidad.Infante) ==

1))
{

refJuego.SeleccionarUnidadMover(U);
refJuego.SeleccionarEspacioMover(E);
}
}
}
}
Normalizar();
}
}
}

629

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Conquista.cs
* Mauricio Paletta
*/
using System;
using System.Windows.Forms;
using System.Drawing;
namespace Conquista
{
public class Sonidos
{
private System.Media.SoundPlayer SonidoMorir, SonidoHerido,
SonidoDisparo1, SonidoDisparo2, SonidoAvance;
public Sonidos()
{
SonidoMorir = new System.Media.SoundPlayer();
SonidoHerido = new System.Media.SoundPlayer();
SonidoDisparo1 = new System.Media.SoundPlayer();
SonidoDisparo2 = new System.Media.SoundPlayer();
SonidoAvance = new System.Media.SoundPlayer();
SonidoMorir.SoundLocation = @Morir.wav;
SonidoHerido.SoundLocation = @Herido.wav;
SonidoDisparo1.SoundLocation = @Can.wav;
SonidoDisparo2.SoundLocation = @Disparo.wav;
SonidoAvance.SoundLocation = @Avance.wav;
}
public void ReproducirSonidoMorir()
{
630

VOLVER A MEN

SonidoMorir.Play();
}
public void ReproducirSonidoHerido()
{
SonidoHerido.Play();
}
public void ReproducirSonidoDisparo1()
{
SonidoDisparo1.Play();
}
public void ReproducirSonidoDisparo2()
{
SonidoDisparo2.Play();
}
public void ReproducirSonidoAvance()
{
SonidoAvance.Play();
}
}
public class Conquista {
protected Campo ElCampo;
private Grupo Conquistadores, Nativos;
private bool LosNativos, Iniciado;
private int Ganador;
private Form Frame;
private Unidad USeleccionadaMover, USeleccionadaAtacar;
private Sonidos LosSonidos;
public Conquista(Form F)
{
LosSonidos = new Sonidos();
ElCampo = null;
631

VOLVER A MEN

Conquistadores = new Grupo(this, 2);


Nativos = new Grupo(this, 1);
Frame = F;
USeleccionadaMover = USeleccionadaAtacar = null;
// Inicialmente el juego automtico est asociado a los nativos y no hay ganador
//
LosNativos = true;
Ganador = 0;
Iniciado = false;
}
private void ConfigurarJuego()
{
// Las unidades se colocan en la base
//
int i;
// Se limpia el campo
//
if (ElCampo != null) ElCampo.Limpiar();
// Se ubican las unidades en sus bases respectivas
// Nativos a la izquierda (base 1) y conquistadores a la derecha (base 2)
//
for (i=0; i < Nativos.ObtCantidad(); i++)
ElCampo.UbicarUnidad(Nativos[i], 1);
Nativos.ColocarEnBase();
for (i=0; i < Conquistadores.ObtCantidad(); i++)
ElCampo.UbicarUnidad(Conquistadores[i], 2);
Conquistadores.ColocarEnBase();
NormalizarGrupos();
Ganador = 0;
632

VOLVER A MEN

Frame.Width = ElCampo.Width + 50;


Frame.Height = ElCampo.Height + 133;
}
public void ConfigurarCampo(int f, int c)
{
if (f <= 0) f = 1;
if (c <= 0) c = 1;
ElCampo = new Campo(this, f, c, ObtNumeroNativos(), ObtNumeroConquistadores());
Frame.Controls.Add(ElCampo);
Frame.Width = ElCampo.Width + 36;
Frame.Height = ElCampo.Height + 150;
ConfigurarJuego();
}
public void AsignarJuegoNativos() { LosNativos = true; }
public void AsignarJuegoConquistadores() { LosNativos = false; }
public bool EsJuegoNativos() { return LosNativos; }
public bool EsJuegoConquistadores() { return !LosNativos; }
public void AgregarConquistador(TipoUnidad T)
{
Conquistadores.AgregarUnidad(T);
}
public void AgregarConquistadores(int C, TipoUnidad T)
{
Conquistadores.AgregarUnidades(C, T);
}
public void AgregarNativo(TipoUnidad T)
{
if (T == TipoUnidad.Artilleria) return;
Nativos.AgregarUnidad(T);
}
633

VOLVER A MEN

public void AgregarNativos(int C, TipoUnidad T)


{
if (T == TipoUnidad.Artilleria) return;
Nativos.AgregarUnidades(C, T);
}

public int ObtNumeroConquistadores() { return Conquistadores.ObtCantidad(); }


public int ObtNumeroNativos() { return Nativos.ObtCantidad(); }
public Base ObtBase(int IdBase) { return (Base)(ElCampo.ObtBase(IdBase)); }
public void Limpiar()
{
Conquistadores.Clear();
Nativos.Clear();
if (ElCampo != null) ElCampo.Limpiar();
Frame.Controls.Remove(ElCampo);
USeleccionadaMover = USeleccionadaAtacar = null;
}
public void IniciarJuego() { Iniciado = true; }
public bool JuegoIniciado() { return Iniciado; }
public void SeleccionarUnidadMover(Unidad U)
{
if (Iniciado) USeleccionadaMover = U;
}
public Unidad ObtUnidadSeleccionadaMover()
{
return USeleccionadaMover;
}
public void SeleccionarUnidadAtacar(Unidad U)
{
if (Iniciado) USeleccionadaAtacar = U;
}
634

VOLVER A MEN

public Unidad ObtUnidadSeleccionadaAtacar()


{
return USeleccionadaAtacar;
}
public bool ValidarMovimientoManual()
{
if (USeleccionadaMover == null) return false;
return (USeleccionadaMover.ObtIdBase() == 1 && !LosNativos) ||
(USeleccionadaMover.ObtIdBase() == 2 && LosNativos);
}
public bool ValidarMovimientoManual(Unidad U)
{
return (U.ObtIdBase() == 1 && !LosNativos) || (U.ObtIdBase() == 2 && LosNativos);
}
public bool ValidarAtaqueManual(Unidad U)
{
return (U.ObtIdBase() == 1 && !LosNativos) || (U.ObtIdBase() == 2 && LosNativos);
}
public void SeleccionarEspacioMover(Espacio E)
{
int IdBase, NCuadros;
bool Valido;
Point Pact, Pnue;
if (E == null || !Iniciado) return;
IdBase = E.ObtIdBase();
// Solo tiene sentido si actualmente hay una unidad para moverse
//
if (USeleccionadaMover == null || USeleccionadaMover.HizoOperacion()) return;

635

VOLVER A MEN

// El movimiento es posible si el espacio seleccionado no tiene una unidad


// enemiga y hay disponibilidad
//
if (IdBase > 0 && IdBase != USeleccionadaMover.ObtIdBase()) return;
// Se busca el espacio actual de la unidad seleccionada
//
Espacio EUn = ElCampo.BuscarUnidad(USeleccionadaMover);
if (EUn == null || EUn == E) return;
// Se valida si el movimiento es posible; depende del tipo de unidad
//
NCuadros = (USeleccionadaMover.ObtTipo() == TipoUnidad.Infante) ? 1 : 2;
if (NCuadros > E.ObtDisponibilidad()) return;
if (USeleccionadaMover.EnSuBase())
{
Pnue = ((Cuadro)E).ObtPosicion();
if (Pnue.X == -1) return;
Valido = (USeleccionadaMover.ObtIdBase() == 1 && Pnue.X <= NCuadros - 1) ||
(USeleccionadaMover.ObtIdBase() == 2 &&
Pnue.X >= ElCampo.ObtNumColumnas() - NCuadros);
}
else
{
if (E == ElCampo.ObtBase(1))
{
Pact = ((Cuadro)EUn).ObtPosicion();
if (Pact.X == -1) return;
Valido = Pact.X <= NCuadros - 1;
}
else
if (E == ElCampo.ObtBase(2))
636

VOLVER A MEN

{
Pact = ((Cuadro)EUn).ObtPosicion();
if (Pact.X == -1) return;
Valido = Pact.X >= ElCampo.ObtNumColumnas() - NCuadros;
}
else
{
// Hay cuatro movimientos posibles: >, ^, v y <
//
Pact = ((Cuadro)EUn).ObtPosicion();
Pnue = ((Cuadro)E).ObtPosicion();
Valido = (Pact.Y == Pnue.Y && Math.Abs(Pact.X - Pnue.X) <= NCuadros) ||
(Pact.X == Pnue.X && Math.Abs(Pact.Y - Pnue.Y) <= NCuadros);
// El caso diagonal es posible siempre y cuando el cuadro inmediato siguiente
// est ocupado por una unidad del bando contrario
//
if (!Valido)
{
if ((Pnue.Y == Pact.Y - 1 && Pnue.X == Pact.X + 1) ||
(Pnue.Y == Pact.Y + 1 && Pnue.X == Pact.X + 1))
{
Valido = ElCampo.HayUnidad(USeleccionadaMover.ObtIdBase() == 1 ? 2 : 1,
new Point(Pact.X + 1, Pact.Y));
}
else
if ((Pnue.Y == Pact.Y - 1 && Pnue.X == Pact.X - 1) ||
(Pnue.Y == Pact.Y + 1 && Pnue.X == Pact.X - 1))
{
Valido = ElCampo.HayUnidad(USeleccionadaMover.ObtIdBase() == 1 ? 2 : 1,
new Point(Pact.X - 1, Pact.Y));
}
637

VOLVER A MEN

}
}
}
// Si el movimiento es vlido se procede
//
if (Valido)
{
// Se saca la unidad del espacio en la cual est actualmente
//
EUn.SacarUnidad(USeleccionadaMover);
// Se agrega la unidad al nuevo espacio
//
E.UbicarUnidad(USeleccionadaMover);
// Se normaliza la unidad movida
//
USeleccionadaMover.NormalizarSeleccion();
USeleccionadaMover = null;
}
}
// Concretar ataque (si es el caso)
//
public void ConcretarAtaque(Unidad U)
{
if (USeleccionadaAtacar == null || U == null || !Iniciado) return;
// Se valida que el ataque es posible
//
Espacio E1 = ElCampo.BuscarUnidad(USeleccionadaAtacar),
E2 = ElCampo.BuscarUnidad(U);
Point P1 = E1.ObtPosicion(), P2 = E2.ObtPosicion();
638

VOLVER A MEN

bool Valido;
if (USeleccionadaAtacar.EnSuBase())
{
Valido = (USeleccionadaAtacar.ObtIdBase() == 1 && P2.X == 0) ||
(USeleccionadaAtacar.ObtIdBase() == 1 &&
USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria && P2.X <= 1) ||
(USeleccionadaAtacar.ObtIdBase() == 2 &&
P2.X == ElCampo.ObtNumColumnas() - 1) ||
(USeleccionadaAtacar.ObtIdBase() == 2 &&
USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria &&
P2.X >= ElCampo.ObtNumColumnas() - 2);
}
else
if (U.EnSuBase())
{
Valido = (U.ObtIdBase() == 1 && P1.X == 0) || (U.ObtIdBase() == 1 &&
USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria && P1.X <= 1) ||
(U.ObtIdBase() == 2 && P1.X == ElCampo.ObtNumColumnas() - 1) ||
(U.ObtIdBase() == 2 && USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria

&&

P1.X >= ElCampo.ObtNumColumnas() - 2);


}
else
{
Valido = (P1.Y == P2.Y && Math.Abs(P1.X - P2.X) == 1) || (P1.Y == P2.Y &&
USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria &&
Math.Abs(P1.X - P2.X) == 2) || (P1.X == P2.X &&
USeleccionadaAtacar.ObtTipo() != TipoUnidad.Artilleria &&
Math.Abs(P1.Y - P2.Y) == 1);
}
if (Valido)
{
639

VOLVER A MEN

int A = 1;
if (P1.Y == P2.Y && USeleccionadaAtacar.ObtTipo() == TipoUnidad.Artilleria &&
Math.Abs(P1.X - P2.X) == 2) A = 2;
if (USeleccionadaAtacar.Atacar(A))
{
U.Herir();
if (!U.EstaVivo()) E2.SacarUnidad(U);
}
USeleccionadaAtacar.NormalizarSeleccion();
}
}
// Normalizar los grupos
//
public void NormalizarConquistadores() { Conquistadores.Normalizar(); }
public void NormalizarNativos() { Nativos.Normalizar(); }
public void NormalizarGrupos()
{
NormalizarConquistadores();
NormalizarNativos();
}
public void NormalizarBase(int IdB)
{
if (IdB == 1) NormalizarNativos();
else NormalizarConquistadores();
}
private void VerificarFinalizacion()
{
if (!Iniciado) return;
// Ganan los nativos
//
640

VOLVER A MEN

if (Conquistadores.ObtCantidadVivos() == 0 || Conquistadores.TodosEnBase() ||
ElCampo.HayNativosBaseConquistadores()) Ganador = 1;
else
// Ganan los conquistadores
//
if (Nativos.ObtCantidadVivos() == 0 || Nativos.TodosEnBase() ||
ElCampo.HayConquistadoresBaseNativos()) Ganador = 2;
if (Ganador > 0) Iniciado = false;
}
public int ObtGanador()
{
VerificarFinalizacion();
return Ganador;
}
// Para el juego automtico
//
public void Jugar()
{
if (!Iniciado) return;
if (LosNativos) Nativos.Jugar();
else Conquistadores.Jugar();
}
public Espacio AbarcarTerritorio(Unidad U)
{
// Buscar un espacio con la cual la unidad dada puede abarcar territorio
//
Espacio E, Eaux, Emin = null;
Point P;
int Max = 0;
bool Asignar;
Random R = new Random();

641

VOLVER A MEN

if (U == null) return null;


E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
if (U.EnSuBase())
{
for (int i=0; i < ElCampo.ObtNumFilas(); i++)
{
Asignar = false;
Eaux = null;
if (U.ObtTipo() == TipoUnidad.Caballeria)
{
Eaux = ElCampo.ObtCuadro(i, U.ObtIdBase() == 1 ? 1 :
ElCampo.ObtNumColumnas()-2);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase())
{
if (Eaux.ObtDisponibilidad() > Max) Asignar = true;
else
if (Eaux.ObtDisponibilidad() == Max) Asignar = R.NextDouble() >= 0.5;
}
}
if (!Asignar)
{
Eaux = ElCampo.ObtCuadro(i, U.ObtIdBase() == 1 ? 0 :
ElCampo.ObtNumColumnas()-1);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase())
{
if (Eaux.ObtDisponibilidad() > Max) Asignar = true;
else
if (Eaux.ObtDisponibilidad() == Max) Asignar = R.NextDouble() >= 0.5;
}
}
if (Asignar && Eaux != null)
{
642

VOLVER A MEN

Max = Eaux.ObtDisponibilidad();
Emin = Eaux;
}
}
if (Emin != null) return Emin;
}
P = E.ObtPosicion();
if (U.ObtIdBase() == 1)
{
if (ElCampo.ObtBase(2).Vacio() && (P.X == ElCampo.ObtNumColumnas() - 1) ||
(P.X == ElCampo.ObtNumColumnas() - 2 && U.ObtTipo() == TipoUnidad.Caballeria))
return ElCampo.ObtBase(2);
if (U.ObtTipo() == TipoUnidad.Caballeria)
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X+2);
if (Eaux.Vacio()) return Eaux;
}
Eaux = ElCampo.ObtCuadro(P.Y, P.X+1);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) return Eaux;
}
else
{
if (ElCampo.ObtBase(1).Vacio() &&
(P.X == 0) || (P.X == 1 && U.ObtTipo() == TipoUnidad.Caballeria))
return ElCampo.ObtBase(1);
if (U.ObtTipo() == TipoUnidad.Caballeria)
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X-2);
if (Eaux.Vacio()) return Eaux;
}
Eaux = ElCampo.ObtCuadro(P.Y, P.X-1);
if (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()) return Eaux;
}
643

VOLVER A MEN

return null;
}
public Unidad DefenderTerritorio(Unidad U)
{
// Verificar si los espacios adyacentes a la unidad dada hay un enemigo
// a fin de atacarlo. Se ataca el ms dbil
//
Espacio E, Eaux;
Point P;
if (U == null) return null;
E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
P = E.ObtPosicion();
if (U.ObtIdBase() == 1)
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X-1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
else
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X+1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
Eaux = ElCampo.ObtCuadro(P.Y-1, P.X);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
Eaux = ElCampo.ObtCuadro(P.Y+1, P.X);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
644

VOLVER A MEN

if (U.ObtIdBase() == 1)
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X+1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
else
{
Eaux = ElCampo.ObtCuadro(P.Y, P.X-1);
if (Eaux != null && !Eaux.Vacio() && Eaux.ObtIdBase() != U.ObtIdBase())
return Eaux.ObtMasDebil();
}
return null;
}
public Espacio Retirada(Unidad U)
{
// Provocar la retirada de la unidad
//
if (U == null || U.EnSuBase()) return null;
Espacio E, Eaux;
Point P;
E = ElCampo.BuscarUnidad(U);
if (E == null) return null;
P = E.ObtPosicion();
if (U.ObtIdBase() == 1)
{
if (P.X == 0) return ElCampo.ObtBase(1);
Eaux = ElCampo.ObtCuadro(P.Y, P.X-1);
if (Eaux != null && (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()))
return Eaux;
}
645

VOLVER A MEN

else
{
if (P.X == ElCampo.ObtNumColumnas()-1) return ElCampo.ObtBase(2);
Eaux = ElCampo.ObtCuadro(P.Y, P.X+1);
if (Eaux != null && (Eaux.Vacio() || Eaux.ObtIdBase() == U.ObtIdBase()))
return Eaux;
}
return null;
}
public void ReproducirSonido(int S)
{
switch (S) {
case 1:
LosSonidos.ReproducirSonidoMorir();
break;
case 2:
LosSonidos.ReproducirSonidoHerido();
break;
case 3:
LosSonidos.ReproducirSonidoDisparo1();
break;
case 4:
LosSonidos.ReproducirSonidoDisparo2();
break;
case 5:
LosSonidos.ReproducirSonidoAvance();
break;
}
}
}
}

646

VOLVER A MEN

6.10 Agentes recolectores de desperdicio


6.10.1 Enunciado
La motivacin principal de este problema tiene que ver con la teora de agentes. El sentido
original del trmino agente es de algo (o alguien) actuando en nombre (o a favor) de alguien
en la ejecucin de una determinada accin. Segn (Russell, 1995) Un agente es cualquier
entidad que pueda percibir informacin de su ambiente a travs de sensores y tomar acciones
sobre el mismo a travs de efectores.
El trmino agente denota un sistema de software (y a veces de hardware) que posee:
Autonoma: un agente opera sin la intervencin directa de seres humanos u otros agentes
y tiene alguna clase de control sobre sus actos y estado interno.
Sociabilidad: un agente interacta con otros agentes (y eventualmente con humanos)
mediante algn tipo de lenguaje.
Reactividad: un agente percibe su ambiente y responde a los cambios que ocurren.
Pro-actividad: los agentes no slo reaccionan a estmulos ambientales, sino que son
capaces de tomar la iniciativa mostrando una conducta orientada por metas.
Otros atributos usualmente exigidos por este enfoque son:
Movilidad: habilidad para desplazarse en su ambiente.
Veracidad: un agente no comunicar falsa informacin a sabiendas.
Benevolencia: un agente no tiene metas en conflicto, por lo que siempre tratar de hacer lo
que se supone debe hacer.
Racionalidad: un agente actuar buscando cumplir sus metas y jams en forma
contraproducente al logro de stas (al menos hasta donde se lo permitan sus creencias).
Una forma de hacer implementaciones de software fue propuesta por (Nilsson, 1994) bajo el
nombre de Secuencias Teleoreactivas (ST). Se trata de una secuencia de acciones que puede
verse como un circuito de eventos discretos (ver Figura 6.62). Se siguen las siguientes reglas
y criterios:

1. La ejecucin de las acciones est jerarquizada:


Si la condicin K1 es verdadera, entonces se ejecuta la accin a1.
647

VOLVER A MEN

La condicin que activa una accin ai (i = 2, 3, ..., m) no es simplemente Ki, sino que
viene dada por la conjuncin de Ki con la negacin de la condicin Ki-1. Basta con que
exista un j menor que i tal que Kj sea verdadera para que se inhiba la ejecucin de ai.
2. Se dice que ai es una accin durativa si ai se ejecuta en forma continua hasta que la
condicin que la activa sea falsa.
3. Las acciones ai pueden ser acciones primitivas o bien otras ST.
4. En el caso de programas organizados en forma jerrquica, todas las condiciones a todos
los niveles de la jerarqua deben ser evaluadas continuamente.
5. Al plantear una ST, hay que pensar de la meta a lograr hacia atrs:
Lograr la meta corresponde a K1 verdadera y a1 es la accin nula.
La accin a2 debe conducir eventualmente a que K2 se haga verdadera
sucesivamente.

y as

En general, una accin no nula aj debe conducir a que alguna condicin Ki (i < j) se haga
verdadera.
6. Se dice que las condiciones Ki (i > 1) son regresiones de otras condiciones de mayor
jerarqua, a travs de las acciones ai que activan esas otras condiciones.
7. Una ST satisface la propiedad de regresin si cada condicin Ki (i > 1) es la regresin de
alguna otra condicin Kj (j < i) de mayor jerarqua en la secuencia a travs de la accin ai.
8. Se dice que una ST es universal si satisface la propiedad de regresin y es completa.
9. Una ST universal siempre alcanzar su meta, si hay perfecta percepcin del ambiente.
10. En ocasiones una accin no tiene el efecto anticipado por el diseador o bien agentes
exgenos cambian el ambiente en forma inesperada; por ello se requiere la retroalimentacin
(feedback) continua.
11. Las ST universales parecen robustas frente a desviaciones ocasionales de la ejecucin
normal.
12. Los agentes se disean para hacer algo en ambientes dinmicos, por lo que deben poseer
capacidad de reaccin (reactividad).
13. Un agente puede tener que atender varias metas en forma simultnea (como por ejemplo
mantenerse a s mismo y cumplir su misin). Estas metas pueden tener diversos niveles de
importancia, a su vez el orden de importancia puede cambiar en el tiempo.
14. Cada meta puede ser atendida por una ST, la cual es activada siguiendo una biblioteca de
648

VOLVER A MEN

planes suministrada por el usuario y algunas nociones inspiradas en el concepto de costo


de oportunidad.
15. Por ahora se tiene que la informacin de entrada a un agente es tal que permite el cmputo
de las condiciones requeridas por sus ST y por los planes establecidos.
16. En el fondo del planteamiento de Nilsson existe un ciclo discreto: percepcin-accin.
Una secuencia teleoreactiva es un programa para controlar agentes que lo gua al logro de sus
metas en un ambiente dinmico. En su forma ms simple una ST est compuesta de reglas
de produccin ordenadas jerrquicamente que se recorren en forma descendente, buscando
alguna regresin verdadera. La ltima condicin de una ST debe ser siempre verdadera y la
accin asociada es la accin por omisin.

Figura 6.62. Circuito que representa una secuencia teleoreactiva.

El problema a resolver en esta seccin tiene que ver con la simulacin de un conjunto de
agentes recolectores de desperdicio. Los agentes recolectores son entidades autnomas cuya
meta es limpiar el ambiente de todos los desperdicios que en l haya, cooperando con los otros
recolectores. Para ello se deben respetar los siguientes criterios:
1. Hay dos tipos de desperdicio en el ambiente: livianos y pesados. Inicialmente y durante la dinmica
del modelo los desperdicios aparecen segn una probabilidad de 0.25 para los livianos y 0.10
para los pesados. Cada desperdicio tiene un peso asociado que se mide en funcin de unidades
discretas de peso. Los desperdicios livianos pueden estar entre 1 y 4 unidades de peso, mientras
que los pesados pueden estar entre 6 y 10 unidades de peso. La posicin de los desperdicios en
el ambiente as como su peso es seleccionado al azar.
2. Los bordes o lmites del ambiente pueden ser cerrados (pared) o de circulacin cclica (salida por
un lado y entrada por el lado contrario).
3. Un agente recolector debe tener sentido de orientacin (ojos) y slo cuando el desperdicio est en
su visin es que puede ser recogido. Sus nicos movimientos posibles son: ir hacia adelante, rotar
a la izquierda rotar a la derecha. Slo se permite una accin o movimiento por unidad de tiempo.
649

VOLVER A MEN

4. Hay dos tipos de recolectores: carga liviana y carga pesada. Los recolectores de carga liviana slo
pueden recoger desperdicios livianos y hasta un mximo de 10 unidades de peso, mientras que los
recolectores de carga pesada pueden recoger tanto desperdicios livianos como pesados y hasta un
mximo de 30 unidades de peso. La cantidad de recolectores livianos es un parmetro cuyo valor
debe ser dado previo a la ejecucin del modelo y por cada 3 recolectores de carga liviana hay un
recolector de carga pesada. La posicin inicial de estos recolectores se selecciona al azar.
5. Los recolectores funcionan con energa propia que se va consumiendo a medida que avanza
la dinmica. Los recolectores de carga liviana tienen una fuente de energa de 30 unidades y
consumen su energa segn los siguientes criterios:
Una unidad por cada movimiento.
Una unidad para recolectar desperdicio.
Dos unidades para vaciar su carga.
Los recolectores de carga pesada tienen una fuente de energa de 50 unidades y su consumo se
rige segn los siguientes criterios:
Una unidad por cada movimiento.
Una unidad para recolectar desperdicio liviano.
Dos unidades para recolectar desperdicio pesado.
Cuatro unidades para vaciar su carga.
Inicialmente la cantidad de energa de cada recolector se asigna al azar.
6. En el ambiente debe existir un centro de recarga de energa donde los recolectores se deben dirigir
para recargar su fuente de energa cuando sea necesario. La posicin de este centro puede ser
fija y conocida por los recolectores y debe contar con una entrada y una salida independiente. Slo
es posible albergar un recolector a la vez por lo que se requiere un manejo de cola para satisfacer
la necesidad de varios recolectores. Los recolectores de carga liviana requieren dos unidades de
tiempo para recargar su energa mientras que los de carga pesada lo hacen en cuatro unidades de
tiempo. En caso de que un recolector se quede sin energa antes de llegar al centro de recarga,
se convierte en un desperdicio de carga pesada de 10 unidades de peso y debe ser tratado igual.
7. En el ambiente debe existir un depsito de desperdicios donde los recolectores se deben dirigir para
vaciar su carga cuando sea necesario. La posicin de este depsito puede ser fija y conocida por
los recolectores y debe contar con una entrada y una salida independiente. Slo es posible albergar
un recolector a la vez por lo que se requiere un manejo de cola para satisfacer la necesidad de
varios recolectores. Los recolectores de carga liviana requieren una unidad de tiempo para vaciar
su carga mientras que los de carga pesada lo hacen en dos unidades de tiempo. Para cada instante
de tiempo se requiere conocer la cantidad de desperdicio de cada tipo almacenado en el depsito.
650

VOLVER A MEN

8. La dinmica de cambio de la simulacin se mide mediante un valor discreto de tiempo que puede
ser medidad en segundos. La simulacin empieza y termina por orden del usuario. Se debe poder
ver el estado actual del ambiente en todo momento. El tamao del ambiente pede ser fijo acorde a
las caractersticas escogidas para la interfaz.
9. La implementacin debe tener en cuenta la autonoma de los agentes.

6.10.2 Anlisis
Paso 1 (Identificar la idea y objetivos bsicos del sistema): La Figura 6.63 presenta un mapa
mental realizado con base al enunciado del problema. Se identifican tres elementos bsicos:
los agentes recolectores, los desperdicios y el ambiente.

Figura 6.63. Mapa mental relativo al problema de los agentes recolectores de desperdicio.

Paso 2 (Identificar actores / nombres): Al revisar el mapa mental de la Figura 6.62 se tiene la
siguiente lista de nombres: simulador, agente recolector, ojo, movimiento, desperdicio, unidad
de peso, unidad de tiempo, carga, energa, posicin, ambiente, centro de recarga de energa,
centro de descarga de desperdicio, cola de atencin, entrada, salida, tamao, autonoma,
agente.
Paso 3 (Identificar procesos / requerimientos): La implementacin de este problema consiste
en mostrar una simulacin; un usuario debe poder configurar algunos parmetros requeridos
y realizar las acciones de iniciar/detener el proceso. En este sentido se tiene el diagrama de
casos de uso que se muestra en la Figura 6.64.

651

VOLVER A MEN

Figura 6.64. Diagrama de casos de uso relativo al problema de los agentes recolectores de desperdicio.

Paso 4 (Identificar clases y asociaciones): De la lista de nombres identificada en el Paso 2, se


tienen los siguientes observables:
Agente y agente recolector son redundantes. Se toma la palabra recolector como el ms
adecuado para este problema.
Unidad de peso, unidad de tiempo, carga y energa son atributos.
El centro de recarga de energa y el centro de descarga de desperdicio tienen
comportamientos similares con objetivos diferentes. Puede verse como un par de objetos
de un concepto identificado como centro. Adicionalmente, Dos centros forman parte del
ambiente.
Ojo, movimiento y autonoma son irrelevantes desde el punto de vista conceptual; no as
desde el punto de vista de la implementacin.
Posicin y cola de atencin son implementaciones.
Nivel de vida, puntera y alcance de tiro son atributos de la unidad.
Turno es una variable de control (atributo) del juego (conquista).
Entrada, salida y tamao son irrelevantes.
Se proponen entonces los siguientes conceptos: Simulador, desperdicio, recolector, ambiente
y centro. Se tiene el diagrama no detallado de clases que se muestra en la Figura 6.65.
652

VOLVER A MEN

Figura 6.65. Diagrama no detallado de clases relativo al problema de los agentes recolectores de desperdicio.

6.10.3 Diseo
Paso 1 (Definir la arquitectura de la solucin): Para este problema es conveniente tener un
formulario con interfaz grfica que comprenda un panel para la configuracin y acciones y otro
panel para visualizar el estado actual. Una de las mayores complejidades en la implementacin
de este problema viene dado por la autonoma que se le debe dar a los agentes recolectores.
Para ello es recomendable el uso de hilos de ejecucin independientes para cada agente. En
este sentido, el ambiente se convierte en un rea de uso compartido y por ende en una regin
en conflicto.
Paso 2 (Detallar las clases): La Figura 6.66 muestra el diagrama de clases detallado previamente
presentado en la Figura 6.65. Se pueden notar algunas variantes, lo cual es normal que ocurra
en estas etapas de la resolucin del problema. Ya no se tiene una clase Simulador la cual fue
integrada con el Ambiente. De los conceptos de Desperdicio y Recolector se identificaron
aspectos comunes relacionados principalmente con el manejo de la parte grfica que originaron
la inclusin en el diagrama de la clase abstracta ElementoGrafico. Adicionalmente, en el
diagrama de la Figura 6.66 se incluyen los enumerados: TipoDesperdicio, Orientacion,
TipoRecolector y Estados.

Figura 6.66. Diagrama detallado de clases relativo al problema de los agentes recolectores de desperdicio.

653

VOLVER A MEN

Paso 3 (Desarrollar los modelos de estado): En la Figura 6.67 se observan los diagramas
de estado de las clases relativas a este problema. Se obvia la clase Ambiente ya que los
cambios de estado correspondientes dependen de los cambios de estado de los centros y de
la implementacin de las listas de desperdicios y recolectores. Al igual que se ha hecho con
los diagramas de estado de los problemas previos, se omiten tanto las transiciones entre los
estados as como tambin las auto-transiciones a fin de eliminar complejidad a la lectura del
diagrama.

Figura 6.67. Diagramas de estado relativos al problema de los agentes recolectores de desperdicio.

Paso 4 (Elaborar los modelos de colaboracin): A fin de mostrar la colaboracin entre los
objetos involucrados en este problema, la Figura 6.68 muestra el diagrama de colaboracin
correspondiente. Ntese la presencia de los dos objetos especficos que representan al centro
de recarga de energa y centro de descarga de desperdicio respectivamente.

Figura 6.68. Diagrama de colaboracin relativo al problema de los agentes recolectores de desperdicio.

654

VOLVER A MEN

Paso 5 (Identificar componentes del dominio): Se tiene el diagrama de componentes que se


muestra en la Figura 6.69 basado en el lenguaje de programacin C#.

Figura 6.69. Diagrama de componentes relativo al problema de los agentes recolectores de desperdicio.

6.10.4 Programacin
Con respecto a la implementacin en C++ se tienen los archivos fuente identificados como
Ambiente.hpp, Ambiente.cpp, Elementos.hpp y Elementos.cpp. Los mismos se muestran
a continuacin. Se utiliza el espacio de nombres identificado como RecoleccionDesperdicios.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.10
* Ambiente.hpp
* Mauricio Paletta
*/
#ifndef AMBIENTE_HPP
#define AMBIENTE_HPP
#include <QTimer>
#include <QQueue>
#include <QObject>
#include Elementos.hpp
655

VOLVER A MEN

namespace RecoleccionDesperdicios {
class Centro {
private:
bool Ocupado;
QQueue<pRecolector> *ColaAtencion;
pRecolector EnAtencion;
int UnidadesTiempo;
public:
Centro();
~Centro();
void Atender(pRecolector R, bool CargarEnergia);
void TaskAtencion();
bool EstaOcupado() { return Ocupado; }
int CantidadColaAtencion() { return ColaAtencion->count(); }
};
typedef Centro *pCentro;
class Ambiente {
private:
pCentro RecargaEnergia, DescargaDesperdicio;
QList<pDesperdicio> *Desperdicios;
QList<pRecolector> *Recolectores;
QList<pRecolector> *ParaDesperdicios;
void TaskDesperdicios();
public:
Ambiente();
~Ambiente();
void Limpiar();
656

VOLVER A MEN

void AgregarDesperdicio(QFrame *P, TipoDesperdicio T) {


Desperdicios->push_back(new Desperdicio(P, T));
}
void AgregarRecolector(QFrame *P, TipoRecolector T) {
Recolectores->push_back(new Recolector(this, P, T));
}
void ConvertirRecolectorDesperdicio(pRecolector R) {
if (R == NULL) return;
ParaDesperdicios->push_back(R);
}
bool CentroRecargaOcupado() {
return RecargaEnergia->EstaOcupado();
}
bool CentroDescargaOcupado() {
return DescargaDesperdicio->EstaOcupado();
}
int NumRecolectores() {
return Recolectores->count();
}
int NumDesperdicios() {
return Desperdicios->count();
}
int NumRecolectoresColaRecarga() {
return RecargaEnergia->CantidadColaAtencion();
}
int NumRecolectoresColaDescarga() {
return DescargaDesperdicio->CantidadColaAtencion();
}
void RecargarEnergia(pRecolector R) {
R->AtenderEnRecarga();
RecargaEnergia->Atender(R, true);
}
void DescargarDesperdicio(pRecolector R) {
657

VOLVER A MEN

R->AtenderEnDescarga();
DescargaDesperdicio->Atender(R, false);
}
void Detener();
void RefrescarRecolectores();
void RealizarTareas();
};
}
#endif /* AMBIENTE_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.10
* Ambiente.cpp
* Mauricio Paletta
*/
#include <QDialog>
#include Ambiente.hpp
namespace RecoleccionDesperdicios {
// Mtodos de la clase Centro
//
Centro::Centro() {
ColaAtencion = new QQueue<pRecolector>();
Ocupado = false;
EnAtencion = NULL;
UnidadesTiempo = 0;
}
Centro::~Centro() {
ColaAtencion->clear();
658

VOLVER A MEN

delete ColaAtencion;
}
void Centro::TaskAtencion() {
if (EnAtencion == NULL) return;
// Se verifica si se venci el tiempo de atencin
//
UnidadesTiempo++;
if (UnidadesTiempo < EnAtencion->ObtTpoAtencion()) return;
// El recolector actual ha sido atendido
// Se verifica alguien ms en cola para su atencin
//
EnAtencion->AtendidoEnCentro();
Ocupado = false;
EnAtencion = NULL;
UnidadesTiempo = 0;
if (ColaAtencion->count() > 0) {
EnAtencion = ColaAtencion->dequeue();
Ocupado = true;
}
}
void Centro::Atender(pRecolector R, bool CargarEnergia) {
// Permite llevar registro del tiempo de atencin
//
if (CargarEnergia)
R->AsgTpoAtencion(R->EsLiviano() ? 2 : 4);
else
R->AsgTpoAtencion(R->EsLiviano() ? 1 : 2);
if (Ocupado) {
ColaAtencion->enqueue(R);
659

VOLVER A MEN

return;
}
Ocupado = true;
EnAtencion = R;
UnidadesTiempo = 0;
}
// Mtodos de la clase Ambiente
//
Ambiente::Ambiente() {
RecargaEnergia = new Centro();
DescargaDesperdicio = new Centro();
Desperdicios = new QList<pDesperdicio>();
Recolectores = new QList<pRecolector>();
ParaDesperdicios = new QList<pRecolector>();
}
Ambiente::~Ambiente() {
delete RecargaEnergia;
delete DescargaDesperdicio;
Limpiar();
ParaDesperdicios->clear();
delete ParaDesperdicios;
}
void Ambiente::Limpiar() {
while (Desperdicios->size() > 0) {
delete Desperdicios->at(0);
Desperdicios->removeAt(0);
}
Desperdicios->clear();
while (Recolectores->size() > 0) {
delete Recolectores->at(0);
Recolectores->removeAt(0);
}
Recolectores->clear();
660

VOLVER A MEN

}
void Ambiente::TaskDesperdicios() {
pDesperdicio D;
pRecolector R;
if (ParaDesperdicios->isEmpty()) return;
R = ParaDesperdicios->at(0);
R->setVisible(false);
D = new Desperdicio((QFrame *)(R->parent()), Pesado, 10);
D->setVisible(false);
D->setGeometry(R->x(), R->y(), R->LaDimension(), R->LaDimension());
D->setVisible(true);
Desperdicios->push_back(D);
Recolectores->removeAt(Recolectores->indexOf(R, 0));
ParaDesperdicios->removeAt(0);
}
void Ambiente::Detener() {
for (int i = 0; i < Recolectores->size(); i++)
(*Recolectores)[i]->Parar();
}
void Ambiente::RefrescarRecolectores() {
for (int i = 0; i < Recolectores->size(); i++)
(*Recolectores)[i]->RefrescarMovimientos();
}
void Ambiente::RealizarTareas() {
TaskDesperdicios();
RecargaEnergia->TaskAtencion();
DescargaDesperdicio->TaskAtencion();
}
}
661

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.10
* Elementos.hpp
* Mauricio Paletta
*/
#ifndef ELEMENTOS_HPP
#define ELEMENTOS_HPP
#include <QtGui>
namespace RecoleccionDesperdicios {
enum TipoOperacion {
Avance, Rotacion, Apagar, Mostrar, Recoger
};
class Desperdicio;
class Movimiento {
private:
QPoint Pt;
QString Img;
Desperdicio *D;
TipoOperacion Operacion;
public:
Movimiento(QPoint P) { Pt = P; Img = ; Operacion = Avance; }
Movimiento(QString I) { Img = I; Operacion = Rotacion; }
Movimiento(bool F) { Operacion = F ? Mostrar : Apagar; }
Movimiento(Desperdicio *pD) { Operacion = Recoger; D = pD; }
QPoint ObtAvance() { return Pt; }
QString ObtRotacion() { return Img; }
662

VOLVER A MEN

Desperdicio *ObtDesperdicio() { return D; }


TipoOperacion ObtOperacion() { return Operacion; }
};
typedef Movimiento *pMovimiento;
class ElementoGrafico : public QGraphicsView {
Q_OBJECT
protected:
static int const Dimension = 11;
QGraphicsScene *ImagenAgente;
QList<pMovimiento> *ListaMovimientos;
QSemaphore *SemLista;
QPoint PtActual;
bool VerificarOcupacion(QPoint Pt);
public:
ElementoGrafico(QFrame *F, bool D);
~ElementoGrafico();
int LaDimension() {
return Dimension;
}
void RefrescarMovimientos();
int ObtX() { return PtActual.x(); }
int ObtY() { return PtActual.y(); }
void AgregarMovimiento(QString S) {
SemLista->acquire();
ListaMovimientos->append(new Movimiento(S));
SemLista->release();
}
void AgregarMovimiento(QPoint P) {
663

VOLVER A MEN

SemLista->acquire();
ListaMovimientos->append(new Movimiento(P));
SemLista->release();
}
void AgregarMovimiento(bool B) {
SemLista->acquire();
ListaMovimientos->append(new Movimiento(B));
SemLista->release();
}
void AgregarMovimiento(Desperdicio *D) {
SemLista->acquire();
ListaMovimientos->append(new Movimiento(D));
SemLista->release();
}
};
enum TipoDesperdicio {
Liviano, Pesado
};
class Desperdicio : public ElementoGrafico {
private:
TipoDesperdicio Tipo;
int Peso;
public:
Desperdicio(QFrame *F, TipoDesperdicio T);
Desperdicio(QFrame *F, TipoDesperdicio T, int Ps);
void Recoger();
int ObtPeso() { return Peso; }
bool EsLiviano() { return Tipo == Liviano; }
};
typedef Desperdicio *pDesperdicio;
664

VOLVER A MEN

enum TipoRecolector {
RLiviano, RPesado
};
enum Orientacion {
Aba, Izq, Arr, Der
};
enum Estados {
Normal, EnRecarga, EnDescarga
};
class Recolector;
class ThreadAgente : public QThread {
Q_OBJECT
private:
Recolector *R;
bool Continuar;
protected:
void run();
public:
ThreadAgente(Recolector *r) {
Continuar = true;
R = r;
}
void Stop() { Continuar = false; }
};
class Ambiente;
class Recolector : public ElementoGrafico {
665

VOLVER A MEN

private:
static QSemaphore Sem;
ThreadAgente *Agente;
TipoRecolector Tipo;
int Carga, Energia, Capacidad, TpoAtencion;
Orientacion Ojos;
Estados Estado;
Ambiente *ElAmbiente;
void Refrescar();
void PuntoVisual(QPoint &Pt);
pDesperdicio BuscarDesperdicio(QPoint Pt);
bool VerificarRecogerDesperdicio();
public:
Recolector(Ambiente *A, QFrame *P, TipoRecolector T);
~Recolector() { delete Agente; }
bool Avanzar();
void RotarIzquierda();
void RotarDerecha();
bool EsLiviano() {
return Tipo == RLiviano;
}
void AtenderEnRecarga() {
Estado = EnRecarga;
AgregarMovimiento(false);
}
void AtenderEnDescarga() {
Estado = EnDescarga;
AgregarMovimiento(false);
}
666

VOLVER A MEN

void AtendidoEnCentro();
void SecuenciaTeleoreactiva();
void Parar();
void AsgTpoAtencion(int Tpo) { if (Tpo >= 0) TpoAtencion = Tpo; }
int ObtTpoAtencion() { return TpoAtencion; }
};
typedef Recolector *pRecolector;
}
#endif /* ELEMENTOS_HPP */
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en C++; caso prctico Seccin 6.10
* Elementos.cpp
* Mauricio Paletta
*/
#include <stdlib.h>
#include <time.h>
#include Elementos.hpp
#include Ambiente.hpp
namespace RecoleccionDesperdicios {
// Mtodos de la clase ElementoGrafico
//
ElementoGrafico::ElementoGrafico(QFrame *F, bool D) : QGraphicsView((QWidget *)F) {
QPoint Pt;
ImagenAgente = new QGraphicsScene();
setScene(ImagenAgente);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
667

VOLVER A MEN

setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setProperty(Tipo, D);
do {
Pt.setX((rand() % (F->width() / Dimension)) * Dimension);
Pt.setY((rand() % (F->height() / Dimension)) * Dimension);
} while (!VerificarOcupacion(Pt));
setGeometry(Pt.x(), Pt.y(), Dimension, Dimension);
setVisible(true);
setFrameShape(NoFrame);
PtActual = pos();
SemLista = new QSemaphore(1);
ListaMovimientos = new QList<pMovimiento>();
}
ElementoGrafico::~ElementoGrafico() {
delete ImagenAgente;
for (int i=0; i < ListaMovimientos->count(); i++) {
pMovimiento Aux = (*ListaMovimientos)[i];
if (Aux != NULL) delete Aux;
}
ListaMovimientos->clear();
delete ListaMovimientos;
delete SemLista;
}
bool ElementoGrafico::VerificarOcupacion(QPoint Pt) {
QFrame *P = (QFrame *)parentWidget();
if (Pt.x() < 0 || Pt.x() + Dimension > P->width() || Pt.y() < 0 || Pt.y() + Dimension > P->height())
return false;
for (int i = 0; i < P->children().count(); i++) {
668

VOLVER A MEN

ElementoGrafico *C = (ElementoGrafico *)(P->children()[i]);


if (C != this && C->isVisible() && Pt.x() == C->PtActual.x() && Pt.y() == C->PtActual.y())
return false;
}
return true;
}
void ElementoGrafico::RefrescarMovimientos() {
SemLista->acquire();
if (ListaMovimientos->isEmpty()) {
SemLista->release();
return;
}
pMovimiento M = ListaMovimientos->takeFirst();
switch (M->ObtOperacion()) {
case Avance:
setGeometry(M->ObtAvance().x(), M->ObtAvance().y(), LaDimension(), LaDimension());
PtActual = pos();
break;
case Rotacion:
ImagenAgente->addPixmap(QPixmap(M->ObtRotacion()));
break;
case Apagar:
setVisible(false);
break;
case Mostrar:
setVisible(true);
break;
case Recoger:
pDesperdicio pD = M->ObtDesperdicio();
if (pD != NULL) pD->setVisible(false);
669

VOLVER A MEN

break;
}
repaint();
delete M;
SemLista->release();
}
// Mtodos de la clase Desperdicio
//
Desperdicio::Desperdicio(QFrame *P, TipoDesperdicio T) : ElementoGrafico(P, true) {
Tipo = T;
if (Tipo == Liviano) {
Peso = 1 + (rand() % 4);
ImagenAgente->addPixmap(QPixmap(DesperdicioL.bmp));
}
else {
Peso = 6 + (rand() % 5);
ImagenAgente->addPixmap(QPixmap(DesperdicioP.bmp));
}
repaint();
}
Desperdicio::Desperdicio(QFrame *P, TipoDesperdicio T, int Ps) : ElementoGrafico(P, true) {
Tipo = T;
if (Ps < 0) Ps = 1;
if (Tipo == Liviano) {
if (Ps > 5) Ps = 5;
ImagenAgente->addPixmap(QPixmap(DesperdicioL.bmp));
}
else {
if (Ps > 10) Ps = 10;
ImagenAgente->addPixmap(QPixmap(DesperdicioP.bmp));
}
Peso = Ps;
670

VOLVER A MEN

repaint();
}
void Desperdicio::Recoger() {
QFrame *P = (QFrame *)parentWidget();
QList<QObject *> L = P->children();
L.removeAt(L.indexOf(this, 0));
}
// Mtodos de la clase Recolector
//
void ThreadAgente::run() {
while (Continuar) {
R->SecuenciaTeleoreactiva();
// Se espera entre 500 y 2000 mseg
//
usleep(500 + rand() % 2000);
}
}
QSemaphore Recolector::Sem(1);
Recolector::Recolector(Ambiente *A, QFrame *P, TipoRecolector T) : ElementoGrafico(P, false) {
Tipo = T;
Carga = 0;
ElAmbiente = A;
if (Tipo == RLiviano) {
Energia = 50 + (rand() % 51);
Capacidad = 10;
}
else {
Energia = 150 + (rand() % 51);
Capacidad = 30;
671

VOLVER A MEN

}
Estado = Normal;
PtActual = this->pos();
TpoAtencion = 0;
// Se decide al azar la posicin inicial y
// localizacin inicial del recolector
//
switch (Tipo) {
case Liviano:
switch (rand() % 4) {
case 0:
Ojos = Aba;
break;
case 1:
Ojos = Izq;
break;
case 2:
Ojos = Arr;
break;
case 3:
Ojos = Der;
break;
}
break;
case Pesado:
switch (rand() % 4) {
case 0:
Ojos = Aba;
break;
case 1:
Ojos = Izq;
break;
672

VOLVER A MEN

case 2:
Ojos = Arr;
break;
case 3:
Ojos = Der;
break;
}
break;
}
Refrescar();
// Hilo de ejecucin
//
Agente = new ThreadAgente(this);
Agente->start();
}
void Recolector::Refrescar() {
std::string Img = ;
switch (Tipo) {
case Liviano:
switch (Ojos) {
case Aba:
Img = LivianoD.bmp;
break;
case Izq:
Img = LivianoL.bmp;
break;
case Arr:
Img = LivianoU.bmp;
break;
case Der:
673

VOLVER A MEN

Img = LivianoR.bmp;
break;
}
break;
case Pesado:
switch (Ojos) {
case Aba:
Img = PesadoD.bmp;
break;
case Izq:
Img = PesadoL.bmp;
break;
case Arr:
Img = PesadoU.bmp;
break;
case Der:
Img = PesadoR.bmp;
break;
}
break;
}
if (Img != ) {
AgregarMovimiento(QString(Img.c_str()));
}
}
void Recolector::PuntoVisual(QPoint &Pt) {
switch (Ojos) {
case Aba:
Pt.setY(Pt.y() + LaDimension());
break;
case Izq:
Pt.setX(Pt.x() - LaDimension());
674

VOLVER A MEN

break;
case Arr:
Pt.setY(Pt.y() - LaDimension());
break;
case Der:
Pt.setX(Pt.x() + LaDimension());
break;
}
}
bool Recolector::Avanzar() {
QPoint Pt = PtActual;
if (Energia <= 0) return false;
PuntoVisual(Pt);
if (!VerificarOcupacion(Pt)) {
return false;
}
PtActual = Pt;
AgregarMovimiento(Pt);
Energia--;
return true;
}
void Recolector::RotarIzquierda() {
if (Energia <= 0) return;
switch (Ojos) {
case Aba:
Ojos = Der;
break;
case Der:
Ojos = Arr;
break;
675

VOLVER A MEN

case Arr:
Ojos = Izq;
break;
case Izq:
Ojos = Aba;
break;
}
Energia--;
Refrescar();
}
void Recolector::RotarDerecha() {
if (Energia <= 0) return;
switch (Ojos) {
case Aba:
Ojos = Izq;
break;
case Izq:
Ojos = Arr;
break;
case Arr:
Ojos = Der;
break;
case Der:
Ojos = Aba;
break;
}
Energia--;
Refrescar();
}

676

VOLVER A MEN

void Recolector::AtendidoEnCentro() {
if (Estado == Normal) return;
if (Estado == EnRecarga)
Energia = Tipo == RLiviano ? 100 : 200;
else {
Carga = 0;
Energia -= Tipo == RLiviano ? 2 : 4;
}
TpoAtencion = 0;
// Hay que ubicar nuevamente el recolector en el rea de simulacin
//
QFrame *P = (QFrame *)parentWidget();
QPoint Pt(0, Estado == EnRecarga ? 0 : P->height() - LaDimension());
while (!VerificarOcupacion(Pt)) Pt.setX(Pt.x() + LaDimension());
Ojos = (Estado == EnRecarga ? Aba : Arr);
Estado = Normal;
PtActual = Pt;
AgregarMovimiento(Pt);
AgregarMovimiento(true);
}
pDesperdicio Recolector::BuscarDesperdicio(QPoint Pt) {
QFrame *P = (QFrame *)parentWidget();
QList<QObject *> L = P->children();
for (int i=0; i < L.count(); i++) {
ElementoGrafico *C = (ElementoGrafico *)L[i];
bool D = C->property(Tipo).toBool();
if (D && C->ObtX() == Pt.x() && C->ObtY() == Pt.y())
return (pDesperdicio)C;
}
677

VOLVER A MEN

return NULL;
}
bool Recolector::VerificarRecogerDesperdicio() {
// Primero se busca la posible presencia de un desperdicio en la
// lnea visual del recolector
//
QPoint Pt = PtActual;
pDesperdicio D;
PuntoVisual(Pt);
D = BuscarDesperdicio(Pt);
if (D == NULL) return false;
// Se tiene un desperdicio, se verifica si el recolector lo puede recoger
//
if (((EsLiviano() && D->EsLiviano()) || !EsLiviano()) && Carga + D->ObtPeso() <= Capacidad) {
// Ver si hay energa suficiente
//
if ((D->EsLiviano() && Energia > 0) || Energia - 1 > 0) {
// Se procede a la recoleccin del desperdicio
//
Carga += D->ObtPeso();
Energia -= D->EsLiviano() ? 1 : 2;
D->Recoger();
AgregarMovimiento(D);
return true;
}
}
return false;
}

678

VOLVER A MEN

void Recolector::Parar() {
Agente->Stop();
AgregarMovimiento(false);
}
void Recolector::SecuenciaTeleoreactiva() {
QFrame *P = (QFrame *)parentWidget();
// Si est siendo atendido en algn centro se duerme
//
if (Estado != Normal) return;
Sem.acquire();
// Si se acab la energa el recolector se convierte en un desperdicio
// pesado de 10 unidades de peso
//
if (Energia <= 0) {
ElAmbiente->ConvertirRecolectorDesperdicio(this);
Parar();
Sem.release();
return;
}
// Lo primero es revisar la energa, de haber problemas hay que
// dirigirse al centro de recarga que est en la coordenada (0, 0)
//
if (Energia <= 25) {
if (PtActual.x() == 0 && PtActual.y() == 0) {
if (Ojos == Arr) {
// Puede entrar al centro de recarga
//
ElAmbiente->RecargarEnergia(this);
679

VOLVER A MEN

}
else {
if (Ojos == Der || Ojos == Aba)
RotarIzquierda();
else
RotarDerecha();
}
}
else {
QPoint Pt = PtActual;
if (PtActual.x() == 0 && Ojos == Izq) {
RotarDerecha();
Sem.release();
return;
}
if (PtActual.y() == 0 && Ojos == Arr) {
RotarIzquierda();
Sem.release();
return;
}
if ((Ojos == Aba || Ojos == Der)) {
RotarIzquierda();
Sem.release();
return;
}
if (!Avanzar()) {
if (rand() < RAND_MAX / 2)
RotarIzquierda();
else
RotarDerecha();
Avanzar();
}
}
680

VOLVER A MEN

Sem.release();
return;
}
// Lo segundo es verificar la carga, de estar casi lleno hay que ir
// al centro de descarga que est en la coordenada (0, H)
//
if (Capacidad - Carga <= 5) {
if (PtActual.x() == 0 && P->height() - (PtActual.y() + LaDimension()) < LaDimension()) {
if (Ojos == Aba) {
// Puede entrar al centro de recarga
//
ElAmbiente->DescargarDesperdicio(this);
}
else {
if (Ojos == Der || Ojos == Arr)
RotarDerecha();
else
RotarIzquierda();
}
}
else {
if (PtActual.x() == 0 && Ojos == Izq) {
RotarIzquierda();
Sem.release();
return;
}
if (P->height() - (PtActual.y() + LaDimension()) < LaDimension() && Ojos == Arr) {
RotarDerecha();
Sem.release();
return;
}
if (Ojos == Arr || Ojos == Der) {
681

VOLVER A MEN

RotarDerecha();
Sem.release();
return;
}
if (!Avanzar()) {
if (rand() < RAND_MAX / 2)
RotarDerecha();
else
RotarIzquierda();
Avanzar();
}
}
Sem.release();
return;
}
// Lo tercero es verificar si tiene un desperdicio en su foco visual
// para recogerlo
//
if (VerificarRecogerDesperdicio()) {
Sem.release();
return;
}
// Lo cuarto es decidir aleatoriamente si avanzar o rotar
//
switch (rand() % 6) {
case 0:
RotarIzquierda();
break;
case 1:
RotarDerecha();
break;
682

VOLVER A MEN

default:
if (!Avanzar()) {
if (rand() < RAND_MAX / 2)
RotarIzquierda();
else
RotarDerecha();
}
break;
}
Sem.release();
}
}
Para la implementacin en Java se tienen los archivos fuente Ambiente.java, Centro.java,
ElementoGrafico.java, Desperdicio.java y Recolector.java. Todas las clases fueron definidas en
el paquete identificado como RecoleccionDesperdicios. A continuacin el listado de estos archivos.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.10
* Ambiete.java
* Mauricio Paletta
*/
package RecoleccionDesperdicios;
import java.awt.Container;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class Ambiente {
class TaskDesperdicio extends TimerTask {
public void run() {
683

VOLVER A MEN

Desperdicio D;
Recolector R;
if (ParaDesperdicios.isEmpty()) return;
R = ParaDesperdicios.get(0);
R.setVisible(false);
D = new Desperdicio(R.getParent(), Desperdicio.TipoDesperdicio.Pesado, 10);
D.setVisible(false);
D.setLocation(R.getX(), R.getY());
D.setVisible(true);
Desperdicios.add(D);
Recolectores.remove(R);
ParaDesperdicios.remove(0);
R.Parar();
}
}
private Centro RecargaEnergia, DescargaDesperdicio;
private List<Desperdicio> Desperdicios;
private List<Recolector> Recolectores;
private List<Recolector> ParaDesperdicios;
private Timer timerD = new Timer();
private TaskDesperdicio TiempoParaDesperdicios = new TaskDesperdicio();
public Ambiente() {
RecargaEnergia = new Centro();
DescargaDesperdicio = new Centro();
Desperdicios = new LinkedList<Desperdicio>();
Recolectores = new LinkedList<Recolector>();
ParaDesperdicios = new LinkedList<Recolector>();
timerD.schedule(TiempoParaDesperdicios, 0, 250);
}
684

VOLVER A MEN

public void Limpiar() {


Desperdicios.clear();
Recolectores.clear();
}
public void AgregarDesperdicio(Container P, Desperdicio.TipoDesperdicio T) {
Desperdicios.add(new Desperdicio(P, T));
}
public void AgregarRecolector(Container P, Recolector.TipoRecolector T) {
Recolectores.add(new Recolector(this, P, T));
}
public void ConvertirRecolectorDesperdicio(Recolector R) {
if (R == null) return;
ParaDesperdicios.add(R);
}
public boolean CentroRecargaOcupado() {
return RecargaEnergia.EstaOcupado();
}
public boolean CentroDescargaOcupado() {
return DescargaDesperdicio.EstaOcupado();
}
public int NumRecolectores() {
return Recolectores.size();
}
public int NumDesperdicios() {
return Desperdicios.size();
}
public int NumRecolectoresColaRecarga() {
return RecargaEnergia.CantidadColaAtencion();
685

VOLVER A MEN

}
public int NumRecolectoresColaDescarga() {
return DescargaDesperdicio.CantidadColaAtencion();
}
public void RecargarEnergia(Recolector R) {
R.AtenderEnRecarga();
RecargaEnergia.Atender(R, true);
}
public void DescargarDesperdicio(Recolector R) {
R.AtenderEnDescarga();
DescargaDesperdicio.Atender(R, false);
}
public void Detener() {
for (int i = 0; i < Recolectores.size(); i++)
Recolectores.get(i).Parar();
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.10
* Centro.java
* Mauricio Paletta
*/
package RecoleccionDesperdicios;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
686

VOLVER A MEN

public class Centro {


class TaskAtencion extends TimerTask {
public void run() {
if (EnAtencion == null) return;
// Se verifica si se venci el tiempo de atencin
//
UnidadesTiempo++;
if (UnidadesTiempo < Integer.parseInt(EnAtencion.getToolTipText())) return;
// El recolector actual ha sido atendido
// Se verifica alguien ms en cola para su atencin
//
EnAtencion.AtendidoEnCentro();
Ocupado = false;
EnAtencion = null;
UnidadesTiempo = 0;
if (ColaAtencion.size() > 0) {
EnAtencion = ColaAtencion.poll();
Ocupado = true;
}
}
}
private boolean Ocupado;
private Queue<Recolector> ColaAtencion;
private Recolector EnAtencion;
private Timer timerA = new Timer();
private TaskAtencion TiempoAtencion = new TaskAtencion();
private int UnidadesTiempo;
public Centro() {
687

VOLVER A MEN

Ocupado = false;
EnAtencion = null;
ColaAtencion = new LinkedList<Recolector>();
UnidadesTiempo = 0;
timerA.schedule(TiempoAtencion, 0, 1000);
}
public void Atender(Recolector R, boolean CargarEnergia) {
// Permite llevar registro del tiempo de atencin
//
if (CargarEnergia)
R.setToolTipText(R.EsLiviano() ? 2 : 4);
else
R.setToolTipText(R.EsLiviano() ? 1 : 2);
if (Ocupado) {
ColaAtencion.add(R);
return;
}
Ocupado = true;
EnAtencion = R;
UnidadesTiempo = 0;
}
public boolean EstaOcupado() { return Ocupado; }
public int CantidadColaAtencion() { return ColaAtencion.size(); }
}

688

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.10
* ElementoGrafico.java
* Mauricio Paletta
*/
package RecoleccionDesperdicios;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.util.Random;
import javax.swing.JLabel;
public abstract class ElementoGrafico extends JLabel {
private final int Dimension = 11;
public ElementoGrafico(Container P) {
super();
Random R = new Random();
Point Pt = new Point();
this.setSize(Dimension, Dimension);
P.add(this);
do {
Pt.x = R.nextInt(P.getWidth() / Dimension) * Dimension;
Pt.y = R.nextInt(P.getHeight() / Dimension) * Dimension;
} while (!VerificarOcupacion(Pt));
this.setLocation(Pt);
}

689

VOLVER A MEN

public final boolean VerificarOcupacion(Point Pt) {


Container P = this.getParent();
if (Pt.x < 0 || Pt.x+Dimension > P.getWidth() || Pt.y < 0 || Pt.y+Dimension > P.getHeight()) return
false;
for (int i = 0; i < P.getComponentCount(); i++) {
Component C = P.getComponent(i);
if (C != this && C.isVisible() && Pt.x == C.getX() && Pt.y == C.getY()) return false;
}
return true;
}
public int LaDimension() {
return Dimension;
}
}
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.10
* Desperdicio.java
* Mauricio Paletta
*/
package RecoleccionDesperdicios;
import java.awt.Container;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
690

VOLVER A MEN

public class Desperdicio extends ElementoGrafico {


public enum TipoDesperdicio {
Liviano, Pesado
}
private TipoDesperdicio Tipo;
private int Peso;
public Desperdicio(Container P, TipoDesperdicio T) {
super(P);
BufferedImage Imagen;
Random R = new Random();
try {
Tipo = T;
if (Tipo == TipoDesperdicio.Liviano) {
Peso = 1 + R.nextInt(4);
Imagen = ImageIO.read(new File(DesperdicioL.bmp));
}
else {
Peso = 6 + R.nextInt(5);
Imagen = ImageIO.read(new File(DesperdicioP.bmp));
}
this.setIcon(new ImageIcon(Imagen));
this.repaint();
} catch (IOException ex) { }
}
public Desperdicio(Container P, TipoDesperdicio T, int Ps) {
super(P);
BufferedImage Imagen;

691

VOLVER A MEN

try {
Tipo = T;
if (Ps < 0) Ps = 1;
if (Tipo == TipoDesperdicio.Liviano) {
if (Ps > 5) Ps = 5;
Imagen = ImageIO.read(new File(DesperdicioL.bmp));
}
else {
if (Ps > 10) Ps = 10;
Imagen = ImageIO.read(new File(DesperdicioP.bmp));
}
Peso = Ps;
this.setIcon(new ImageIcon(Imagen));
this.repaint();
} catch (IOException ex) { }
}
public void Recoger() {
Container P = getParent();
setVisible(false);
P.remove(this);
}
public int ObtPeso() { return Peso; }
public boolean EsLiviano() { return Tipo == TipoDesperdicio.Liviano; }
}

692

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.10
* Recolector.java
* Mauricio Paletta
*/
package RecoleccionDesperdicios;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Recolector extends ElementoGrafico {
public enum TipoRecolector {
Liviano, Pesado
}
private enum Orientacion {
Aba, Izq, Arr, Der
}
private enum Estados {
Normal, EnRecarga, EnDescarga
693

VOLVER A MEN

}
private class ThreadAgente extends Thread {
protected Semaphore Sem = new Semaphore(1);
private boolean Continuar = true;
@Override
public void run() {
try {
while (Continuar) {
Sem.acquire();
SecuenciaTeleoreactiva();
Sem.release();
// Se espera entre 250 y 1000 mseg
//
Thread.sleep(250 + Rnd.nextInt(750));
}
} catch (InterruptedException ex) {
Logger.getLogger(Recolector.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void Stop() {
Continuar = false;
}
}
private final int Dimension = 11;
private TipoRecolector Tipo;
private int Carga, Energia, Capacidad;
694

VOLVER A MEN

private Orientacion Ojos;


private Estados Estado;
private Random Rnd = new Random();
private ThreadAgente Agente;
private Ambiente ElAmbiente;
public Recolector(Ambiente A, Container P, TipoRecolector T) {
super(P);
Tipo = T;
Carga = 0;
ElAmbiente = A;
if (Tipo == TipoRecolector.Liviano) {
Energia = 50 + Rnd.nextInt(51);
Capacidad = 10;
}
else {
Energia = 150 + Rnd.nextInt(51);
Capacidad = 30;
}
Estado = Estados.Normal;
// Se decide al azar la posicin inicial y
// localizacin inicial del recolector
//
switch (Tipo) {
case Liviano:
switch (Rnd.nextInt(4)) {
case 0:
Ojos = Orientacion.Aba;
break;
case 1:
Ojos = Orientacion.Izq;
break;
695

VOLVER A MEN

case 2:
Ojos = Orientacion.Arr;
break;
case 3:
Ojos = Orientacion.Der;
break;
}
break;
case Pesado:
switch (Rnd.nextInt(4)) {
case 0:
Ojos = Orientacion.Aba;
break;
case 1:
Ojos = Orientacion.Izq;
break;
case 2:
Ojos = Orientacion.Arr;
break;
case 3:
Ojos = Orientacion.Der;
break;
}
break;
}
Refrescar();
// Hilo de ejecucin
//
Agente = new ThreadAgente();
Agente.start();
}

696

VOLVER A MEN

private void Refrescar() {


BufferedImage Imagen;
String Img = ;
switch (Tipo) {
case Liviano:
switch (Ojos) {
case Aba:
Img = LivianoD.bmp;
break;
case Izq:
Img = LivianoL.bmp;
break;
case Arr:
Img = LivianoU.bmp;
break;
case Der:
Img = LivianoR.bmp;
break;
}
break;
case Pesado:
switch (Ojos) {
case Aba:
Img = PesadoD.bmp;
break;
case Izq:
Img = PesadoL.bmp;
break;
case Arr:
Img = PesadoU.bmp;
break;
case Der:
697

VOLVER A MEN

Img = PesadoR.bmp;
break;
}
break;
}
try {
if (Img.compareTo() != 0) {
Imagen = ImageIO.read(new File(Img));
this.setIcon(new ImageIcon(Imagen));
this.repaint();
}
} catch (IOException ex) { }
}
private Point PuntoVisual(Point Pt) {
switch (Ojos) {
case Aba:
Pt.y += LaDimension();
break;
case Izq:
Pt.x -= LaDimension();
break;
case Arr:
Pt.y -= LaDimension();
break;
case Der:
Pt.x += LaDimension();
break;
}
return new Point(Pt.x, Pt.y);
}

698

VOLVER A MEN

public boolean Avanzar() {


Point Pt = new Point(this.getX(), this.getY());
if (Energia <= 0) return false;
Pt = PuntoVisual(Pt);
if (!VerificarOcupacion(Pt)) return false;
setLocation(Pt);
Energia--;
return true;
}
public void RotarIzquierda() {
if (Energia <= 0) return;
switch (Ojos) {
case Aba:
Ojos = Orientacion.Der;
break;
case Der:
Ojos = Orientacion.Arr;
break;
case Arr:
Ojos = Orientacion.Izq;
break;
case Izq:
Ojos = Orientacion.Aba;
break;
}
Energia--;
Refrescar();
}

699

VOLVER A MEN

public void RotarDerecha() {


if (Energia <= 0) return;
switch (Ojos) {
case Aba:
Ojos = Orientacion.Izq;
break;
case Izq:
Ojos = Orientacion.Arr;
break;
case Arr:
Ojos = Orientacion.Der;
break;
case Der:
Ojos = Orientacion.Aba;
break;
}
Energia--;
Refrescar();
}
public boolean EsLiviano() {
return Tipo == TipoRecolector.Liviano;
}
public void AtenderEnRecarga() {
Estado = Estados.EnRecarga;
setVisible(false);
}
public void AtenderEnDescarga() {
Estado = Estados.EnDescarga;
setVisible(false);
700

VOLVER A MEN

}
public void AtendidoEnCentro() {
if (Estado == Estados.Normal) return;
if (Estado == Estados.EnRecarga)
Energia = Tipo == TipoRecolector.Liviano ? 100 : 200;
else {
Carga = 0;
Energia -= Tipo == TipoRecolector.Liviano ? 2 : 4;
}
// Hay que ubicar nuevamente el recolector en el rea de simulacin
//
Container P = this.getParent();
Point Pt = new Point(0, Estado == Estados.EnRecarga ? 0 : P.getHeight() - LaDimension());
while (!VerificarOcupacion(Pt)) Pt.x += LaDimension();
Ojos = (Estado == Estados.EnRecarga ? Orientacion.Aba : Orientacion.Arr);
Estado = Estados.Normal;
setLocation(Pt);
setVisible(true);
}
private Desperdicio BuscarDesperdicio(Point Pt) {
Container P = this.getParent();
for (int i=0; i < P.getComponentCount(); i++) {
Component C = P.getComponent(i);
if (C.getClass().getName().compareTo(RecoleccionDesperdicios.Desperdicio) == 0 &&
C.getX() == Pt.x && C.getY() == Pt.y)
return (Desperdicio)C;
}

701

VOLVER A MEN

return null;
}
private boolean VerificarRecogerDesperdicio() {
// Primero se busca la posible presencia de un desperdicio en la
// lnea visual del recolector
//
Point Pt = new Point(this.getX(), this.getY());
Desperdicio D;
D = BuscarDesperdicio(PuntoVisual(Pt));
if (D == null) return false;
// Se tiene un desperdicio, se verifica si el recolector lo puede recoger
//
if (((EsLiviano() && D.EsLiviano()) || !EsLiviano()) && Carga + D.ObtPeso() <= Capacidad) {
// Ver si hay energa suficiente
//
if ((D.EsLiviano() && Energia > 0) || Energia - 1 > 0) {
// Se procede a la recoleccin del desperdicio
//
Carga += D.ObtPeso();
Energia -= D.EsLiviano() ? 1 : 2;
D.Recoger();
return true;
}
}
return false;
}
public void Parar() {
Agente.Stop();
702

VOLVER A MEN

setVisible(false);
}
private void SecuenciaTeleoreactiva() {
// Si est siendo atendido en algn centro se duerme
//
if (Estado != Estados.Normal) return;
// Si se acab la energa el recolector se convierte en un desperdicio
// pesado de 10 unidades de peso
//
if (Energia <= 0) {
ElAmbiente.ConvertirRecolectorDesperdicio(this);
return;
}
// Lo primero es revisar la energa, de haber problemas hay que
// dirigirse al centro de recarga que est en la coordenada (0, 0)
//
if (Energia <= 25) {
if (this.getX() == 0 && this.getY() == 0) {
if (Ojos == Orientacion.Arr) {
// Puede entrar al centro de recarga
//
ElAmbiente.RecargarEnergia(this);
}
else {
if (Ojos == Orientacion.Der || Ojos == Orientacion.Aba)
RotarIzquierda();
else
RotarDerecha();
}
}
703

VOLVER A MEN

else {
Point Pt = new Point(this.getX(), this.getY());
if (this.getX() == 0 && Ojos == Orientacion.Izq) {
RotarDerecha();
return;
}
if (this.getY() == 0 && Ojos == Orientacion.Arr) {
RotarIzquierda();
return;
}
if ((Ojos == Orientacion.Aba || Ojos == Orientacion.Der)) {
RotarIzquierda();
return;
}
if (!Avanzar()) {
if (Rnd.nextDouble() < 0.5)
RotarIzquierda();
else
RotarDerecha();
Avanzar();
}
}
return;
}
// Lo segundo es verificar la carga, de estar casi lleno hay que ir
// al centro de descarga que est en la coordenada (0, H)
//
if (Capacidad - Carga <= 5) {
if (this.getX() == 0 && this.getParent().getHeight() - (this.getY() + LaDimension()) <
LaDimension()) {
if (Ojos == Orientacion.Aba) {

704

VOLVER A MEN

// Puede entrar al centro de recarga


//
ElAmbiente.DescargarDesperdicio(this);
}
else {
if (Ojos == Orientacion.Der || Ojos == Orientacion.Arr)
RotarDerecha();
else
RotarIzquierda();
}
}
else {
if (this.getX() == 0 && Ojos == Orientacion.Izq) {
RotarIzquierda();
return;
}
if (this.getParent().getHeight() - (this.getY() + LaDimension()) < LaDimension() &&
Ojos == Orientacion.Arr) {
RotarDerecha();
return;
}
if (Ojos == Orientacion.Arr || Ojos == Orientacion.Der) {
RotarDerecha();
return;
}
if (!Avanzar()) {
if (Rnd.nextDouble() < 0.5)
RotarDerecha();
else
RotarIzquierda();
Avanzar();
}
}
705

VOLVER A MEN

return;
}
// Lo tercero es verificar si tiene un desperdicio en su foco visual
// para recogerlo
//
if (VerificarRecogerDesperdicio()) return;
// Lo cuarto es decidir aleatoriamente si avanzar o rotar
//
switch (Rnd.nextInt(6)) {
case 0:
RotarIzquierda();
break;
case 1:
RotarDerecha();
break;
default:
if (!Avanzar()) {
if (Rnd.nextDouble() < 0.5)
RotarIzquierda();
else
RotarDerecha();
}
break;
}
}
}

706

VOLVER A MEN

A continuacin el cdigo fuente escrito en C# para implementar este problema. Se tienen


los archivos Simulador.cs y Ambiente.cs. Todos los elementos estn definidos dentro del
espacio de nombres identificado como RecoleccionDesperdicios.
/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Simulador.cs
* Mauricio Paletta
*/
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
namespace RecoleccionDesperdicios
{
public abstract class ElementoGrafico : PictureBox
{
private const int Dimension = 11;
protected static Semaphore Sem = new Semaphore(1, 1);
public ElementoGrafico(Control P) : base()
{
Random R = new Random();
Point Pt = new Point();
SetParent(P);
this.Size = new System.Drawing.Size(Dimension, Dimension);
do
{
Pt.X = R.Next(P.Width / Dimension) * Dimension;
Pt.Y = R.Next(P.Height / Dimension) * Dimension;
} while (!VerificarOcupacion(Pt));
707

VOLVER A MEN

SetLocation(Pt);
}
public bool VerificarOcupacion(Point Pt)
{
Control P = this.Parent;
if (Pt.X < 0 || Pt.X + Dimension > P.Width || Pt.Y < 0 || Pt.Y + Dimension > P.Height)
return false;
for (int i = 0; i < P.Controls.Count; i++)
{
Control C = P.Controls[i];
if (C != this && C.Visible && Pt.X == C.Location.X && Pt.Y == C.Location.Y) return false;
}
return true;
}
public int LaDimension
{
get { return Dimension; }
}
private delegate void SetLocationDelegate(Point Pt);
protected void SetLocation(Point Pt)
{
if (this.InvokeRequired)
this.Invoke(new SetLocationDelegate(SetLocation), Pt);
else
this.Location = new Point(Pt.X, Pt.Y);
}
private delegate void SetVisibleDelegate(bool V);
708

VOLVER A MEN

protected void SetVisible(bool V)


{
if (this.InvokeRequired)
this.Invoke(new SetVisibleDelegate(SetVisible), V);
else
this.Visible = V;
}
private delegate void SetParentDelegate(Control C);
protected void SetParent(Control C)
{
if (this.InvokeRequired)
this.Invoke(new SetParentDelegate(SetParent), C);
else
this.Parent = C;
}
private delegate void RemoveControlDelegate(Control C);
protected void RemoveControl(Control C)
{
if (this.InvokeRequired)
this.Invoke(new RemoveControlDelegate(RemoveControl), C);
else
C.Controls.Remove(this);
}
}
public class Desperdicio : ElementoGrafico
{
public enum TipoDesperdicio
{
Liviano, Pesado
}
709

VOLVER A MEN

private TipoDesperdicio Tipo;


private int Peso;
public Desperdicio(Control P, TipoDesperdicio T) : base(P)
{
Random R = new Random();
Tipo = T;
if (Tipo == TipoDesperdicio.Liviano)
{
Peso = 1 + R.Next(4);
this.Load(DesperdicioL.bmp);
}
else
{
Peso = 6 + R.Next(5);
this.Load(DesperdicioP.bmp);
}
}
public Desperdicio(Control P, TipoDesperdicio T, int Ps) : base(P)
{
Tipo = T;
if (Ps < 0) Ps = 1;
if (Tipo == TipoDesperdicio.Liviano)
{
if (Ps > 5) Ps = 5;
this.Load(DesperdicioL.bmp);
}
else
{
if (Ps > 10) Ps = 10;
this.Load(DesperdicioP.bmp);
710

VOLVER A MEN

}
Peso = Ps;
}
public void Recoger()
{
Control P = this.Parent;
SetVisible(false);
RemoveControl(P);
}
public int ObtPeso() { return Peso; }
public bool EsLiviano() { return Tipo == TipoDesperdicio.Liviano; }
}
public class Recolector : ElementoGrafico
{
public enum TipoRecolector
{
Liviano, Pesado
}
private enum Orientacion
{
Aba, Izq, Arr, Der
}
private enum Estados
{
Normal, EnRecarga, EnDescarga
}
private const int Dimension = 11;
private TipoRecolector Tipo;
711

VOLVER A MEN

private int Carga, Energia, Capacidad;


private Orientacion Ojos;
private Estados Estado;
private Random Rnd = new Random();
private Thread Agente;
private Ambiente ElAmbiente;
public Recolector(Ambiente A, Control P, TipoRecolector T) : base(P)
{
Tipo = T;
Carga = 0;
ElAmbiente = A;
if (Tipo == TipoRecolector.Liviano)
{
Energia = 50 + Rnd.Next(51);
Capacidad = 10;
}
else
{
Energia = 150 + Rnd.Next(51);
Capacidad = 30;
}
Estado = Estados.Normal;
// Se decide al azar la posicin inicial y
// localizacin inicial del recolector
//
switch (Tipo)
{
case TipoRecolector.Liviano:
switch (Rnd.Next(4))
{
case 0:
712

VOLVER A MEN

Ojos = Orientacion.Aba;
break;
case 1:
Ojos = Orientacion.Izq;
break;
case 2:
Ojos = Orientacion.Arr;
break;
case 3:
Ojos = Orientacion.Der;
break;
}
break;
case TipoRecolector.Pesado:
switch (Rnd.Next(4))
{
case 0:
Ojos = Orientacion.Aba;
break;
case 1:
Ojos = Orientacion.Izq;
break;
case 2:
Ojos = Orientacion.Arr;
break;
case 3:
Ojos = Orientacion.Der;
break;
}
break;
}
Refrescar();

713

VOLVER A MEN

// Hilo de ejecucin
//
Agente = new Thread(SecuenciaTeleoreactiva);
Agente.Start();
}
private void Refrescar()
{
string Img = ;
switch (Tipo)
{
case TipoRecolector.Liviano:
switch (Ojos)
{
case Orientacion.Aba:
Img = LivianoD.bmp;
break;
case Orientacion.Izq:
Img = LivianoL.bmp;
break;
case Orientacion.Arr:
Img = LivianoU.bmp;
break;
case Orientacion.Der:
Img = LivianoR.bmp;
break;
}
break;
case TipoRecolector.Pesado:
switch (Ojos)
{
case Orientacion.Aba:
714

VOLVER A MEN

Img = PesadoD.bmp;
break;
case Orientacion.Izq:
Img = PesadoL.bmp;
break;
case Orientacion.Arr:
Img = PesadoU.bmp;
break;
case Orientacion.Der:
Img = PesadoR.bmp;
break;
}
break;
}
if (Img != ) this.Load(Img);
}
private Point PuntoVisual(Point Pt)
{
switch (Ojos)
{
case Orientacion.Aba:
Pt.Y += LaDimension;
break;
case Orientacion.Izq:
Pt.X -= LaDimension;
break;
case Orientacion.Arr:
Pt.Y -= LaDimension;
break;
case Orientacion.Der:
Pt.X += LaDimension;
715

VOLVER A MEN

break;
}
return new Point(Pt.X, Pt.Y);
}
public bool Avanzar()
{
Point Pt = new Point(this.Location.X, this.Location.Y);
if (Energia <= 0) return false;
Pt = PuntoVisual(Pt);
if (!VerificarOcupacion(Pt)) return false;
SetLocation(Pt);
Energia--;
return true;
}
public void RotarIzquierda()
{
if (Energia <= 0) return;
switch (Ojos)
{
case Orientacion.Aba:
Ojos = Orientacion.Der;
break;
case Orientacion.Der:
Ojos = Orientacion.Arr;
break;
case Orientacion.Arr:
Ojos = Orientacion.Izq;
break;
case Orientacion.Izq:
Ojos = Orientacion.Aba;
716

VOLVER A MEN

break;
}
Energia--;
Refrescar();
}
public void RotarDerecha()
{
if (Energia <= 0) return;
switch (Ojos)
{
case Orientacion.Aba:
Ojos = Orientacion.Izq;
break;
case Orientacion.Izq:
Ojos = Orientacion.Arr;
break;
case Orientacion.Arr:
Ojos = Orientacion.Der;
break;
case Orientacion.Der:
Ojos = Orientacion.Aba;
break;
}
Energia--;
Refrescar();
}
public bool EsLiviano()
{
return Tipo == TipoRecolector.Liviano;
717

VOLVER A MEN

}
public void AtenderEnRecarga()
{
Estado = Estados.EnRecarga;
SetVisible(false);
}
public void AtenderEnDescarga()
{
Estado = Estados.EnDescarga;
SetVisible(false);
}
public void AtendidoEnCentro()
{
if (Estado == Estados.Normal) return;
if (Estado == Estados.EnRecarga)
Energia = Tipo == TipoRecolector.Liviano ? 100 : 200;
else
{
Carga = 0;
Energia -= Tipo == TipoRecolector.Liviano ? 2 : 4;
}
// Hay que ubicar nuevamente el recolector en el rea de simulacin
//
Control P = this.Parent;
Point Pt = new Point(0, Estado == Estados.EnRecarga ? 0 : P.Height - LaDimension);
while (!VerificarOcupacion(Pt)) Pt.X += LaDimension;
Ojos = (Estado == Estados.EnRecarga ? Orientacion.Aba : Orientacion.Arr);
Estado = Estados.Normal;
718

VOLVER A MEN

SetLocation(Pt);
SetVisible(true);
}
private Desperdicio BuscarDesperdicio(Point Pt)
{
Control P = this.Parent;
for (int i=0; i < P.Controls.Count; i++)
{
Control C = P.Controls[i];
if (C is Desperdicio && C.Location.X == Pt.X && C.Location.Y == Pt.Y)
return C as Desperdicio;
}
return null;
}
private bool VerificarRecogerDesperdicio()
{
// Primero se busca la posible presencia de un desperdicio en la
// lnea visual del recolector
//
Point Pt = new Point(this.Location.X, this.Location.Y);
Desperdicio D;
D = BuscarDesperdicio(PuntoVisual(Pt));
if (D == null) return false;
// Se tiene un desperdicio, se verifica si el recolector lo puede recoger
//
if (((EsLiviano() && D.EsLiviano()) || !EsLiviano()) && Carga + D.ObtPeso() <= Capacidad)
{
719

VOLVER A MEN

// Ver si hay energa suficiente


//
if ((D.EsLiviano() && Energia > 0) || Energia - 1 > 0)
{
// Se procede a la recoleccin del desperdicio
//
Carga += D.ObtPeso();
Energia -= D.EsLiviano() ? 1 : 2;
D.Recoger();
return true;
}
}
return false;
}
public void Parar()
{
Agente.Abort();
SetVisible(false);
}
private void SecuenciaTeleoreactiva()
{
bool Accion;
while (true)
{
// Si est siendo atendido en algn centro se duerme
//
Accion = false;
if (Estado != Estados.Normal) Accion = true;

720

VOLVER A MEN

Sem.WaitOne();
// Si se acab la energa el recolector se convierte en un desperdicio
// pesado de 10 unidades de peso
//
if (!Accion && Energia <= 0)
{
ElAmbiente.ConvertirRecolectorDesperdicio(this);
Sem.Release();
break;
}
// Lo primero es revisar la energa, de haber problemas hay que
// dirigirse al centro de recarga que est en la coordenada (0, 0)
//
if (!Accion && Energia <= 25)
{
if (this.Location.X == 0 && this.Location.Y == 0)
{
if (Ojos == Orientacion.Arr)
{
// Puede entrar al centro de recarga
//
ElAmbiente.RecargarEnergia(this);
}
else
{
if (Ojos == Orientacion.Der || Ojos == Orientacion.Aba)
RotarIzquierda();
else
RotarDerecha();
}
}
721

VOLVER A MEN

else
{
Point Pt = new Point(this.Location.X, this.Location.Y);
if (this.Location.X == 0 && Ojos == Orientacion.Izq)
{
RotarDerecha();
Accion = true;
}
if (!Accion && this.Location.Y == 0 && Ojos == Orientacion.Arr)
{
RotarIzquierda();
Accion = true;
}
if (!Accion && (Ojos == Orientacion.Aba || Ojos == Orientacion.Der))
{
RotarIzquierda();
Accion = true;
}
if (!Accion)
{
if (!Avanzar())
{
if (Rnd.NextDouble() < 0.5)
RotarIzquierda();
else
RotarDerecha();
Avanzar();
}
Accion = true;
}
}
Accion = true;
}
722

VOLVER A MEN

// Lo segundo es verificar la carga, de estar casi lleno hay que ir


// al centro de descarga que est en la coordenada (0, H)
//
if (!Accion && Capacidad - Carga <= 5)
{
if (Location.X == 0 && this.Parent.Height - (Location.Y + LaDimension) < LaDimension)
{
if (Ojos == Orientacion.Aba)
{
// Puede entrar al centro de recarga
//
ElAmbiente.DescargarDesperdicio(this);
}
else
{
if (Ojos == Orientacion.Der || Ojos == Orientacion.Arr)
RotarDerecha();
else
RotarIzquierda();
}
}
else
{
if (this.Location.X == 0 && Ojos == Orientacion.Izq)
{
RotarIzquierda();
Accion = true;
}
if (!Accion && this.Parent.Height - (Location.Y + LaDimension) < LaDimension &&
Ojos == Orientacion.Arr)
{
RotarDerecha();
Accion = true;
723

VOLVER A MEN

}
if (!Accion && (Ojos == Orientacion.Arr || Ojos == Orientacion.Der))
{
RotarDerecha();
Accion = true;
}
if (!Accion)
{
if (!Avanzar())
{
if (Rnd.NextDouble() < 0.5)
RotarDerecha();
else
RotarIzquierda();
Avanzar();
}
Accion = true;
}
}
Accion = true;
}
// Lo tercero es verificar si tiene un desperdicio en su foco visual
// para recogerlo
//
if (!Accion && VerificarRecogerDesperdicio()) Accion = true;
// Lo cuarto es decidir aleatoriamente si avanzar o rotar
//
if (!Accion)
{
switch (Rnd.Next(6))
724

VOLVER A MEN

{
case 0:
RotarIzquierda();
break;
case 1:
RotarDerecha();
break;
default:
if (!Avanzar())
{
if (Rnd.NextDouble() < 0.5)
RotarIzquierda();
else
RotarDerecha();
}
break;
}
}
// Se espera entre 250 y 1000 mseg
//
Sem.Release();
Thread.Sleep(250 + Rnd.Next(750));
}
}
}
}

725

VOLVER A MEN

/*
* Modelado y programacin Orientado a Objetos: Un enfoque prctico
* Desarrollo en Java; caso prctico Seccin 6.9
* Ambiente.cs
* Mauricio Paletta
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
namespace RecoleccionDesperdicios
{
public class Centro
{
private bool Ocupado;
private Queue<Recolector> ColaAtencion;
private Recolector EnAtencion;
private Timer TiempoAtencion;
private int UnidadesTiempo;
public Centro()
{
Ocupado = false;
EnAtencion = null;
ColaAtencion = new Queue<Recolector>();
UnidadesTiempo = 0;
TiempoAtencion = new Timer();
TiempoAtencion.Interval = 1000;
TiempoAtencion.Tick += TiempoAtencionTick;
TiempoAtencion.Enabled = true;
}

726

VOLVER A MEN

public void Atender(Recolector R, bool CargarEnergia)


{
// Permite llevar registro del tiempo de atencin
//
R.Tag = new object();
if (CargarEnergia)
R.Tag = R.EsLiviano() ? 2 : 4;
else
R.Tag = R.EsLiviano() ? 1 : 2;
if (Ocupado)
{
ColaAtencion.Enqueue(R);
return;
}
Ocupado = true;
EnAtencion = R;
UnidadesTiempo = 0;
}
private void TiempoAtencionTick(object sender, EventArgs e)
{
if (EnAtencion == null) return;
// Se verifica si se venci el tiempo de atencin
//
UnidadesTiempo++;
if (UnidadesTiempo < (int)EnAtencion.Tag) return;
// El recolector actual ha sido atendido
// Se verifica alguien ms en cola para su atencin
//
EnAtencion.AtendidoEnCentro();
Ocupado = false;
727

VOLVER A MEN

EnAtencion = null;
UnidadesTiempo = 0;
if (ColaAtencion.Count > 0)
{
EnAtencion = ColaAtencion.Dequeue();
Ocupado = true;
}
}
public bool EstaOcupado() { return Ocupado; }
public int CantidadColaAtencion() { return ColaAtencion.Count; }
}
public class Ambiente
{
private Centro RecargaEnergia, DescargaDesperdicio;
private List<Desperdicio> Desperdicios;
private List<Recolector> Recolectores;
private List<Recolector> ParaDesperdicios;
private Timer TiempoParaDesperdicios;
public Ambiente()
{
RecargaEnergia = new Centro();
DescargaDesperdicio = new Centro();
Desperdicios = new List<Desperdicio>();
Recolectores = new List<Recolector>();
ParaDesperdicios = new List<Recolector>();
TiempoParaDesperdicios = new Timer();
TiempoParaDesperdicios.Interval = 250;
TiempoParaDesperdicios.Enabled = true;
TiempoParaDesperdicios.Tick += TiempoParaDesperdiciosTick;
}
728

VOLVER A MEN

private void TiempoParaDesperdiciosTick(object sender, EventArgs e)


{
Desperdicio D;
Recolector R;
if (ParaDesperdicios.Count == 0) return;
R = ParaDesperdicios[0];
R.Visible = false;
D = new Desperdicio(R.Parent, Desperdicio.TipoDesperdicio.Pesado, 10);
D.Visible = false;
D.Location = new Point(R.Location.X, R.Location.Y);
D.Visible = true;
Desperdicios.Add(D);
Recolectores.Remove(R);
ParaDesperdicios.RemoveAt(0);
R.Parar();
}
public void Limpiar()
{
Desperdicios.Clear();
Recolectores.Clear();
}
public void AgregarDesperdicio(Control P, Desperdicio.TipoDesperdicio T)
{
Desperdicios.Add(new Desperdicio(P, T));
}
public void AgregarRecolector(Control P, Recolector.TipoRecolector T)
{
Recolectores.Add(new Recolector(this, P, T));
729

VOLVER A MEN

}
public void ConvertirRecolectorDesperdicio(Recolector R)
{
if (R == null) return;
ParaDesperdicios.Add(R);
}
public bool CentroRecargaOcupado()
{
return RecargaEnergia.EstaOcupado();
}
public bool CentroDescargaOcupado()
{
return DescargaDesperdicio.EstaOcupado();
}
public int NumRecolectores()
{
return Recolectores.Count;
}
public int NumDesperdicios()
{
return Desperdicios.Count;
}
public int NumRecolectoresColaRecarga()
{
return RecargaEnergia.CantidadColaAtencion();
}
public int NumRecolectoresColaDescarga()
{
return DescargaDesperdicio.CantidadColaAtencion();
730

VOLVER A MEN

}
public void RecargarEnergia(Recolector R)
{
R.AtenderEnRecarga();
RecargaEnergia.Atender(R, true);
}
public void DescargarDesperdicio(Recolector R)
{
R.AtenderEnDescarga();
DescargaDesperdicio.Atender(R, false);
}
public void Detener()
{
for (int i = 0; i < Recolectores.Count; i++)
Recolectores[i].Parar();
}
}
}

731

VOLVER A MEN

6.11 Ejercicios
1) Manejo de redes semnticas. Se desea que desarrolle una aplicacin que permita

administrar la informacin relativa a una Red Semntica (RS). Una RS es una forma de
representacin de conocimiento lingstico en la cual, las interrelaciones entre diversos
conceptos o elementos semnticos, se les da la forma de un grafo. Las RSs pueden ser
mapas conceptuales y mentales y estn formadas por un conjunto de elementos semnticos
o conceptos (nodos) y relaciones semnticas (aristas) entre esos conceptos. Por ejemplo, si
se tienen los conceptos {Vrtebra, Gato, Pelo, Mamfero, Animal, Oso, Pez, Agua, Ballena}
y las relaciones {tiene, es un, vive en} un ejemplo de RS con estos conjuntos es la que
se muestra en la Figura 6.70.

Figura 6.70. Ejemplo de una RS.

El problema a resolver debe permitir el siguiente conjunto de operaciones:


a) Agregar, suprimir o modificar un elemento semntico.
b) Agregar o suprimir una relacin semntica.
c) Guardar en un archivo (editable modo texto) la RS que se est administrando actualmente.
Se debe indicar el nombre del archivo.
d) Cargar de un archivo una RS. Se debe indicar el nombre del archivo. El archivo debe poderse
editar fuera del contexto de la aplicacin.
e) Listar todo lo que se sabe de un concepto segn la RS actual. Por ejemplo, para el concepto
Ballena de la Figura 6.70 se sabe lo siguiente: es un Mamfero y vive en Agua.
f) Indicar cul es el concepto con ms relaciones semnticas incidentes. En el ejemplo de la
Figura 6.70 es Mamfero con 3 incidencias.
g) Indicar cul es el concepto que ms relaciones semnticas tiene. En el ejemplo estn los
conceptos Gato, Oso, Mamfero, Pez y Ballena con dos relaciones cada uno.
h) Indicar si existen elementos semnticos de los cuales no se sabe nada. En el ejemplo, se
tienen los conceptos Vrtebra, Pelo, Agua y Animal.
732

VOLVER A MEN

i) Indicar si existen elementos semnticos sin estar relacionados semnticamente (nodos


aislados). No hay ninguno en el ejemplo de la Figura 6.70.
j) Mostrar la RS. Puede ser grficamente (recomendado) o en forma de lista de relaciones
semnticas. Por ejemplo: Gato es un Mamfero, Gato tiene Pelo, Oso tiene Pelo, etc.
2) Agentes en competencia. Basado en la misma teora presentada en el problema de la Seccin
6.10, relativo a la teora de agentes, se desea realizar un simulador de software de una
competencia de robots inspirada en una similar pero a nivel de hardware, que se realiza en
varias universidades a nivel mundial. En una superficie se distribuyen dos equipos diferentes
de robots y una cantidad determinada de pelotas blancas y negras. Un par de depsitos se
ubican a ambos lados del rea de juego, uno para cada equipo. El objetivo de la competencia
es localizar las pelotas y llevarlas al depsito correspondiente segn el equipo. Gana el equipo
que llega a acumular la mayor cantidad de puntos basado en las pelotas que se encuentran
en los depsitos correspondientes sabiendo que cada pelota blanca acumula 1 punto y cada
pelota negra 3 puntos. Se tienen las siguientes consideraciones:
a) La superficie de juego se puede representar mediante una matriz de celdas cada una de las
cuales puede albergar un robot una pelota. El tamao de esta matriz es variable y debe
ser establecido antes de iniciar la simulacin.
b) El nmero de pelotas blancas y negras para la simulacin debe ser establecido antes del
inicio de la misma. Siempre, el nmero de pelotas blancas debe superar al de pelotas negras
en una proporcin aproximada de 3 a 1 (30 blancas y 10 negras por ejemplo).
c) El nmero de robots por equipo es el mismo y puede variar entre un mnimo de 1 y un
mximo de 10 robots por equipo. Se debe indicar este valor antes de iniciar la simulacin.
d) La simulacin es controlada por un reloj de valores discretos cuyo intervalo debe ser
indicado previo a la simulacin. Por cada iteracin del reloj, hay un turno de juego para
cada uno de los robots, seleccionados de forma aleatoria y alternados uno por cada equipo.
Tambin al azar se decide qu equipo empieza. Otra alternativa es hacer que cada robot en
competencia sea un hilo diferente de ejecucin.
e) Los robots tienen orientacin (ojos) y su alcance visual es de mximo 3 celdas en lnea
recta.
f) Los nicos movimientos permitidos para un robot son avanzar una celda en su visual (lnea
recta) o hacer un giro de 90 grados tanto a la izquierda como a la derecha.
g) Slo cuando haya una pelota en la celda inmediata de la visual de un robot (lnea recta), el
robot toma posesin de la pelota y nadie se la puede quitar a menos que el robot la libere.
h) Una vez que una pelota es liberada en alguno de los depsitos, la misma queda fuera del
juego por lo que no puede ser tomada nuevamente por ningn robot.
733

VOLVER A MEN

i) Los robots tienen una fuente de energa limitada que viene dada inicialmente por una
cantidad aleatoria entre 50 y 100 unidades de energa. El robot gasta una unidad de energa
por cada movimiento que realiza, ya sea de desplazamiento o de posesin / liberacin de
pelotas. Un robot que llega a una fuente de energa baja (5 unidades de energa), cambia
su estado al de energa baja y queda condenado a permanecer esttico en su celda actual
hasta que sea recogido por un robot recolector. En el caso de que un robot que est en este
estado tenga posesin de alguna pelota, deber liberarla cuando pueda, en alguna de las
celdas vecinas que queden libres, haciendo uso de lo poco que le queda de energa.
j) Un robot recolector es un tipo especial de robot cuyo objetivo es limpiar el campo de robots
en estado de baja energa y sin posesin de pelotas. Tienen energa ilimitada y capacidad
de visin y desplazamiento de una celda de alcance en toda su vecindad de Von Neumann
(arriba, abajo, a la derecha y a la izquierda). Tienen una capacidad para albergar y transportar
hasta tres robots. El nmero de robots recolectores es variable para la simulacin y debe
estar entre un mnimo de 1 y un mximo de 4. Al igual que el resto de los robots, tienen
su oportunidad de accin en cada iteracin de tiempo de la simulacin, slo despus de
que todos los robots jugadores de ambos equipos hayan culminado su actividad por cada
iteracin ( a menos que se escoja el camino de implementacin por hilos de ejecucin).
k) Una vez que un robot recolector alcanza su cota mxima de carga, debe trasladarse a
uno de los centros de descarga que se encuentran en la parte central, a ambos lados
(superior e inferior) del rea de la simulacin. Se requiere 1 unidad de tiempo por cada
robot a descargar, es decir, 3 unidades de tiempo en total para liberarse completamente de
la carga. Cada centro de descarga puede albergar slo a un robot recolector y no admite
robots recolectores que no tengan un mximo de carga acumulada.
l) La posicin inicial de las pelotas y los robots se asignar de forma aleatoria previo al inicio
de la simulacin.
m) Los depsitos de pelotas de los equipos tienen un tamao equivalente a tres celdas colocadas
en lnea recta. Los centros de descarga tienen un tamao equivalente a una celda.
n) La competencia termina cuando se alcanza el tiempo mximo de iteraciones indicado previo
al inicio de la simulacin, se hayan recolectado y ubicado en los depsitos todas las pelotas
en juego o, no queden robots para ninguno de los equipos.

734

VOLVER A MEN

La Figura 6.71 muestra un ejemplo con un rea de juego en una matriz de 9 x 15 celdas.

3) Sistema de transporte urbano individual. La motivacin principal de este ejercicio est

inspirado en un sistema de transporte urbano basado en el uso de bicicletas, que me toc


personalmente utilizar durante me estada en la Ciudad de Mxico, justo en el momento
en la cual se estaba realizando este libro. Se tiene una cantidad especfica de bicicletas
cada una de las cuales viene identificada por un nmero. Se tiene tambin una cantidad
especfica de estaciones de bicicleta cada una de las cuales se identifica con un nmero,
est en una ubicacin fsica determinada (direccin) y tiene capacidad para un nmero
determinado de bicicletas (por ejemplo 18, 24, 36).
Los usuarios del servicio se inscriben con solo llenar un registro con sus datos personales
(nombre, direccin, telfono y correo electrnico) y pagan una cuota anual afiliando el
cobro a una tarjeta de crdito. La inscripcin les da derecho a poseer una tarjeta con la
cual podrn tener acceso al servicio segn se explica a continuacin. Todas las tarjetas
vienen identificadas con un nmero y tienen una asociacin 1 a 1 con cada usuario del
servicio. Es decir, un mismo usuario no puede tener ms de una tarjeta y cualquier actividad
realizada con una tarjeta estar asociada al usuario que se inscribi previamente y que en
consecuencia haya recibido la tarjeta.
Las bicicletas estn dotadas con un mecanismo de anclaje a la estacin de manera tal que
ninguna bicicleta puede ser liberada a menos que el mecanismo de anclaje lo permita. Para
liberar el mecanismo de anclaje de una bicicleta, una tarjeta vlida debe ser presentada en
el lector que se encuentra en cada una de las estaciones de bicicleta. El procedimiento es
como sigue:
a) Un usuario llega a una estacin y presenta su tarjeta en el lector correspondiente. Un
programa interno del hardware que maneja la estacin ejecuta un algoritmo de bsqueda
aleatoria y, basado en las bicicletas actualmente disponibles y habilitadas, le presenta al
usuario el nmero de posicin en la estacin donde se encuantra la bicicleta que debe
retirar.
735

VOLVER A MEN

b) El mecanismo de anclaje de esa bicicleta especfica en la estacin es liberado (ninguna


otra bicicleta se puede liberar en ese mismo momento) y un pequeo foco que se
encuentra en cada puesto de bicicleta inicia una intermitencia haciendo uso del color
verde en su estado de encendido, a fin de que el usuario tenga mayor facilidad de ubicar
el sitio concreto. Por defecto ese mismo foco antes mencionado se encuentra en un
estado de encendido fijo haciendo uso del color rojo, cuando hay una bicicleta anclada
en el puesto verde cuando hay un espacio libre para estacionar o anclar cualquier otra
bicicleta.
c) El usuario tiene unos 30 segundos para dirigirse desde la posicin del lector hasta el
puesto donde se encuentra la bicicleta asignada. Durante ese tiempo la bicicleta puede
ser tomada libremente. Si vencido ese tiempo la bicicleta sigue en el mismo sitio, ya sea
porque nunca fue tomada o bien porque fue tomada y vuelta a poner nuevamente (lo cual
puede ocurrir si el usuario no desea esa bicicleta por algn motivo, como por ejemplo
que haya notado que hay una llanta espinchada), entonces se cancela la solicitud inicial.
El usuario puede hacer otra solicitud si as lo desea.
d) Una vez que un usuario tenga asignado el uso de una bicicleta, ninguna otra bicicleta
puede ser liberada en ninguna estacin. Un mensaje indicando que ya se tiene una
bicicleta actualmente en uso aparecer en la pantalla del controlador de cada estacin.
e) El usuario tiene hasta un mximo de 45 minutos para usar la bicicleta antes de volverla
a estacionar en cualquier estacin. Si pasado ese tiempo la bicicleta sigue fuera de
cualquier estacin, entonces se cobrar una penalidad por cada minuto adicional que se
exceda del tiempo mximo permitido.
f) Las bicicletas pueden ser estacionadas en cualquier espacio libre (indicado con el foco
en color verde) de cualquiera de las estaciones disponibles. Una vez que se ha entregado
una bicicleta, el usuario tiene que esperar 5 minutos antes de poder recoger de nuevo
otra bicicleta. Si durante ese tiempo el usuario solicita se le entregue una bicicleta, un
mensaje indicando que la bicicleta fue entregada correctamente y deber esperar 5
minutos para retirar otra, aparacer en la pantalla del controlador de la estacin.
Por otro lado, si un usuario observa que hay alguna bicicleta daada que debe ser
reparada, ste puede llamar al centro de control maestro del servicio a fin de reportar la
bicicleta daada (basta con dar el nmero correspondiente) e inmediatamente la bicicleta
es inhabilitada de manera tal que no pueda ms ser ofrecida a ningn usuario hasta tanto
no se haga el mantenimiento correspondiente y se regrese nuevamente a su estado de
habilitacin.
Para la resolucin de este problema se debe considerar la satisfaccin de los siguientes
requerimientos:
736

VOLVER A MEN

a) Agregar / suprimir bicicletas y estaciones de bicicletas. Hay que tomar en cuenta que
no todas las bicicletas deben estar necesariamente ubicadas en una estacin. Pueden
estar tambin en un depsito de almacenamiento y/o en mntenimiento.
b) Dar el servicio de alta / baja a los usuarios. Las renovaciones del servicio se realizan de
manera automtica siempre y cuando sea posible hacer el cobro de la anualidad en la
tarjeta de crdito inicialmente dada por el usuario en el momento de su inscripcin. Los
usuarios pueden darse de baja en el servicio en cualquier momento.
c) Prestar el servicio segn lo explicado anteriormente.
d) Calcular los montos que cada usuario debe pagar mensualmente con base a las
penalidades que se tengan por romper las reglas del servicio.
4) Sistema metro. La Unidad de Planificacin Urbana de la Comunidad Econmica Europea
(UPUCEE) ha decidido instalar sistemas automatizados que permitan a los usuarios del
metro de una ciudad, conocer la forma ms rpida de llegar de una estacin a otra. El
plan es instalar en cada estacin un terminal corriendo un programa que permita ingresar
la estacin de destino y suministre como salida las indicaciones necesarias para que el
usuario pueda llegar rpida y felizmente a sus destino.
La UPUCEE ha solicitado sus servicios como analista para el diseo y elaboracin del
programa que debe correr en cada estacin. Dicho programa debe ser lo ms general
posible para que pueda ser adaptado sin dificultad a las configuraciones particulares del
sistema de metro de cualquier ciudad.
La UPUCEE ha suministrado el siguiente extracto de un informe, resumiendo las
caractersticas ms importantes de los sistemas de metro de los pases de la CEE:
El metro es un sistema de transporte urbano diseado especialmente para proveer un
servicio confiable y eficaz para la movilizacin masiva de personas. El metro est constituido
bsicamente por vas y estaciones dedicadas, que permiten el desplazamiento de trenes
a una velocidad constante, lo cual hace que el tiempo de viaje entre estaciones vare muy
poco. Una lnea es un conjunto de vas de estaciones que son recorridas nicamente
por determinados trenes. Para viajar entre dos estaciones que pertenecen a una misma
lnea, no es necesario cambiar de tren. En caso de que se quiera viajar entre estaciones
de dos lneas diferentes, ser necesario hacer un cambio de tren en alguna estacin de
transferencia. En ocasiones es posible que se deba hacer ms de una transferencia entre
lneas para alcanzar el destino.
Para ilustrar los conceptos, considere la Figura 6.72. En este caso el sistema de metro
tiene 23 estaciones, cuatro lneas A, B, C y D (ntese que la lnea D no para en todas
las estaciones de la lnea C). Cualquier estacin por la que pase ms de una lnea es
una estacin de transferencia (T1, T2 y T3 en la Figura 6.72). Por ejemplo, para ir de la
737

VOLVER A MEN

estacin x a la estacin y se puede tomar el tren de la lnea B en sentido B1 y hacer una


transferencia a la lnea C o D en sentido C2 y D2 respectivamente. Una lnea siempre tiene
exactamente dos extremos; el sentido de movimiento de un tren se indica a travs del
nombre de la estacin terminal de la lnea hacia la que se dirige el tren. Por ejemplo, los
extremos de la lnea 1 del metro de Caracas son Propatria y Palo Verde. En este sentido,
cuando se va a tomar este metro hay que fijarse si se va a tomar en direccin Propatria o
en direccin Palo Verde.
El programa a desarrollar deber ser capaz de satisfacer los siguientes requerimientos:
a) Cargar de un archivo la siguiente informacin relativa al sistema de metros:
a. Nmero de lneas e identificacin de ellas. En algunos pases se suelen usar nmeros,
letras, colores, etc.
b. Nombre de todas las estaciones que recorre cada lnea indicadas en orden desde uno
de los extremos hasta el otro.
c. Tiempo estimado de recorrido entre cada par de estaciones.
d. Cantidad de trfico de personas que tiene cada estacin medido en muy alto (entre 85
y 100%), alto (entre 60 y 85 %), medio (entre 40 y 60%), bajo (entre 20 y 40%) y muy
bajo (entre 0 y 20%), sobre los siguientes intervalos del da: desde la hora de apertura
del metro a 7 am, de 7 am a 9 am, de 9 am a 11 am, de 11 am a 2 pm, de 2 pm a 5
pm, de 5 pm a 9 pm y de 9 pm hasta la hora de cierre del metro.
b) Una vez que el programa ha cargado la informacin de un sistema de metro especfico,
cualquier usuario puede hacer uso del programa para consultar la ruta que hay desde
una estacin cualquiera a otra. El usuario puede decidir si quiere reducir el nmero de
estaciones, reducir el tiempo de viaje o, segn una hora del da en la cual se haga la
consulta, minimizar la cantidad de trfico de personas que hay en el recorrido basado
en el trfico de las estaciones. En este caso, el programa dar como salida un reporte
detallado del recorrido indicando:
a. Lneas y sentido a tomar.
b. Para cada una de las lneas, cada una de las estaciones que hay que pasar.
c. Indicar las estaciones de transferencia y el cambio de lnea.
Por ejemplo, supongamos que un usuario desea usar el programa en la estacin x a
cualquier hora del da y desea saber una ruta para dirigirse a z. Una salida del programa
pudiera ser:
738

VOLVER A MEN

Salida: estacin x, lnea A, direccin A1

Recorrido: x, x1, T1, x2, x3, x4, T2

Transferencia: lnea A lnea C en estacin T2

Salida: estacin T2, lnea C direccin C2

Recorrido: T2, z1, z

Tiempo estimado de recorrido: 65 min.

Total estaciones en el recorrido: 8

Estimado de trfico de personas segn hora actual: medio

Usted decidi reducir el nmero de estaciones en el recorrido.

Figura 6.72. Ejemplo de un sistema de lneas de metro.

5) Grafo de flujo. Se define a continuacin un lenguaje intermedio para escribir cierto tipo de
programas. Un programa en este contexto consiste de una secuencia de instrucciones
cada una de las cuales puede estar etiquetada por un identificador. Existen cinco tipos
bsicos de instrucciones: asignacin, salto, condicional, entrada-salida y parar:
a) Una asignacin es una cadena de caracteres de la forma A f B1Br donde A es una
variable y B1, , Br son variables o constantes. es una operacin r-aria (una instruccin
de la forma A B cae dentro de esta categora).
b) Un salto es una instruccin de la forma SALTO <etiqueta> donde <etiqueta> es una
cadena de caracteres alfabticos. Si se usa la instruccin SALTO en un programa
entonces la etiqueta que precede a la palabra SALTO debe aparecer como etiqueta en
una instruccin en el programa.
c) Un condicional es de la forma SI A <relacin> B SALTO <etiqueta> donde A y B son
variables o constantes y <relacin> es una operacin relacional vlida del tipo: <=, =,
<>, >=, < y >.
d) Una instruccin entrada-salida es una instruccin de lectura de la forma LEER A donde
A es una variable o una instruccin de escritura, o una instruccin de escritura ESCRIBIR
B donde B es una variable o una constante.
e) PARAR es una instruccin que termina la ejecucin del programa.
739

VOLVER A MEN

f) El significado intuitivo de cada instruccin es evidente. Por ejemplo, una instruccin


condicional de la forma SI A > B SALTO L significa que si la relacin > se satisface
para los valores actuales de A y B entonces el control del programa debe ser transferido
a la instruccin etiquetada con L. Sino, el control pasa a la siguiente instruccin.
Las etiquetas no se repetirn en el programa y todas las que aparecen en un programa
sern usadas al menos en un salto.
Una de las operaciones realizadas en la compilacin de un programa es la optimizacin de
cdigo. El primer paso en la optimizacin de un programa es determinar el flujo de control
dentro del programa. Para ello se debe dividir el programa en grupos de instrucciones o
bloques tales que no exista ninguna transferencia de control dentro del grupo excepto en
la primera instruccin y una vez que la primera instruccin es ejecutada, el resto de las
instrucciones del bloque son ejecutadas secuencialmente.
Una instruccin S en un programa P es una entrada a un bloque si:
S es la primera instruccin en P,
S est etiquetada con un identificador, o
S es una instruccin que sigue inmediatamente a una instruccin condicional.
El bloque perteneciente a la entrada de bloque S consiste de S y todas las instrucciones
siguientes a S:
g) Hasta una instruccin PARAR (inclusive), o
Hasta la prxima entrada de bloque (sin incluir).
Por ejemplo, considere el siguiente programa en el que existen cuatro entradas de bloque:
la primera instruccin, la etiquetada con lazo, la asignacin p q y la etiquetada por
listo. El diagrama de flujo correspondiente se puede observar en la Figura 6.73.

BLOQUE 1

LEER p


LEER q

BLOQUE 2 lazo: r Resto(p, q)


SI r = 0 SALTO listo

BLOQUE 3

pq

qr


SALTO lazo

BLOQUE 4 listo: ESCRIBIR q


PARAR
740

VOLVER A MEN

De los bloques de un programa podemos construir un grafo que representa el diagrama de


flujo del programa. Un grafo de flujo es un grafo dirigido G que contiene un nodo c tal que,
todo nodo G es alcanzable desde c, es decir, para todo nodo v en G existe un camino de c
a v. El nodo c se denomina nodo comienzo.
El grafo de flujo de un programa es un grafo de flujo en el cual cada nodo del grafo
corresponde con un bloque del programa. Sean i y j dos bloques del programa, entonces
existe un arco o enlace i j si y slo si:
La ltima instruccin en el bloque i no es una instruccin SALTO o PARAR y el bloque j
es el siguiente al bloque i en el programa, o
La ltima instruccin en el bloque i es una instruccin SALTO L o SI SALTO L y L
es la etiqueta de la primera instruccin del bloque j.

Figura 6.73. Ejemplo de un grafo de flujo de un programa.

Para realizar transformaciones de optimizacin sobre programas es conveniente conocer,


dado un bloque B, si existe otro bloque B tal que cada vez que B es ejecutado, B fue
ejecutado previamente. Una aplicacin de este resultado es que si se calcula el mismo
valor en B y B, entonces podemos almacenar este valor despus de calcularlo en B y
evitar de esta manera recalcularlo en B.
Sea F un grafo de flujo cuyos bloques se denotan Bi. Una secuencia B1, B2, , Bn es un
camino de ejecucin de F si:
B1 es el nodo comienzo de F (es el que contiene la primera instruccin del programa).
Para 1 < i n, existe un arco del bloque Bi-1 al Bi.
En otras palabras, B1, B2, , Bn es un camino de B1 a Bn en F tal que B1 es el nodo comienzo.
Se dice que el bloque B domina a B si B es diferente de B y todo camino de ejecucin a
B (es decir, del nodo comienzo a B) contiene a B. Se dice que B es dominador directo de
B si:
B domina a B.
Si B domina a B y B es diferente a B entonces B domina a B.

741

VOLVER A MEN

Es decir, B domina directamente a B si B es el bloque mas cercano que domina a B. La


relacin domina a cumple las siguientes propiedades algebraicas:
Es transitiva.
Es antisimtrica.
Si B1 domina a B3 y B2 domina a B3 entonces B1 domina a B2 o B2 domina a B1.
Se desea hacer un programa que permita satisfacer los siguientes requerimientos:
a) Leer de un archivo texto cuyo nombre es dado, un programa escrito en el lenguaje
intermedio y construir en memoria el grafo de flujo del programa. Se asume que el
programa est sintctica y semnticamente bien escrito. Slo se puede procesar un
programa a la vez, por lo que si se repite esta operacin con otro archivo (programa,
la memoria ocupada por el grafo de flujo del primer programa debe ser liberada y se
procede a construir y cargar en memoria el grafo de flujo del otro programa.
b) Guardar el diagrama de flujo del programa actualmente almacenado en memoria (si lo
hubiere), en un archivo binario cuyo nombre debe ser dado.
c) Cargar el diagrama de flujo de un programa, almacenado en un archivo binario cuyo
nombre es dado.
d) Mostrar en pantalla el diagrama de flujo actualmente almacenado en memoria haciendo
referencia a los identificadores de los bloques (1, 2, etc.) y no a los contenidos. Hay
muchas formas y formatos para mostrar esta informacin. Se sugiere una representacin
matricial para ello.
e) Dado el identificador de un bloque vlido del programa actualmente almacenado en
memoria, mostrar el contenido correspondiente del bloque (segn la sintaxis del lenguaje
intermedio).
f) Para cada bloque del programa actualmente cargado en memoria, calcular y mostrar su
dominador directo y el conjunto de bloques que lo dominan.

742

VOLVER A MEN

Referencias

y Bibliografa sugerida

Aho, A.V., Hopcroft, J.E. y Ullman, J.D. (1983). Data Structures & Algorithms. AddisonWesley, ISBN: 0201000237.
Atwood, T. (1985). An Object-Oriented DBMS for Design Support Applications. Proceedings of
the IEEE COMPINT 85, pp. 299-307.
Booch, G.(1993).Object-oriented Analysis and Design with Applications(2nd ed.). Redwood
City: Benjamin Cummings.ISBN:0-8053-5340-2.
Booch, G. (1996). Anlisis y Diseo Orientado a Objetos. Addison Wesley Longman 2da.
Edicin, ISBN: 978-9684443525.
Brassard, G. y Bratley, P. (1997). Fundamentos de Algoritmia. Prentice Hall, ISBN: 978-8489660-00-7.
Brooks, F. (1987). No Silver Bullet: Essence and Accidents of Software Engineering. IEEE
Computer, Vol. 20, Nro. 4, p. 1.
Burbeck, S. (1987). Applications Programming in Smalltalk-80(TM): How to use Model-ViewController (MVC). Disponible en http://st-www.cs.illinois.edu/users/ smarch/st-docs/mvc.html
(ltima vez consultado en junio de 2012).
Cockshott, P.,McCaskill, G.yBarrie, P. (1993). Realising massively concurrent systems on
the SPACE machine. En FPGAs for Custom Computing Machines, IEEE Workshop, pp. 26-32.
Cohoon, J. y Davidson, J. (2005). Programacin en Java 5.0. McGraw-Hill, ISBN: 84-4815061-9.
Corner, D.S. (2008). Deadlock Detection Using DTrace. CS 671 Project I, OS Observability
Tools. Disponible en http://cs.gmu.edu/~hfoxwell/ cs671projects/corner_DT.pdf (ltima vez
consultado en agosto de 2012).
Derrett, N., Kent, W. y Lyngbaek, P. (1985). Some Aspects of Operations in an Object-Oriented
Database.Database Engineering, Vol. 8, Nro. 4, IEEE Computer Society.
Dijkstra, E. (1979). Programming Considered as a Human activity. Classics in Software
Engineering, New York.
743

VOLVER A MEN

Ferguson, J., Patterson, B., Beres, J., Boutquin, P. y Gupta, M. (2003). La biblia de C#.
Anaya Multimedia. ISBN: 84-415-1484-4.
Goldberg, A., y Robson, D. (1983). Smalltalk-80: The Language and its Implementation.
Addison-Wesley, Readig, Massachusetts.
Goodrich, M.T. y Tammassia R. (2006). Data Structures & Algorithms in Java. John Wiley &
Sons, Inc., ISBN: 0-471-73884-0.
Grahan, I. (1996). Mtodos Orientados a Objetos. Addison-Wesley, ISBN: 0-201-65355-9.
Holt, R.C. (1972). Some Deadlock Properties of Computer Systems. Computing Surveys, Vol.
4, Nro. 3, pp. 179-196.
Jacobson, I. (1992). Object-Oriented Software Engineering: A Use Case Driven Approach.
Addison-Wesley, ISBN: 0-201-54435-0.
Koffman, E.B. y Wolfgang, P.A. (2008). Estructura de datos con C++. Objetos, abstracciones
y diseo. McGraw Hill. ISBN: 0-471-46755-3.
Lafore, R. (2003). Data Structures & Algorithms in Java. SAMS, ISBN: 0-672-32453-9.
Lewis, J. y Chase, J. (2006). Estructuras de datos con Java Diseo de estructuras y
algoritmos. Addison-Wesley, ISBN: 84-205-5034-5.
Maier, D., Otis, A. y Purdy, A. (1985). Object-Oriented Database Development at Servio
Logic.Database Engineering, Vol. 18, Nro.4, IEEE Computer Society.
Meyer,

B.

(1998).

Costruccin

de

Software

Orientado

Objetos.

Prentice-Hall,

ISBN:9788483220405.
Nilsson, N.J. (1994). Teleo-Reactive Programs for Agent Control, Journal of Artificial Intelligence
Research, 1 pp. 139158.
Oestereich, B. (2001). Developing Software with UML Object-Oriented Analysis and Design
in Practice. Addison-Wesley, ISBN: 0-201-75603-X.
Rumbaugh, J., Blaha, M., Premerlani, W., Eddy, F., y Lorensen, W. (1990).Object-Oriented
Modeling and Design. Prentice Hall.ISBN 0-13-629841-9.
Rumbaugh, J., Jacobson, I. y Booch, G. (1999). The Unified Modeling Language Reference
Manual. Addison-Wesley, ISBN: 0-201-30998-X.
Russell, N. (1995). Artificial Intelligence: A Modern Approach, Prentice Hall.
Seco, A.A., Gallego, J.J., Lafuente, E.E., Guzmn, J.G., Snchez, L.G., Fernndez, P.M.
744

VOLVER A MEN

y Segura M.I. (2003). Anlisis y Diseo estructurado y Orientado a Objetos de Sistemas


Informticos. McGraw Hill. ISBN: 84-481-3924-0.
Shaffer, C.A. (1997). A Practical Introduction to Data Structures and Algorithm Analysis.
Prentice Hall. ISBN: 0-13-190752-2.
Stroustrup,B. (1998). El lenguaje de programacin C++.Addison Wesley, ISBN: 84-7829019-2.
von Neumann, J. (1966). The Theory of Self-reproducing Automata, Ed. University of Illinois
Press, ISBN: 0598377980.

745

VOLVER A MEN

ndice de Figuras
Figura 1.1. Hacer descomposicin, abstraccin y jerarqua para salir del caos
(imagen realizada por Juan C. Candelori / www.pietrogiordano.info).

13

Figura 1.2. Resolver un problema con sub-problemas ya resueltos versus


resolver el mismo problema como un todo (imagen realizada por Juan C. Candelori /
www.pietrogiordano.info). 15
Figura 1.3. Ejemplo de descomposicin funcional versus OO.

20

Figura 1.4. Evolucin de los lenguajes de programacin.

23

Figura 1.5. Evolucin de los lenguajes de modelado.

24

Figura 2.1. Enlace esttico (izquierda) versus enlace dinmico (derecha).

38

Figura 2.2. Vista genrica de un programa OO.

41

Figura 2.3. El encapsulamiento y los mtodos tpicos Set / Get.

42

Figura 2.4. Ejemplo de diagrama de clases escrito en UML.

45

Figura 2.5. Ejemplo de diagrama de clases escrito en UML.

46

Figura 2.6. Ejemplo de diagrama de clases escrito en UML.

49

Figura 2.7. Ejemplo de diagrama de clases escrito en UML.

49

Figura 3.1. Smbolo para identificar un actor.

57

Figura 3.2. Smbolo para identificar un caso de uso.

58

Figura 3.3. Ejemplo de una generalizacin entre actores.

58

Figura 3.4. Ejemplo de una generalizacin entre casos de uso.

59

Figura 3.5. Ejemplo de una relacin de extensin entre casos de uso.

59

Figura 3.6. Ejemplo de una relacin de inclusin entre casos de uso.

60

Figura 3.7. Ejemplo de relaciones entre actores y casos de uso.

60

Figura 3.8. Ejemplo de un diagrama de casos de uso relacionado con


el problema de una mquina expendedora de refrescos.

61

Figura 3.9. Ejemplo de un diagrama de casos de uso relacionado con


un problema de gestin de ventas.

62

Figura 3.10. Elementos sintcticos de un diagrama de actividad.

63

Figura 3.11. Ejemplo de un diagrama de actividad relacionado con un

746

problema de gestin de ventas.

64

Figura 3.12. Ejemplo de una generalizacin de clases.

65

VOLVER A MEN

Figura 3.13. Ejemplo de la relacin de dependencia con el estereotipo <<friend>>.

66

Figura 3.14. Ejemplo de la relacin de dependencia Metaclase.

66

Figura 3.15. Ejemplo de una relacin de refinamiento.

66

Figura 3.16. Ejemplos de asociaciones normales.

67

Figura 3.17. Ejemplos de asociaciones recursivas.

68

Figura 3.18. Ejemplos de asociaciones cualificadas.

68

Figura 3.19. Ejemplo de una asociacin disyuntiva.

69

Figura 3.20. Ejemplo de una asociacin con restriccin.

69

Figura 3.21. Ejemplo de una asociacin ternaria.

69

Figura 3.22. Ejemplo de asociaciones binarias equivalente a la Figura 3.21,


en lugar de hacer una asociacin ternaria.

70

Figura 3.23. Ejemplos de asociaciones de agregacin y composicin.

70

Figura 3.24. Ejemplo de una asociacin del tipo interfaz.

71

Figura 3.25. Ejemplos del uso de multiplicidades.

71

Figura 3.26. Smbolo usado para representar una clase parametrizada.

72

Figura 3.27. Smbolo usado para representar una clase de utilidad.

72

Figura 3.28. Ejemplo de uso de paquetes.

73

Figura 3.29. Ejemplo de un diagrama de clases relacionado con el


problema de controles grficos.

74

Figura 3.30. Ejemplo de un diagrama de clases.

75

Figura 3.31. Smbolos usados en el diagrama de cambio de estados.

76

Figura 3.32. Ejemplo de la clase detallada Vlvula y el diagrama


de estados correspondiente.

77

Figura 3.33. Ejemplo de un diagrama de componentes.

78

Figura 3.34. Ejemplo de un diagrama de colaboracin.

79

Figura 3.35. Smbolos usados para representar un diagrama de secuencia.

80

Figura 3.36. Ejemplo del diagrama de secuencia equivalente al diagrama


de colaboracin de la Figura 3.34.

80

Figura 3.37. Smbolos usados para representar un diagrama de distribucin.

81

Figura 3.38. Ejemplo de un diagrama de distribucin con el detalle de los componentes.

81

Figura 3.39. Notacin de las clases: control, lmite y entidad.

83

Figura 3.40. Ejemplo de un diagrama de clases expresado en la notacin

747

control, lmite y entidad.

83

Figura 4.1. Ejemplo de la definicin de una clase en C++.

93

VOLVER A MEN

Figura 4.2. Otra manera de escribir el ejemplo mostrado en la Figura 4.1.

94

Figura 4.3. Ejemplo del uso de la declarativa inline.

94

Figura 4.4. Ejemplo de la definicin de una clase en Java / C#.

95

Figura 4.5. Ejemplo de la definicin de atributos en C#.

96

Figura 4.6. Ejemplo de la instanciacin de objetos en C++.

97

Figura 4.7. Ejemplo de la instanciacin de objetos en Java.

97

Figura 4.8. Ejemplo del acceso de los elementos de una clase en C++.

98

Figura 4.9. Ejemplo del acceso de los elementos de una clase en Java / C#.

99

Figura 4.10. Ejemplo del acceso de un atributo de una clase en C#.

99

Figura 4.11. Ejemplo de la definicin de un constructor en C++.

100

Figura 4.12. Ejemplo de la definicin de un constructor en Java / C#.

100

Figura 4.13. Ejemplo del uso de polimorfismo en C++.

102

Figura 4.14. Ejemplo del uso de polimorfismo en Java / C#.

102

Figura 4.15. Ejemplo del uso de inicializacin por defecto en C++.

103

Figura 4.16. Ejemplo del uso de inicializacin por defecto en C++


cuando el mtodo es outline.

104

Figura 4.17. Ejemplo de la inicializacin de atributos en Java / C#.

104

Figura 4.18. Ejemplo del uso del destructor en C++.

105

Figura 4.19. Ejemplo del uso del mtodo de finalizacin en Java.

106

Figura 4.20. Ejemplo del uso del mtodo de finalizacin en C#.

106

Figura 4.21. Ejemplo del uso de elementos estticos en C++.

107

Figura 4.22. Complemento del ejemplo presentado en la Figura 4.21.

108

Figura 4.23. Ejemplo de uso de elementos estticos en Java.

109

Figura 4.24. Ejemplo de uso de elementos estticos en C#.

109

Figura 4.25. Ejemplo de uso del operador de asignacin o copia.

111

Figura 4.26. Ejemplo de uso del operador de asignacin o copia en C++


cuando hay direcciones de memoria involucradas.

111

Figura 4.27. Ejemplo de la sobrecarga del operador + en C++.

113

Figura 4.28. Ejemplo de la sobrecarga del operador unario de


conversin explcita double en C++.

114

Figura 4.29. Ejemplo de la implementacin de una suma en Java.

115

Figura 4.30. Ejemplo de la sobrecarga del operador + en C#.

116

Figura 4.31. Ejemplo de la sobrecarga de los operadores de conversin


implcita y explcita en C#.
748

VOLVER A MEN

116

Figura 4.32. Ejemplo de la sobrecarga del operador ++ en C++


y el uso de this para hacer el retorno.

117

Figura 4.33. Ejemplo del uso de this en Java.

118

Figura 4.34. Ejemplo del uso de this en C#.

118

Figura 4.35. Ejemplo del uso de herencia en C++.

119

Figura 4.36. Herencia con interfaz privada en C++.

119

Figura 4.37. Herencia con interfaz protegida en C++.

120

Figura 4.38. Herencia con interfaz pblica en C++.

120

Figura 4.39. Ejemplo de herencia mltiple en C++.

121

Figura 4.40. Ejemplo de cmo resolver ambigedades en una herencia mltiple en C++.

121

Figura 4.41. Ejemplo de cmo resolver redundancias en una herencia mltiple en C++.

122

Figura 4.42. Ejemplo de uso de la herencia en Java.

123

Figura 4.43. Ejemplo de cmo resolver ambigedades en una herencia en Java.

123

Figura 4.44. Ejemplo de uso de interfaces en Java.

124

Figura 4.45. Ejemplo del uso combinado de la herencia y la implementacin


de interfaces en Java.

124

Figura 4.46. Ejemplo de uso de la herencia en C#.

125

Figura 4.47. Ejemplo de cmo resolver ambigedades en una herencia en C#.

125

Figura 4.48. Ejemplo de uso de interfaces en C#.

126

Figura 4.49. Ejemplo del uso combinado de la herencia y la implementacin


de interfaces en C#.

126

Figura 4.50. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en C++.

127

Figura 4.51. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en Java.

127

Figura 4.52. Ejemplo de la definicin de una clase abstracta y un mtodo virtual en C#.

128

Figura 4.53. Ejemplo de la definicin de mtodos virtuales en C#.

128

Figura 4.54. Ejemplo de la definicin de un espacio de nombres en C++.

129

Figura 4.55. Ejemplo de uso de un espacio de nombres en C++.

129

Figura 4.56. Sintaxis utilizada en Java para la definicin y uso de paquetes.

130

Figura 4.57. Sintaxis utilizada en C# para la definicin y uso de espacios de nombres.

1130

Figura 4.58. Ejemplo del uso de funciones y clases amigas en C++.

132

Figura 4.59. Complemento del ejemplo de la Figura 4.58.

132

Figura 4.60. Ejemplo del uso de plantillas en C++ para la generalizacin

749

de parmetros en un mtodo.

133

Figura 4.61. Ejemplo del uso de plantillas en C++ para la generalizacin de una clase.

133

VOLVER A MEN

Figura 4.62. Ejemplo del uso de plantillas en C++ para la especializacin de una clase.

134

Figura 4.63. Otros usos de plantillas en C++.

134

Figura 4.64. Ejemplo de uso del operador de conversin reinterpret_cast de C++.

135

Figura 4.65. Ejemplo de uso del operador de conversin static_cast de C++.

135

Figura 4.66. Ejemplo de uso del operador de conversin dynamic_cast de C++.

136

Figura 4.67. Ejemplo de uso del operador de conversin const_cast de C++.

136

Figura 4.68. Ejemplo de uso del operador de conversin typeid de C++.

136

Figura 4.69. Ejemplo de uso de clases y mtodos finales en Java.

137

Figura 4.70. Ejemplo de clases anidadas en Java.

138

Figura 4.71. Ejemplo de uso de clases y mtodos finales en C#.

139

Figura 4.72. Ejemplo de uso de constructor esttico en C#.

140

Figura 4.73. Ejemplo de uso de indexadores en C#.

140

Figura 4.74. Ejemplo de uso de indexador definido en ejemplo de Figura 4.73.

141

Figura 4.75. Ejemplo de uso de clases anidadas en C#.

141

Figura 4.76. Ejemplo de uso de operaciones de conversin en C#.

142

Figura 4.77. Ejemplo de uso de delegados y eventos en C#.

143

Figura 4.78. Ejemplo de uso de atributos en C#.

144

Figura 4.79. Otro ejemplo de uso de atributos en C#.

144

Figura 4.80. Diagrama de clases para representar la operacin de un banco.

146

Figura 5.1. Proceso metodolgico y estimados de tiempo / recursos requeridos.

149

Figura 5.2. Ejemplo de mapa mental realizado en XMind 2012, versin 3.3.0.

151

Figura 5.3. Diagrama de clases correspondiente al Ejemplo 1.

156

Figura 5.4. Diagrama de clases correspondiente al Ejemplo 2.

158

Figura 5.5. Plantilla y ejemplo del detalle de una clase.

160

Figura 5.6. Representacin grfica de un reloj digital.

163

Figura 5.7. Diagrama de casos de uso del reloj digital.

164

Figura 5.8. Diagrama no detallado de clases del reloj digital.

164

Figura 5.9. Diagrama detallado de clases del reloj digital.

165

Figura 5.10. Diagrama de estados de la clase Segmento.

165

Figura 5.11. Diagrama de estados de las clases Panel y Dgito.

166

Figura 5.12. Diagrama de estados de la clase Reloj.

166

Figura 5.13. Diagrama de colaboracin y secuencia asociado al caso de uso


inicializar el reloj.

750

VOLVER A MEN

167

Figura 5.14. Diagrama de colaboracin y secuencia asociado al caso


de uso incrementar hora del reloj.

168

Figura 5.15. Diagrama de colaboracin y secuencia asociado al caso de


uso Asignar valor al reloj.

169

Figura 5.16. Diagrama de colaboracin y secuencia asociado al caso


de uso Mostrar hora actual del reloj.

169

Figura 5.17. Diagrama de componentes del reloj digital.

170

Figura 5.18. Diagrama de distribucin del reloj digital.

170

Figura 6.1. Mapa mental relativo al problema de la calculadora.

175

Figura 6.2. Diagrama de casos de uso relativo al problema de la calculadora.

176

Figura 6.3. Diagrama no detallado de clases relativo al problema de la calculadora.

177

Figura 6.4. Diagrama detallado de clases relativo al problema de la calculadora.

177

Figura 6.5. Diagramas de estado relativos al problema de la calculadora.

178

Figura 6.6. Diagrama de colaboracin relativo al problema de la calculadora.

179

Figura 6.7. Diagrama de componentes relativo al problema de la calculadora.

179

Figura 6.8. Ejemplo de una corrida del problema de simulacin de trfico.

221

Figura 6.9. Mapa mental relativo al problema de la simulacin de trfico.

222

Figura 6.10. Diagrama de casos de uso relativo al problema de la simulacin de trfico.

222

Figura 6.11. Diagrama no detallado de clases relativo al problema de la simulacin de trfico.

223

Figura 6.12. Diagrama detallado de clases relativo al problema de la simulacin de trfico.

224

Figura 6.13. Diagramas de estado relativos al problema de la simulacin de trfico.

224

Figura 6.14. Diagrama de secuencias relativo al problema de la simulacin de trfico.

225

Figura 6.15. Diagrama de componentes relativo al problema de la simulacin de trfico.

225

Figura 6.16. Mapa mental relativo al problema de la simulacin de trfico.

242

Figura 6.17. Diagrama de casos de uso relativo al problema del ambiente para el
manejo de conjuntos.

243

Figura 6.18. Diagrama no detallado de clases relativo al problema del ambiente


para el manejo de conjuntos.

244

Figura 6.19. Diagrama detallado de clases relativo al problema del ambiente para
el manejo de conjuntos.

245

Figura 6.20. Diagramas de estado relativos al problema del ambiente para el manejo
de conjuntos.

245

Figura 6.21. Diagrama de colaboracin relativo al problema del ambiente para el


manejo de conjuntos.
751

VOLVER A MEN

246

Figura 6.22. Diagrama de componentes relativo al problema del ambiente para el


manejo de conjuntos.

246

Figura 6.23. Mapa mental relativo al problema del manejo inteligente de colas de atencin.

299

Figura 6.24. Diagrama de casos de uso relativo al problema del manejo inteligente
de colas de atencin.

300

Figura 6.25. Diagrama no detallado de clases relativo al problema del manejo


inteligente de colas de atencin.

301

Figura 6.26. Diagrama detallado de clases relativo al problema del manejo


inteligente de colas de atencin.

303

Figura 6.27. Diagramas de estado relativos al problema del manejo inteligente de


colas de atencin.

304

Figura 6.28. Diagramas de secuencia relativos al problema del manejo inteligente


de colas de atencin: 1) configuracin (parte superior); 2) simulacin (parte inferior).

305

Figura 6.29. Diagrama de componentes relativo al problema del manejo inteligente


de colas de atencin.

306

Figura 6.30. Diagrama esquemtico del generador de energa.

390

Figura 6.31. Mapa mental relativo al problema del generador de energa.

390

Figura 6.32. Diagrama de casos de uso relativo al problema del generador de energa.

391

Figura 6.33. Diagrama no detallado de clases relativo al problema del generador de energa.

391

Figura 6.34. Diagrama detallado de clases relativo al problema del generador de energa.

392

Figura 6.35. Diagramas de estado relativos al problema del generador de energa.

393

Figura 6.36. Diagrama de colaboracin relativo al problema del generador de energa.

393

Figura 6.37. Diagrama de componentes relativo al problema del generador de energa.

394

Figura 6.38. Diagrama esquemtico de la fbrica de galleta.

417

Figura 6.39. Mapa mental relativo al problema de la fbrica de galletas.

417

Figura 6.40. Diagrama de casos de uso relativo al problema de la fbrica de galletas.

418

Figura 6.41. Diagrama no detallado de clases relativo al problema de la fbrica de galletas.

419

Figura 6.42. Diagrama detallado de clases relativo al problema de la fbrica de galletas.

420

Figura 6.43. Diagrama de estado relativos al problema de la fbrica de galletas.

421

Figura 6.44. Diagrama de secuencias relativo al problema de la fbrica de galletas.

422

Figura 6.45. Diagrama de componentes relativo al problema de la fbrica de galletas.

422

Figura 6.46. Ejemplo de la representacin grfica de varios GARs (Corner, 2008).


A la izquierda se tiene un GAR sin la presencia de un deadlock; a la izquierda otro
GAR con un deadlock.
752

VOLVER A MEN

460

Figura 6.47. Mapa mental relativo al problema del grafo de asignacin de recursos.

461

Figura 6.48. Diagrama de casos de uso relativo al problema del grafo de asignacin
de recursos.

462

Figura 6.49. Diagrama no detallado de clases relativo al problema del grafo


de asignacin de recursos.

463

Figura 6.50. Diagrama detallado de clases relativo al problema del grafo


de asignacin de recursos.

463

Figura 6.51. Diagrama de estado relativo al problema del grafo de asignacin


de recursos.

464

Figura 6.52. Diagrama de colaboracin relativo al problema del grafo


de asignacin de recursos.

464

Figura 6.53. Diagrama de componentes relativo al problema del grafo


de asignacin de recursos.

465

Figura 6.54. Ejemplo de un campo de batalla para el problema de la


conquista del nuevo mundo.

513

Figura 6.55. Mapa mental relativo al problema de la conquista


del nuevo mundo.

515

Figura 6.56. Diagrama de casos de uso relativo al problema de la


conquista del nuevo mundo.

515

Figura 6.57. Diagrama no detallado de clases relativo al problema


de la conquista del nuevo mundo.

516

Figura 6.58. Diagrama detallado de clases relativo al problema de la


conquista del nuevo mundo.

517

Figura 6.59. Diagramas de estado relativos al problema de


la conquista del nuevo mundo.

518

Figura 6.60. Diagrama de secuencias relativo al problema de la conquista


del nuevo mundo.

519

Figura 6.61. Diagrama de componentes relativo al problema de la conquista


del nuevo mundo.

519

Figura 6.62. Circuito que representa una secuencia teleoreactiva.

549

Figura 6.63. Mapa mental relativo al problema de los agentes recolectores de desperdicio.

651

Figura 6.64. Diagrama de casos de uso relativo al problema de los agentes recolectores
de desperdicio.

753

VOLVER A MEN

652

Figura 6.65. Diagrama no detallado de clases relativo al problema de los agentes


recolectores de desperdicio.

653

Figura 6.66. Diagrama detallado de clases relativo al problema de los agentes


recolectores de desperdicio.

653

Figura 6.67. Diagramas de estado relativos al problema de los agentes


recolectores de desperdicio.

654

Figura 6.68. Diagrama de colaboracin relativo al problema de los agentes


recolectores de desperdicio.

654

Figura 6.69. Diagrama de componentes relativo al problema de los agentes


recolectores de desperdicio.

655

Figura 6.70. Ejemplo de una RS.

732

Figura 6.71. Ejemplo de una superficie para la simulacin en el problema

754

de agentes en competencia.

735

Figura 6.72. Ejemplo de un sistema de lneas de metro.

739

Figura 6.73. Ejemplo de un grafo de flujo de un programa.

741

VOLVER A MEN

ndice de Tablas
Tabla 1.1. Preguntas bsicas relativas a hacer OO.

18

Tabla 1.2. Tabla comparativa entre la descomposicin funcional y la OO.

20

Tabla 1.3. Resumen de la historia de la OO.

21

Tabla 1.4. Anlisis de la historia de la OO.

22

Tabla 2.1. Tipos de interfaz de los elementos de una clase.

44

Tabla 3.1. Listado de smbolos sintcticos de UML.

84

Tabla 3.2. Listado de conectores entre smbolos.

85

Tabla 5.1. Resumen del proceso metodolgico.

162

Tabla 6.1. rdenes a satisfacer en el problema de la calculadora


de operaciones bsicas.

173

Tabla 6.2. Operaciones a soportar en el problema de la calculadora

755

de operaciones bsicas.

174

Tabla 6.3. Operaciones a soportar en el problema del manejo de conjuntos.

242

VOLVER A MEN


A
Abstraccin, 18, Vase Principios de la OO
Agente, 451
Agregacin, 49
Anlisis OO, 165
Archivo
Binario, 239
de eventos, 236
Manejo, 235
Asociaciones incorrectas o innecesarias, 169
Atributo, 43, 47
Introduccin, 22
Autmata celular, 218

B
Biblioteca de clases, 163

C
C#
Acceso a los elementos, 105
Atributo, 102
Atributos, 156
Clase abstracta, 138
Clases anidadas, 153
Clases y mtodos finales, 150
Constructor, 107
Constructor esttico, 151
Conversin entre clases, 154
Definicin de clase, 102
Delegados y eventos, 155
Elementos estticos, 117
756

VOLVER A MEN

ndice

Espacio de nombres, 141


Herencia, 135
Indexadores, 152
Instanciacin de objetos, 104
Interfaces, 136
Mtodo de finalizacin, 113
Mtodo virtual, 138
Operador de asignacin o copia, 118
Paquetes. Vase espacio de nombres
Polimorfismo, 109
Referencia al objeto actual, 125
Sobrecarga de operadores, 123
C++, 48
Acceso a los elementos, 105
Clase abstracta, 137
Constructor, 106
Conversin de clases, 146
Definicin de clase, 99
Destructor, 112
Elementos estticos, 115
Espacio de nombres, 139
Funciones y clases amigas, 142
Herencia, 127
Herencia mltiple, 129
Inicializacin por defecto, 110
Instanciacin de objetos, 103
Mtodo virtual, 137
Operador de asignacin o copia, 118
Paquete. Vase espacio de nombres
Plantillas, 144
Polimorfismo, 108
Referencia al objeto actual, 125
Sobrecarga de operadores, 120
Caos, 15
757

VOLVER A MEN

Clase, 47
Abstracta, 53
Clase base. Vase superclase
clase derivada. Vase subclase
Detalle, 174
Diagrama. Vase diagrama de clases de UML
Incorrectas o innecesarias, 168
Interfaz, 48
Introduccin, 21
Redundante, 168
Complejidad del software, 14
Crisis del software, 13

D
Descomposicin, 16
Diseo OO, 173
Divide y vencers, 16

E
Encapsulamiento. Vase Principios de la OO
Enlace dinmico. Vase trmino tcnico de la orientacin a objetos
Espaguetis electrnico, 18
Especializacin, 50
Extend. Vase relacin de extensin de UML

G
GAR, 354
Generalizacin, 50
Grafo, 189

H
Herencia, 48, 50
Mltiple, 50
Simple, 50
758

VOLVER A MEN

Hilos
Manejo, 457

I
Include. Vase relacin de inclusin de UML
Instanciacin de objetos, 35
Interfaz
De la herencia, 54
Interfaz del objeto, 33
Interfaz grfica, 317

J
Java, 48
Acceso a los elementos, 105
Clase abstracta, 138
Clases anidadas, 149
Clases y mtodos finales, 149
Constructor, 107
Definicin de clase, 102
Elementos estticos, 116
Herencia, 132
Instanciacin de objetos, 104
Interfaces, 133
Mtodo de finalizacin, 113
Mtodo virtual, 138
Operador de asignacin o copia, 118
Paquetes, 141
Polimorfismo, 109
Referencia al objeto actual, 125
Jerarqua, 18

L
Lenguaje de modelado unificado
Actor, 62
759

VOLVER A MEN

Agregacin, 76
Asociacin cualificada, 74
Clase de utilidad, 78
Clase parametrizada, 78
Componentes, 60
Diagrama, 61
Diagrama de actividad, 68
Diagrama de cambio de estados, 82
Diagrama de casos de uso, 62
Diagrama de clases, 49, 70
Diagrama de colaboracin, 85
Diagrama de componentes, 84
Diagrama de distribucin, 88
Diagrama de objetos, 81
Diagrama de secuencia, 86
Elementos del modelo, 62
Estereotipos, 90
Generalizacin, 71
Interfaz, 77
Paquete, 79
Refinamiento, 72
Relacin de extensin, 64
Relacin de inclusin, 65
Restricciones, 89
Resumen de la sintaxis, 91
Valores etiquetados, 89
Vista, 60
Lenguaje de Modelado Unificado, 27

M
Mensaje, 45
Mtodo, 43, 45, 47
Introduccin, 22
Virtual, 53
760

VOLVER A MEN

Modularidad. Vase Principios de la OO


Abierto / Cerrado, 37
Acceso uniforme, 36
Auto-documentacin, 36
Composicin modular, 35
Comprensibilidad modular, 35
Continuidad modular, 35
Correspondencia directa, 36
Descomposicin modular, 35
Eleccin nica, 37
Interfaces explcitas, 36
Ocultamiento de informacin, 36
Pequeas interfaces, 36
Pocas interfaces, 36
Proteccin modular, 36
Unidades modulares lingsticas, 36
Multiplicidad, 19

O
Objeto, 43
Comportamiento, 45
Diagrama. Vase diagrama de objetos de UML
Estado, 43
Identidad, 44
Introduccin, 22
OO. Vase Orientacin a Objetos
Orientacin a Objetos
Beneficios, 21
Conceptos bsicos, 43
Definicin, 19
Diferencias con la descomposicin funcional, 22
Fundamentos, 31
Historia, 23
Origen, 13
761

VOLVER A MEN

Principios, 31
Trmino tcnico, 41

Paquete, 48
Plantilla
Manejo, 241
Polimorfismo
por inclusin, 52
Principios de la OO
Abstraccin, 31
Concurrencia, 39
Encapsulamiento, 33
Jerarqua, 37
Modularidad, 34
Persistencia, 39
Polimorfismo, 39
Tipos, 38
Problemas, 189
Agentes recolectores, 451
Conquista del nuevo mundo, 383
Fbrica de galletas, 330
Generador de energa, 314
Grafo de asignacin de recursos, 354
La calculadora de operaciones bsicas, 190
Manejo de conjuntos, 234
Manejo inteligente de colas, 266
Simulacin de trfico, 218
Protocolo de un objeto, 47

R
Reusabilidad de elementos, 21

S
Secuencias Teleoreactivas, 452
762

VOLVER A MEN

Simula, 23
Smalltalk, 23
Sonido
Manejo, 189
Subclase, 50
Superclase, 50

U
UML. Vase Lenguaje de Modelado Unificado

X
XML, 270

763

VOLVER A MEN

Você também pode gostar