jueves, 24 de mayo de 2012

2.5 Constructores y Destructores: Declaración, Uso y sus Aplicaciones.


Para crear un objeto se necesita reservar suficiente espacio en memoria e inicializar los valores de los campos que representan el estado del objeto. Este trabajo es realizado por un tipo especial de método denominado constructor.

Constructor
Un método constructor de una clase es un método especial que: tiene el mismo nombre que la clase y  no tiene tipo de retorno.  La sintaxis para la declaración de un método constructor es:

[atributos] [modificadores] <identificador> ( [parámetros] ) [inicializador]
{
// Cuerpo del constructor.
}
Donde: atributos (opcional) es información declarativa adicional, modificadores (opcional) se restringen a extern y a los modificadores de acceso, identificador es el nombre del método constructor (igual al nombre de la clase), parámetros (opcional) es la lista de parámetros pasados al constructor, inicializador (opcional). Con el inicializador, el constructor invoca previamente a otro constructor.
El inicializador puede ser uno de los siguientes: base([listaDeParámetros]) this([listaDeParámetros])
Cuerpo del constructor es el bloque de programa que contiene las instrucciones para inicializar la instancia de clase (objeto).
Destructor
La sintaxis para declarar un destructor es:
[Atributos] ~ <Identificador> ( ) 
{
// Cuerpo del destructor.
}
Una clase solamente puede tener un destructor. Los destructores no pueden heredarse o sobrecargarse, los destructores no pueden invocarse, sino que son invocados automáticamente, un destructor no acepta modificadores ni parámetros, por ejemplo, la siguiente es una declaración de un destructor para la clase Figura:
~ Figura()
{
// Instrucciones para limpiar.
}
La destrucción por defecto: Recogida de basura

El intérprete de Java posee un sistema de recogida de basura, que por lo general permite que no nos preocupemos de liberar la memoria asignada explícitamente.
El recolector de basura será el encargado de liberar una zona de memoria dinámica que había sido reservada mediante el operador new, cuando el objeto ya no va a ser utilizado más durante el programa (por ejemplo, sale del ámbito de utilización, o no es referenciado nuevamente).
El sistema de recogida de basura se ejecuta periódicamente, buscando objetos que ya no estén referenciados.

La destrucción personalizada: finalize

A veces una clase mantiene un recurso que no es de Java como un descriptor de archivo o un tipo de letra del sistema de ventanas. En este caso sería acertado el utilizar la finalización explícita, para asegurar que dicho recurso se libera. Esto se hace mediante la destrucción personalizada, un sistema similar a los destructores de C++.
Para especificar una destrucción personalizada se añade un método a la clase con el nombre finalize.

2.4 Métodos : Declaración , Mensajes , Paso de parámetros, Retorno de valores.


Los métodos o funciones miembro se definen dentro de la clase a la que pertenecen y constituyen la interfaz o forma de acceder a la estructura interna de los objetos es decir a los datos privados.

Los métodos definen cual son las operaciones que se pueden realizar con los atributos de los objetos de la clase. La ejecución de un programa orientado a objetos consiste, en recibir, interpretar y responder unos objetos a los mensajes que envían otros objetos. En P.O.O. un mensaje está asociado siempre con un método, de manera que cuando un objeto recibe un mensaje la respuesta a ese mensaje es ejecutar el método asociado

Modo de acceso
Específica el tipo de acceso permitido indicando que usuarios de la clase podrán acceder a ese método, los métodos son la única forma de acceso a los atributos privados. Por defecto los métodos tienen protección paquete, es decir son accesibles desde cualquier clase que pertenezca al mismo paquete. Todas las clases de un mismo fichero .java pertenecen a un mismo paquete.
  • Public: Accesible desde cualquier otra clase.
  • Package: Accesible sólo desde el mismo paquete.
  • Protected: Se comporta como un método público para los métodos del mismo paquete o de las subclases y para el resto como un método privado.
  • Prívate: Sólo accesible a través de métodos de la propia clase.

Retorno de valores:
 Un método puede devolver un valor a quien lo llama o no devolver nada. El valor devuelto por un método puede ser de un tipo primitivo de datos o una referencia, pero nunca  puede devolver más de un valor. El valor de retorno nunca puede ser un objeto de una superclase, sí de la misma clase o de una subclase. Si el método no devuelve nada el tipo devuelto por el método es el tipo void.

Paso de parámetros a una función o método.

Los parámetros de una función son variables locales que se inicializan en el momento de la llamada al método. Fuera de la función no se conocen y no pueden ser accedidas. Se crean al entrar en la función y se destruyen al salir de ella.
El paso de parámetros o argumentos a las funciones se puede hacer de dos formas. Paso por valor , paso por  referencia.

2.3 Referencia al objeto actual.

La utilización de THIS en lugar de hacer referencia explícitamente al objeto actual por su nombre (por ejemplo, thisform.command1.caption) hace que el código de programa pueda alternarse entre objetos, porque evita el nombre del objeto y encapsula automáticamente la clase primaria del objeto.

THIS permite hacer referencia a una propiedad o un objeto de una definición de clase. Los métodos de un bloque de definición de clase pueden utilizar THIS para especificar una propiedad o un objeto que existirá cuando se cree la clase.

Puesto que múltiples instancias de objetos comparten el mismo código de método, THIS siempre hace referencia a la instancia en la que está ejecutándose el código. Si hay múltiples instancias de un objeto, y se llama a uno de los métodos del objeto, THIS hace referencia al objeto correcto.

Cada objeto puede acceder a una referencia a si mismo mediante la palabra this. Cuando se hace una llamada a un método no static para un objeto especifico, el cuerpo del método utiliza en forma implícita la palabra this para hacer referencia a las variables de instancia y los demás métodos del objeto.

Ahora demostraremos el uso implícito y explicito de la referencia this para permitir al método main de la clase PruebaThis que muestre en pantalla los datos prívate de un objeto de la clase TiempoSimple. Hicimos esto para demostrar que, al compilador un archivo .java que contiene mas de una clase, el compilador produce un archivo de clase separado por la extensión .class para cada clase compilada.


// Ejemplo This.java

//Uso implicito y explicito de this para hacer referencia a los miembros de un objeto

public class PruebaThis
{
public static void main (String args[])
{
TiempoSimple tiempo=new TiempoSimple(15, 30, 19 );
System.out.println(tiempo.crearString());
}//fin de main
}//fin de la clase PruebaThis
//la clase TiempoSimple demuestra la referencia "this"
public class TiempoSimple
{
private int hora; //0-23
private int minuto; //0-59
private int segundo;//0-59
//si el constructor utiliza nombres de parametros identicos a
//los nombres de las variables de instancia, se reuiere la
//referencia "this" para diferenciar unos nombres de otros
public TiempoSimple (int hora,int minuto, int segundo)
{
this.hora=hora;//establece la hora del objeto "this"
this.minuto=minuto;//establece el minuto del objeto "this"
this.segundo=segundo;//establece el segundo del objeto "this"
}//fin del constructor de TiempoSimple
//usa la referencia "this" explicita e implicita para llamar StringUniversal
public String crearString()
{
return String.format("%24s:%s\n%24s:%s",
"this.aStringUniversal()",this.aStringUniversal(),
"aStringUniversal()",aStringUniversal());
}//fin del metodo crearString
//convierte a String en formato de hora universal (HH:MM:SS)
public String aStringUniversal()
{
//"this"no se requiere aqui para acceder a las variables de instancia,
//ya que el metodo no tiene variable locales con los mismos
//nombres que las variables de instancia
return String.format("%02d:%02d:%02d",
this.hora, this.minuto, this.segundo);
}//fin del metodo sStringUniversal
}//fin de la clase TiempoSimple



2.2 Instanciación de una clase.


Podemos interpretar que una clase es el plano que describe como es un objeto de la clase, por tanto podemos entender que a partir de la clase podemos fabricar objetos. A ese objeto construido se le denomina instancia, y al proceso de construir un objeto se le llama instanciación.
Cuando se construye un objeto es necesario dar un valor inicial a sus atributos, es por ello que existe un método especial en cada clase, llamado constructor, que es ejecutado de forma automática cada vez que es instanciada una variable. Generalmente el constructor se llama igual que la clase y no devuelve ningún valor. Análogamente, destructor es un método perteneciente a una clase que es ejecutado de forma automática cuando un objeto es destruido. Java no soporta los destructores. Es posible que exista más de un constructor en una clase, diferenciados sólo en los parámetros que recibe, pero en la instanciación sólo será utilizado uno de los constructores.

Ejemplo:

clase base

package ejemplo_de_libro;

import java.util.Scanner;
public class Main {

  
    public static void main(String[] args) {
     Scanner entrada = new Scanner(System.in);
     LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones();
     System.out.println("Escribe algo: ");
     String nombreDelCurso = entrada.nextLine();
     System.out.println();
     miLibroCalificaciones.mostrarMensaje(nombreDelCurso);
    }
}


clase derivada
package ejemplo_de_libro;


public class LibroCalificaciones {
public void mostrarMensaje(String nombredelsaludo)
{
    System.out.printf("Bienvenido al libro de calificaciones para :\n%s\n", nombredelsaludo);
}
}


miércoles, 23 de mayo de 2012

2.1 Declaración de clases: atributos, métodos, encapsulamiento.


La declaración de una clase define la estructura de la misma. Dicho de otra forma, la declaración de una clase informa de los elementos que la conforman. Posteriormente a ser declarada, una clase debe ser implementada convenientemente, es decir, se debe escribir el código correspondiente a los procedimientos y funciones que determinan el funcionamiento de esa clase.
Las clases se declaran en la sección TIPO del script pues las clases son, al fin y al cabo, tipos de datos.
La programación orientada a objetos se basa en la programación de clases; a diferencia de la programación estructurada, que está centrada en las funciones.
Una clase es un molde del que luego se pueden crear múltiples objetos, con similares características.
Una clase es una plantilla (molde), que define atributos (variables) y métodos (funciones)
La clase define los atributos y métodos comunes a los objetos de ese tipo, pero luego, cada objeto tendrá sus propios valores y compartirán las mismas funciones.
Debemos crear una clase antes de poder crear objetos (instancias) de esa clase. Al crear un objeto de una clase, se dice que se crea una instancia de la clase o un objeto propiamente dicho.

Tipos de atributos.
Objetivos:
a) Profundizar en el concepto de atributo de una clase e indicar los tipos de  atributos en Java
b) Interpretar  el  código fuente de una aplicación Java donde aparecen distintos tipos de atributos
c) Construir una aplicación Java sencilla,  convenientemente especificada,  que emplee clases con diferentes tipos de atributos.

Los atributos, también llamados datos o variables miembro son porciones de información que un objeto posee o conoce de sí mismo. Una clase puede tener cualquier número de atributos o no  Tener ninguno. Se declaran con un identificador y el tipo de dato correspondiente.


Modificador       Visibilidad
public                 Pública (+)
protectec            Protegida / en la herencia(#)
private                Privada(-)
package              De paquete (~)

Métodos.
Java como todo lenguaje de programación orientado a objetos utiliza los llamados métodos. A continuación veremos cómo se crea un método y como se utilizan.
Se podría decir que existen 2 grandes tipos de métodos, el primer tipo de método son métodos que realizan procesos, puedes realizar cualquier operación con ellos, sin embargo el propósito es manipular variables existentes. El segundo tipo de métodos son los que realizan un proceso o cálculo, y calculan una variable específica, un ejemplo podría ser un método para obtener el valor de una multiplicación.
Los métodos en java pueden tener parámetros, es decir,  que un método puede utilizar variables predefinidas para ser utilizadas en sus procesos.

Encapsulamiento.
Como se puede observar de los diagramas, las variables del objeto se localizan en el centro o núcleo del objeto. Los métodos rodean y esconden el núcleo del objeto de otros objetos en el programa. Al empaquetamiento de las variables de un objeto con la protección de sus métodos se le llama encapsulamiento. Típicamente, el encapsulamiento es utilizado para esconder detalles de la puesta en práctica no importantes de otros objetos. Entonces, los detalles de la puesta en práctica pueden cambiar en cualquier tiempo sin afectar otras partes del programa.

El encapsulamiento de variables y métodos en un componente de software ordenado es, todavía, una simple idea poderosa que provee dos principales beneficios a los desarrolladores de software.


Modularidad.
 esto es, el código fuente de un objeto puede ser escrito, así como darle mantenimiento, independientemente del código fuente de otros objetos. Así mismo, un objeto puede ser transferido alrededor del sistema sin alterar su estado y conducta.
Ocultamiento de la información, es decir, un objeto tiene una "interfaz publica" que otros objetos pueden utilizar para comunicarse con él. Pero el objeto puede mantener información y métodos privados que pueden ser cambiados en cualquier tiempo sin afectar a los otros objetos que dependan de ello.
Los objetos proveen el beneficio de la modularidad y el ocultamiento de la información. Las clases proveen el beneficio de la reutilización. Los programadores de software utilizan la misma clase, y por lo tanto el mismo código, una y otra vez para crear muchos objetos.
En las implantaciones orientadas a objetos se percibe un objeto como un paquete de datos y procedimientos que se pueden llevar a cabo con estos datos. Esto encapsula los datos y los procedimientos. La realidad es diferente: los atributos se relacionan al objeto o instancia y los métodos a la clase. ¿Por qué se hace así? Los atributos son variables comunes en cada objeto de una clase y cada uno de ellos puede tener un valor asociado, para cada variable, diferente al que tienen para esa misma variable los demás objetos. Los métodos, por su parte, pertenecen a la clase y no se almacenan en cada objeto, puesto que sería un desperdicio almacenar el mismo procedimiento varias veces y ello va contra el principio de reutilización de código.







sábado, 19 de mayo de 2012

1.2 Lenguaje de modelado unificado: diagrama de clases.

¿Qué es el UML?
El “Lenguaje de Modelado Unificado” – del inglés Unified Modeling Language (UML) – es un lenguaje basado en diagramas para la especificación, visualización, construcción y documentación de cualquier sistema complejo, aunque nosotros nos centraremos en el caso específico de sistemas software.

*Nota: otro de los ámbitos en los que UML se utiliza habitualmente es el modelado de los procesos de negocio de una organización. Por ejemplo, se puede hacer un modelo de cómo funciona (cómo desarrolla su labor diaria) el Departamento de Compras de una determinada empresa.

Por tanto, UML es un lenguaje para describir modelos. Básicamente, un modelo es una simplificación de la realidad que construimos para comprender mejor el sistema que queremos desarrollar. Un modelo proporciona los “planos” de un sistema, incluyendo tanto los que ofrecen una visión global del sistema como los más detallados de alguna de sus partes. Para comprender el objetivo del modelado con UML, es muy útil compararlo con otras áreas de ingeniería, como es la construcción de edificios o automóviles, con sus diferentes planos y vistas; o incluso con la industria cinematográfica, donde la técnica del storyboarding (representación de las secuencias de un película con viñetas dibujadas a mano) constituye un modelado del producto.

Si bien UML es independiente de las metodologías de análisis y diseño y de los lenguajes de programación que se utilicen en la construcción de los sistemas software, es importante destacar que se basa en el paradigma de la orientación a objetos. Por tanto, es especialmente adecuado cuando se ataca la construcción de sistemas software desde la perspectiva de la orientación a objetos.

La especificación, visualización, construcción y documentación de cualquier sistema software requiere que el sistema pueda ser estudiado desde diferentes puntos de vista, ya que un usuario final necesita una visión diferente del sistema de la que necesita un analista o un programador. UML incorpora toda una serie de diagramas y notaciones gráficas y textuales destinadas a mostrar el sistema desde las diferentes perspectivas, que pueden utilizarse en las diferentes fases del ciclo de desarrollo del software. En este documento presentamos uno de estos puntos de vista, concretamente el conocido como “modelado estático”, que se concreta en los diagramas de clases. Los diagramas de clases muestran para nosotros, pues, la estructura estática de un sistema software (en las diferentes fases de su construcción, por ejemplo, se suele hablar de diagramas de clases “de análisis”, que posteriormente se convierten en diagramas de clases “de diseño”). Más concretamente, describen los elementos que en él existen (por ejemplo, clases), la estructura interna de estos elementos y cómo estos se interrelacionan entre sí. Es importante destacar que no examinaremos de manera exhaustiva todos los elementos que se pueden incorporar en un diagrama de clases, sino que únicamente estudiaremos aquellos que son de especial interés en esta asignatura. 

Debemos tener claro también que un diagrama de clases UML (esto es aplicable a cualquier tipo de diagrama UML) es tan sólo una vista del modelo estático del sistema en cuestión, esto quiere decir que una misma clase puede aparecer en varios diagramas y que podemos crear más de un diagrama, bien si es el número de clases es elevado o bien si queremos mostrar dos o más partes del sistema que tienen poco que ver en diagramas separados. Hay que tener en cuenta que los diagramas son herramientas de comunicación con otras personas que quizá mañana vean nuestro trabajo, y lo importante es que la información sea legible a la vez que completa.

El término “estructura estática” está relacionado con el hecho de que los diagramas de clases muestran todas las relaciones y datos que el sistema necesita durante su operación. Esta vista se complementaría con la “estructura dinámica”, que muestra las relaciones entre los elementos teniendo en cuenta la dimensión temporal, que no tratamos en este documento.


Clases, atributos y operaciones 

Una clase es una descripción de un conjunto de objetos que comparten la misma estructura y semántica y que presentan el mismo comportamiento y relaciones. En UML se presentan mediante un rectángulo con tres compartimentos. En el primero de estos compartimentos se indica cual es el nombre de la clase, en el segundo se indican cuáles son las propiedades de la clase (atributos en terminología UML), mientras que en el último se indica el comportamiento de la clase, formado por una colección de servicios (operaciones en terminología UML y métodos en algunos lenguajes de programación como Java). Por ejemplo, el siguiente diagrama muestra una clase Complejo, con dos atributos, un constructor y algunas operaciones para la manipulación sencilla de números complejos.



En relación con el nombre de la clase, es importante destacar las siguientes recomendaciones (convenciones de nombrado): 
  • El nombre de una clase debería ser un sustantivo en singular y debería comenzar con mayúsculas. 
  • El nombre de una clase debe de estar de acuerdo a su semántica o intención, en otras palabras, debe ser significativo a la vez que simple. De no ser así, se dificultará la posible reutilización posterior de esa clase.



Las propiedades de las clases se representan mediante una lista de atributos, que figuran en el segundo compartimento. Para cada atributo se pueden indicar, entre otras características, su nombre, su tipo, y su visibilidad.

El nombre de cada atributo debe ser también significativo, pero a diferencia de los nombres de clase, suelen comenzar en minúscula. El tipo de cada atributo puede ser cualquier clase o bien puede ser un tipo de datos básico. El nombre de cada atributo y su tipo quedan separados mediante dos puntos (‘:’). 

Finalmente, la visibilidad de cada atributo nos indica hasta que punto las operaciones de otras clases pueden acceder al atributo. Tenemos tres posibilidades:
  • Público: el atributo puede ser accedido desde otras clases. Para indicar en UML que un atributo es de acceso público, es necesario anteponer al nombre del atributo el símbolo ‘+’. Cabe recordar que en orientación a objetos los atributos no suelen definirse como públicos (por el principio de ocultación de la información), y si es necesario que sean accesibles se definen operaciones de acceso, conocidas como getters (como las operaciones getParteReal y getParteImaginaria del ejemplo anterior) y setters (como podrían ser operaciones setParteReal y setParteImaginaria que podríamos añadir en el ejemplo anterior). 

  • Protegido: el atributo puede ser accedido desde las clases descendientes. Para indicar en UML que un atributo es de acceso protegido, es necesario anteponer el símbolo ‘#’ al nombre del atributo. 

  • Privado: el atributo no puede ser accedido desde otras clases. Para indicar en UML que un atributo es de acceso privado, es necesario anteponer al nombre del atributo el símbolo ‘-’.
Finalmente, en el tercer compartimento se indica el comportamiento de la clase, que queda representado mediante una lista de operaciones. Para cada operación es necesario especificar su signatura, que tiene la siguiente sintaxis: 

visibilidad nombreMetodo (Parámetro, Parámetro, ...)[: tipo de retorno]

Tal y como figura en la especificación de la signatura, hay que indicar la visibilidad de las operaciones de la clase, anteponiendo los símbolos ‘+’ (operación pública), ‘#’ (operación protegida) o ‘-‘ (operación privada) al nombre de la operación. La semántica de estos símbolos es idéntica a la de los atributos. 

Los parámetros (se pueden especificar cero, uno o más) responde a la sintaxis:

{[dirección] nombre: tipo [=valor por defecto]}

donde la dirección puede tomar uno de estos valores: 
  • in, para especificar que se trata de un parámetro de entrada que no se puede modificar. 
  • out, para especificar los parámetros de salida, que pueden modificarse para comunicar información al invocador. 
  • inout, que especifica un parámetro de entrada que puede modificarse.
Nótese que UML permite adaptar las notaciones gráficas anteriores a lenguajes de programación concretos, por lo cual podríamos también dibujar la clase anterior cambiando la notación de atributos y operaciones por la sintaxis propia de Java, como se muestra en la siguiente figura:


También es posible ocultar alguno de los compartimentos o alguno de los detalles de los atributos u operaciones si creemos que no añaden información importante en el diagrama o bien están visibles en otro diagrama, en el caso de que una clase aparezca en varios de ellos.



Atributos y operaciones de las clases

UML proporciona una notación especial para distinguir los miembros (atributos y operaciones) de las instancias y de los miembros de las clases. Hasta ahora, solo hemos utilizado propiedades de las instancias, pero vamos a suponer ahora que quisiéramos añadir un atributo de la clase a CuentaBancaria con el número de cuentas que se han creado, y un método de la clase para tener acceso a ese atributo. 

En UML, se subrayan los atributos y operaciones de la clase para diferenciarlos de los de las instancias, como se muestra en la siguiente figura:



Relaciones entre clases

Una relación entre clases se puede definir como una conexión entre dos o más clases. En orientación a objetos, las principales relaciones que se pueden establecer entre clases son las de generalización (o herencia), asociación, agregación y dependencia (o uso).

=Asociaciones y dependencias=
Las asociaciones representan relaciones estructurales entre clases. En UML, se define una asociación como una relación que describe un conjunto de enlaces, donde cada enlace define una interconexión semántica entre instancias de las clases que participan en la asociación. Se dice que las instancias de una de las clases “conocen” a las instancias de la otra clase a las que están enlazadas. 

A grandes rasgos, podemos distinguir dos tipos de asociaciones: 
  • Asociaciones binarias: en este caso, el vínculo se establece entre dos clases. 
  • Asociaciones n-arias: en este caso, el vínculo se establece entre tres o más clases.

=Asociaciones binarias=
En UML, las asociaciones binarias se representan mediante una línea continua que conectan dos clases (aunque puede darse el caso particular en que las dos clases involucradas en la asociación binaria sean la misma. En este caso, hablamos de asociaciones reflexivas).


A la representación de las asociaciones se le pueden añadir ciertos elementos que aportan información adicional, como la direccionalidad, los nombres de rol o la cardinalidad, que veremos a continuación.

Una asociación binaria representa una interconexión entre las instancias (u objetos) de dos clases A y B. Esta interconexión puede ser o no bidireccional. En caso que la interconexión sea bidireccional, (opción por defecto) será posible navegar desde las instancias de la clase A a las instancias de la clase B, y a la inversa, es decir, desde las instancias de la clase B podremos acceder a las instancias de la clase A. Si decidimos que la interconexión debe ser unidireccional, entonces será necesario añadir una flecha abierta encima de la línea de asociación; esta flecha indica el sentido de la navegación.


Las asociaciones pueden tener nombre. Dado que por defecto las asociaciones son bidireccionales, se pueden especificar dos nombres en la asociación. En UML, estos nombres son los roles que juegan las clases en la asociación.

Dado que una asociación binaria representa una interconexión de instancias de dos clases, es necesario indicar cuántos objetos de cada clase pueden estar involucrados en dicha interconexión. En otras palabras, es necesario indicar la cardinalidad (o multiplicidad) de la asociación. Podemos indicar el valor máximo y mínimo de esta cardinalidad. Dadas dos clases A y B, la cardinalidad indica con cuántas instancias de B se puede relacionar cada instancia de A (valor mínimo y máximo). Los casos más generales de cardinalidad son los siguientes: 
  • Cardinalidad “uno a uno”: en este caso una instancia de la clase A se relaciona con una única instancia de la clase B, y cada instancia de la clase B se relaciona con una única instancia de la clase A. 
  • Cardinalidad “uno a muchos”: en este caso una instancia de la clase A se relaciona con varias instancias de la clase B, y cada instancia de la clase B se relaciona con una única instancia de la clase A. 
  • Cardinalidad “muchos a muchos”: en este caso una instancia de la clase A se relaciona con varias instancias de la clase B, y cada instancia de la clase B se relaciona con varias instancias de la clase A.
=Asociaciones binarias reflexivas=
Las asociaciones reflexivas son un tipo de asociación binaria en el cual la clase de origen y destino de la asociación es la misma. Como ejemplo típico, trataremos la organización de una empresa, definida mediante la relación “es responsable de” entre instancias de la clase Empleado.


En el ejemplo anterior hemos dibujado una asociación bidireccional donde un empleado puede tener cero, uno o muchos empleados bajo su responsabilidad, mientras que todos los empleados tienen un único responsable (excepto el Director General, que no tiene ningún responsable). 

Es importante “visualizar” la estructura de instancias que se desprenden de este diagrama. Esta estructura, en nuestro caso particular, representará un árbol de instancias, por la restricción de que una persona tiene un solo responsable como mucho.

=Asociaciones n-arias=
Una asociación n-aria constituye una relación que se establece entre tres o más clases. Un ejemplo típico es el de la cuenta de los goles marcados por jugadores de los distintos equipos en el campeonato de Liga. En este caso, hay que enlazar a un jugador con un equipo en una temporada, ya que en diferentes temporadas puede estar en diferentes equipos (incluso quizá en la misma). Estas relaciones se representan en UML mediante un rombo, y pueden tener una clase de la asociación con la información que depende de la existencia del propio enlace, en nuestro caso, una clase Registro con los datos del jugador en el equipo y temporada indicado en el enlace.


Dado un jugador concreto que juega en un equipo concreto tendrá varios registros de goles en cada temporada. Dada una temporada concreta y un equipo concreto, habrá un registro de goles para cada jugador del equipo. Finalmente, dada una temporada y jugador concreto, puede meter goles en diversos equipos (asumimos que un jugador dentro de una misma temporada puede cambiar de equipo). 

A la hora de implementar en Java, se suele introducir una clase adicional y se expresan los enlaces n-arios mediante enlaces binarios.

=Relaciones de Agregación y Composición=
Las relaciones de agregación y composición son tipos especiales de asociación. En las asociaciones binarias, se asume que las instancias de ambas clases son independientes, en el sentido de que no dependen de la existencia de las instancias de la otra clase. Sin embargo, las relaciones de agregación asumen una subordinación conceptual del tipo “todo/parte”, o bien “tiene un”.

A efectos de implementación en Java, las técnicas son las mismas, con la salvedad de que no se pueden crean ciclos de enlaces, es decir, una “parte” no puede estar agregada en sí misma, ni directa ni indirectamente. Se puede eliminar la instancia que representa al “todo” y seguir existiendo las instancias que eran sus “partes”.
Las agregaciones se representan mediante un rombo en la parte del objeto agregado, para distinguirlo de los objetos “más pequeños” que contiene. Por ejemplo, puede considerarse que un Grupo de Trabajo es un agregado de sus miembros, lo cual no implica que cuando el grupo se disuelva, las instancias que representan a sus miembros deban eliminarse también. Nótese que las estructuras de objetos resultantes son en general un grafo dirigido acíclico.


La composición es un tipo de agregación más específico en el cual se requiere que una instancia de la clase que representa a las partes esté asociada como mucho con una de las instancias que representan el “todo” (nótese que no puede haber multiplicidad “muchos” en el extremo del agregado). De esta manera, el objeto compuesto gestiona sus partes en exclusiva. Por lo general, la estructura de objetos resultantes es siempre un árbol. Un ejemplo típico es un procesador de textos que estructure los documentos en páginas, y éstas en párrafos. La notación en UML es utilizar un rombo relleno en el lado de la clase que representa al todo”.


Otro ejemplo clarificador es el de un Directorio y las entradas de directorio que contiene (ficheros, otros directorios), que se deben eliminar cuando se elimina el directorio. Por otro lado, un fichero está en un solo directorio (aunque los enlaces simbólicos puedan hacer parecer que esto no se cumple, internamente es así en los sistemas operativos).

En Java, las composiciones se implementan con las mismas técnicas que las asociaciones, con la salvedad de que en la composición la gestión de la memoria de las “partes” debe obligatoriamente ser responsabilidad de la clase agregada. En Java, esto significa que el documento guardará referencias a sus páginas en exclusiva, es decir, no las cederá a otros objetos, y deberá proveer mecanismos para la creación y el borrado de páginas. Nótese que en Java el borrado solo requiere “perder” la referencia, ya que si borramos una página de este modo, el recolector de basura la eliminará y a su vez eliminará las instancias de los párrafos contenidos en la página, puesto que ninguna otra instancia podía contener referencias a esos párrafos. Por ello, se dice que los ciclos de vida de las “partes” están supeditados al ciclo de vida del “todo”.

Las composiciones implican la propagación a las “partes” de las invocaciones a las operaciones sobre el “todo”. Siguiendo el ejemplo anterior, si pedimos que se copie o imprima un documento, el documento hará copia o mandará imprimir a sus páginas y a su vez, las páginas harán lo mismo con los párrafos que contienen.

=Relaciones de Dependencia o Uso=
Una dependencia entre clases denota una relación de uso entre las mismas. Esto quiere decir que si la clase de la que se depende cambia, es posible que se tengan que introducir cambios en la clase dependiente. Por ejemplo, cuando un método de la clase A tiene como parámetro un objeto de la clase B, decimos que A depende de B, usa sus servicios. 

Por ejemplo, si tenemos una clase Impresora que tiene una operación imprimir que toma como parámetro una instancia de la clase Documento, diremos que Impresora depende de Documento. Nótese que las impresoras no mantienen enlaces con los documentos que imprimen, sólo los procesan con la operación correspondiente, pero después de cada llamada a imprimir, no “recuerdan” los documentos que imprimieron. Se dice que este es el tipo de relación entre clases “más débil”, y como recomendación general, sólo debería mostrarse en los diagramas cuando aporten alguna información importante.


Imaginemos que la clase impresora invoca a las tres operaciones de la instancia de Documento para imprimirlo. Pues bien, si por ejemplo, eliminamos la operación “obtenerPie” (o cambiamos los parámetros o el tipo de retorno de alguna de las operaciones de Documento), habrá que cambiar la implementación de imprimir.

Las dependencias no se reflejan en ningún elemento específico de Java, sólo que la clase dependiente hará referencia a la clase de la que depende en algún punto (variable local, parámetro), y por tanto suele ser habitual encontrar declaraciones import de las clases de las que se depende.

Nótese que toda relación de asociación implica lógicamente una relación de dependencia (una para cada uno de los sentidos de la asociación), pero no al contrario. Recordemos que una relación de asociación implica enlaces entre las clases que participan en la asociación; esto no es necesario en una dependencia.

Relaciones de generalización

La generalización es una relación taxonómica entre una clase más general (denominada superclase o clase padre) y una clase más específica (denominada subclase o clase hija). Lo fundamental de esta relaciones es que tienen que reflejar relaciones “es un tipo de”, es decir, las instancias de la subclase son un tipo específico de instancias de la clase padre. De ello se deriva una propiedad importante: los objetos de la clase hija pueden emplearse en cualquier lugar en que se requiera una instancia de la clase padre, pero no a la inversa. La subclase hereda las propiedades, el comportamiento y las relaciones de la superclase, a la vez que puede añadir sus propias propiedades, relaciones y comportamiento.

Nótese que la relación de herencia es transitiva “de abajo hacia arriba”, es decir, si C es un tipo de B y B es un tipo de A, entonces C es un tipo de A.

En UML la relación de generalización se representa mediante una línea sólida que une superclase y subclase. Además, esta línea sólida incorpora una flecha con punta triangular que queda localizada al lado de la superclase. Un conjunto de clases relacionadas entre si mediante relaciones de generalización constituye una jerarquía de herencia que puede ser un grafo o un árbol dependiendo, respectivamente, de si existe o no herencia múltiple.

Ejemplo. Cuentas Bancarias: Relación de generalización 
Vamos a extender la clase CuentaBancaria anterior para soportar dos nuevos tipos de cuentas (una vez más, es un ejemplo no realista, sólo con propósitos didácticos): 

1. Cuentas a plazo fijo, en las cuales si se realiza una retirada de dinero se produce una penalización sobre el importe retirado (por simplicidad, no consideramos la fecha de vencimiento). En estas cuentas, el saldo nunca puede ser negativo. 

2. Cuentas con crédito limitado, en las cuales no se permite un nivel de “números rojos” superior a uno especificado al abrir la cuenta.

En ambos casos redefiniremos la operación retirar de la clase CuentaBancaria, y añadiremos el código necesario para cubrir los requisitos de los nuevos tipos de cuentas.


Con esta nueva extensión, podríamos considerar abstracta la clase CuentaBancaria (si es que en nuestra entidad bancaria imaginaria nuestra entidad bancaria sólo considera dos tipos de cuentas). En el diagrama anterior hemos marcado la clase CuentaBancaria como abstracta, lo que se visualiza e UML con el nombre de la clase en cursiva (aunque no lo mostramos aquí,la cursiva se utiliza también para diferenciar los métodos abstractos en UML). Para cambiar la implementación en Java anterior, bastaría con poner el cualificador abstract antes de la palabra clase class. 

El código correspondiente a la subclase CuentaPlazoFijo es el siguiente (el de la clase CuentaCreditoLimitado se deja como ejercicio al lector).

public class CuentaPlazoFijo extends CuentaBancaria {

/** Porcentaje de penalizacion en retiradas */
private float penalizacion;

/**
* Crea una cuenta a plazo fijo indicando
* numero, saldo inicial y penalizacion sobre la
* cantidad retirada en caso de sacar dinero.

* @param penalizacion porcentaje entre 0 y 1 de penalizacion

*/
public CuentaPlazoFijo(String numero, float saldoInicial,
float penalizacion){
super(numero, saldoInicial);
this.penalizacion = penalizacion;
}

/**
* Redefinicion de retirar para imponer una penalizacion
* a la cantidad retirada.
*/
public void retirar(float cantidad) throws SaldoResultanteNoPermitido {
if (saldo - cantidad - cantidad*penalizacion >=0 ){
saldo -= cantidad;
saldo -= cantidad * penalizacion;
}
else
throw new SaldoResultanteNoPermitido();
}
}

Otros Elementos de Modelado

=Interfaces=
Una interfaz en UML es la especificación de un conjunto de operaciones relacionadas que describen un servicio que puede implementar una clase (o un componente, aunque no lo vemos aquí). El uso de interfaces permite separar la implementación de las especificaciones, y por tanto es un concepto de diseño orientado a objetos muy importante. El esfuerzo de recopilación de conocimiento en diseño orientado a objetos –véase el primer capítulo de (Gamma 1995)– se fundamenta en un uso intensivo de las interfaces. Las interfaces en UML se representan como clases estereotipadas con el estereotipo <<interface>>, que se muestra encima del nombre de la interfaz.

Esto quiere decir que son un tipo especial de otro elemento de modelado de UML (en este caso, las interfaces se consideran un tipo de clase, pero esto queda fuera del alcance de este documento) 

Las interfaces sólo poseen operaciones abstractas (en cursiva). Las interfaces pueden derivar de otras, en cuyo caso “heredan” la especificación de todas sus interfaces antecesoras. 

Las clases que implementan la interfaz (es decir, que proveen la implementación para todas las operaciones especificadas en ella) se indican en el diagrama mediante un símbolo de generalización pero con línea discontinua.

Ejemplo. Cuentas Bancarias: Utilización de Interfaces 
Siguiendo nuestro ejemplo anterior, consideremos ahora que se requiere una clase para el cálculo de impuestos. Esta clase necesita tener en cuenta todos los productos bancarios sujetos a impuestos que tiene el contribuyente. En este caso, nuestra clase CálculoImponibles proveerá un método para que la aplicación le indique los productos del cliente. Nótese que el servicio que requiere esta clase es solamente preguntar la base imponible en Euros del producto. Para ello, diseñamos una interfaz ProductoBancario con un método a tal efecto. Así, clases de jerarquías disjuntas, como cuentas bancarias y carteras de fondos, por ejemplo, pueden implementar esa interfaz y aparecer a la clase CálculoImponibles como ProductosBancarios, gracias al polimorfismo. Esto permite que en el futuro añadamos nuevos productos sin más que implementar la interfaz.


En Java, las interfaces son una construcción sintáctica parecida a las clases, pero que no permite implementar operaciones ni incluir atributos de instancia. Aunque en ocasiones se utilizan para “simular” la herencia múltiple que no posee el lenguaje, éste no es su cometido principal. Por ejemplo, la interfaz ProductoBancario se codificaría de la siguiente forma:
   
public interface ProductoBancario {
public float getValor();
}

Y las clases que implementan la interfaz la incluirían en su cláusula implements, como por ejemplo:

public class CuentaPlazoFijo extends CuentaBancaria implements ProductoBancario{

//métodos y operaciones, incluyendo la implementación de getValor().

}

=Excepciones=
En UML, las excepciones son clases “estereotipadas” mediante <<exception>>. 

Como ya se explicó, esto quiere decir que son un tipo especial de otro elemento de modelado de UML (en este caso, las excepciones se consideran un tipo de señal, que a su vez es un tipo de clase, pero esto queda fuera del alcance de este documento)

Visualmente, se pueden representar igual que una clase, pero mostrando el estereotipo <<exception>> encima del nombre de la excepción. Por ejemplo, la siguiente figura muestra una excepción que podría aparecer en nuestra aplicación bancaria.


Una excepción puede tener atributos (que en este caso se suelen llamar parámetros de la excepción) que aportan más información sobre por qué se ha lanzado una determinada instancia de una excepción. En el ejemplo anterior, si se intenta hacer una operación que supera el crédito asignado a una cuenta, la cantidad en la cual se supera se puede incluir como información adicional. Las excepciones también pueden tener operaciones, aunque no es el caso de nuestro ejemplo.

Ejemplo. Cuentas Bancarias: Excepciones
Las excepciones que puede lanzar una determinada operación forman parte de la cabecera de la operación en muchos lenguajes, y en ocasiones, puede resultar clarificador indicarlo explícitamente en el modelo. Por ejemplo, el siguiente diagrama indica que la operación retirar de la clase CuentaBancaria puede lanzar instancias de la excepción Crédito Superado mediante una asociación con el estereotipo <<send>> entre la operación y la clase que representa la excepción.


Estructuración de las Clases: Paquetes y Múltiples Diagramas 

Los paquetes en UML son agrupaciones de elementos del modelo de nuestro sistema, útiles cuando estamos modelando problemas grandes, que pueden llegar a contener cientos de clases y de otros elementos de UML. 

Un paquete contiene un conjunto de elementos cualesquiera del modelo (clases, relaciones, etc.) y puede contener otros paquetes, de modo que el modelo del sistema se puede estructurar en una jerarquía de paquetes anidados unos dentro de otros, formando un árbol. 

La norma para agrupar en paquetes es poner juntos en el mismo paquete los elementos que estén semánticamente relacionados. Por otro lado, las propiedades generales que debe cumplir la estructuración en paquetes son alcanzar el mínimo acoplamiento entre paquetes (mínimo número de dependencias entre las clases o elementos de diferentes paquetes) y máxima cohesión entre los elementos de un mismo paquete. 

Gráficamente, un paquete se representa con forma de carpeta. Existen elementos notacionales adicionales para representar visibilidad dentro de los paquetes y otros elementos, pero quedan fuera del alcance de este documento. 

Ejemplo. Cuentas Bancarias: Paquetes UML 
En nuestro ejemplo podríamos agrupar las clases vistas anteriormente sobre cuentas bancarias en un paquete denominado “Cuentas Bancarias”.


=Dependencias entre paquetes=
Pueden definirse dependencias entre paquetes que son el reflejo de las dependencias entre las clases que están contenidas en los paquetes. 

Ejemplo. Cuentas Bancarias: Dependencias entre paquetes
Por ejemplo, si añadimos la gestión de las declaraciones de la Renta por el banco, tendremos una clase DeclaraciónRenta –que incluiremos en un nuevo paquete “Gestión Hacienda”– que tendrá una dependencia (y quizá una asociación, pero eso no nos interesa aquí) con la clase CuentaBancaria, indicando la cuenta en la que se debe hacer el reintegro o pago (dependiendo de si es positiva o negativa) del resultado de la declaración. 

Así, la dependencia entre las clases, se traducirá en una dependencia entre los paquetes.


=Paquetes UML y paquetes Java=
Aunque tienen el mismo nombre, son conceptos diferentes, ya que un paquete en UML es un agrupador de elementos del modelo, mientras que un paquete Java agrupa clases Java, que no necesariamente tienen que guardar una correspondencia uno a uno con los elementos del modelo.

Podemos representar la estructura de las clases Java resultantes del modelo mediante un diagrama de paquetes aparte. 

Así, nuestro anterior diagrama podría traducirse a la siguiente estructuración de paquetes en Java (siguiendo las convenciones de nombrado de paquetes Java típica de acuerdo al dominio de la organización donde se desarrolla el software, en nuestro caso, las Ingenierías Técnicas en Informáticas– ITIs):


También hemos incluido el paquete de las librerías estándar java.io mostrando que la implementación en Java de las clases se haría guardando en disco la información mediante las bibliotecas de flujos de Java.

Ejemplo. Cuentas Bancarias: Paquetes en Java
Como ejemplo ilustrativo, la clase DeclaracionRenta podría tener la siguiente cabecera que refleja de manera explícita sus dependencias (nótese que la clase CuentaBancaria debe haberse declarado como public class para poder ser vista desde otros paquetes Java): 

package es.uoc.iti.bancario.hacienda;
import java.io.*;
import es.uoc.iti.bancario.cuentas.CuentaBancaria;

public class DeclaracionRenta{
// operaciones que usan CuentaBancaria y clases de java.io
// y otras operaciones... 
}


1.1 Elementos del modelo de objetos: clases, objetos, abstracción, modularidad, encapsulamiento, herencia y polimorfismo.


Clase 
En la programación orientada a objetos, una clase es una construcción que se utiliza como un modelo (o plantilla) para crear objetos de ese tipo. El modelo describe el estado y el comportamiento que todos los objetos de la clase comparten. Un objeto de una determinada clase se denomina una instancia de la clase. La clase que contiene (y se utilizó para crear) esa instancia se puede considerar como del tipo de ese objeto, por ejemplo, una instancia del objeto de la clase "Personas" sería del tipo "Personas".
Una clase por lo general representa un sustantivo, como una persona, lugar o (posiblemente bastante abstracta) cosa - es el modelo de un concepto dentro de un programa de computadora. Fundamentalmente, encapsula el estado y el comportamiento del concepto que representa. Encapsula el estado a través de marcadores de datos llamados atributos (o variables miembro o variables de instancia), encapsula el comportamiento a través de secciones de código reutilizables llamados métodos.

Objeto 
En el paradigma de programación orientada a objetos (POO, o bien OOP en inglés), un objeto se define como la unidad que en tiempo de ejecución realiza las tareas de un programa. También a un nivel más básico se define como la instancia de una clase.
Estos objetos interactúan unos con otros, en contraposición a la visión tradicional en la cual un programa es una colección de subrutinas (funciones o procedimientos), o simplemente una lista de instrucciones para el computador. Cada objeto es capaz de recibir mensajes, procesar datos y enviar mensajes a otros objetos de manera similar a un servicio.

Abstracción 
La abstracción consiste en aislar un elemento de su contexto o del resto de los elementos que lo acompañan. En programación, el término se refiere al énfasis en el "¿qué hace?" más que en el "¿cómo lo hace?" (característica de caja negra). El común denominador en la evolución de los lenguajes de programación, desde los clásicos o imperativos hasta los orientados a objetos, ha sido el nivel de abstracción del que cada uno de ellos hace uso.
Los lenguajes de programación son las herramientas mediante las cuales los diseñadores de lenguajes pueden implementar los modelos abstractos. La abstracción ofrecida por los lenguajes de programación se puede dividir en dos categorías: abstracción de datos (pertenecientes a los datos) y abstracción de control (perteneciente a las estructuras de control).
Los diferentes paradigmas de programación han aumentado su nivel de abstracción, comenzando desde los lenguajes de máquina, lo más próximo al ordenador y más lejano a la comprensión humana; pasando por los lenguajes de comandos, los imperativos, la orientación a objetos (OO), la Programación Orientada a Aspectos (POA); u otros paradigmas como la programación declarativa, etc.

Modularidad 
En programación modular, y más específicamente en programación orientada a objetos, se denomina Modularidad a la propiedad que permite subdividir una aplicación en partes más pequeñas (llamadas módulos), cada una de las cuales debe ser tan independiente como sea posible de la aplicación en sí y de las restantes partes.
Estos módulos que se puedan compilar por separado, pero que tienen conexiones con otros módulos. Al igual que la encapsulación, los lenguajes soportan la Modularidad de diversas formas.
Según Bertrand Meyer "El acto de particionar un programa en componentes individuales para reducir su complejidad en algún grado. . . . A pesar de particionar un programa es útil por esta razón, una justificación más poderosa para particionar un programa es que crea una serie de límites bien definidos y documentados en el programa. Estos límites, o interfaces, son muy valiosos en la comprensión del programa.

Encapsulamiento 
En programación modular, y más específicamente en programación orientada a objetos, se denomina encapsulamiento al ocultamiento del estado, es decir, de los datos miembro, de un objeto de manera que sólo se puede cambiar mediante las operaciones definidas para ese objeto.
Cada objeto está aislado del exterior, es un módulo natural, y la aplicación entera se reduce a un agregado o rompecabezas de objetos. El aislamiento protege a los datos asociados a un objeto contra su modificación por quien no tenga derecho a acceder a ellos, eliminando efectos secundarios e interacciones.

Herencia 
En orientación a objetos la herencia es el mecanismo fundamental para implementar la reutilización y extensibilidad del software. A través de ella los diseñadores pueden construir nuevas clases partiendo de una jerarquía de clases ya existente (comprobadas y verificadas) evitando con ello el rediseño, la modificación y verificación de la parte ya implementada. La herencia facilita la creación de objetos a partir de otros ya existentes, obteniendo características (métodos y atributos) similares a los ya existentes.
Es la relación entre una clase general y otra clase más especifica. Por ejemplo: Si declaramos una clase párrafo derivada de una clase texto, todos los métodos y variables asociadas con la clase texto, son automáticamente heredados por la subclase párrafo.

Polimorfismo 
En programación orientada a objetos el polimorfismo se refiere a la capacidad para que varias clases derivadas de una antecesora utilicen un mismo método de forma diferente.
Por ejemplo, podemos crear dos clases distintas: Pez y Ave que heredan de la superclase Animal. La clase Animal tiene el método abstracto mover que se implementa de forma distinta en cada una de las subclases (peces y aves se mueven de forma distinta).
Como se mencionó anteriormente, el concepto de polimorfismo se puede aplicar tanto a funciones como a tipos de datos. Así nacen los conceptos de funciones polimórficas y tipos polimórficos. Las primeras son aquellas funciones que pueden evaluarse o ser aplicadas a diferentes tipos de datos de forma indistinta; los tipos polimórficos, por su parte, son aquellos tipos de datos que contienen al menos un elemento cuyo tipo no está especificado.