Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
1
DIPLOMATURA EN PROGRAMACION JAVA
Capítulo 1
Diplomatura en Programación Java
Estructuras de Datos
Vectores
Desde que se comenzaron a utilizar vectores, siempre se buscó agrupar los datos bajo un mismo
nombre que definiera a un conjunto de ellos. De esta manera, un vector es un conjunto de datos
del mismo tipo (por ejemplo, entero) agrupados con un único nombre que define a dicho vector.
De esta manera, si se declara en un programa un vector de 8 enteros con los primeros ocho
números naturales, una representación de cómo se distribuye en la memoria lo muestra la Figura
1-1.
Los números se almacenan en forma continua uno al lado del otro y el nombre del vector
representa la dirección en donde comienzan a almacenarse los elementos. Los lenguajes
determinan la cantidad de bytes que se reserva para cada elemento del vector en base a su
declaración. En este caso se utilizó un tipo de datos entero. Suponiendo que para el lenguaje en
particular, un entero tiene 4 bytes (32 bits), el vector ocuparía 32 bytes de memoria (256 bits). La
forma de encontrar en memoria al vector es a través de la dirección de memoria en el que
comienza. Si se quiere un elemento en particular, por ejemplo el tercero, basta con desplazarse al
tercer lugar, es decir, moverse al tercer entero dentro del vector. Esto se hace de la siguiente
manera:
Un ejemplo de utilización es colocar doce valores enteros dentro de un vector que representa las
veces que usó un auto una persona en cada uno de los 12 meses. Así cada elemento es un mes y el
valor que almacena es la cantidad de veces que usó el auto. El nombre del vector podría ser, por
ejemplo, usoDelAutoPorJuan y el vector sería como el que muestra la figura:
El uso de esta manera de un vector permite hacer una abstracción del uso de la información para
que un programa interprete los datos de la siguiente manera:
Enero 10
Febrero 6
Marzo 12
Abril 11
Mayo 3
Junio 22
Julio 6
Agosto 15
Septiembre 21
Octubre 11
Noviembre 30
Diciembre 33
Claramente se establece una relación entre el nombre del vector y la información que contiene
cada uno de sus elementos, por lo tanto este “agrupamiento” de datos tiene un valor agregado
conceptual, la posición del elemento indica el número de mes que se “asocia” a las veces que se
utilizó el auto.
Estructuras de datos
Una vez agrupados datos de un mismo tipo como se hizo en el vector, el siguiente desafío es
agrupar datos de diferentes tipos con el mismo fin, dar un valor agregado conceptual al
“agrupamiento”. Muchos lenguajes de programación resolvieron esta cuestión incorporando
estructuras de datos, las cuales siguen el mismo precepto que el vector, salvo que para cada
elemento ahora debe definirse el tipo de datos que es. Cada lenguaje define estas estructuras
según su gramática particular, pero en todos los casos, las estructuras de datos son modelos que
definen un nuevo tipo de datos. Es decir, una estructura se define para declarar variables del tipo
de la estructura.
Para no caer en un lenguaje en particular, se utiliza pseudocódigo para mostrar una declaración
tipo, según el siguiente formato:
ESTRUCTURA Nombre
DECLARACIONES DE VARIABLES
FIN ESTRUCTURA
Notar que no se tiene una variable en memoria hasta que se la declara explícitamente, como se
hace al final de la declaración y que al igual que en el vector, el nombre de la estructura
representa el lugar de memoria en donde se almacena la misma, pero a diferencia de este, cada
vez que se quiera acceder a un elemento de la estructura, se deberá desplazar la dirección tantos
bytes como mida el elemento en particular
Ejemplo
ESTRUCTURA Persona
CADENA nombre
CADENA apellido
ENTERO edad
FIN ESTRUCTURA
Notar que el uso de la abstracción en una estructura de datos es mayor que en el caso de un
vector. En estas se agrupan datos más genéricos que en el caso del vector y tiene mayor
flexibilidad para realizar asociaciones conceptuales entre el nombre de la variable del tipo
estructura y su contenido. Esto quiere decir que se pueden crear “modelos” de conceptos más
complejos que en el caso del vector. De esta manera, se puede modelar, por ejemplo, los datos
que pertenecen a una Persona, un Auto, etc., porque dichos conceptos tienen datos de diferente
tipo que lo describen (por ejemplo, en el caso de una persona, el nombre, apellido, D.N.I, etc.). Se
concluye entonces que una estructura de datos puede “modelar” los datos de “entidades
complejas” que un vector no puede hacer al tener todos sus elementos “del mismo tipo”.
Otro punto importante a tener en cuenta es que se pueden declarar tantas variables del tipo de
la estructura como se necesiten. La declaración de una estructura define un nuevo tipo de datos
(por ejemplo, como si fuera un entero o un punto flotante) y por definición se pueden declarar
tantas variables como se deseen de un determinado tipo de datos. La importancia de este hecho
radica en qué si bien la estructura puede modelar, por ejemplo, los datos de una Persona, existen
muchas de ellas en particular y cada una posee su conjunto exclusivo de datos, por lo tanto se
deben tener tantas variables como “entidades” se trabajen con la estructura de datos.
variablePersona.nombre = “Martín”
variablePersona.apellido = “García”
variablePersona.edad = 33
Se debe tener en cuenta que una variable que se encuentra declarada dentro de otra de tipo
estructura es básicamente una variable, con lo que tiene todas sus propiedades: se pueden
asignar y recuperar valores, se pueden retornar desde procedimientos o funciones y pueden ser
sus parámetros.
Una estructura de datos se puede utilizar para realizar una abstracción de una entidad
Las variables dentro de la estructura de datos son los atributos de dicha entidad
Así, en el ejemplo, la entidad es Persona, y sus atributos son nombre, apellido y edad.
Si la persona cumple años, se debe cambiar el atributo edad y almacenar, por ejemplo, 34 en lugar
de 33. Este hecho demuestra el siguiente importante enunciado:
Los valores que almacena una determinada entidad, definen el estado de la misma
Por lo tanto, las variables declaradas dentro de una estructura son sus atributos y los valores que
almacenan definen su estado.
Tomando un ejemplo sencillo, es válido suponer querer realizar una función o procedimiento que
verifique que la edad que se le asigna a una persona está en un rango entre 0 y 120 años y pasarle
como parámetro una variable de tipo estructura que tenga en su interior declarada otra que
almacena el dato requerido (como la edad de una Persona). Con lo que se definió hasta el
momento, dicho procedimiento o función debe ser una parte independiente del programa, pero
opera con datos específicos de la estructura, es más, opera tan sólo con los almacenados en una
variable declarada dentro de la estructura. Por lo tanto, una asignación incorrecta de los valores
podría causar errores inesperados si estos no son los propios de la estructura (por ejemplo, por
error se pone el valor de la altura de la persona en la variable que corresponde a la edad).
Otro punto importante es que estas funciones o procedimientos sirven como servicios que la
entidad brinda para su utilización (por ejemplo, en este caso, el servicio es “no equivocarse al
asignar una determinada edad”). Una conclusión importante derivada de esto es el siguiente
enunciado:
Las entidades poseen atributos que definen su estado y servicios mediante los cuales se
comunican con otras entidades
Se puede concluir entonces que sería provechoso que dichos procedimientos o funciones
estuvieran incluidos dentro de la estructura y no en una parte independiente del código que se
utiliza y la única forma de acceder a los valores almacenados sea a través de ellos.
Para declarar un elemento dentro de una estructura, es fundamental conocer el tipo de datos para
saber cuántos bytes de memoria se deben reservar para almacenar la información, pero ¿cuántos
bytes de memoria hay que reservar si se va a colocar dentro de una estructura una función o
procedimiento? En realidad, no es necesario saber el tamaño de una función o procedimiento para
colocarlo dentro de una estructura, basta con saber en qué lugar de la memoria se encuentra el
mismo y guardar una referencia a ese espacio. Además, las direcciones de memoria en un
computador son siempre del mismo tamaño ya que este lo fija el bus de direcciones. Por lo tanto,
un lenguaje “sabe” el tamaño de una dirección de memoria para una determinada plataforma de
trabajo donde se intenta crear un programa. Esto permite definir tamaños fijos dentro de la
estructura de datos para indicar cuales son las direcciones de los procedimientos o funciones que
operan con los datos declarados dentro de la estructura.
El diagrama de bloque de la Figura 1-3 muestra cómo queda conformada lógicamente la memoria
RAM para la ejecución de un programa.
Creación de Instancias
Cada vez que se declara una variable del tipo de una estructura se crea una instancia y cada
instancia puede tener su propio estado porque cada nueva variable se almacena en un lugar
diferente de la memoria. Entonces, cabe la siguiente pregunta ¿Cómo encuentra cada instancia
diferente los atributos que definen su estado?
Es obvio que de alguna manera hay que tener una referencia al lugar de memoria que se debe
utilizar. Estas referencias se resuelven mediante el uso del nombre de la variable y la notación de
punto cuando se acceden a los atributos, pero ¿cómo sabe un servicio cuáles son los atributos
sobre los cuales operar? La solución lógica es pasarle cada vez que se invoca al servicio la dirección
de memoria en donde se encuentran los atributos.
Como esta es una tarea repetitiva y promueve errores de programación si los programadores
debieran hacerlo cada vez que invocan a un servicio, los lenguajes de programación resuelven el
tema “tras bambalinas” y pasan por defecto esta referencia en cada invocación.
Para los lenguajes orientados a objetos las estructuras se denominan clases y las variables del tipo
de la estructura se denominan objetos. Por lo tanto un objeto se obtiene como consecuencia de
crear una instancia de una clase y tiene todas las propiedades de las variables.
Definiciones
Para empezar a separar conceptos y clarificarlos, este es el punto donde las definiciones empiezan
a ser necesarias. Las mismas comienzan con la realización de sistemas y van siendo cada vez más
específicas hasta llegar a los mismos componentes que pertenecen al desarrollo de un programa,
por lo tanto debemos establecer una diferencia clara entre “sistema” y “programa”:
Con estos conceptos en mente, lo próximo es entender cuáles son los elementos que permiten
construir un proyecto informático en un lenguaje orientado a objetos.
Clase: es una construcción que se utiliza como un modelo (o plantilla) para crear objetos
de ese tipo. Define un nuevo tipo de datos creado por el usuario (programador) que es
justamente el de la clase. A este modelo se lo denomina también abstracción del objeto
que representa.
Miembros de una Clase: son los atributos y servicios declarados dentro de la clase.
Ingeniería de software
Cuando se quiere desarrollar software, se necesita alguna “técnica” para hacerlo que nos guie por
el camino correcto respecto de las dificultades que se pueden encontrar en el desarrollo de un
proyecto. A las mencionadas técnicas se las llamó disciplinas. Cuando se tuvo un conjunto de las
mismas se las denominó sub disciplinas y se las definió como los componentes del proceso
sistemático del desarrollo de software para que sea tanto sistemático (se posee un sistema para
llevarlo a cabo) como cuantificable (se lo puede medir para, fundamentalmente, la toma de
decisiones. Un ejemplo de esto es el tiempo de desarrollo).
Existen muchas definiciones que se pueden ajustar muy a la descripción de lo que significa
ingeniería de software, por lo tanto utilizaré un conjunto de ellas, formuladas por prestigiosos
autores, extraído de Wikipedia en https://es.wikipedia.org/wiki/Ingenier%C3%ADa_de_software y
son las siguientes:
Sin embargo, sólo a efectos de ser más claro que preciso, llamaremos ingeniería de software a
todo lo que está incluido en el proceso de creación del mismo.
Primera
Iteración
Segunda
Iteración
• Concepción
• Elaboración
• Construcción
• Transición
La Figura 1-5 muestra las fases principales de un ciclo de vida iterativo e incremental y como se
relacionan con los incrementos.
Elaboración Concepción
Primera
Iteración
Segunda
Iteración
Construcción Transición
En la Figura 1-6 se puede ver como las distintas fases afectan las etapas de un proyecto. Las
siguientes, son las iteraciones de cada etapa del ciclo de vida de un proyecto y sus respectivas
fases.
Requerimientos
Análisis
Diseño
Implementación
Verificación
Iteración
Tiempo
Nota: se debe tener en cuenta que las áreas sólidas indican la carga de actividad para cada
etapa en cada iteración y que es una guía simplemente y puede variar de proyecto a proyecto
dependiendo exclusivamente de los requerimientos que cada uno tenga. Cuando se completa
una iteración en cada etapa, implica un incremento.
Ejemplo de relevamiento
La biblioteca Municipal desea realizar un sistema para administrar la gestión de los libros que
presta al público general y a sus socios registrados. La información relevada de distintas fuentes se
encuentra a continuación:
Para comenzar, se quiere tener un pequeño control de cómo se arman los puestos de
lectura, a los que se les llama puesto de trabajo. Los puestos se dividen en informáticos y
de lectura. Los primeros tienen un escritorio con una sola silla y los segundos un escritorio
con cuatro. Algunos escritorios tienen asignados una PC de escritorio mientras que otros
sólo se usan con notebooks que se proveen cuando se las solicita ya que se debe ser socio
de la biblioteca para tener acceso a los recursos informáticos. Por el momento se cuenta
tan sólo con 20 máquinas pero en el futuro habrá más y se quiere empezar la realización
de un sistema que controle los recursos con los que cuenta la biblioteca. Actualmente,
sólo interesa indicar como se arman los puestos y que recursos fueron asignados. Cada
recurso posee un número de identificación para reconocerlo. Los libros y las
computadoras se asignan a los lectores, no a los puestos de trabajo.
Para las necesidades actuales, en lo que refiere a los escritorios con computadoras o
notebooks prestadas, sólo se pretende saber que socios las están utilizando
La biblioteca atiende a dos tipos de personas: el público en general que se presenta para
leer en el salón y los socios. Cuando cualquiera de estos dos tipos de personas accede al
salón, la biblioteca los considera lectores más allá de su situación administrativa. Los
socios adquieren su condición en un sistema independiente en el cual les entregan un
carnet de acreditación. Los libros se pueden entregar para leer en el salón o en el domicilio
en caso de ser socios, pero el público en general sólo los puede leer en el salón. Cuando
los libros se entregan para leer en el domicilio del socio tiene una fecha de devolución. Los
socios sólo pueden retirar un libro para su domicilio si previamente realizaron una
reservación o cuando lo solicita en el mismo salón y se encuentra disponible. Pueden
existir muchas copias de un mismo libro y todos los mismos son físicos ya que la biblioteca
no maneja libros en formato electrónico.
Cuando los socios crean una reserva se la considera activa. Si el socio posteriormente por
alguna razón desea dar de baja la reserva, la única operación que se realiza es poner a la
misma como inactiva. Esto brinda la posibilidad que en un futuro se puedan reactivar las
reservas aunque en este momento no es un requerimiento para la primera versión de la
solución. Por otro lado, cuando el socio retira el libro, la reserva se considera inactiva pero
se marca para indicar que el o los libros en ella se encuentran prestados.
Nunca se entregan dos copias de un mismo libro a un lector, sea socio o público. Esto se da
tanto para el caso en el cual el libro se presta para el domicilio de un socio o cuando se
entrega a un lector que se encuentre en el salón.
Tampoco se desea crear una interfaz gráfica para interaccionar con el sistema, sólo
controlar que la funcionalidad se lleva a cabo correctamente para diseñar adecuadamente
como se presentará la información usando el tiempo que se genera mientras se realiza
este desarrollo.
Ejemplo
Nota: la denominación “sentencia del problema” es un nombre histórico y se respeta como tal.
En la actualidad se lo podría llamar “enumeración de funcionalidades a resolver” y tal vez sería
más adecuado. Sin embargo, como es una terminología instalada, se lo sigue llamando de esta
manera.
Delimitar la solución
Cada vez que se realiza un análisis de un problema es importante determinar cuáles son los límites
de la solución. Es muy fácil cuando se crean modelos concluir que otros objetos se pueden
modelar a partir de las abstracciones principales. De alguna manera hay que establecer donde se
debe parar y hay que tener presente en todo momento dichas limitaciones, mientras se analiza o
se diseña una clase.
No siempre es claro cómo realizar este trabajo. Sin embargo, el uso las definiciones anteriores
ayudan a manejar esta complejidad lo que permite establecer dónde está contenida la solución
buscada.
Los desarrolladores siempre buscan solucionar funcionalidades, lo que nos lleva a la creación de
sistemas compuestos de uno o varios programas mediante el análisis del mismo. Estos pueden
basarse en un sistema manual o crear uno nuevo.
Es entonces fundamental encontrar dónde se “ubica” el sistema comparado con otros posibles,
cuáles son las tareas que se tienen que realizar para desarrollarlo y hasta dónde llega cada una de
las soluciones brindadas. Para determinar todo esto se debe comprender el significado de tres
definiciones fundamentales:
• Dominio
• Contexto
• Alcance
Dominio de un problema
Es el universo donde se encuentran todas las posibles soluciones de un determinado
problema.
¿Cuál es la importancia de manejar este concepto? Bueno, sirve nada más y nada menos que para
solicitar ayuda. Comprendiendo el dominio se pueden consultar soluciones similares para resolver
dificultades que se encuentren al desarrollar un sistema. También se puede consultar a personas
que sean idóneas por conocer la problemática a resolver (a estos se le suele denominar expertos
de dominio). Se puede buscar bibliografía del tema, etc.
En ejemplo del sistema contable a resolver, comprender el dominio permite que le podamos hacer
consultas, por ejemplo, a un contador. Sin embargo, ¿se puede preguntar algo sin saber cómo
hacerlo? La respuesta es simple, no. Para saber que preguntar, se debe entender el dominio en el
que se encuentra la solución. En palabras simples, no le puedo preguntar a un contador como se
realiza un circuito eléctrico porque estoy en otro dominio.
También se puede ver un sistema similar para entender como resuelve cierta funcionalidad. En
este caso, el “experto del domino” que se consulta es un sistema y no una persona.
Es importante tener en cuenta que el “experto” pertenece al dominio, motivo por el cual puede no
estar involucrado directamente con el sistema a desarrollar ya que pertenece al universo de todas
las soluciones posibles.
Otro punto importante para aclarar es que los dominios no son necesariamente estáticos, sino
más bien todo lo contrario. En el ejemplo del contador, supongamos que se dictamina una nueva
ley para llevar la contabilidad. Esto implica una variación en el dominio que seguramente
repercute en todo lo realizado.
Ejemplo
Establecimiento del dominio del proyecto para la biblioteca.
Contexto de un problema
Es el subconjunto dentro del universo definido por el dominio que determina la solución del
sistema a realizar.
Una vez determinado el dominio de un sistema el próximo paso es encontrar dentro de todas las
posibles soluciones la que se ajusta al sistema a desarrollar. En otras palabras, se debe encontrar
la solución que se adecue al problema a resolver estableciendo el límite en cual dicha solución es
válida. En palabras sencillas, resolver sólo lo necesario y nada en exceso.
Muchas veces estas limitaciones se establecen como una lista de lo que la solución hace y lo que
no. Al conjunto de elementos que pertenece a dicha lista se lo denomina contexto de un
problema.
En un ejemplo anterior, se modela como una Persona come una Fruta. En la abstracción creada no
se tiene en cuenta todas las cosas que puede comer una persona o de cuantas formas diferentes
se puede “dejar comer” una fruta. Estas limitaciones pueden no ser muy claras durante el análisis
pero si no se pierde de vista el contexto que se define para la solución del problema, se sabe
exactamente donde terminar el análisis para no modelar más de lo que el problema requiere para
su solución.
Retomando el ejemplo del sistema contable, si la solución buscada es un sistema que mantenga
las cuentas, pero que no realice el balance porque el mismo lo desarrolla un contador en su
estudio profesional, el desarrollo del balance está fuera de contexto.
El contexto de un problema no es estático y puede variar a medida que avanza el análisis (por
ejemplo, se contrata a un nuevo contador y este no realiza el balance en su estudio y el sistema
ahora deberá resolverlo – por eso se dice que los sistemas evolucionan). Mientras se determinan
que elementos son parte de la solución o no, se va extendiendo la lista que pertenece al contexto.
Cabe destacar, que en algunos casos, los menos, el sistema se reduce en lugar de crecer, es decir,
se requiere menos funcionalidad del mismo. Vamos a avanzar con el caso en el cual el sistema
crece sólo para favorecer una comprensión más simple del concepto.
Como el problema que resolverá la solución lo determina el contexto de la misma, muchas veces
se denomina al contexto como dominio de la solución. Sin embargo, vale la pena aclarar que para
que el contexto coincida con el dominio debe resolver todos los sistemas posibles que se pueden
crear en dicho universo, lo cual es prácticamente imposible ya que el mismo universo puede
evolucionar y variar (este es el problema que enfrentan las soluciones llamadas “paquetes
cerrados de software" porque siempre hay algún contexto de solución que no estará incluido).
Podemos concluir entonces que el dominio de la solución es la suma de los contextos particulares
de las diferentes soluciones que se realizaron para resolver el problema de dominio.
Contexto
1. Gestión del armado de los puestos de trabajo
2. Gestión de público en general que ingresa a la biblioteca para leer en el salón
3. Gestión de socios para lectura y acceso a computadoras en el salón
4. Manejo de socios y público en general como lectores cuando acceden al salón
5. Gestión de socios para reservas y préstamos de libros
6. Administración de libros físicos
7. Las salidas se producen a través de la consola mediante simulaciones de uso del sistema
En palabras sencillas, una vez que se determina que se va a realizar dentro de un contexto, el
alcance determina hasta donde se llega en la implementación de dicha funcionalidad. Si existe una
funcionalidad dentro del contexto, se debe decidir si se realiza total o parcialmente, inclusive, si no
se la desarrolla.
En el caso del ejemplo del sistema contable, que el sistema no realice balances es una
funcionalidad que se determina como parte del contexto. Sin embargo, en ese mismo contexto se
indica que se deben manejar las cuentas y el alcance puede establecer que se manejarán las
cuentas sin realizar los “mayores” de las mismas (los mayores es una forma de agrupamiento de
valores que asumen las diferentes cuentas). Si bien se cumple el requisito de manejar las cuentas
que el contexto especificó, el alcance determina cómo se van a manejar estableciendo que se va a
incluir y que no como parte de la solución.
Funcionalidades de la solución
Manejo de socios y público en Creación de lectores de salón Asignación de lectores a
general como lectores cuando puestos de trabajo
acceden al salón Asignación de computadoras Control sobre las asignaciones
Creación y activación de Control de reservas duplicadas
reservas y que no sean más de
5
Desactivación de reservas Cualquier gestión de reservas
marcando las mismas desactivadas
Creación de préstamos y que Seguimiento y control de
Gestión de socios para reservas
no sean más de 5 préstamos
y préstamos de libros
Asignación de préstamos
marcando la reserva
Asignación de fechas de Verificación o gestión de fechas
reserva
Asignación de fechas de Verificación o gestión de fechas
préstamo
Entrega de libros para el salón
Administración de libros físicos
Entrega de libros a préstamo
Las salidas se producen a través Creación de datos para simular Validaciones de asignaciones de
de la consola mediante el funcionamiento datos
simulaciones de uso del Creación de salidas por consola
sistema
Si se accede a los atributos de un objeto sin ningún control, se permite que cualquier código que
accede a un objeto modifique el valor que almacena y esto puede derivar en errores. Si se utiliza
como ejemplo una clase del tipo Persona igual a la estructura de datos previamente definida, se
puede dar el caso que a la variable edad se le asigne un valor que no lo controla el servicio que
valida que las edades estén dentro del rango de 0 a 120, con lo cual el atributo almacena un valor
que es inconsistente con el diseño de la clase, causando una anomalía en tiempo de ejecución.
Se hace evidente entonces que deben existir controles de acceso a los elementos (variables de
instancia y métodos) que componen una clase. En los lenguajes de programación esto se define
mediante una técnica llamada visibilidad. La visibilidad especifica el alcance de una variable, es
decir, los lugares dentro del código en los cuales es válido su acceso (por eso muchas veces se
utilizan los términos indistintamente – visibilidad o alcance de una variable). Cuando una variable
sale del alcance es porque pierde su visibilidad y no puede ser invocada.
Una primera definición para realizar es la diferencia entre los miembros que son accesibles desde
fuera de la clase cuando se crea una instancia de ella (objeto) y los que no:
Las clases definen miembros públicos y privados. Los primeros son accesibles desde fuera
de la clase por cualquier código que lo requiera. Los segundos sólo se pueden acceder
desde dentro de la clase, por lo tanto tiene su visibilidad circunscripta a la misma.
Se denomina interfaz de la clase a todo miembro público, y es la definición que establece como se
acceden sus elementos. Si se piensa en una interfaz, bien se podría decir que la clase consta de
ciertos elementos internos que se comunicarán a través de esta con otras clases, actuando como
un canal que controla dicha comunicación. Lo que se quiere señalar con esto es que, los
elementos privados de un módulo no deben ser accesibles desde afuera de este por ningún otro,
salvo por medio de la interfaz definida para hacerlo, que es la parte pública del mismo.
En cada módulo debe existir una división entre sus componentes en secciones que sean
públicas y privadas de manera que toda información acerca del mismo sea privada a éste,
a no ser que se especifique como pública explícitamente.
Para clarificar el principio, se puede pensar en una clase que posee un vector interno que
almacena números enteros y un servicio que permite realizar una búsqueda sobre el vector para
acceder a cada uno de sus elementos.
Como interfaz se puede brindar un servicio que busque un determinado elemento y retorne el
índice del vector (la posición) del elemento. Si se cambia el elemento del vector, mientras se lleva
a cabo la búsqueda, del valor almacenado que es justamente el que se deseaba hallar, el servicio
retornará que encontró un valor que ya no está almacenado en el vector porque otro código lo
accedió y lo cambió sin que se pueda realizar ninguna acción de control previa. Esto es, se cambia
un valor almacenado sin que un servicio propio del objeto registre el acceso a la variable de
instancia (el elemento del vector) y pueda realizar alguna acción en consecuencia. Si por el
contrario, el acceso a la variable de instancia se realiza mediante un servicio, este puede verificar
que otro servicio de la clase no esté accediendo al elemento y la búsqueda no retornará un valor
erróneo o viceversa, que no se realice una búsqueda mientras este en proceso un cambio de valor.
Se suele llamar a este principio "del iceberg" puesto que la interfaz de la clase, que es lo que se ve
desde afuera del módulo, es la punta del iceberg, mientras que los elementos privados, que son
por lo general el grueso de la implementación, se encuentran ocultos al mundo exterior del
módulo.
La importancia de este principio radica en el control que permite del flujo de datos hacia y desde
la clase, además de los servicios brindados por esta. Respecto de los mencionados servicios que
brinda, al estar oculta la forma en que los lleva a cabo, la comunicación con la clase estará
garantizada a través de su parte pública, ocultando detalles y uso no deseado de los componentes
del módulo (clase) que se encuentren en la parte privada.
Para acceder a los atributos de la clase se utilizan métodos especiales diseñados para tal fin
llamados “de acceso” y “de mutación” (en inglés, accessors y mutators). Estos métodos por un
estándar “de facto” en la industria se nombran antecediendo la palabra “get” al nombre del
atributo para los primeros y lo mismo con la palabra “set” para los segundos. De esta manera, si se
tiene el atributo “nombre” el método de acceso se lo llama getNombre() mientras que el de
mutación se denomina setNombre(). El primero sirve para retornar el valor que almacena el
atributo mientras que el segundo sirve para modificar el valor que almacena.
Además, en una situación en la que un método cumple una funcionalidad similar pero se lo quiere
diferenciar, se lo hace fácilmente cambiándole el nombre. En el ejemplo anterior podría ser
obtenerNombre().
Encapsulado
Siguiendo el ejemplo enunciado, ¿qué pasa si el servicio que realiza la verificación del rango de
valores del atributo edad de la clase Persona es una interfaz de la clase? Como la verificación se
hace sobre los atributos dentro de un objeto del tipo Persona, puede dejar a los mismo en una
condición inconsistente, es decir, se puede cometer el error de invocar la verificación usándola
como un servicio diferente a como se pensó originalmente. Supongamos que la verificación recibe
como parámetro el índice del elemento que debe verificar y cierto código ajeno a la clase
confunde ese parámetro con el valor que se debe verificar en la validación. El código tratará de
acceder a un elemento que probablemente no exista causando un error.
Para evitar este tipo de situaciones, se debe diseñar un servicio interno de la clase que sea
utilizado sólo por los miembros de esta y que acceda a los atributos privados de la misma. Esto
permite mantener el control de la prestación de servicios de manera que sólo se utilicen para los
fines que fueron creados.
Como se mencionó anteriormente para los atributos, existe una técnica utilizada por los lenguajes
de programación que limita el acceso y alcance de las variables llamado visibilidad. Esta se puede
aplicar de manera análoga a los servicios para que se accedan sólo desde el interior de la clase. Por
lo tanto, el servicio interno sólo se invocará desde otros elementos de la clase y su
comportamiento estará restringido a la visibilidad de la clase en la que está definido.
Cuando una clase tiene definido un servicio interno, esto es, una función o procedimiento
con visibilidad privada, se define un comportamiento encapsulado dentro del objeto en
tiempo de ejecución. Para que el comportamiento del servicio sea correcto, los atributos
deben estar ocultos de manera que un cambio en los mismos no provoque un error al
ejecutarlo.
Encapsular significa limitar la situación al entorno al que pertenece, en este caso, la clase. Por lo
tanto, para que se cumpla correctamente el encapsulamiento se debe cumplir las siguientes reglas
sencillas al mismo tiempo:
¿Cómo se determinan las operaciones que realiza un objeto? Porque se deben pensar en los
servicios que presta el objeto, directa o indirectamente. Por ejemplo, en un vaso se puede
servir líquido, por lo tanto presta el servicio de recibir el líquido que se sirve en él. Sin
embargo, no se puede interaccionar con la forma en la cual contiene el líquido, ya que este es
un servicio interno, pero el servicio que presta al permitir que lo sirva en él seguramente
invocará a dicho servicio interno. Podemos afirmar que el primero es un servicio en la interfaz
del objeto mientras que el segundo está encapsulado. O sea, el primero se brinda
directamente mientras que el segundo lo hace indirectamente. Las operaciones que realiza un
objeto son los servicios que brinda el mismo.
¿Qué pasa si el vaso se rompe? En ese caso, el objeto no se usa para lo que fue diseñado y sus
servicios, tanto directos como indirectos deben manejar una situación anómala (la rotura del
Lic.vaso). Esto
Marcelo es lo que genera las condiciones y el manejo de errores en un desarrollo.
F. Samia
23
Diplomatura en Programación Java
Si prevalecen las funciones, la solución será hacer tan importantes a los datos como a las
operaciones que se realizan con ellos, agruparlos dentro de un módulo y que las operaciones sean
los servicios antes explicados.
A partir de esta afirmación, las cosas se complican, porque normalmente se pensaba en los datos
sólo como elementos a transformar por diferentes procesos.
Además, no se pueden pensar a los datos como entidades aisladas, puesto que los mismos
transformados por ciertos procesos pueden generar nuevos datos de los que se alimente el
sistema.
La respuesta al dilema se encuentra en poder unir bajo una misma estructura tanto a los datos
como a las operaciones que manejan a los mismos. Cuando las transformaciones sufridas a través
de procesos generan nuevos datos, pero estos están representados como atributos dentro de una
entidad, sus modificaciones provocan sólo cambios de estado dentro de los objetos a los que
pertenecen.
Por lo tanto, la clave del diseño de la programación orientada a objetos radica en detectar los
objetos que sirven para la solución del problema en particular que se aborda.
Todo objeto está representado de forma natural en el lenguaje por medio de los sustantivos. Por
ejemplo, tomando la frase “Juan come una manzana” se pueden detectar los objetos separando
los sustantivos y analizando la frase. De esta manera los sustantivos son:
Juan
Manzana
Come
Esta información permite un primer análisis. Existen dos objetos, Juan y manzana, y una
operación, comer. Como se puede apreciar en la frase, los objetos se detectan en tiempo de
ejecución (Juan come - ahora - una manzana). A dichos objetos se los denomina candidatos
porque se debe corroborar que se puede generar una abstracción única a partir de ellos. Por
ejemplo, si dos objetos candidatos son sinónimos, ambos tendrán como abstracción a la misma
clase.
Una vez detectados, se debe crear una abstracción de ellos (o sea, una clase). Se puede realizar la
siguiente abstracción para representar al objeto:
Al realizar semejante afirmación se está indicando que si se genera una clase que se llame
Persona, los atributos y servicios definidos dentro de ella pueden representar perfectamente a un
objeto como Juan cuando se cree una instancia de la misma. Análogamente, se puede crear una
abstracción de manzana por medio de la clase Fruta.
Sin embargo, se puede extraer aún más información de la frase analizada. Queda por ver qué pasa
con la acción que se define en ella. Las acciones en el mundo de la programación orientada a
objetos se representan como servicios que los diferentes objetos ofrecen en tiempo de ejecución.
Por lo tanto se debe definir que objeto tiene la responsabilidad de ofrecer el servicio comer.
Esto puede inducir a errores de razonamiento. Si no se es cuidadoso y se plantea bien a que objeto
pertenece la responsabilidad del servicio en dichos términos, se puede caer en la tentación de
deducir que el mismo pertenece a Juan “porque es Juan el que come”. Esto es un error. Juan
puede comer la manzana porque la manzana tiene el servicio que permite a Juan comerla. Por lo
tanto, la responsabilidad del servicio recae sobre el objeto manzana. Cuando se genera una
abstracción de la misma por medio de la clase Fruta, se puede asegurar que Fruta posee un
servicio que es “dejarseComer” y que el mismo pertenece a la interfaz de la clase. En otras
palabras, el objeto “manzana” posee los elementos que permiten determinar en tiempo de
ejecución si es posible comerla.
Sin embargo, Juan también posee un servicio, que puede ser interno o externo, que recibe un
parámetro del tipo fruta e invoca a través de su método público “dejarseComer” el servicio de una
instancia en particular, en este caso, manzana
Resumiendo, de una simple frase se pudo analizar la siguiente información para diseñar las
abstracciones:
Los objetos pueden invocar métodos públicos de otros objetos para llevar a cabo
aquellos servicios que son sus responsabilidades
Todavía se puede sacar algunas conclusiones más para realizar un buen modelo por medio de la
clase. Cuando se obtiene la abstracción, se puede inferir cuales son los atributos que la misma
posee. En el caso de persona se puede determinar rápidamente sin miedo a cometer un error, que
se debe incluir entre sus atributos nombre y apellido. En particular, el objeto analizado, Juan,
guarda en el atributo nombre este valor y define así su estado. No confundir el nombre de la
instancia del objeto con el valor del atributo. En este caso en particular, el nombre de la instancia
coincide con el valor que asume el atributo nombre en tiempo de ejecución.
Abstracciones principales
En el ejemplo anterior rápidamente se encontró en la frase analizada dos objetos candidatos.
Cuando se detectan objetos rápidamente en una frase es porque los mismos son los más
importantes o destacados, ya que puede haber muchos otros objetos que se determinen cuando
se profundice el análisis, por medio de obtener más información o porque descubrimos que
ciertos atributos de los objetos deben tratarse como objetos en sí mismo.
En el ejemplo se determinó que las abstracciones eran Persona y Fruta y como fueron las primeras
en detectarse se las denomina abstracciones principales. En el caso de la primera se puede
determinar la necesidad de un atributo que se llame dirección. Sin embargo un análisis más
minucioso revela que el atributo puede tener servicios en sí mismo, por ejemplo uno que permita
realizar cambios de dirección. Cuando un atributo se detecta como un posible objeto se lo
denomina objeto derivado y a su modelado como abstracción derivada.
Una vez desarrollada la lista, se debe analizar cada entrada en la misma y decidir cuál se descarta y
cuál no. Para esto se suele colocar otra entrada en donde se indica si el objeto está Activo o no. En
otra columna se debe agregar la justificación de la decisión para consultas futuras.
Estas listas no tienen un formato específico pero un buen modelo propuesto es el de la Tabla 1-3.
Los motivos principales para descartar un posible objeto candidato pueden ser porque: es un
atributo de otra clase, es un sinónimo de dominio, está fuera de contexto, dominio o alcance.
Nota: la falta de acentos en los nombres de las abstracciones es intencional. Si bien en UML se
pueden asignar nombres con acentos, no es una buena práctica. La razón reside en que
algunos compiladores no manejan muy bien los caracteres acentuados o los específicos de un
lenguaje, como la “ñ” del español. Por esta razón es una buena práctica evitar cualquier
acento o letra específica del lenguaje y se pueden ver nombres escritos mal a propósito para
evitar este problema, como poner anio en lugar de año. De esta manera se evitan errores que
se puedan producir a causa de una mala interpretación del carácter por parte del compilador
que son sumamente difíciles de encontrar.
Ejemplo
Creación de la lista de objetos candidatos y análisis de los mismos para descubrir las abstracciones
principales
¿Cuál es el criterio para decidir si un objeto está dentro del alcance del desarrollo a realizar?
Cuando un objeto presta servicios (tiene operaciones) necesarias para el desarrollo se debe
considerar como candidato y crear una abstracción a partir de él.
Con la lista de objetos candidatos encontrados se arma la Tabla 1-4 para analizarlos y descubrir las
abstracciones principales.
Las clases tienen una representación gráfica en un lenguaje universal de modelado llamado UML
(Universal Modelling Language).
Las clases se representan en un rectángulo con tres divisiones en su interior, como muestra la
Figura 1-7.
Dejando de lado la división que lleva el nombre de la clase, las otras dos son opcionales y pueden
no estar presentes en el diagrama, ya sea una o ambas. Sin embargo, por una cuestión de claridad
visual, se recomienda usar todas las divisiones aunque alguna de ellas esté vacía.
Los diagramas de clases se utilizan tanto para el análisis como para el diseño, por lo cual es normal
que dependiendo de la etapa del análisis en la que se encuentre, una de las divisiones opcionales
o ambas esté vacías. Como ejemplo, se puede observar la Figura 1-8.
En el diagrama se modela una clase que se llama Ejemplo, que posee dos atributos, y dos servicios.
A continuación se explica la forma de leer las declaraciones que tienen los miembros.
Visibilidad: en este caso pueden ser públicas, privadas o protegidas (las visibilidades más
comunes en los lenguajes de programación aunque no las únicas). Si un atributo es público
se representa con el símbolo +, si es privado con – y si es protegido con #. En el ejemplo
tanto atributo1 como atributo2 tienen visibilidad privada
Nombre: el nombre que se le asigna a la variable de instancia. En este caso, las variables
de instancias se llaman atributo1 y atributo2
Tipo: es el tipo de variable definida. Para el caso del ejemplo, atributo1 es un int y
atributo2 es un double. La declaración del tipo de variable va siempre a continuación del
símbolo “:”.
Visibilidad: en este caso pueden ser públicos, privados o protegidos (las visibilidades más
comunes en los lenguajes de programación aunque no las únicas). Si un servicio es público
También se puede modelar gráficamente un objeto o instancia de una clase. Utilizando el ejemplo
de la clase Persona y la instancia Juan, se conoce para dicho caso tanto el nombre de la clase como
el del objeto y el diagrama es como el que se muestra en la Figura 1-9
Pero si se debe modelar una instancia de una clase que todavía no tiene asignado un nombre, se lo
puede hacer como muestra la Figura 1-10
Ejemplo
En este ejemplo se comienzan a analizar cómo modelar las abstracciones principales. Por lo
general, un analista con experiencia lo hace mientras elabora el diagrama. Por motivos didácticos
se presentará una tabla por cada abstracción antes de crear el diagrama en particular de cada
clase para que el lector pueda comprender la justificación de su diseño. Cabe destacar que en esta
fase del ciclo de vida se suelen solapar el análisis y el diseño. Para establecer una delimitación,
podemos afirmar que mientras no se establecen los detalles de cómo estarán conformados los
atributos y los métodos, se está llevando a cabo un análisis, mientras que cuando se detalla cómo
están conformados, se está diseñando. La explicación en cada tabla sirve para verificar en el
enunciado del problema como se concluye la necesidad de cada uno de los componentes de la
abstracción. Se puede revisar cada una teniendo a mano el enunciado para corroborar la
necesidad de cada una.
Puesto de trabajo
Esta clase representa un recurso de la biblioteca. Es normal cuando se representa un recurso o una
entidad (mucha veces ambas palabras se usan como sinónimos en diferentes contextos) que
tengan pocos servicios y el grueso de la clase sea para el manejo de atributos bajo el principio de
ocultamiento de la información. Sin embargo, suelen ser clases que tiene declarados muchos
atributos los cuales en tiempo de ejecución representan el “estado del objeto” (los valores de las
variables que almacena un objeto).
PuestoDeTrabaj o
Escritorio
Esta clase representa un recurso o entidad.
Escritorio
- altura: doubl e
- anchura: double
- cantidadDeAsientosPosibles: int
- descripcion: String
- id: String
- informatica: boolean
- longitud: double
+ getAltura(): double
+ getAnchura(): double
+ getCantidadDeAsientosPosibles(): int
+ getDescripcion(): String
+ getDetalles(): String
+ getId(): String
+ getLongitud(): double
+ isInformatica(): boolean
+ setAltura(double): void
+ setAnchura(double): void
+ setCantidadDeAsientosPosibles(int): void
+ setDescripcion(String): void
+ setId(String): void
+ setInformatica(boolean): void
+ setLongitud(double): voi d
Silla
Esta clase representa un recurso o entidad.
Silla
- descripcion: Stri ng
- id: String
- informati ca: boolean
Computadora
Esta clase representa un recurso o entidad.
Computadora
+ getDescripcion(): String
+ getDetalles(): String
+ getId(): Stri ng
+ getMemori a(): String
+ getProcesador(): String
+ getVelocidadMemori a(): String
+ getVelocidadProcesador(): String
+ isNotebook(): boolean
+ setDescripcion(String): void
+ setId(String): voi d
+ setMemoria(Stri ng): void
+ setNotebook(boolean): void
+ setProcesador(String): void
+ setVelocidadMemoria(String): void
+ setVelocidadProcesador(String): void
Biblioteca
No se encontraron atributos en esta abstracción, sin embargo esta clase tiene la particular función
de representar las acciones principales de la biblioteca y “encamina” hacia los objetos que se
necesitan para cumplir la acción los requerimientos de servicios que estos poseen.
Biblioteca
+ agregarLibroEnReservaExistente(): void
+ agregarPrestado(): void
+ agregarPuestoDeTrabajoAlSalon(): void
+ agregarPuestoDeTrabajoInformaticoAlSalon(): void
+ creaPrestamoBasadoEnLaReserva(): void
+ creaReservaDelSocio(): void
+ obtenerLitaLectoresEnElSalon(): void
Socio
Esta clase es claramente una entidad y es importante desde el punto de vista que será el receptor
de la funcionalidad que se desarrolle. Notar que los servicios suelen ser simples porque la
funcionalidad principal la resuelven los objetos que invocan a éste.
Socio
Publico
Esta clase es claramente una entidad y es importante desde el punto de vista que será el receptor
de la funcionalidad que se desarrolle.
Publico
+ getApellido(): String
+ getCelular(): String
+ getDetalles(): Stri ng
+ getDni(): String
+ getEmail(): String
+ getNombre(): Stri ng
+ getSegundoNombre(): String
+ getTel(): String
+ setApellido(String): voi d
+ setCel ular(String): void
+ setDni (String): void
+ setEmail(String): void
+ setNombre(String): void
+ setSegundoNombre(String): void
+ setTel (String): void
Lector
No se encontraron métodos en esta abstracción, pero tiene sentido porque es una clase que
representa como trata administrativamente la biblioteca a sus dos entidades principales: el socio y
el público en general. Se podría haber considerado un sinónimo de dominio pero esto sería
incorrecto ya que los lectores sólo lo son para el salón de lectura y los socios tienen la posibilidad
adicional de retirar libros a préstamos, lo cual no se ajusta a la definición utilizada de “lector”. Esta
abstracción sólo la podremos resolver correctamente al final cuando se exponga el concepto de
interfaces. Por el momento se la considerará una clase más que claramente es independiente de
otras abstracciones.
Lector
+ getDetalles(): String
+ isEsSocio(): boolean
Salon
No se encontraron atributos en esta abstracción. Esto tiene como explicación que la clase sólo
representa la funcionalidad que cumple el salón para la biblioteca y los servicios expuestos
brindan principalmente dicha información.
Salon
+ agregarLector(): void
+ agregarPuestoDeTrabajo(): void
+ agregarPuestoDeTrabajoInformatico(): void
+ getListaLectores(): void
+ getListaPuestosDeTrabajo(): void
Libro
Esta clase es una entidad principal puesto que es el motivo sobre el cual gira todo el desarrollo.
Libro
- autor: String
- cantidad: int
- editorial: String
- genero: String
- isbn: String
- titulo: String
+ agregarPrestamo(): void
+ agregarReserva(): void
+ asignarPublico(): void
+ asignarSocio(): void
+ getAutor(): String
+ getCantidad(): int
+ getDetalles(): String
+ getEditorial(): String
+ getGenero(): String
+ getIsbn(): String
+ getPrestamosDelLibro(): void
+ getReservasDelibro(): void
+ getTitulo(): String
+ obtenerPublicoQueEstanUsandoEsteLibroEnElSalon(): void
+ obtenerSociosQueEstanUsandoEsteLibroEnElSalon(): void
+ setAutor(String): void
+ setCantidad(int): void
+ setEditorial(String): void
+ setGenero(String): void
+ setIsbn(String): void
+ setTitulo(String): void
Direccion
Esta clase se desarrolló independiente porque es brinda servicios para el proyecto
independientemente de donde se la use. Por ejemplo, no es lo mismo hablar de la dirección de un
socio que del público ya que el primero tiene libros que pertenecen a la biblioteca en su domicilio.
Por supuesto que es una decisión de diseño cuestionable y muchos lo pueden ver como un simple
atributo.
Direccion
- dpto: String
- esAvenida: bool ean
- esDepartamento: boolean
- nombreCal le: String
- nro: int
- piso: int
Calendario
Se diseñó esta abstracción de forma independiente usando el mismo criterio que con la clase
Direccion
Calendario
- fechaFin: String
- fechaIni cio: String
Prestamo
Representa la actividad de la biblioteca al prestar un libro al socio.
Prestamo
- id: int
+ getDetalles(): String
+ getId(): int
Reserva
Representa la actividad de reservar libros que puede realizar un socio
Reserv a
- activa: boolean
- id: int
- prestado: bool ean
BibliotecaTest
Esta clase se diseña para probar las funcionalidades requeridas por el desarrollo. Más adelante se
explica el concepto de simulación en los proyectos modernos de desarrollo informático. En este
caso, en lugar de usar una herramienta diseñada con este fin, se creó esta clase para que cumpla
la misma funcionalidad: usar un conjunto de datos fijos que simulan la interacción con el sistema.
BibliotecaTest
+ agregaPuestosDeTrabajo(): void
+ agregarLibrosEnReservasExistentes(): void
+ creaPrestamoBasadoEnLasDosPrimerasReservas(): void
+ l ibrosPedidosPorElPublico(): void
+ l ibrosPedidosPorSocios(): voi d
+ reservasDeSocios(): void
+ sociosQueSolicitaronComputadoras(): void
Existen muchas maneras de realizar simulaciones y muchas soluciones de software que se pueden
incorporar a un desarrollo en forma de API (Application Program Interface) para llevarlas a cabo.
Sin embargo, se puede lograr el mismo fin con clases sencillas diseñadas por el usuario y esta es la
solución que se utiliza en este desarrollo. El motivo es incorporar la menor cantidad de conceptos
nuevos fuera de los que se centran en las herramientas de la programación orientada a objetos
pero sin dejar de conocer la forma moderna de trabajar.
Paquetes en UML
Utilizando la definición que brinda Wikipedia:
Dado que normalmente un paquete está pensado como un directorio, los diagramas de
paquetes suministran una descomposición de la jerarquía lógica de un sistema.
Los paquetes están normalmente organizados para maximizar la coherencia interna dentro
de cada paquete y minimizar el acoplamiento externo entre los paquetes. Con estas líneas
maestras sobre la mesa, los paquetes son buenos elementos de gestión. Cada paquete
puede asignarse a un individuo o a un equipo, y las dependencias entre ellos pueden
indicar el orden de desarrollo requerido.
Esta definición es “casi” correcta. Se deben modificar algunos conceptos para que quede una
definición mejor ajustada a la realidad.
Una vez comprendido esto, la definición tiene un error bastante grave. Los paquetes definen
“espacios de nombres” mediante los cuales realizan los mencionados agrupamientos. Está bien
hacer la analogía con los directorios porque permite una imagen lógica del funcionamiento
jerárquico que es análogo al de los espacios de nombre, pero sin lugar a dudas, lo más importante
es el hecho de ser un espacio de nombres porque no crea una dependencia con ningún lenguaje
en particular que lo use de esta manera exactamente. Por lo tanto, como digo siempre, es bueno
buscar cosas en Internet porque ayudan, pero no se debe confiar ciegamente en lo que se
encuentra, se debe verificar siempre.
Sin embargo, la noción de jerarquía como si fueran directorios ayudan a comprender dos
conceptos importantes: el gráfico en forma de carpeta y las líneas de dependencia que indican
cuando un paquete depende de otro
Paquete
Ejemplo
Un diseño posible de la jerarquía de los espacios de nombre para el sistema de biblioteca se
muestra en la Figura 1-27.
entidades
gestion prestamos
reserv as
recursos
salidas
test
Notar que el agrupamiento de clases se realizó siguiendo “cierta lógica”, de manera que el nombre
del paquete indique su contenido y aísle las clases en él de otras. La mayor ventaja de esto es que
los espacios de nombres en sí mismos definen una visibilidad y permite que si dos clases se llaman
igual pero están en diferentes espacios de nombre, una declaración no colisione con la otra y sea
posible y permite claridad en la estructura del proyecto a realizar.
Sin embargo, este no es el modo usual en el que se muestran los paquetes y sub paquetes en UML
sino como gráficos de carpeta que muestran sus contenidos, lo cual incluye clases y sub paquetes.
entidades
+ Publico
+ Socio
gestion
+ Calendario + Prestamo
+ Direccion
+ GestionBiblioteca
+ Lector
+ prestamos (from gestion)
+ reservas
+ salidas
reserv as
recursos + Reserva
+ Computadora
+ Escritorio
+ Libro
(from gestion)
+ PuestoDeTrabajo
+ Salon
+ Silla
salidas
+ Consola
test
+ BibliotecaTest
(from gestion)
Figura 1-28: Representación de paquetes UML usado por la mayoría de herramientas de diseño
Se pueden observar los sub paquetes contenidos y las clases dentro de cada paquete según el
diseño que se elaboró del mismo.
Ejercicios
Los temas de los que tratan los ejercicios de este módulo son los siguientes: