Poo INSPT Utn
Poo INSPT Utn
Poo INSPT Utn
603
Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 1
El Modelo de Objetos
En vez de un procesador de bits consumiendo estructuras de datos, tenemos
un universo de objetos bien comportados, cada uno de ellos pidiéndole a otro
que cortésmente le realice sus variados deseos. 1
Objeto: Es una unidad que tiene estado, comportamiento e identidad. Un objeto puede
caracterizar una entidad física (un teléfono, un cliente) o una entidad abstracta (un
número, una fecha). Su estado está dado por los valores de sus “atributos” (“variables
de instancia”), su comportamiento por las funciones (“métodos”) que ejecuta cuando
recibe solicitudes (“mensajes”) y su identidad por sus nombres.
1 “Instead of a bit-grinding processor raping and plundering data structures, we have a universe of well-
behaved objects that courteously ask each other to carry out their various desires.”
Ingalls, Daniel H. H. “Design Principles Behind Smalltalk”. En: BYTE Magazine (Agosto, 1981). McGraw-
Hill, Nueva York, p. 290
-1-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Cada vez que se le solicita a una instancia de Perro que ladre (invocando su
método ladrar, al cual se accede colocando el nombre del objeto seguido del operador .
y el nombre del método), se muestra por pantalla el valor del atributo ladrido de ese
objeto, mediante el método println del objeto System.out (la salida estándar) 1.
El nombre del archivo donde se guarda una clase pública (aquella a la que puede
accederse desde clases alojadas en otros paquetes) debe coincidir con el nombre de la
clase. Por ejemplo, la clase Perro debe guardarse en el archivo Perro.java.
Por convención, en Java los nombres de las clases comienzan con mayúscula.
Como veremos más adelante, es conveniente calificar los atributos como private
y los constructores y la mayoría de los métodos como public.
En la clase Universo, el modificador static del método main indica que se trata
de un método estático (que puede ser invocado a pesar de no haber sido instanciado un
objeto de la clase a la que pertenece). Para poder ser utilizado dentro de un método
estático, un atributo o un método también debe ser estático, como ocurre con
dogoArgentino y pastorIngles. En cambio, en la clase Perro, dentro del método
ladrar (no estático) es posible usar el atributo ladrido (que tampoco es estático).
1 Observe que se cumple el principio “Tell, don’t ask”. En vez de pedirle al perro que devuelva el ladrido y
mostrarlo en otra clase, se le dice directamente que ladre. Esto es un correcto diseño OO.
-2-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
-3-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-4-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Dentro del método main de la clase Main se declara y crea una lista de objetos de
la clase PuntoTuristico y se la utiliza como argumento del constructor al crear luego
un objeto de la clase CityTour. Por ello, tanto entre Main y PuntoTuristico como entre
Main y CityTour, la relación existente es una: ........................................................
Dado que un objeto de la clase CityTour tiene como atributo una lista de
instancias de PuntoTuristico, pero ésta fue creada fuera de la clase CityTour y en
consecuencia puede existir independientemente de ella, la relación entre CityTour y
PuntoTuristico es una: .....................................................................................................
Tanto Omnibus como Van son subclases de Vehiculo. La relación entre Omnibus y
Vehiculo y la relación entre Van y Vehiculo es una: ....................................................
Un vehículo tiene como atributo una lista de instancias de Asiento. Como ésta se
crea dentro del constructor de Vehiculo, sólo puede existir mientras exista la instancia
de Vehiculo. Por lo tanto, la relación entre Vehiculo y Asiento es una:
...............................................................................................................................................
Cada asiento “es ocupado por” un pasajero. Por eso, cada instancia de Asiento
contiene en su atributo p una referencia a un objeto de la clase Pasajero. Entonces, la
relación entre Asiento y Pasajero es una: ........................................................................
-5-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
EJERCICIO Nº 1
EJERCICIO RESUELTO
Main.java
package palitos;
-6-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
EntradaSalida.java
package palitos;
import javax.swing.*;
public class EntradaSalida {
public static void mostrarMensaje(String mensaje) {
JOptionPane.showMessageDialog(null, mensaje);
}
public static String leerCadena(String mensaje) {
return JOptionPane.showInputDialog(mensaje);
}
public static int leerEntero(String mensaje) {
String strLeida = JOptionPane.showInputDialog(mensaje);
int iLeido;
try {
iLeido = Integer.parseInt(strLeida);
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "El valor ingresado es incorrecto.\n" + ex);
iLeido = 0;
}
return iLeido;
}
}
UltimoPalitoPierde.java
package palitos;
import static palitos.EntradaSalida.*;
public class UltimoPalitoPierde {
private final Jugador j1;
private final Jugador j2;
private final PilaDePalitos p;
public UltimoPalitoPierde(int i) {
mostrarMensaje("Hay " + i + " palitos apilados."
+ "\nCada uno de los 2 jugadores sacará 1, 2 o 3 palitos por turno."
+ "\nEl jugador que saca el último palito pierde."
+ "\nSuerte!");
p = new PilaDePalitos(i);
j1 = new Jugador(1);
j2 = new Jugador(2);
}
public void ejecutar() {
while (p.partidaSigue()) {
j1.jugar(p);
if (p.jugadaGanadora()) {
j1.cantarVictoria();
} else if (p.jugadaPerdedora()) {
j2.cantarVictoria();
} else {
j2.jugar(p);
if (p.jugadaGanadora()) {
j2.cantarVictoria();
} else if (p.jugadaPerdedora()) {
j1.cantarVictoria();
}
}
}
}
}
-7-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Jugador.java
package palitos;
import static palitos.EntradaSalida.*;
public Jugador(int i) {
do {
nombre = leerCadena("Jugador Nro. " + i + ", cómo te llamas?");
} while (nombre.equals(""));
}
PilaDePalitos.java
package palitos;
public class PilaDePalitos {
public PilaDePalitos(int i) {
cantidad = i;
}
-8-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 2
Estado de los objetos
Como se mencionó en la unidad anterior, los objetos que interactúan en un
sistema orientado a objetos tienen un estado que está dado por los valores de sus
“atributos” (“variables de instancia”) en determinado momento.
En el caso anterior, los atributos clave y doc se inicializan dentro del constructor
de Usuario. Como el valor para inicializar el atributo clave se recibe en el constructor
mediante un parámetro que también tiene el nombre clave, debe utilizarse this.clave
para referirse al atributo. En el caso del atributo doc, éste se inicializa dentro del
constructor con una instancia de DocumentoDeIdentidad creada en ese mismo lugar.
Recordemos que dentro del método ingresar es posible acceder a clave y a doc.
PREGUNTA DE REPASO
¿Qué relación hay entre la clase Usuario y la clase DocumentoDeIdentidad? ¿Por qué?
...............................................................................................................................................
...............................................................................................................................................
-9-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El valor por defecto se refiere al valor que toma una variable de instancia (es
decir, un atributo) cuando se la usa sin inicialización previa. En el caso de las variables
locales de los constructores y de los métodos, su uso sin inicialización previa no es posible
(el compilador lo marca como un error).
El valor por defecto de los atributos que son instancias de la clase String (o de
cualquier otra clase) es null. Cuando una variable vale null, ésta no se refiere a ningún
objeto, por ello no es posible enviarle mensajes. La cadena vacía "" sí es un objeto. Por
lo tanto, una variable String inicializada con la cadena vacía puede responder a los
mensajes que le enviemos.
-10-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Los mensajes que puede responder una instancia de la clase String son los
métodos que implementa esta clase. Los más comúnmente utilizados son los siguientes:
length, equals, charAt, substring, concat, contains, indexOf, isEmpty, trim,
startsWith, endsWith, toLowerCase, toUpperCase, replace, replaceAll.
Mis anotaciones: Qué hacen y cómo se usan los principales métodos de String
length
................................................................................................................................................
................................................................................................................................................
equals
................................................................................................................................................
................................................................................................................................................
charAt
................................................................................................................................................
................................................................................................................................................
substring
................................................................................................................................................
................................................................................................................................................
concat (aunque exista este método, lo más usual es usar el operador ..........)
................................................................................................................................................
................................................................................................................................................
contains
................................................................................................................................................
................................................................................................................................................
indexOf
................................................................................................................................................
................................................................................................................................................
isEmpty
................................................................................................................................................
................................................................................................................................................
trim
................................................................................................................................................
................................................................................................................................................
startsWith / endsWith
................................................................................................................................................
................................................................................................................................................
toLowerCase / toUpperCase
................................................................................................................................................
................................................................................................................................................
replace / replaceAll
................................................................................................................................................
................................................................................................................................................
-11-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Un atributo que represente un grupo de elementos del mismo tipo primitivo (por
ejemplo, los números premiados en un sorteo) o un grupo de elementos de la misma
clase (por ejemplo, los alumnos inscriptos en un curso) puede implementarse en Java
como un arreglo.
Una vez declarada una variable que hará referencia a un arreglo, es necesario
instanciar este último antes de comenzar a usarlo:
Para acceder a cierta posición del arreglo, se especifica su índice entre los
corchetes:
-12-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
int[][] imagen2D;
A veces, es necesario guardar una copia del contenido de un arreglo. Por ejemplo,
si necesitamos tener en un arreglo ciertas letras en minúsculas y en otro arreglo las
mismas letras en mayúsculas, podríamos generar una copia del primer arreglo y luego
pasar a mayúsculas el contenido copiado. El siguiente código muestra un intento fallido
para realizar esta tarea:
System.out.println(new String(minusculas));
System.out.println(new String(mayusculas));
OBSERVACIÓN
-13-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Lo que ocurre en el ejemplo anterior es que, como los arreglos son objetos, la
asignación mayusculas = minusculas no copia los caracteres contenidos en el arreglo
minusculas en el arreglo mayusculas. De hecho, lo que copia la asignación es la
dirección de memoria donde está guardado el objeto a que hace referencia la variable de
la derecha. Por ello, ambas variables terminan refiriéndose al mismo objeto, y los
cambios hechos a éste cuando es referenciado mediante la variable mayusculas también
se ven cuando más tarde se lo referencia mediante la variable minusculas.
OBSERVACIÓN
mayusculas = java.util.Arrays.copyOfRange(minusculas, 1, 3); hace que el
arreglo mayusculas contenga los siguientes elementos: ..................................................
-14-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-15-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
isEmpty ...............................................................................................................................
size .....................................................................................................................................
add .......................................................................................................................................
set .......................................................................................................................................
get .......................................................................................................................................
remove .................................................................................................................................
contains .............................................................................................................................
indexOf ...............................................................................................................................
iterator .............................................................................................................................
Para investigar
¿Qué caracteriza a los objetos agregados en una instancia de TreeSet? (ver abajo)
................................................................................................................................................
................................................................................................................................................
EJERCICIO Nº 2
-16-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 3
Comportamiento de los objetos
Como se mencionó en la Unidad 1, todo objeto que interactúa en un sistema
orientado a objetos tiene un comportamiento que está dado por las funciones
(“métodos”) que ejecuta cuando recibe solicitudes (“mensajes”) de otros objetos.
Cada método debe limitarse a realizar una única tarea bien definida, y su
nombre debe expresar esa tarea con efectividad. Esto hace que los programas sean más
fáciles de escribir, depurar, mantener y modificar.
Desde el resto de los objetos de un sistema se debería poder solicitarle a un objeto
la ejecución de sus métodos, por eso a éstos casi siempre se los califica como public.
Una excepción son aquellos métodos auxiliares que sólo se invocarán desde dentro del
propio objeto, en cuyo caso se los debe hacer invisibles para los demás objetos,
calificándolos como private. Este asunto se trata en detalle en la Unidad 5.
Recordemos que hay tres formas de invocar un método:
1. Pasándole un mensaje a un objeto, es decir, usando una variable que se refiera al
objeto, seguida de punto (.) y el nombre del método. Ej: .....................................
2. Utilizando el nombre de la clase, seguido de punto (.) y el nombre de un método
de la clase, si éste último es estático (static). Ej: ..................................................
3. Utilizando sólo el nombre del método, si éste es parte de la misma clase.
-17-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package perros;
public Perro(String s) {
ladrido = s;
}
Los métodos pueden devolver como máximo un valor, pero el valor devuelto puede
ser una referencia a un objeto que contenga varios valores. El tipo del valor devuelto se
indica antes del nombre del método, al declararse este último. Como ya se explicó
anteriormente, la palabra reservada void se utiliza para indicar que un método no
devuelve ningún valor.
-18-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Definiciones
Parámetro: Dato declarado entre paréntesis junto al nombre de un método.
Argumento: Valor que se le proporciona a un parámetro al invocar un método.
PruebaSobrecargaMetodos.java
public class PruebaSobrecargaMetodos {
-19-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Las reglas de promoción se aplican a los valores de tipos primitivos que se les
pasan como argumentos a los métodos. La tabla siguiente lista los tipos a los cuales se
puede promover cada uno de los tipos primitivos. Observe que las promociones válidas
para un tipo dado siempre se realizan a un tipo que aparece más arriba en la tabla. Por
ejemplo, un int puede promoverse a los tipos más altos long, float y double.
-20-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 4
Herencia
Una de las características principales de la programación orientada a objetos es
la herencia, que es una forma de reutilización de software en la que se crea una nueva
clase aprovechando los miembros de una clase existente (herencia simple, como en Java)
o de varias (herencia múltiple, como en C++). Con la herencia, los programadores
ahorran tiempo durante el desarrollo, al reutilizar software probado y depurado de alta
calidad. Esto también aumenta la probabilidad de que un sistema se implemente con
efectividad.
Una subclase generalmente agrega sus propios atributos y métodos. Por lo tanto,
una subclase es más específica que su superclase y representa a un grupo más
especializado de objetos. Generalmente, la subclase exhibe los comportamientos de su
superclase junto con comportamientos adicionales específicos de esta subclase. Es por
ello que a la herencia se la conoce algunas veces como especialización.
1En adelante, emplearemos exclusivamente el singular, ya que en este curso sólo vamos a usar la herencia
simple. Cabe destacar que en Java es posible utilizar las interfaces para obtener muchos de los beneficios
de la herencia múltiple, evitando al mismo tiempo los problemas asociados. Recordemos que la relación
entre una clase y una interfaz se denomina realización.
-21-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El siguiente ejemplo muestra cómo la clase ObjetoEducado, por ser (de manera
implícita) una subclase de Object, hereda el método finalize, el cual, en lugar de ser
conservado tal como está definido en Object, es redefinido para mostrar un mensaje de
despedida.
ObjetoEducado.java
package ejemploFinalize;
public ObjetoEducado() {
System.out.println("Soy un objeto educado: Digo HOLA al aparecer!");
}
Main.java
package ejemploFinalize;
El ejemplo anterior mostró un caso de herencia implícita, ya que todas las clases
heredan de Object. La herencia, en Java, se hace explícita mediante la palabra
reservada extends. Por lo tanto, aunque resulte redundante, podría haberse declarado
la clase ObjetoEducado de la siguiente manera:
-22-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
En el siguiente ejemplo, la clase MiFecha extiende la clase Date, que es una clase
de la biblioteca de clases java.util. Por eso, las instancias de MiFecha (........................
y ......................) disponen de los mismos miembros públicos que las instancias de Date
(........................ y ......................). En el caso del método getTime, la instancia de
.............................. utiliza el método que heredó de Date. En cambio, el método
..................................... fue sobrescrito, por lo tanto los comportamientos de miHoy y
miAyer no serán iguales a los de hoy y ayer, cuando se invoque ese método.
MiFecha.java
package ejemploFechas;
import java.text.SimpleDateFormat;
import java.util.Date;
Main.java
package ejemploFechas;
import java.util.Date;
-23-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
EJERCICIO Nº 3
Dibuje un diagrama UML que muestre las relaciones entre las siguientes clases.
Indique, además, qué aparece por la salida del programa.
package cadenadellamadas; package cadenadellamadas;
public class Main { public class Torcaza extends Paloma {
public static void main(String[] args){ public Torcaza(String s) {
Torcaza t = new Torcaza("torcaza"); System.out.println("Soy una "+s+"!");
} }
} }
package cadenadellamadas; package cadenadellamadas;
public class Animal { public class Ave extends Animal {
public Animal() { public Ave() {
System.out.println("Soy un animal!"); System.out.println("Sin uso!");
} }
}
public Ave(int n) {
package cadenadellamadas;
System.out.println("Soy " +
public class Paloma extends Ave { (n==1? "un" : "") +
" ave!");
public Paloma() { }
super(1); }
System.out.println("Soy una paloma!");
}
}
-24-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
PoligonoRegular.java
package poligonosRegulares;
public abstract class PoligonoRegular {
private double lado;
public PoligonoRegular(double lado) {
this.lado = lado;
}
public double getLado() {
return lado;
}
-25-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
TrianguloEquilatero.java
package poligonosRegulares;
public class TrianguloEquilatero extends PoligonoRegular {
public TrianguloEquilatero(double lado) {
super(lado);
}
public double calcularArea() {
return getLado() * getLado() * Math.sqrt(3) / 4;
}
}
Cuadrado.java
package poligonosRegulares;
public class Cuadrado extends PoligonoRegular {
public Cuadrado(double lado) {
super(lado);
}
public double calcularArea() {
return getLado() * getLado();
}
}
Main.java
package poligonosRegulares;
public class Main {
public static void main(String[] args) {
TrianguloEquilatero tri = new TrianguloEquilatero(5);
Cuadrado cua = new Cuadrado(5);
System.out.println ("Triangulo: " + tri.calcularArea());
System.out.println ("Cuadrado : " + cua.calcularArea());
}
}
-26-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
EnviadorDeMensajes.java
package comunicaciones;
-27-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Telefono.java
package comunicaciones;
PalomaMensajera.java
package comunicaciones;
Animal.java
package comunicaciones;
Main.java
package comunicaciones;
-28-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 5
Encapsulamiento
El término encapsulamiento suele utilizarse para referirse a cosas diferentes:
1. la agrupación de estado y comportamiento en una unidad conceptual (la clase);
2. la aplicación de mecanismos de restricción de acceso a los atributos y métodos;
3. el ocultamiento de información como principio de diseño.
Java ofrece cuatro niveles de accesibilidad para los miembros de una clase:
visibilidad de paquete (por defecto) y la que se obtiene al anteponerle a cada declaración
alguno de los especificadores de acceso public, private o protected.
-29-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-30-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 6
Polimorfismo
El cuarto y último de los pilares de la programación orientada a objetos, junto con
la abstracción (pág. 4), el encapsulamiento (pág. 29) y la herencia (pág. 21), es el
polimorfismo. En griego, πολλύς μορφή significa “muchas formas”. Es por ello que, en
programación, se denomina así a la capacidad de contener valores de distintos tipos (en
el caso de las variables polimórficas) o a la capacidad de recibir argumentos de
diferentes tipos (en el caso de los métodos polimórficos).
Según cómo sea el conjunto de los tipos posibles, Cardelli y Wegner 1 clasifican el
polimorfismo en dos categorías: polimorfismo ad-hoc 2 (funciona con un número
limitado y conocido de tipos, no necesariamente relacionados entre sí) y polimorfismo
universal (funciona con un número prácticamente infinito de tipos relacionados entre
sí). En cada categoría, a su vez, es posible encontrar dos variedades de polimorfismo:
como variantes del polimorfismo ad-hoc pueden clasificarse el polimorfismo por
sobrecarga y el polimorfismo por coerción, mientras que el polimorfismo paramétrico y
el polimorfismo por inclusión son variantes del polimorfismo universal.
Polimorfismo por sobrecarga
Dentro de una misma clase, es posible escribir dos o más métodos con el mismo
nombre pero diferente firma (signature), como se explicó en la pág. 18. El resultado es
un polimorfismo aparente, ya que no se trata de un método que puede recibir muchos
tipos de argumentos, sino que hay varios métodos con el mismo nombre, y durante el
proceso de compilación se elije cuál usar según el o los argumento(s) pasado(s). En el
siguiente ejemplo, el método unir está sobrecargado para recibir dos .......................... o
dos ................................
package uniones;
public class SobrecargaTest {
public static void main(String[] args) {
Unidor u = new Unidor();
System.out.println(u.unir("123", "45"));
System.out.println(u.unir(123, 45));
}
}
package uniones;
public class Unidor {
public String unir(String a, String b) {
return a.concat(b);
}
public int unir(int a, int b) {
return (int) (a * Math.pow(10, Math.ceil(Math.log10(b))) + b);
}
}
1 Cardelli, L. & Wegner, P. “On Understanding Types, Data Abstraction, and Polymorphism”. En:
Computing Surveys (Diciembre, 1985). Vol. 17, n. 4, p. 471
2 Ad hoc es una locución latina que significa literalmente “para esto”. Se usa para referirse a algo
específico que es adecuado sólo para un determinado fin o en una determinada situación.
-31-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package coercion;
public class CoercionTest {
public static void main(String[] args) {
double x = 12.5;
int n = 12;
Duplicador d = new Duplicador();
System.out.println(d.duplicar(x));
System.out.println(d.duplicar(n));
}
}
package coercion;
public class Duplicador {
public double duplicar(double x) {
return 2 * x;
}
}
Polimorfismo paramétrico
Cuando se escribe código genérico, es decir, sin mencionar ningún tipo de datos
específico, para que pueda ser usado con datos que serán tratados de manera idéntica
independientemente de su tipo, se está aprovechando el polimorfismo paramétrico. En
Java, a esta variante de polimorfismo se la conoce como Generics. En el siguiente
ejemplo, la clase PilaGenerica puede usarse para instanciar pilas específicas para
cualquier tipo de objeto. Para mostrar esto, dentro del método main se instancian
pInt (una pila de .............................) y pStr (una pila de ...............................).
package generics;
public class GenericsTest {
public static void main(String[] args) {
PilaGenerica<Integer> pInt = new PilaGenerica<>();
pInt.push(10); pInt.push(20); pInt.push(30);
System.out.println(pInt.pop());
System.out.println(pInt.pop());
System.out.println(pInt.pop());
PilaGenerica<String> pStr = new PilaGenerica<>();
pStr.push("Arbol"); pStr.push("Casa"); pStr.push("Auto");
System.out.println(pStr.pop());
System.out.println(pStr.pop());
System.out.println(pStr.pop());
}
}
-32-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package generics;
public class PilaGenerica<T> {
private T elementoTope;
private PilaGenerica<T> restoPila;
public PilaGenerica() {
elementoTope = null;
restoPila = null;
}
public void push(T elem) {
PilaGenerica<T> aux = new PilaGenerica<>();
aux.elementoTope = elementoTope;
aux.restoPila = restoPila;
restoPila = aux;
elementoTope = elem;
}
public T pop() {
T tope = elementoTope;
if (restoPila != null) {
elementoTope = restoPila.elementoTope;
restoPila = restoPila.restoPila;
}
return tope;
}
}
package poligonosRegulares;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<PoligonoRegular> c = new ArrayList<>();
c.add(new TrianguloEquilatero(5));
c.add(new Cuadrado(5));
c.add(new PentagonoRegular(5));
for (int i = 0; i < c.size(); i++) {
PoligonoRegular p = c.get(i);
System.out.println(p.getNombre() + ": " + Math.floor(p.calcularArea()*100)/100);
}
}
}
-33-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package poligonosRegulares;
public abstract class PoligonoRegular {
private double lado;
private String nombre;
public PoligonoRegular(double lado) {
this.lado = lado;
}
public double getLado() {
return lado;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public abstract double calcularArea();
}
package poligonosRegulares;
public class TrianguloEquilatero extends PoligonoRegular {
public TrianguloEquilatero(double lado) {
super(lado);
setNombre("Triangulo equilatero");
}
public double calcularArea() {
return getLado() * getLado() * Math.sqrt(3) / 4;
}
}
package poligonosRegulares;
public class Cuadrado extends PoligonoRegular {
public Cuadrado(double lado) {
super(lado);
setNombre("Cuadrado");
}
public double calcularArea() {
return getLado() * getLado();
}
}
package poligonosRegulares;
public class PentagonoRegular extends PoligonoRegular {
public PentagonoRegular(double lado) {
super(lado);
setNombre("Pentagono regular");
}
public double calcularArea() {
return 5 * getLado() * getLado() / (4 * Math.tan(Math.PI / 5.0));
}
}
-34-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-35-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Tal como habíamos anticipado en la pág. 27, modelar una jerarquía de clases
basándose exclusivamente en las relaciones de herencia produce diseños poco flexibles.
Observemos el siguiente ejemplo:
package polimorfismo;
public class Main {
public static void main(String[] args) {
Lapicera lap = new Lapicera();
Lapicera bol = new Boligrafo();
Lapicera lapFuente = new LapiceraFuente();
Persona p = new Persona();
p.dibujar(lap);
p.dibujar(bol);
p.dibujar(lapFuente);
}
}
package polimorfismo;
public class Persona {
public void dibujar(Lapicera lap) {
lap.dibujarLinea();
lap.dibujarCirculo();
}
}
package polimorfismo;
public class Lapicera {
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA");
}
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA");
}
}
-36-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package polimorfismo;
public class Boligrafo extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con un BOLIGRAFO");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con un BOLIGRAFO");
}
}
package polimorfismo;
public class LapiceraFuente extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA FUENTE");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA FUENTE");
}
public void recargar() {
System.out.println("Se ha recargado una LAPICERA FUENTE");
}
}
Así como una persona es capaz, en el mundo real, de dibujar tanto con una
lapicera en general como con una lapicera fuente o un bolígrafo en particular, en la clase
Main el objeto p reacciona correctamente cuando le pasamos el mensaje dibujar con
cualquier argumento que sea una Lapicera (lap es obviamente una instancia de
Lapicera, y también lapFuente, que se refiere a una LapiceraFuente, y bol, que se
refiere a un Boligrafo, pasan el test “es-un(a)” Lapicera).
OBSERVACIÓN
Sabiendo que las lapiceras fuente son recargables (de hecho, la clase
LapiceraFuente contiene un método denominado recargar), se podría requerir que,
una vez que el objeto p haya terminado de dibujar usando la lapicera fuente a que se
refiere lapFuente, ésta fuera recargada. El código a agregar en main sería el siguiente:
lapFuente.recargar();
-37-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
OBSERVACIÓN
Recordemos que el objetivo del ejemplo que estamos viendo es mostrar cómo
modelar una jerarquía de clases basándose exclusivamente en las relaciones de herencia
produce diseños poco flexibles. Para ello, sigamos adelante e incorporemos en la
jerarquía de clases la Carbonilla que, al igual que una Lapicera, sirve para dibujar,
pero además puede arder, por ejemplo, cuando se la quema para asar comidas a la
parrilla. Agreguemos en la clase Persona el método quemar para poder darle este
segundo uso a la carbonilla. Como la carbonilla no es un tipo de lapicera y el método
dibujar exige un argumento que sea una instancia de Lapicera, es necesario agregar
a la jerarquía una nueva clase (ElementoQueDibuja) como superclase de Carbonilla y
Lapicera y arreglar el método dibujar en la clase Persona para que acepte instancias
de ElementoQueDibuja. Las variables polimórficas en Main ahora deben ser declaradas
como ElementoQueDibuja en lugar de Lapicera. En consecuencia, debe cambiársele el
tipo a la variable carbo al pasársela al método quemar. El nuevo diagrama de clases es
el siguiente (se omitieron las dependencias que tiene Main):
-38-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Como puede verse, es necesario hacer muchos cambios para agregar una nueva
clase en este diseño basado exclusivamente en herencia. El código modificado es el
siguiente:
package polimorfismo;
public class Main {
public static void main(String[] args) {
ElementoQueDibuja lap = new Lapicera();
ElementoQueDibuja bol = new Boligrafo();
ElementoQueDibuja lapFuente = new LapiceraFuente();
ElementoQueDibuja carbo = new Carbonilla();
Persona p = new Persona();
p.dibujar(lap);
p.dibujar(bol);
p.dibujar(lapFuente);
LapiceraFuente lapF = (LapiceraFuente) lapFuente;
lapF.recargar();
p.dibujar(carbo);
p.quemar((Carbonilla) carbo);
}
}
package polimorfismo;
public class Persona {
public void dibujar(ElementoQueDibuja elem) {
elem.dibujarLinea();
elem.dibujarCirculo();
}
public void quemar(Carbonilla carb) {
carb.arder();
}
}
package polimorfismo;
public abstract class ElementoQueDibuja {
public abstract void dibujarLinea();
public abstract void dibujarCirculo();
}
package polimorfismo;
public class Lapicera extends ElementoQueDibuja {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA");
}
}
-39-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package polimorfismo;
public class Boligrafo extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con un BOLIGRAFO");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con un BOLIGRAFO");
}
}
package polimorfismo;
public class LapiceraFuente extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA FUENTE");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA FUENTE");
}
public void recargar() {
System.out.println("Se ha recargado una LAPICERA FUENTE");
}
}
package polimorfismo;
public class Carbonilla extends ElementoQueDibuja {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con CARBONILLA");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con CARBONILLA");
}
public void arder() {
System.out.println("Se ha quemado CARBONILLA");
}
}
OBSERVACIÓN
Al correr el ejemplo anterior, la salida por pantalla es:
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
-40-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Como puede observarse en el diagrama, tanto dibujar como quemar son métodos
polimórficos de Persona, ya que aceptan cualquier argumento que implemente las
interfaces UsableParaTrazar o Inflamable, respectivamente.
-41-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package polimorfismo;
public class Main {
public static void main(String[] args) {
UsableParaTrazar lap = new Lapicera();
UsableParaTrazar bol = new Boligrafo();
UsableParaTrazar lapFuente = new LapiceraFuente();
UsableParaTrazar carbo = new Carbonilla();
Persona p = new Persona();
p.dibujar(lap);
p.dibujar(bol);
p.dibujar(lapFuente);
LapiceraFuente lapF = (LapiceraFuente) lapFuente;
lapF.recargar();
p.dibujar(carbo);
p.quemar((Inflamable) carbo);
Inflamable papel = new Papel();
p.quemar(papel);
}
}
package polimorfismo;
public class Persona {
public void dibujar(UsableParaTrazar elem) {
elem.dibujarLinea();
elem.dibujarCirculo();
}
public void quemar(Inflamable elem) {
elem.arder();
}
}
package polimorfismo;
public interface UsableParaTrazar {
void dibujarLinea();
void dibujarCirculo();
}
package polimorfismo;
public interface Inflamable {
void arder();
}
package polimorfismo;
public class Papel implements Inflamable {
@Override
public void arder() {
System.out.println("Se ha quemado PAPEL");
}
}
-42-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package polimorfismo;
public class Lapicera implements UsableParaTrazar {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA");
}
}
package polimorfismo;
public class Boligrafo extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con un BOLIGRAFO");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con un BOLIGRAFO");
}
}
package polimorfismo;
public class LapiceraFuente extends Lapicera {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con una LAPICERA FUENTE");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con una LAPICERA FUENTE");
}
public void recargar() {
System.out.println("Se ha recargado una LAPICERA FUENTE");
}
}
package polimorfismo;
public class Carbonilla implements UsableParaTrazar, Inflamable {
@Override
public void dibujarLinea() {
System.out.println("Se ha dibujado una linea con CARBONILLA");
}
@Override
public void dibujarCirculo() {
System.out.println("Se ha dibujado un circulo con CARBONILLA");
}
@Override
public void arder() {
System.out.println("Se ha quemado CARBONILLA");
}
}
-43-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
OBSERVACIÓN
package solid;
public class LlamadaHaciaFijoCABA {
public void efectuar(int numero) {
if (validar(numero)) {
System.out.println("Llamando al +54 11 " + numero + "...");
} else {
System.out.println("Error: " + numero + " no es un numero fijo valido...");
}
}
public boolean validar(int numero) {
return numero > 19999999 && numero < 70000000;
}
}
package solid;
public class Main {
public static void main(String[] args) {
LlamadaHaciaFijoCABA llamada = new LlamadaHaciaFijoCABA();
llamada.efectuar(43112700);
llamada.efectuar(1530366486);
}
}
1Martin, R. C. (2003). Agile Software Development: Principles, Patterns, and Practices. Prentice Hall
2Los principios S.O.L.I.D. en inglés son:
1) The Single Responsibility Principle: A class should have one, and only one, reason to change.
2) The Open Closed Principle: You should be able to extend a classes behavior, without modifying it.
3) The Liskov Substitution Principle: Derived classes must be substitutable for their base classes.
4) The Interface Segregation Principle: Make fine grained interfaces that are client specific.
5) The Dependency Inversion Principle: Depend on abstractions, not on concretions.
-44-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public class ServicioDeValidacion {
public boolean validar(int numero) {
return numero > 19999999 && numero < 70000000;
}
}
2. Principio abierto/cerrado
El comportamiento de una clase debe estar abierto a la extensión, pero cerrado a
la modificación (se debe poder extender sin modificar el código existente). Por ello, en el
siguiente ejemplo, la clase CalculadoraDeAreas viola este principio:
package solid;
import java.util.ArrayList;
public class CalculadoraDeAreas {
private ArrayList c;
public CalculadoraDeAreas(ArrayList c) {
this.c = c;
}
public void calcular() {
for (int i = 0; i < c.size(); i++) {
Object p = c.get(i);
if (p instanceof TrianguloEquilatero) {
TrianguloEquilatero t = (TrianguloEquilatero) p;
System.out.println(t.getNombre()+": "+Math.floor(t.calcular()*100)/100);
} else if (p instanceof Cuadrado) {
Cuadrado q = (Cuadrado) p;
System.out.println(q.getNombre()+": "+Math.floor(q.calcularArea()*100)/100);
} else if (p instanceof PentagonoRegular) {
PentagonoRegular z = (PentagonoRegular) p;
System.out.println(z.getNombre()+": "+Math.floor(z.calcularSup()*100)/100);
}
}
}
}
-45-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList c = new ArrayList();
c.add(new TrianguloEquilatero(5));
c.add(new Cuadrado(5));
c.add(new PentagonoRegular(5));
CalculadoraDeAreas calcu = new CalculadoraDeAreas(c);
calcu.calcular();
}
}
package solid;
public class TrianguloEquilatero {
private double lado;
private String nombre;
public TrianguloEquilatero(double lado) {
this.lado = lado;
nombre = "Triangulo equilatero";
}
public String getNombre() {
return nombre;
}
public double calcular() {
return lado * lado * Math.sqrt(3) / 4;
}
}
package solid;
public class Cuadrado {
private double lado;
private String nombre;
public Cuadrado(double lado) {
this.lado = lado;
nombre = "Cuadrado";
}
public String getNombre() {
return nombre;
}
public double calcularArea() {
return lado * lado;
}
}
package solid;
public class PentagonoRegular {
private double lado;
private String nombre;
public PentagonoRegular(double lado) {
this.lado = lado;
nombre = "Pentagono regular";
}
public String getNombre() {
return nombre;
}
public double calcularSup() {
return 5 * lado * lado / (4 * Math.tan(Math.PI / 5.0));
}
}
-46-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
import java.util.ArrayList;
public class CalculadoraDeAreas {
private ArrayList<PoligonoRegular> c;
public CalculadoraDeAreas(ArrayList<PoligonoRegular> c) {
this.c = c;
}
public void calcular() {
for (int i = 0; i < c.size(); i++) {
PoligonoRegular p = c.get(i);
System.out.println(p.getNombre()+": "+Math.floor(p.calcularArea()*100)/100);
}
}
}
-47-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public class Reproductor {
public void reproducirAudio() {
System.out.println("Reproduciendo audio...");
}
public void reproducirVideo() {
System.out.println("Reproduciendo video...");
}
}
package solid;
public class MediaPlayerClassic extends Reproductor {
}
package solid;
public class VLC extends Reproductor {
}
package solid;
public class Winamp extends Reproductor {
@Override
public void reproducirVideo() {
throw new RuntimeException("Winamp no puede reproducir video...");
}
}
-48-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Una manera de corregir el código anterior es reconocer que hay dos clases de
reproductores: los que reproducen audio y video (como Media Player Classic y VLC) y
los que sólo reproducen audio (como Winamp). Así, el código resultante es el siguiente:
package solid;
public class Main {
public static void main(String[] args) {
ReproductorDeAudioVideo r;
r = new ReproductorDeAudioVideo();
r.reproducirAudio();
r.reproducirVideo();
r = new VLC();
r.reproducirAudio();
r.reproducirVideo();
r = new MediaPlayerClassic();
r.reproducirAudio();
r.reproducirVideo();
ReproductorDeAudio a = new ReproductorDeAudio();
a.reproducirAudio();
a = new Winamp();
a.reproducirAudio();
// a.reproducirVideo(); // Esta línea directamente no compila
}
}
package solid;
public class ReproductorDeAudio {
public void reproducirAudio() {
System.out.println("Reproduciendo audio...");
}
}
package solid;
public class ReproductorDeAudioVideo {
public void reproducirVideo() {
System.out.println("Reproduciendo video...");
}
public void reproducirAudio() {
System.out.println("Reproduciendo audio...");
}
}
package solid;
class MediaPlayerClassic extends ReproductorDeAudioVideo {
}
package solid;
public class VLC extends ReproductorDeAudioVideo {
}
package solid;
public class Winamp extends ReproductorDeAudio {
}
Resumiendo, este principio establece que todas las subclases deben tener el
comportamiento que los clientes esperan de la superclase. Por ello, como se afirma en
un afiche muy conocido: “Si tiene el aspecto de un pato y se oye como un pato, pero usa
pilas, probablemente considerarlo un pato sea una abstracción equivocada.”
-49-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public class Main {
public static void main(String[] args) {
EmpleableParaTrabajar trabajadorDiurno = new TrabajadorDiurno();
trabajadorDiurno.trabajar();
trabajadorDiurno.comer();
EmpleableParaTrabajar trabajadorNocturno = new TrabajadorNocturno();
trabajadorNocturno.trabajar();
trabajadorNocturno.comer();
}
}
package solid;
public class TrabajadorDiurno implements EmpleableParaTrabajar {
@Override
public void trabajar() {
System.out.println("Trabaja de dia...");
}
@Override
public void comer() {
System.out.println("Hace una pausa para almorzar...");
}
}
package solid;
public class TrabajadorNocturno implements EmpleableParaTrabajar {
@Override
public void trabajar() {
System.out.println("Trabaja de noche...");
}
@Override
public void comer() {
System.out.println("Hace una pausa para cenar...");
}
}
-50-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public interface Alimentable {
void comer();
}
package solid;
public interface EmpleableHumano extends EmpleableParaTrabajar, Alimentable {
}
package solid;
public class Main {
public static void main(String[] args) {
EmpleableHumano trabajadorDiurno = new TrabajadorDiurno();
trabajadorDiurno.trabajar();
trabajadorDiurno.comer();
EmpleableHumano trabajadorNocturno = new TrabajadorNocturno();
trabajadorNocturno.trabajar();
trabajadorNocturno.comer();
EmpleableParaTrabajar robot = new Robot();
robot.trabajar();
}
}
package solid;
public class TrabajadorDiurno implements EmpleableHumano {
@Override
public void trabajar() {
System.out.println("Trabaja de dia...");
}
@Override
public void comer() {
System.out.println("Hace una pausa para almorzar...");
}
}
package solid;
public class TrabajadorNocturno implements EmpleableHumano {
@Override
public void trabajar() {
System.out.println("Trabaja de noche...");
}
@Override
public void comer() {
System.out.println("Hace una pausa para cenar...");
}
}
package solid;
public class Robot implements EmpleableParaTrabajar {
@Override
public void trabajar() {
System.out.println("Trabaja todo el dia sin pausa...");
}
}
-51-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public class Gerente {
private Operario operarioACargo;
public void asignarOperario(Operario o) {
operarioACargo = o;
}
public void trabajar() {
System.out.println("Gerente dando ordenes...");
operarioACargo.trabajar();
}
}
package solid;
public class Operario {
public void trabajar() {
System.out.println("Operario trabajando...");
}
}
-52-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package solid;
public interface ContratableParaTrabajar {
void trabajar();
}
package solid;
public class Operario implements ContratableParaTrabajar {
@Override
public void trabajar() {
System.out.println("Operario trabajando...");
}
}
package solid;
public class Secretaria implements ContratableParaTrabajar {
@Override
public void trabajar() {
System.out.println("Secretaria trabajando...");
}
}
package solid;
public class Main {
public static void main(String[] args) {
Operario op = new Operario();
Secretaria se = new Secretaria();
Gerente ge = new Gerente();
ge.asignarTrabajador(op);
ge.trabajar();
ge.asignarTrabajador(se);
ge.trabajar();
}
}
-53-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package figuras;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<PoligonoRegular> c = new ArrayList<>();
c.add(FactoriaDePoligonos.crearPoligono("TrianguloEquilatero", 5));
c.add(FactoriaDePoligonos.crearPoligono("Cuadrado", 5));
c.add(FactoriaDePoligonos.crearPoligono("PentagonoRegular", 5));
for (int i = 0; i < c.size(); i++) {
PoligonoRegular p = c.get(i);
System.out.println(p.getNombre()+": "+Math.floor(p.calcularArea()*100)/100);
}
}
}
Como se puede observar, la clase Main ahora sólo está acoplada a las clases
PoligonoRegular y FactoriaDePoligonos, y es en el método estático crearPoligono
perteneciente a ésta última donde se crean las instancias de las clases
TrianguloEquilatero, Cuadrado y Pentagono, dependiendo del valor del primer
argumento (.............................).
1 Un patrón de diseño es una solución a un problema de diseño, considerada efectiva y reutilizable. Los
más conocidos fueron publicados en el libro Design Patterns escrito por el grupo Gang of Four (GoF)
compuesto por Erich Gamma, Richard Helm, Ralph Johnson y John Vlisides, en el que se recogían 23
patrones de diseño comunes.
-54-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package figuras;
import java.util.ArrayList;
-55-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package figuras;
private FactoriaDePoligonos() {
}
package figuras;
-56-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package figuras;
public TrianguloEquilatero() {
setNombre("Triangulo equilatero");
}
@Override
public double calcularArea() {
return getLado() * getLado() * Math.sqrt(3) / 4;
}
package figuras;
public Cuadrado() {
setNombre("Cuadrado");
}
@Override
public double calcularArea() {
return getLado() * getLado();
}
package figuras;
public PentagonoRegular() {
setNombre("Pentagono Regular");
}
@Override
public double calcularArea() {
return 5 * getLado() * getLado() / (4 * Math.tan(Math.PI / 5.0));
}
-57-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-58-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Una empresa produce un juego en el cual deben cazarse patos. El juego muestra
una gran variedad de patos nadando y parpando.
package patos;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Pato> c = new ArrayList<>();
FactoriaDePatos fp = FactoriaDePatos.getInstance();
c.add(fp.crearPato("PatoOvero"));
c.add(fp.crearPato("PatoCapuchino"));
c.add(fp.crearPato("PatoDeGoma"));
for (int i = 0; i < c.size(); i++) {
Pato p = c.get(i);
p.mostrar();
p.parpar();
p.nadar();
System.out.println();
}
}
}
package patos;
private FactoriaDePatos() {
}
1 Freeman, E. et al. (2004): Head First. Design Patterns. O'Reilly Media, pp. 2-24 (adaptado)
-59-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato overo.");
}
}
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato capuchino.");
}
}
package patos;
@Override
public void parpar() {
System.out.println("<sonido artificial de pato>");
}
@Override
public void mostrar() {
System.out.println("Se muestra un pato de goma.");
}
}
-60-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-61-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
import java.util.ArrayList;
package patos;
package patos;
package patos;
package patos;
-62-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato decorativo de ceramica.");
}
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato de goma.");
}
@Override
public void parpar() {
System.out.println("<sonido artificial de pato >");
}
@Override
public void nadar() {
System.out.println("Pato nadando...");
}
-63-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato overo.");
}
@Override
public void parpar() {
System.out.println("Cua, cua!");
}
@Override
public void nadar() {
System.out.println("Pato nadando...");
}
@Override
public void volar() {
System.out.println("Pato volando...");
}
}
package patos;
@Override
public void mostrar() {
System.out.println("Se muestra un pato capuchino.");
}
@Override
public void parpar() {
System.out.println("Cua, cua!");
}
@Override
public void nadar() {
System.out.println("Pato nadando...");
}
@Override
public void volar() {
System.out.println("Pato volando...");
}
}
-64-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
En ese momento, José se dio cuenta de que las interfaces, aunque ayudan a
resolver ciertos efectos indeseados de la herencia, pueden crear una pesadilla de
mantenimiento cuando atentan contra la reutilización de código.
-65-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El siguiente diagrama de clases muestra que la clase Pato tiene los atributos
capacidadDeVoz, capacidadDeVuelo y capacidadDeNado (con sus correspondientes
setters). Estos atributos son referencias a objetos creados al instanciar las subclases de
Pato y proporcionan implementaciones de los métodos parpar, volar y nadar,
respectivamente. Al encapsular el comportamiento en clases independientes
y utilizarlo mediante delegación, logramos que éste pueda cambiarse sin tener
que modificar el código de las clases que lo utilizan. Esto se conoce como patrón de diseño
Strategy.
-66-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
public abstract class Pato {
private CapazDeParpar capacidadDeVoz;
private CapazDeNadar capacidadDeNado;
private CapazDeVolar capacidadDeVuelo;
public void setCapacidadDeVoz(CapazDeParpar capacidadDeVoz) {
this.capacidadDeVoz = capacidadDeVoz;
}
public void setCapacidadDeNado(CapazDeNadar capacidadDeNado) {
this.capacidadDeNado = capacidadDeNado;
}
public void setCapacidadDeVuelo(CapazDeVolar capacidadDeVuelo) {
this.capacidadDeVuelo = capacidadDeVuelo;
}
-67-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
public PatoCapuchino() {
setCapacidadDeVoz(new CapacidadDeVozNatural());
setCapacidadDeNado(new CapacidadDeNado());
setCapacidadDeVuelo(new CapacidadDeVuelo());
}
@Override
public void mostrar() {
System.out.println("Se muestra un pato capuchino.");
}
}
package patos;
public PatoOvero() {
setCapacidadDeVoz(new CapacidadDeVozNatural());
setCapacidadDeNado(new CapacidadDeNado());
setCapacidadDeVuelo(new CapacidadDeVuelo());
}
@Override
public void mostrar() {
System.out.println("Se muestra un pato overo.");
}
}
package patos;
public PatoDeGoma() {
setCapacidadDeVoz(new CapacidadDeVozArtificial());
setCapacidadDeNado(new CapacidadDeNado());
setCapacidadDeVuelo(new IncapacidadDeVuelo());
}
@Override
public void mostrar() {
System.out.println("Se muestra un pato de goma.");
}
}
-68-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
public PatoDecorativoDeCeramica() {
setCapacidadDeVoz(new IncapacidadDeVoz());
setCapacidadDeNado(new IncapacidadDeNado());
setCapacidadDeVuelo(new IncapacidadDeVuelo());
}
@Override
public void mostrar() {
System.out.println("Se muestra un pato decorativo de ceramica.");
}
package patos;
@Override
public void parpar() {
System.out.println("Cua, cua!");
}
}
package patos;
@Override
public void parpar() {
System.out.println("<sonido artificial de pato>");
}
}
package patos;
@Override
public void parpar() {
System.out.println("<silencio>");
}
}
-69-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package patos;
@Override
public void nadar() {
System.out.println("Pato nadando...");
}
}
package patos;
@Override
public void nadar() {
System.out.println("Pato incapaz de nadar...");
}
}
package patos;
@Override
public void volar() {
System.out.println("Pato volando...");
}
}
package patos;
@Override
public void volar() {
System.out.println("Pato incapaz de volar...");
}
}
DESARROLLADORA GURÚ DE
ESCÉPTICA LOS PATRONES
DE DISEÑO
-70-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Repaso
Ejemplo práctico de Ingeniería de Software
(Extraído de Deitel, P. & Deitel, H.: Cómo programar en Java, 7ª ed., Pearson, 2008)
La orientación a objetos es una manera natural de pensar acerca del mundo real
y de escribir programas de cómputo. Nuestro objetivo aquí es ayudarle a desarrollar una
forma de pensar orientada a objetos, y de presentarle el Lenguaje Unificado de Modelado
(UML), un lenguaje gráfico que permite a las personas que diseñan sistemas de software
utilizar una notación estándar en la industria para representarlos.
Repasemos primero cierta terminología clave. En cualquier parte del mundo real
puede ver objetos: gente, animales, plantas, automóviles, aviones, edificios,
computadoras, etcétera. Los humanos pensamos en términos de objetos. Los teléfonos,
casas, semáforos, hornos de microondas y enfriadores de agua son sólo unos cuantos
objetos más. Los programas de cómputo están compuestos por muchos objetos de
software con capacidad de interacción.
-71-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Los humanos aprenden acerca de los objetos existentes estudiando sus atributos
y observando sus comportamientos. Distintos objetos pueden tener atributos similares
y pueden exhibir comportamientos similares. Por ejemplo, pueden hacerse
comparaciones entre los bebés y los adultos, y entre los humanos y los chimpancés.
-72-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Las clases son para los objetos lo que los planos de construcción, para las casas.
Así como podemos construir muchas casas a partir de un plano, podemos instanciar
(crear) muchos objetos a partir de una clase. No puede cocinar alimentos en la cocina de
un plano de construcción; puede cocinarlos en la cocina de una casa.
Las clases pueden tener relaciones con otras clases. Por ejemplo, en un diseño
orientado a objetos de un banco, la clase “cajero” necesita relacionarse con las clases
“cliente”, “cajón de efectivo”, “bóveda”, etcétera. A estas relaciones se les llama
asociaciones.
Pronto estará escribiendo programas en Java. ¿Cómo creará el código para sus
programas? Tal vez, como muchos programadores principiantes, simplemente
encenderá su computadora y empezará a teclear. Esta metodología puede funcionar
para programas pequeños, pero ¿qué haría usted si se le pidiera crear un sistema de
software para controlar miles de máquinas de cajero automático para un importante
banco? O suponga que le pidieron trabajar en un equipo de 1.000 desarrolladores de
software para construir el nuevo sistema de control de tráfico aéreo. Para proyectos tan
grandes y complejos, no podría simplemente sentarse y empezar a escribir programas.
-73-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Para crear las mejores soluciones, debe seguir un proceso detallado para analizar
los requerimientos de su proyecto (es decir, determinar qué es lo que se supone debe
hacer el sistema) y desarrollar un diseño que cumpla con esos requerimientos (es decir,
decidir cómo debe hacerlo el sistema). Idealmente usted pasaría por este proceso y
revisaría cuidadosamente el diseño (o haría que otros profesionales de software lo
revisaran) antes de escribir cualquier código. Si este proceso implica analizar y diseñar
su sistema desde un punto de vista orientado a objetos, lo llamamos un proceso de
análisis y diseño orientado a objetos (A/D OO). Los programadores
experimentados saben que el análisis y el diseño pueden ahorrar innumerables horas,
ya que les ayudan a evitar un método de desarrollo de un sistema mal planeado, que
tiene que abandonarse en plena implementación, con la posibilidad de desperdiciar una
cantidad considerable de tiempo, dinero y esfuerzo.
A medida que los problemas y los grupos de personas que los resuelven aumentan
en tamaño, los métodos de A/D OO se vuelven más apropiados que el pseudocódigo.
Idealmente, un grupo debería acordar un proceso estrictamente definido para resolver
su problema, y establecer también una manera uniforme para que los miembros del
grupo se comuniquen los resultados de ese proceso entre sí. Aunque existen diversos
procesos de A/D OO, hay un lenguaje gráfico para comunicar los resultados de cualquier
proceso A/D OO que se ha vuelto muy popular. Este lenguaje, conocido como Lenguaje
Unificado de Modelado (UML), se desarrolló a mediados de la década de los noventa,
bajo la dirección inicial de tres metodologistas de software: Grady Booch, James
Rumbaugh e Ivar Jacobson.
¿Qué es UML?
-74-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Para obtener más información acerca de UML, consulte los siguientes sitios Web:
• www.uml.org
Esta página de recursos de UML del Grupo de Administración de Objetos (OMG)
proporciona documentos de la especificación para UML y otras tecnologías
orientadas a objetos.
• es.wikipedia.org/wiki/UML
La definición de Wikipedia del UML en español.
Ejercicios de autorrepaso
1. Liste tres ejemplos de objetos reales que no mencionamos. Para cada objeto, liste
varios atributos y comportamientos.
2. El pseudocódigo es __________.
a) otro término para el A/D OO
b) un lenguaje de programación utilizado para visualizar diagramas de UML
c) un medio informal para expresar la lógica de un programa
d) un esquema de representación gráfica para modelar sistemas orientados a
objetos
1. [Nota: las respuestas pueden variar]. a) Los atributos de una televisión incluyen el
tamaño de la pantalla, el número de colores que puede mostrar, su canal actual y su
volumen actual. Una televisión se enciende y se apaga, cambia de canales, muestra
video y reproduce sonidos. b) Los atributos de una cafetera incluyen el volumen
máximo de agua que puede contener, el tiempo requerido para preparar una jarra
de café y la temperatura del plato calentador bajo la jarra de café. Una cafetera se
enciende y se apaga, prepara café y lo calienta. c) Los atributos de una tortuga
incluyen su edad, el tamaño de su caparazón y su peso. Una tortuga camina, se mete
en su caparazón, emerge del mismo y come vegetación.
2. c.
3. b.
-75-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Documento de requerimientos
Un banco local pretende instalar una nueva máquina de cajero automático (ATM),
para permitir a los usuarios (es decir, los clientes del banco) realizar transacciones
financieras básicas (figura 1). Cada usuario sólo puede tener una cuenta en el banco.
Los usuarios del ATM deben poder ver el saldo de su cuenta, retirar efectivo (es decir,
sacar dinero de una cuenta) y depositar fondos (es decir, meter dinero en una cuenta).
La interfaz de usuario del cajero automático contiene los siguientes componentes:
• una pantalla que muestra mensajes al usuario
• un teclado que recibe datos numéricos de entrada del usuario
• un dispensador de efectivo que dispensa efectivo al usuario, y
• una ranura de depósito que recibe sobres para depósitos del usuario.
El dispensador de efectivo comienza cada día cargado con 500 billetes de $20.
[Nota: debido al alcance limitado de este ejemplo práctico, ciertos elementos del ATM
que se describen aquí no imitan exactamente a los de un ATM real. Por ejemplo,
generalmente un ATM contiene un dispositivo que lee el número de cuenta del usuario
de una tarjeta para ATM, mientras que este ATM pide al usuario que escriba su número
de cuenta. Un ATM real también imprime por lo general un recibo al final de una sesión,
pero toda la salida de este ATM aparece en la pantalla].
-76-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El banco desea que usted desarrolle software para realizar las transacciones
financieras que inicien los clientes a través del ATM. Posteriormente, el banco integrará
el software con el hardware del ATM. El software debe encapsular la funcionalidad de
los dispositivos de hardware (por ejemplo: dispensador de efectivo, ranura para
depósito) dentro de los componentes de software, pero no necesita estar involucrado en
la manera en que estos dispositivos ejecutan su tarea. El hardware del ATM no se ha
desarrollado aún, en vez de que usted escriba un software para ejecutarse en el ATM,
deberá desarrollar una primera versión del software para que se ejecute en una
computadora personal. Esta versión debe utilizar el monitor de la computadora para
simular la pantalla del ATM y el teclado de la computadora para simular el teclado
numérico del ATM.
-77-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Una vez que el ATM autentica al usuario, el menú principal (figura 2) debe
contener una opción numerada para cada uno de los tres tipos de transacciones: solicitud
de saldo (opción 1), retiro (opción 2) y depósito (opción 3). El menú principal también
debe contener una opción para que el usuario pueda salir del sistema (opción 4). Después
el usuario elegirá si desea realizar una transacción (oprimiendo 1, 2 o 3) o salir del
sistema (oprimiendo 4).
-78-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Los siguientes pasos describen las acciones que ocurren cuando el usuario elige
la opción 2 para hacer un retiro:
-79-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Los siguientes pasos describen las acciones que ocurren cuando el usuario elige
la opción 3 para hacer un depósito:
Una vez que el sistema ejecuta una transacción en forma exitosa, debe volver a
mostrar el menú principal para que el usuario pueda realizar transacciones adicionales.
Si el usuario elije salir del sistema, la pantalla debe mostrar un mensaje de
agradecimiento y después el mensaje de bienvenida para el siguiente usuario.
-80-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
La etapa de análisis del ciclo de vida del software se enfoca en definir el problema
a resolver. Al diseñar cualquier sistema, uno debe resolver el problema de la manera
correcta, pero de igual manera uno debe resolver el problema correcto. Los analistas de
sistemas recolectan los requerimientos que indican el problema específico a resolver.
Nuestro documento de requerimientos describe nuestro sistema ATM con el suficiente
detalle como para que usted no necesite pasar por una etapa de análisis exhaustiva; ya
lo hicimos por usted.
Cada uno de los casos de uso describe un escenario común en el cual el usuario
utiliza el sistema. Usted ya leyó las descripciones de los casos de uso del sistema ATM
en el documento de requerimientos; las listas de pasos requeridos para realizar cada
tipo de transacción (como solicitud de saldo, retiro y depósito) describen en realidad los
tres casos de uso de nuestro ATM: “Ver saldo de cuenta”, “Retirar efectivo” y “Depositar
fondos”, respectivamente.
Diagramas de caso-uso
-81-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Nuestro documento de requerimientos provee los actores: “los usuarios del ATM
deben poder ver el saldo de su cuenta, retirar efectivo y depositar fondos”. Por lo tanto,
el actor en cada uno de estos tres casos de uso es el usuario que interactúa con el ATM.
Una entidad externa (una persona real) desempeña el papel del usuario para realizar
transacciones financieras. La figura 4 muestra un actor, cuyo nombre (Usuario) aparece
debajo del actor en el diagrama. UML modela cada caso de uso como un óvalo conectado
a un actor con una línea sólida.
-82-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-83-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Ejercicios de autoevaluación
2. Los _________________ modelan las interacciones entre los objetos en un sistema, con
énfasis acerca de cuándo ocurren estas interacciones.
a) Diagramas de clases
b) Diagramas de secuencia
c) Diagramas de comunicación
d) Diagramas de actividad
3. ¿Cuál de las siguientes opciones lista las etapas de un típico ciclo de vida de software,
en orden secuencial?
a) diseño, análisis, implementación, prueba
b) diseño, análisis, prueba, implementación
c) análisis, diseño, prueba, implementación
d) análisis, diseño, implementación, prueba
2. b.
3. d.
-84-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 5: Diagrama de caso-uso para una versión modificada de nuestro sistema ATM,
que también permite a los usuarios transferir dinero entre varias cuentas.
-85-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Crearemos clases sólo para los sustantivos y frases nominales que tengan
importancia en el sistema ATM. No modelamos “banco” como una clase, ya que el banco
no es una parte del sistema ATM; el banco sólo quiere que nosotros construyamos el
ATM. “Cliente” y “usuario” también representan entidades fuera del sistema; son
importantes debido a que interactúan con nuestro sistema ATM, pero no necesitamos
modelarlos como clases en el software del ATM. Recuerde que modelamos un usuario
del ATM (es decir, un cliente del banco) como el actor en el diagrama de casos de uso de
la figura 4.
No necesitamos modelar “billete de $20” ni “sobre de depósito” como clases. Éstos
son objetos físicos en el mundo real, pero no forman parte de lo que se va a automatizar.
Podemos representar en forma adecuada la presencia de billetes en el sistema, mediante
el uso de un atributo de la clase que modela el dispensador de efectivo (más adelante
asignaremos atributos a las clases del sistema ATM). Por ejemplo, el dispensador de
efectivo mantiene un conteo del número de billetes que contiene. El documento de
requerimientos no dice nada acerca de lo que debe hacer el sistema con los sobres de
depósito después de recibirlos. Podemos suponer que con sólo admitir la recepción de un
sobre (una operación que realiza la clase que modela la ranura de depósito) es suficiente
para representar la presencia de un sobre en el sistema (más adelante asignaremos
operaciones a las clases del sistema ATM).
En nuestro sistema ATM simplificado, lo más apropiado sería representar varios
montos de “dinero”, incluyendo el “saldo” de una cuenta, como atributos de clases. De
igual forma, los sustantivos “número de cuenta” y “NIP” representan piezas importantes
de información en el sistema ATM. Son atributos importantes de una cuenta bancaria.
Sin embargo, no exhiben comportamientos. Por ende, podemos modelarlos de la manera
más apropiada como atributos de una clase de cuenta.
Aunque, con frecuencia, el documento de requerimientos describe una
“transacción” en un sentido general, no modelaremos la amplia noción de una
transacción financiera en este momento. En vez de ello, modelaremos los tres tipos de
transacciones (es decir, “solicitud de saldo”, “retiro” y “depósito”) como clases
individuales. Estas clases poseen los atributos específicos necesarios para ejecutar las
transacciones que representan. Por ejemplo, para un retiro se necesita conocer el monto
de dinero que el usuario desea retirar. Sin embargo, una solicitud de saldo no requiere
datos adicionales. Lo que es más, las tres clases de transacciones exhiben
comportamientos únicos. Para un retiro se requiere entregar efectivo al usuario,
mientras que para un depósito se requiere recibir un sobre de depósito del usuario.
[Nota: más adelante, “factorizaremos” las características comunes de todas las
transacciones en una clase de “transacción” general, mediante el uso del concepto
orientado a objetos de herencia].
Determinaremos las clases para nuestro sistema con base en los sustantivos y
frases nominales restantes de la figura 6. Cada una de ellas se refiere a uno o varios de
los siguientes elementos:
ATM cuenta
pantalla base de datos del banco
teclado numérico solicitud de saldo
dispensador de efectivo retiro
ranura de depósito depósito
-86-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Es probable que los elementos de esta lista sean clases que necesitaremos
implementar en nuestro sistema.
Ahora podemos modelar las clases en nuestro sistema, con base en la lista que
hemos creado. En el proceso de diseño escribimos los nombres de las clases con la
primera letra en mayúscula (una convención de UML), como lo haremos cuando
escribamos el código de Java para implementar nuestro diseño. Si el nombre de una
clase contiene más de una palabra, juntaremos todas las palabras y escribiremos la
primera letra de cada una de ellas en mayúscula. De esta manera, vamos a crear las
clases ATM, Pantalla, Teclado, DispensadorEfectivo, RanuraDeposito, Cuenta,
BaseDatosBanco, SolicitudSaldo, Retiro y Deposito. Construiremos nuestro sistema
mediante el uso de todas estas clases como bloques de construcción. Sin embargo, antes
de empezar a construir el sistema, debemos comprender mejor la forma en que las clases
se relacionan entre sí.
UML nos permite modelar, a través de los diagramas de clases, las clases en el
sistema ATM y sus interrelaciones. La figura 7 representa a la clase ATM. En UML, cada
clase se modela como un rectángulo con tres compartimientos. El compartimiento
superior contiene el nombre de la clase, centrado horizontalmente y en negrita. El
compartimiento intermedio contiene los atributos de la clase. El compartimiento inferior
contiene las operaciones de la clase. En la figura 7, los compartimientos intermedio e
inferior están vacíos, ya que no hemos determinado los atributos y operaciones de esta
clase todavía.
Los diagramas de clases también muestran las relaciones entre las clases del
sistema. La figura 8 muestra cómo nuestras clases ATM y Retiro se relacionan una con
la otra. Por el momento modelaremos sólo este subconjunto de las clases del ATM, por
cuestión de simpleza. Más adelante, presentaremos un diagrama de clases más
completo. Observe que los rectángulos que representan a las clases en este diagrama no
están subdivididos en compartimientos. UML permite suprimir los atributos y las
operaciones de una clase de esta forma, cuando sea apropiado, para crear diagramas
más legibles. Un diagrama de este tipo se denomina diagrama con elementos
omitidos (elided diagram): su información, como el contenido de los compartimientos
segundo y tercero, no se modela. Más adelante, vamos a colocar información en estos
compartimientos.
-87-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
En la figura 8, la línea sólida que conecta a las dos clases representa una
asociación: una relación entre clases. Los números cerca de cada extremo de la línea
son valores de multiplicidad; éstos indican cuántos objetos de cada clase participan en
la asociación. En este caso, al seguir la línea de un extremo al otro se revela que, en un
momento dado, un objeto ATM participa en una asociación con cero o con un objeto
Retiro; cero si el usuario actual no está realizando una transacción o si ha solicitado un
tipo distinto de transacción, y uno si el usuario ha solicitado un retiro. UML puede
modelar muchos tipos de multiplicidad. La figura 9 lista y explica los tipos de
multiplicidad.
Una asociación puede tener nombre. Por ejemplo, la palabra Ejecuta por encima
de la línea que conecta a las clases ATM y Retiro en la figura 8 indica el nombre de esa
asociación. Esta parte del diagrama se lee así: “un objeto de la clase ATM ejecuta cero o
un objeto de la clase Retiro”. Los nombres de las asociaciones son direccionales, como
lo indica la punta de flecha rellena; por lo tanto, sería inapropiado, por ejemplo, leer la
anterior asociación de derecha a izquierda como “cero o un objeto de la clase Retiro
ejecuta un objeto de la clase ATM”.
-88-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
En la figura 10, los diamantes sólidos que se adjuntan a las líneas de asociación
de la clase ATM indican que esta clase tiene una relación de composición con las clases
Pantalla, Teclado, DispensadorEfectivo y RanuraDeposito. La composición implica
una relación todo/parte. La clase que tiene el símbolo de composición (el diamante
sólido) en su extremo de la línea de asociación es el todo (en este caso, ATM), y las clases
en el otro extremo de las líneas de asociación son las partes; en este caso, las clases
Pantalla, Teclado, DispensadorEfectivo y RanuraDeposito. Las composiciones en la
figura 10 indican que un objeto de la clase ATM está formado por un objeto de la clase
Pantalla, un objeto de la clase DispensadorEfectivo, un objeto de la clase Teclado y
un objeto de la clase RanuraDeposito. El ATM “tiene una” pantalla, un teclado, un
dispensador de efectivo y una ranura de depósito. La relación “tiene un” define la
composición (más adelante, veremos que la relación “es un” define la herencia).
-89-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-90-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
de composición con cero o más objetos de la clase Cuenta. Recuerde que en la figura 9 se
muestra que el valor de multiplicidad 0..* en el extremo de la clase Cuenta, de la
asociación entre las clases BaseDatosBanco y Cuenta, indica que cero o más objetos de
la clase Cuenta participan en la asociación. La clase BaseDatosBanco tiene una
relación de uno a varios con la clase Cuenta; BaseDatosBanco puede contener muchos
objetos Cuenta. De manera similar, la clase Cuenta tiene una relación de varios a
uno con la clase BaseDatosBanco; puede haber muchos objetos Cuenta en
BaseDatosBanco. [Nota: si recuerda la figura 9, el valor de multiplicidad * es idéntico a
0..*. Incluimos 0..* en nuestros diagramas de clases por cuestión de claridad].
Ya hemos identificado las clases en nuestro sistema ATM (aunque tal vez
descubramos otras, a medida que avancemos con el diseño y la implementación). A
continuación, determinaremos los atributos para cada una de estas clases, y luego
utilizaremos estos atributos para examinar la forma en que cambia el sistema con el
tiempo.
-91-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Ejercicios de autoevaluación
1. Suponga que tenemos una clase llamada Auto, la cual representa a un automóvil.
Piense en algunas de las distintas piezas que podría reunir un fabricante para
producir un automóvil completo. Cree un diagrama de clases (similar a la figura 10)
que modele algunas de las relaciones de composición de la clase Auto.
2. Suponga que tenemos una clase llamada Archivo, la cual representa un documento
electrónico en una computadora independiente, sin conexión de red, representada
por la clase Computadora. ¿Qué tipo de asociación existe entre la clase Computadora
y la clase Archivo?
a) La clase Computadora tiene una relación de uno a uno con la clase Archivo.
b) La clase Computadora tiene una relación de varios a uno con la clase Archivo.
c) La clase Computadora tiene una relación de uno a varios con la clase Archivo.
d) La clase Computadora tiene una relación de varios a varios con la clase Archivo.
Figura 12: Diagrama de clases que muestra algunas relaciones de composición de una clase Auto.
2. c. [Nota: en una computadora con conexión de red, esta relación podría ser de varios
a varios].
3. Verdadera.
-92-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 13: Diagrama de clases para el modelo del sistema ATM, incluyendo la clase Deposito.
Considere los atributos de algunos objetos reales: los atributos de una persona
incluyen su altura, peso y si es zurdo, diestro o ambidiestro. Los atributos de un radio
incluyen la estación, el volumen y si está en AM o FM. Los atributos de un automóvil
incluyen las lecturas de su velocímetro y odómetro, la cantidad de gasolina en su tanque
y la velocidad de marcha en la que se encuentra. Los atributos de una computadora
personal incluyen su fabricante (por ejemplo, Dell, Apple o Lenovo), el tipo de pantalla
(por ejemplo, LCD o CRT), el tamaño de su memoria principal y el de su disco duro.
-93-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 14: Palabras y frases descriptivas del documento de requerimientos del ATM
La figura 14 nos conduce a crear un atributo de la clase ATM. Esta clase mantiene
información acerca del estado del ATM. La frase “el usuario es autenticado” describe un
estado del ATM (más adelante hablaremos con detalle sobre los estados), por lo que
incluimos usuarioAutenticado como un atributo Boolean (es decir, un atributo que
tiene un valor de true o false) en la clase ATM. Observe que el atributo tipo Boolean en
UML es equivalente al tipo boolean en Java. Este atributo indica si el ATM autenticó
con éxito al usuario actual o no; usuarioAutenticado debe ser true para que el sistema
permita al usuario realizar transacciones y acceder a la información de la cuenta. Este
atributo nos ayuda a cerciorarnos de la seguridad de los datos en el sistema.
-94-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-95-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Por cuestión de simpleza, la figura 15 no muestra las asociaciones entre las clases;
en la figura 11 mostramos estas asociaciones. Ésta es una práctica común de los
diseñadores de sistemas, a la hora de desarrollar los diseños. Como vimos
anteriormente, en UML los atributos de una clase se colocan en el compartimiento
intermedio del rectángulo de la clase. Listamos el nombre de cada atributo y su tipo,
separados por un signo de dos puntos (:), seguido en algunos casos de un signo de igual
(=) y de un valor inicial.
Considere el atributo usuarioAutenticado de la clase ATM:
usuarioAutenticado : Boolean = false
-96-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-97-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Ejercicios de autoevaluación
1. Por lo general, identificamos los atributos de las clases en nuestro sistema mediante
el análisis de ____________ en el documento de requerimientos.
a) Los sustantivos y las frases nominales.
b) Las palabras y frases descriptivas.
c) Los verbos y las frases verbales.
d) Todo lo anterior.
1. b.
3. Esta declaración indica que el atributo conteo es de tipo Integer, con un valor inicial
de 500. Este atributo lleva la cuenta del número de billetes disponibles en el
DispensadorEfectivo, en cualquier momento dado.
Cada objeto en un sistema pasa a través de una serie de estados. El estado actual
de un objeto se indica mediante los valores de los atributos del objeto en cualquier
momento dado. Los diagramas de máquina de estado (que se conocen comúnmente
como diagramas de estado) modelan varios estados de un objeto y muestran bajo qué
circunstancias el objeto cambia de estado. A diferencia de los diagramas de clases, que
se enfocan principalmente en la estructura del sistema, los diagramas de estado
modelan parte del comportamiento del sistema.
-98-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Las flechas indican las transiciones entre los estados. Un objeto puede pasar de
un estado a otro, en respuesta a los diversos eventos que ocurren en el sistema. El
nombre o la descripción del evento que ocasiona una transición se escribe cerca de la
línea que corresponde a esa transición. Por ejemplo, el objeto ATM cambia del estado
“Usuario no autenticado” al estado “Usuario autenticado”, una vez que la base de datos
autentica al usuario. En el documento de requerimientos vimos que para autenticar a
un usuario, la base de datos compara el número de cuenta y el NIP introducidos por el
usuario con los de la cuenta correspondiente en la base de datos. Si la base de datos
indica que el usuario ha introducido un número de cuenta válido y el NIP correcto, el
objeto ATM pasa al estado “Usuario autenticado” y cambia su atributo
usuarioAutenticado al valor true. Cuando el usuario sale del sistema al seleccionar la
opción “salir” del menú principal, el objeto ATM regresa al estado “Usuario no
autenticado”.
Diagramas de actividad
-99-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-100-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-101-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Hemos llevado a cabo los primeros pasos para modelar el comportamiento del
sistema ATM y hemos mostrado cómo participan los atributos de un objeto para realizar
las actividades del mismo. A continuación, investigaremos los comportamientos para
todas las clases, de manera que obtengamos una interpretación más precisa del
comportamiento del sistema, al “completar” los terceros compartimientos de las clases
en nuestro diagrama de clases.
Ejercicios de autoevaluación
2. Un diagrama de actividad modela las (los) _________ que realiza un objeto y el orden
en el que las(los) realiza.
a) acciones
b) atributos
c) estados
d) transiciones de estado
1. Falso. Los diagramas de estado modelan parte del comportamiento del sistema.
2. a.
-102-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Una operación es un servicio que proporcionan los objetos de una clase a los
clientes (usuarios) de esa clase. Considere las operaciones de algunos objetos reales. Las
operaciones de un radio incluyen el sintonizar su estación y ajustar su volumen (que,
por lo general, lo hace una persona que ajusta los controles del radio). Las operaciones
de un automóvil incluyen acelerar (operación invocada por el conductor cuando oprime
el pedal del acelerador), desacelerar (operación invocada por el conductor cuando oprime
el pedal del freno o cuando suelta el pedal del acelerador), dar vuelta y cambiar
velocidades. Los objetos de software también pueden ofrecer operaciones; por ejemplo,
-103-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 20: Verbos y frases verbales para cada clase en el sistema ATM.
Para identificar las operaciones, analizamos las frases verbales que se listan para
cada clase en la figura 20. La frase “ejecuta transacciones financieras” asociada con la
clase ATM implica que esta clase instruye a las transacciones a que se ejecuten. Por lo
tanto, cada una de las clases SolicitudSaldo, Retiro y Deposito necesitan una
operación para proporcionar este servicio al ATM. Colocamos esta operación (que hemos
nombrado ejecutar) en el tercer compartimiento de las tres clases de transacciones en
el diagrama de clases actualizado de la figura 21. Durante una sesión con el ATM, el
objeto ATM invocará estas operaciones de transacciones, según sea necesario.
-104-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
nombreParámetro : tipoParámetro
-105-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
La frase “obtiene el saldo de una cuenta” sugiere que las clases BaseDatosBanco
y Cuenta necesitan una operación obtenerSaldo. Sin embargo, recuerde que creamos
dos atributos en la clase Cuenta para representar un saldo: saldoDisponible y
saldoTotal. Una solicitud de saldo requiere el acceso a estos dos atributos del saldo, de
manera que pueda mostrarlos al usuario, pero un retiro sólo requiere verificar el valor
de saldoDisponible. Para permitir que los objetos en el sistema obtengan cada atributo
de saldo en forma individual, agregamos las operaciones obtenerSaldoDisponible y
obtenerSaldoTotal al tercer compartimiento de las clases BaseDatosBanco y Cuenta
(figura 21). Especificamos un tipo de valor de retorno Double para estas operaciones,
debido a que los atributos de los saldos que van a obtener son de tipo Double.
Las frases “abona un monto de depósito a una cuenta” y “carga un monto de retiro
a una cuenta” indican que las clases BaseDatosBanco y Cuenta deben realizar
operaciones para actualizar una cuenta durante un depósito y un retiro,
respectivamente. Por lo tanto, asignamos las operaciones abonar y cargar a las clases
BaseDatosBanco y Cuenta. Tal vez recuerde que cuando se abona a una cuenta (como
en un depósito) se suma un monto sólo al atributo saldoTotal. Por otro lado, cuando se
carga a una cuenta (como en un retiro) se resta el monto tanto del saldo total como del
saldo disponible. Ocultamos estos detalles de implementación dentro de la clase Cuenta.
Éste es un buen ejemplo de encapsulamiento y ocultamiento de información.
-106-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Si éste fuera un sistema ATM real, las clases BaseDatosBanco y Cuenta también
proporcionarían un conjunto de operaciones para permitir que otro sistema bancario
actualizara el saldo de la cuenta de un usuario después de confirmar o rechazar todo, o
parte de, un depósito. Por ejemplo, la operación confirmarMontoDeposito sumaría un
monto al atributo saldoDisponible, y haría que los fondos depositados estuvieran
disponibles para retirarlos. La operación rechazarMontoDeposito restaría un monto al
atributo saldoTotal para indicar que un monto especificado, que se había depositado
recientemente a través del ATM y se había sumado al saldoTotal, no se encontró en el
sobre de depósito. El banco invocaría esta operación después de determinar que el
usuario no incluyó el monto correcto de efectivo o que algún cheque no fue validado (es
decir, que “rebotó”). Aunque al agregar estas operaciones nuestro sistema estaría más
completo, no las incluiremos en nuestros diagramas de clases ni en nuestra
implementación, ya que se encuentran más allá del alcance de este ejemplo práctico.
De la frase “recibe entrada numérica del usuario” listada por la clase Teclado en
la figura 20, podemos concluir que la clase Teclado debe realizar una operación
obtenerEntrada. A diferencia del teclado de una computadora, el teclado del ATM sólo
contiene los números del 0 al 9, por lo cual especificamos que esta operación devuelve
un valor entero. Si recuerda, en el documento de requerimientos vimos que en distintas
situaciones, tal vez se requiera que el usuario introduzca un tipo distinto de número
(por ejemplo, un número de cuenta, un NIP, el número de una opción del menú, un
monto de depósito como número de centavos). La clase Teclado sólo obtiene un valor
numérico para un cliente de la clase; no determina si el valor cumple con algún criterio
específico. Cualquier clase que utilice esta operación debe verificar que el usuario haya
introducido un número apropiado según el caso, y después debe responder de manera
acorde (por ejemplo, mostrar un mensaje de error a través de la clase Pantalla). [Nota:
cuando implementemos el sistema, simularemos el teclado del ATM con el teclado de
una computadora y, por cuestión de simpleza, asumiremos que el usuario no escribirá
datos de entrada que no sean números, usando las teclas en el teclado de la computadora
que no aparezcan en el teclado del ATM].
-107-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-108-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Recuerde que para modelar cada parámetro en una lista de parámetros separados
por comas, UML lista el nombre del parámetro, seguido de un signo de dos puntos y el
tipo del parámetro (en notación de UML). Así, la figura 22 especifica que la operación
autenticarUsuario recibe dos parámetros: numeroCuentaUsuario y nipUsuario,
ambos de tipo Integer. Cuando implementemos el sistema en Java, representaremos
estos parámetros con valores int.
-109-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Observe que no hablamos sobre los parámetros para la operación ejecutar de las
clases SolicitudSaldo, Retiro y Depósito, de la operación obtenerEntrada de la clase
Teclado y la operación seRecibioSobre de la clase RanuraDeposito. En este punto de
nuestro proceso de diseño, no podemos determinar si estas operaciones requieren datos
adicionales para realizar sus tareas, por lo que dejaremos sus listas de parámetros
vacías. A medida que avancemos por el ejemplo práctico, tal vez decidamos agregar
parámetros a estas operaciones.
En esta sección hemos determinado muchas de las operaciones que realizan las
clases en el sistema ATM. Identificamos los parámetros y los tipos de valores de retorno
de algunas operaciones. A medida que continuemos con nuestro proceso de diseño, el
número de operaciones que pertenezcan a cada clase puede variar; podríamos descubrir
que se necesitan nuevas operaciones o que ciertas operaciones actuales no son
necesarias; y podríamos determinar que algunas de las operaciones de nuestras clases
necesitan parámetros adicionales y tipos de valores de retorno distintos.
-110-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Ejercicios de autoevaluación
2. Si quisiera agregar al sistema ATM una operación que devuelva el atributo monto de
la clase Retiro, ¿cómo y en dónde especificaría esta operación en el diagrama de
clases de la figura 21?
1. c.
2. Para especificar una operación que obtenga el atributo monto de la clase Retiro, se
debe colocar el siguiente listado de operaciones en el (tercer) compartimiento de
operaciones de la clase Retiro: obtenerMonto( ) : Double
3. Este listado de operaciones indica una operación llamada sumar, la cual recibe los
enteros x e y como parámetros y devuelve un valor entero.
Cuando dos objetos se comunican entre sí para realizar una tarea, se dice que
colaboran (para ello, un objeto invoca a las operaciones del otro). Una colaboración
consiste en que un objeto de una clase envía un mensaje a un objeto de otra clase. En
Java, los mensajes se envían mediante llamadas a métodos.
-111-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-112-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Una vez autenticado el usuario, el ATM muestra el menú principal enviando una
serie de mensajes mostrarMensaje a la Pantalla y obtiene la entrada que contiene una
selección de menú; para ello envía un mensaje obtenerEntrada al Teclado. Ya hemos
tomado en cuenta estas colaboraciones, por lo que no agregamos nada a la figura 26.
Una vez que el usuario selecciona un tipo de transacción a realizar, el ATM ejecuta la
transacción enviando un mensaje ejecutar a un objeto de la clase de transacción
apropiada (es decir, un objeto SolicitudSaldo, Retiro o Deposito). Por ejemplo, si el
usuario elije realizar una solicitud de saldo, el ATM envía un mensaje ejecutar a un
objeto SolicitudSaldo.
-113-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Diagramas de interacción
Diagramas de comunicación
-114-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
un signo de dos puntos (:) seguido del nombre de la clase. [Nota: se recomienda
especificar el nombre de cada objeto en un diagrama de comunicación cuando se modelan
varios objetos del mismo tipo]. Los objetos que se comunican se conectan con líneas
sólidas y los mensajes se pasan entre los objetos a lo largo de estas líneas, en la dirección
mostrada por las flechas. El nombre del mensaje, que aparece enseguida de la flecha, es
el nombre de una operación (es decir, un método en Java) que pertenece al objeto
receptor; considere el nombre como un “servicio” que el objeto receptor proporciona a los
objetos emisores (sus “clientes”).
Figura 27: Diagrama de comunicación del ATM, ejecutando una solicitud de saldo.
-115-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Observe que la figura 28 modela dos mensajes adicionales que se pasan del objeto
BaseDatosBanco a un objeto Cuenta (mensaje 1.1 y mensaje 2.1). para proveer al ATM
los dos saldos de la Cuenta del usuario (según lo solicitado por los mensajes 1 y 2), el
objeto BaseDatosBanco debe pasar un mensaje obtenerSaldoDisponible y un mensaje
obtenerSaldoTotal a la Cuenta del usuario. Dichos mensajes que se pasan dentro del
manejo de otro mensaje se llaman mensajes anidados. UML recomienda utilizar un
esquema de numeración decimal para indicar mensajes anidados. Por ejemplo, el
mensaje 1.1 es el primer mensaje anidado en el mensaje 1; el objeto BaseDatosBanco
pasa un mensaje obtenerSaldoDisponible durante el procesamiento de
BaseDatosBanco de un mensaje con el mismo nombre. [Nota: si el objeto
BaseDatosBanco necesita pasar un segundo mensaje anidado mientras procesa el
mensaje 1, el segundo mensaje se numera como 1.2]. Un mensaje puede pasarse sólo
cuando se han pasado ya todos los mensajes anidados del mensaje anterior. Por ejemplo,
el objeto SolicitudSaldo pasa el mensaje 3 sólo hasta que se han pasado los mensajes
2 y 2.1, en ese orden.
-116-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Diagramas de secuencia
-117-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-118-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Ejercicios de autoevaluación
1. Un(a) __________ consiste en que un objeto de una clase envía un mensaje a un objeto
de otra clase.
a) asociación
b) agregación
c) colaboración
d) composición
3. Cree un diagrama de secuencia para modelar las interacciones entre los objetos del
sistema ATM, que ocurran cuando se ejecute un Deposito con éxito. Explique la
secuencia de los mensajes modelados por el diagrama.
1. c.
3. Un diagrama de secuencia que modele las interacciones entre los objetos del sistema
ATM cuando un Deposito se ejecuta con éxito debe indicar que un Deposito primero
envía un mensaje mostrarMensaje a la Pantalla, para pedirle al usuario que
introduzca un monto de depósito. A continuación, el Deposito envía un mensaje
obtenerEntrada al Teclado para recibir la entrada del usuario. Después, el
Deposito pide al usuario que inserte un sobre de depósito; para ello envía un
mensaje mostrarMensaje a la Pantalla. Luego, el Deposito envía un mensaje
seRecibioSobreDeposito al objeto RanuraDeposito para confirmar
que el ATM haya recibido el sobre de depósito. Por último, el objeto Deposito
incrementa el atributo saldoTotal (pero no el atributo saldoDisponible) de la
Cuenta del usuario, enviando al objeto BaseDatosBanco un mensaje abonar. El
objeto BaseDatosBanco responde enviando el mismo mensaje a la Cuenta del
usuario.
-119-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Por lo general, los atributos deben ser private, y que los métodos invocados por
los clientes de una clase dada deben ser public. Sin embargo, los métodos que se llaman
sólo por otros métodos de la clase como “métodos utilitarios” deben ser private. UML
emplea marcadores de visibilidad para modelar la visibilidad de los atributos y las
operaciones. La visibilidad pública se indica mediante la colocación de un signo más (+)
antes de una operación o atributo, mientras que un signo menos (-) indica una visibilidad
privada. La figura 30 muestra nuestro diagrama de clases actualizado, en el cual se
incluyen los marcadores de visibilidad. [Nota: no incluimos parámetros de operación en
la figura 30; esto es perfectamente normal. Agregar los marcadores de visibilidad no
afecta a los parámetros que ya están modelados en los diagramas de clases de las figuras
22 a 25].
Navegabilidad
-120-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-121-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
y asociaciones adicionales en nuestro diagrama de clases final, una vez que hayamos
simplificado la estructura de nuestro sistema, al incorporar el concepto orientado a
objetos de la herencia].
2. Use los atributos que se localizan en el segundo compartimiento para declarar las
variables de instancia. Por ejemplo, los atributos private numeroCuenta y monto de
la clase Retiro producen el código de la figura 33. [Nota: el constructor de la versión
funcional completa de esta clase asignará valores a estos atributos].
3. Use las asociaciones descritas en el diagrama de clases para declarar las referencias
a otros objetos. Por ejemplo, de acuerdo con la figura 31, Retiro puede acceder a un
objeto de la clase Pantalla, a un objeto de la clase Teclado, a un objeto de la clase
DispensadorEfectivo y a un objeto de la clase BaseDatosBanco. Esto produce el
código de la figura 34. [Nota: el constructor de la versión funcional completa de esta
clase inicializará estas variables de instancia con referencias a objetos reales].
-122-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 32: Código de Java para la clase Retiro, con base en las figuras 30 y 31.
Figura 33: Código de Java para la clase Retiro, con base en las figuras 30 y 31.
Figura 34: Código de Java para la clase Retiro, con base en las figuras 30 y 31.
-123-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 35: Código de Java para la clase Retiro, con base en las figuras 30 y 31.
Ejercicios de autoevaluación
3. Escriba código de Java para empezar a implementar el diseño para la clase Teclado.
2. b.
3. El diseño para la clase Teclado produce el código de la figura 36. Recuerde que la
clase Teclado no tiene atributos en estos momentos, pero pueden volverse aparentes
a medida que continuemos con la implementación. Observe además que, si fuéramos
a diseñar un ATM real, el método obtenerEntrada tendría que interactuar con el
hardware del teclado del ATM. En realidad recibiremos la entrada del teclado de una
computadora personal, cuando escribamos el código de Java completo.
-124-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 36: Código de Java para la clase Teclado, con base en las figuras 30 y 31.
Ahora mejoraremos nuestro diseño del sistema ATM para ver cómo podría
beneficiarse de la herencia. Para aplicar la herencia, primero buscamos características
comunes entre las clases del sistema. Creamos una jerarquía de herencia para modelar
las clases similares (pero no idénticas). Después modificamos nuestro diagrama de
clases para incorporar las nuevas relaciones de herencia. Por último, demostramos cómo
traducir nuestro diseño actualizado en código de Java.
-125-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Como aprendió antes, una clase abstracta como Transaccion es una para la cual
el programador nunca tendrá la intención de crear instancias de objetos. Una clase
abstracta sólo declara los atributos y comportamientos comunes de sus subclases en una
jerarquía de herencia. La clase Transaccion define el concepto de lo que significa ser
una transacción que tiene un número de cuenta y puede ejecutarse. Tal vez usted se
pregunte por qué nos tomamos la molestia de incluir el método abstract ejecutar en
la clase Transaccion, si carece de una implementación concreta. En concepto, incluimos
este método porque corresponde al comportamiento que define a todas las transacciones:
ejecutarse. Técnicamente, debemos incluir el método ejecutar en la superclase
Transaccion, de manera que la clase ATM (o cualquier otra clase) pueda invocar
mediante el polimorfismo a la versión sobrescrita de este método en cada subclase, a
través de una referencia Transaccion. Además, desde la perspectiva de la ingeniería de
software, al incluir un método abstracto en una superclase, el que implementa las
subclases se ve obligado a sobrescribir ese método con implementaciones concretas en
las subclases, o de lo contrario, las subclases también serán abstractas, lo cual impedirá
que se creen instancias de objetos de esas subclases.
Figura 39: Diagrama de clases del sistema ATM (en el que se incorpora la herencia).
-128-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Este diagrama abreviado no muestra las relaciones de herencia, sino los atributos
y los métodos después de haber empleado la herencia en nuestro sistema. Para ahorrar
espacio, como hicimos en la figura 15, no incluimos los atributos mostrados por las
asociaciones en la figura 39; sin embargo, los incluimos en la implementación completa
-129-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
en Java. También omitimos todos los parámetros de las operaciones, como hicimos en la
figura 30; al incorporar la herencia no se afectan los parámetros que ya estaban
modelados en las figuras 22 a 25.
Implementación del diseño del sistema ATM (en el que se incorpora la herencia)
-130-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Figura 42: Código de Java para la clase Retiro, basada en las figuras 39 y 40.
Ejercicios de autoevaluación
1. UML utiliza una flecha con una _________________ para indicar una relación de
generalización.
a) punta con relleno sólido
b) punta triangular sin relleno
c) punta hueca en forma de diamante
d) punta lineal
-131-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
1. b.
2. Falso. UML requiere que se escriban los nombres de las clases abstractas y de los
métodos abstractos en cursiva.
3. El diseño para la clase Transaccion produce el código de la figura 43. Los cuerpos
del constructor de la clase y los métodos se completarán más adelante. Cuando estén
implementados por completo, los métodos obtenerPantalla y
obtenerBaseDatosBanco devolverán los atributos de referencias private de la
superclase Transaccion, llamados pantalla y baseDatosBanco, respectivamente.
Estos métodos permiten que las subclases de Transaccion accedan a la pantalla del
ATM e interactúen con la base de datos del banco.
Figura 43: Código de Java para la clase Transaccion, basada en las figuras 39 y 40.
-132-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-133-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-134-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Realizar transacciones
El método realizarTransacciones (líneas 67 a 98) lleva a cabo una sesión con
el ATM para un usuario autenticado. La línea 69 declara una variable local del tipo
Transaccion a la que asignaremos un objeto SolicitudSaldo, Retiro o Deposito, el
cual representa la transacción del ATM que el usuario seleccionó. Usamos una variable
del tipo Transaccion para poder sacar provecho del polimorfismo. Además, nombramos
a esta variable con base en el nombre de rol incluido en el diagrama de clases de la figura
8: transaccionActual. La línea 71 declara otra variable boolean llamada
usuarioSalio, la cual lleva el registro que indica si el usuario ha elegido salir o no. Esta
variable controla un ciclo while (líneas 74 a 97), el cual permite al usuario ejecutar un
número ilimitado de transacciones antes de que elija salir del sistema. Dentro de este
ciclo, la línea 76 muestra el menú principal y obtiene la selección del menú del usuario
al llamar a un método utilitario de ATM, llamado mostrarMenuPrincipal (declarado en
las líneas 101 a 109). Este método muestra el menú principal invocando a los métodos
de la pantalla del ATM, y devuelve una selección del menú que obtiene del usuario, a
través del teclado del ATM. La línea 76 almacena la selección del usuario devuelta por
mostrarMenuPrincipal en la variable local seleccionMenuPrincipal. Después de
obtener una selección del menú principal, el método realizarTransacciones usa una
instrucción switch (líneas 79 a 96) para responder a esa selección en forma apropiada.
Si seleccionMenuPrincipal es igual a cualquiera de las tres constantes enteras que
representan los tipos de transacciones (es decir, si el usuario elige realizar una
transacción), la línea 85 llama al método utilitario crearTransaccion (declarado en las
líneas 112 a 129) para regresar un objeto recién instanciado del tipo que corresponde a
la transacción seleccionada. A la variable transaccionActual se le asigna la referencia
devuelta por crearTransaccion, y después la línea 87 invoca al método ejecutar de
esta transacción para ejecutarla. En breve hablaremos sobre el método ejecutar de
Transaccion y sobre las tres subclases de Transaccion. Asignamos a la variable
transaccionActual de Transaccion un objeto de una de las tres subclases de
Transaccion, de modo que podamos ejecutar las transacciones mediante el
polimorfismo. Por ejemplo, si el usuario opta por realizar una solicitud de saldo,
seleccionMenuPrincipal es igual a SOLICITUD_SALDO, lo cual conduce a que
crearTransaccion devuelva un objeto SolicitudSaldo. Por ende, transaccionActual
se refiere a una SolicitudSaldo, y la invocación de transaccionActual.ejecutar()
produce como resultado la invocación a la versión de ejecutar que corresponde a
SolicitudSaldo.
-135-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-136-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-138-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
la clase Cuenta sólo incluye los métodos requeridos para realizar transacciones con el
ATM. Por lo tanto, omitiremos los métodos que invocaría cualquier otro sistema
bancario para sumar al atributo saldoDisponible (confirmar un depósito) o restar del
atributo saldoTotal (rechazar un depósito). El método cargar (líneas 44 a 47) resta un
monto de dinero (el parámetro monto) de una Cuenta, como parte de una transacción de
retiro. Este método resta el monto tanto del atributo saldoDisponible (línea 45) como
del atributo saldoTotal (línea 46), debido a que un retiro afecta ambas unidades del
saldo de una cuenta. El método obtenerNumeroCuenta (líneas 50 a 52) proporciona
acceso al numeroCuenta de una Cuenta. Incluimos este método en nuestra
implementación de modo que un cliente de la clase (por ejemplo, BaseDatosBanco)
pueda identificar a una Cuenta específica. Por ejemplo, BaseDatosBanco contiene
muchos objetos Cuenta, y puede invocar este método en cada uno de sus objetos Cuenta
para localizar el que tenga cierto número de cuenta específico.
-139-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
una Cuenta con numeroCuentaUsuario como su número de cuenta, o null para indicar
que el numeroCuentaUsuario es inválido. Si obtenerCuenta devuelve un objeto Cuenta,
la línea 34 regresa el valor boolean devuelto por el método validarNIP de ese objeto. El
método autenticarUsuario de BaseDatosBanco no realiza la comparación de NIP por
sí solo, sino que envía nipUsuario al método validarNIP del objeto Cuenta para que lo
haga. El valor devuelto por el método validarNIP de Cuenta indica si el NIP especificado
por el usuario coincide con el NIP de la Cuenta del usuario, por lo que el método
autenticarUsuario simplemente devuelve este valor al cliente de la clase (es decir, el
ATM). BaseDatosBanco confía en que el ATM invoque al método autenticarUsuario y
reciba un valor de retorno true antes de permitir que el usuario realice transacciones.
BaseDatosBanco también confía que cada objeto Transaccion creado por el ATM
contendrá el número de cuenta válido del usuario actual autenticado, y que éste será el
número de cuenta que se pase al resto de los métodos de BaseDatosBanco como el
argumento numeroCuentaUsuario. Por lo tanto, los métodos obtenerSaldoDisponible
(líneas 41 a 43), obtenerSaldoTotal (líneas 46 a 48), abonar (líneas 51 a 53) y cargar
(líneas 56 a 58) tan sólo obtienen el objeto Cuenta del usuario con el método utilitario
obtenerCuenta, y después invocan al método de Cuenta apropiado con base en ese
objeto. Sabemos que las llamadas a obtenerCuenta desde estos métodos nunca
devolverán null, puesto que numeroCuentaUsuario se debe referir a una Cuenta
existente. Los métodos obtenerSaldoDisponible y obtenerSaldoTotal devuelven los
valores que regresan los correspondientes métodos de Cuenta. Además, abonar y cargar
simplemente redirigen el parámetro monto a los métodos de Cuenta que invocan.
-140-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
hasta que efectivoDispensado se vuelva true) o hasta que el usuario elija cancelar (en
cuyo caso, el ciclo termina). Usamos este ciclo para regresar en forma continua al
usuario al inicio de la transacción en caso de que ocurra un error (por ejemplo, si el
monto de retiro solicitado es mayor que el saldo disponible del usuario, o mayor que la
cantidad de efectivo en el dispensador). La línea 35 muestra un menú de montos de
retiro y obtiene una selección del usuario mediante una llamada al método private
utilitario mostarMenuDeMontos (declarado en las líneas 74 a 114). Este método muestra
el menú de montos y devuelve un monto de retiro int, o la constante int CANCELO para
indicar que el usuario optó por cancelar la transacción. El método
mostrarMenuDeMontos (líneas 74 a 114) declara primero la variable local
opcionUsuario (que en un principio vale 0) para almacenar el valor que devolverá el
método (línea 75). La línea 77 obtiene una referencia a la pantalla mediante una
llamada al método obtenerPantalla heredado de la superclase Transaccion. La línea
80 declara un arreglo entero de montos de retiro que corresponden a los montos
mostrados en el menú de retiro. Ignoramos el primer elemento en el arreglo (índice 0),
debido a que el menú no tiene opción 0. La instrucción while en las líneas 83 a 111 se
repite hasta que opcionUsuario recibe un valor distinto de 0. En breve veremos que
esto ocurre cuando el usuario realiza una selección válida del menú. Las líneas 85 a 92
muestran el menú de retiro en la pantalla y piden al usuario que introduzca una opción.
La línea 94 obtiene el entero entrada por medio del teclado. La instrucción switch en
las líneas 97 a 110 determina cómo proceder con base en la entrada del usuario. Si éste
selecciona un número entre 1 y 5, la línea 103 establece opcionUsuario en el valor que
montos contiene en el índice entrada. Por ejemplo, si el usuario introduce 3 para retirar
$60, la línea 103 establece opcionUsuario en el valor de montos[3] (es decir, 60). La
línea 104 termina la instrucción switch. La variable opcionUsuario ya no es igual a 0,
por lo que la instrucción while en las líneas 83 a 111 termina y la línea 113 devuelve
opcionUsuario. Si el usuario selecciona la opción del menú para cancelar, se ejecutan
las líneas 106 y 107, las cuales establecen opcionUsuario en CANCELO y hacen que el
método devuelva este valor. Si el usuario no introduce una selección válida del menú, la
línea 109 muestra un mensaje de error y el usuario regresa al menú de retiro. La línea
38 en el método ejecutar determina si el usuario seleccionó un monto de retiro o eligió
cancelar. Si el usuario cancela, se ejecutan las líneas 66 y 67 para mostrar un mensaje
apropiado al usuario, antes de devolver el control al método que hizo la llamada (el
método realizarTransacciones de ATM). Si el usuario selecciona un monto de retiro, la
línea 40 obtiene el saldo disponible de la Cuenta del usuario actual y lo almacenan en la
variable saldoDisponible. Luego, la línea 43 determina si el monto seleccionado es
menor o igual que el saldo disponible del usuario. Si no es así, la línea 61 muestra un
mensaje de error apropiado. Después el control continúa hasta el final del ciclo do-
while, y éste se repite debido a que efectivoDispensado sigue siendo false. Si el saldo
del usuario es suficientemente alto, la instrucción if en la línea 45 determina si el
dispensador de efectivo tiene dinero suficiente para cumplir con la solicitud de retiro,
invocando al método haySuficienteEfectivoDisponible de dispensadorEfectivo. Si
este método devuelve false, la línea 56 muestra un mensaje de error apropiado y se
repite el ciclo do-while. Si hay suficiente efectivo disponible, entonces se cumplen los
requisitos para el retiro y la línea 47 carga monto a la cuenta del usuario en la base de
datos. Después, las líneas 49 y 50 instruyen al dispensador de efectivo para que dispense
el efectivo al usuario y establecen efectivoDispensado en true. Por último, la línea 52
-142-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
muestra un mensaje al usuario para indicarle que se dispensó el efectivo. Puesto que
ahora efectivoDispensado es true, el control continúa después del ciclo do-while. No
aparecen instrucciones adicionales debajo del ciclo, por lo que el método regresa.
-143-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
buena ingeniería de software exige que los programas tomen en cuenta todos los posibles
valores de retorno. Por ende, la clase Deposito está preparada para futuras versiones
de seRecibioSobre que pudieran devolver false. Las líneas 41 a 45 se ejecutan si la
ranura de depósito recibe un sobre. Las líneas 41 a 43 muestran un mensaje apropiado
al usuario. Después, la línea 45 abona el monto de depósito a la cuenta del usuario en
la base de datos. La línea 49 se ejecutará si la ranura de depósito no recibe un sobre de
depósito. En este caso, mostramos un mensaje al usuario para indicarle que el ATM
canceló la transacción. A continuación, el método regresa sin modificar la cuenta del
usuario.
http://www.mediafire.com/download/1n2c5x4pdb7t9db/ATM.zip
-144-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 7
Excepciones
En Java, los errores que se producen durante la ejecución de un programa pueden
ser tratados mediante un poderoso mecanismo denominado manejo de excepciones,
que permite evitar la terminación anormal del programa y continuar con su ejecución.
-145-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package excepciones;
package excepciones;
package excepciones;
import java.io.FileNotFoundException;
-146-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package excepciones;
try {
if (a1.getNota() >= 4) {
throw new AprobadoException(a1.getNombre() + " ha aprobado!");
} else {
throw new ReprobadoException(a1.getNombre() + " fue reprobado!");
}
} catch (AprobadoException | ReprobadoException ex) {
System.out.println(ex.getMessage());
}
if (a2.getNota() >= 4) {
try {
a2.sonreir();
} catch (RuntimeException ex) {
System.out.println("Sonrie " + a2.getNombre() + "...");
} catch (Exception ex) {
System.out.println("Se rie " + a2.getNombre() + "...");
} finally {
System.out.println("Porque se ha sacado un " + a2.getNota() + "!");
}
} else {
try {
a2.llorar();
} catch (Exception ex) {
System.out.println("Llora " + a2.getNombre() + "...");
} finally {
System.out.println("Porque le pusieron un " + a2.getNota() + "!");
}
}
}
}
El sistema está compuesto por las siguientes cuatro clases: Alumno, que posee dos
atributos (nombre y nota) con sus correspondientes getters y dos comportamientos
(sonreir y llorar), AprobadoException y ReprobadoException, que son clases de
excepciones definidas ad-hoc y, por último, Main, donde se instancian dos alumnos
a1 y a2, y donde, además, según cómo sea la nota de a1, se lanza una de las dos
excepciones anteriores y según cómo sea la nota de a2, se le envía a éste un mensaje
sonreir o llorar. Estos dos últimos métodos lanzan excepciones estándar de Java, las
cuales son capturadas en Main.
-147-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
OBSERVACIONES
1) Complete y con los valores indicados y registre la salida obtenida en cada caso:
1.a) : 1 y : 10
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
1.b) : 2 y : 1
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
1.c) : 8 y : 5
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
1.d) : 6 y : 2
...............................................................................................................................................
...............................................................................................................................................
...............................................................................................................................................
4) ¿Por qué en no se declara (con throws) que el método lanza una excepción?
...............................................................................................................................................
5) ¿Por qué en se tuvo que declarar que el método lanza esta excepción?
...............................................................................................................................................
-148-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 8
Archivos
Java proporciona un amplio conjunto de clases e interfaces para manipular
archivos. El paquete java.io contiene 51 clases (por ejemplo: File), 12 interfaces (por
ejemplo: Serializable), 16 excepciones (por ejemplo: FileNotFoundException) y un
error (IOError). Otros paquetes también proveen clases e interfaces que sirven para
trabajar con archivos: java.util.zip contiene clases para trabajar con archivos
comprimidos (por ejemplo: ZipInputStream), javax.sound.sampled contiene clases
para trabajar con archivos de audio (por ejemplo: AudioInputStream), etc..
Los diseñadores del lenguaje pensaron que utilizar la herencia para componer las
combinaciones de funcionalidades más comunes requeridas para el manejo de archivos
habría resultado en una verdadera explosión de clases. Por eso, diseñaron las clases para
el manejo de archivos siguiendo el patrón de diseño Decorator. En lugar de usar la
herencia para agregar funcionalidad a las clases, este patrón permite agregar
funcionalidad a los objetos individuales (en tiempo de ejecución).
-149-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-150-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El patrón Decorator original establece que debe haber una clase abstracta
extendida de la primera para actuar como superclase de los decoradores. En este ejemplo
sólo hay un decorador (BufferedReader) y, por lo tanto, su clase hereda directamente
de la primera clase abstracta (Reader):
package decorator;
public class BufferedReader extends Reader {
private Reader c;
public BufferedReader(Reader c) {
this.c = c;
}
@Override
public int read() {
System.out.println("read() de BufferedReader");
System.out.println("Devuelve un carácter del buffer cargado a partir de: " + c);
return 65;
}
public String readLine() {
System.out.println("readLine() de BufferedReader");
System.out.println("Devuelve un renglón del buffer cargado a partir de: " + c);
return "Hola mundo";
}
}
-151-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
PREGUNTA DE REPASO
¿Por qué en vez de crear un FileReader y después decorarlo con un BufferedReader:
FileReader fr2 = new FileReader(nomArch);
BufferedReader br = new BufferedReader(fr2);
no se crea diretamente un BufferedReader:
BufferedReader br = new BufferedReader(nomArch);
si, al fin y al cabo, los métodos utilizados están disponibles en este último?
...............................................................................................................................................
...............................................................................................................................................
Aunque fue útil para ver el patrón de diseño en que se basan las clases del paquete
java.io, el ejemplo anterior no realizó ningún trabajo con archivos. Al recibir el
mensaje read, los objetos siempre devolverán 65 (que convertido a char representa una
letra A), y al recibir el mensaje readLine, el objeto decorado siempre devolverá la cadena
Hola mundo.
Dado que se requiere el acceso a un archivo de texto, para obtener la misma salida
que el ejemplo anterior, debería crearse un archivo de texto como el siguiente:
hola.txt
AHola mundo
-152-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Veamos ahora cómo utilizar las principales clases mostradas en la pág. 149.
Primer caso: La clase File
package claseFile;
import java.io.File;
import java.text.DateFormat;
import java.util.Date;
import javax.swing.JFileChooser;
public class Main {
public static void main(String[] args) {
String nomElegido = "";
JFileChooser fc = new JFileChooser("src/clasefile");
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
nomElegido = fc.getSelectedFile().getPath();
}
File f = new File(nomElegido);
System.out.println("Nombre: " + f.getName());
System.out.println("Ubicado en: " + f.getParent());
System.out.println("Última modificación: " +
DateFormat.getInstance().format(new Date(f.lastModified())));
if (f.isFile()) {
System.out.println("ES UN ARCHIVO");
System.out.println("Tamaño: " + f.length() + " bytes");
} else if (f.isDirectory()) {
System.out.println("ES UN DIRECTORIO");
System.out.println("Contenido:");
int cantArchivos = 0;
long tamArchivos = 0;
int cantDirectorios = 0;
for (File elemento : f.listFiles()) {
System.out.print(DateFormat.getInstance().format(new Date(elemento.lastModified())));
if (elemento.isFile()) {
cantArchivos++;
tamArchivos += elemento.length();
System.out.printf(" %,14d ", elemento.length());
} else if (elemento.isDirectory()) {
cantDirectorios++;
System.out.printf(" <DIR> ");
}
System.out.println(elemento.getName());
}
System.out.printf(" %,7d archivos %,14d bytes\n", cantArchivos, tamArchivos);
System.out.printf(" %,7d dirs %,14d bytes libres\n",
cantDirectorios, f.getFreeSpace());
}
}
}
-153-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package archivosDeTexto;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import javax.swing.JFileChooser;
String renglon;
while ((renglon = lr.readLine()) != null) {
pw.println(lr.getLineNumber() + ") " + renglon);
}
pw.close();
}
}
Para leer datos desde este tipo de archivos, puede decorarse un objeto de la clase
................................. mediante los decoradores .......................................... (para mejorar
la eficiencia y poder usar ........................................................) y ........................................
(para poder usar los métodos .............................................................................................
..............................................................................................................................................).
Para grabar datos en este tipo de archivos, puede decorarse un objeto de la clase
................................. mediante los decoradores .......................................... (para mejorar
la eficiencia y poder usar ........................................................) y ........................................
(para poder usar los métodos .............................................................................................
..............................................................................................................................................).
Para garantizar que los datos se graben en los archivos, éstos deben cerrarse
mediante ...............................................................................................................................
-154-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Para leer datos desde este tipo de archivos, puede decorarse un objeto de la clase
................................. mediante los decoradores .......................................... (para mejorar
la eficiencia) y ........................................................ (para poder usar los métodos
................................................................................................................................................
..............................................................................................................................................).
No quedan datos por leer, si .....................................................................................
Para grabar datos en este tipo de archivos, puede decorarse un objeto de la clase
................................. mediante los decoradores .......................................... (para mejorar
la eficiencia) y ......................................................... (para poder usar los métodos
................................................................................................................................................
..............................................................................................................................................).
Para garantizar que los datos se graben en los archivos, éstos deben cerrarse
mediante ...............................................................................................................................
-155-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-156-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package serializacion;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
Lista li = new Lista();
try {
li = li.deSerializar("lista.txt");
if (!EntradaSalida.leerBoolean("Ya hay una lista. ¿Desea reutilizarla?")) {
li = new Lista();
}
} catch (IOException | ClassNotFoundException e) {
EntradaSalida.mostrarString("Lista nueva!");
}
do {
String dato = EntradaSalida.leerString("Ingrese una cadena");
li.agregar(dato);
} while (EntradaSalida.leerBoolean("Desea seguir agregando valores?"));
li.mostrar();
try {
li.serializar("lista.txt");
} catch (Exception e) {
EntradaSalida.mostrarString(e.getMessage() + "\nERROR AL GRABAR!");
}
}
}
package serializacion;
import java.io.*;
import java.util.ArrayList;
public class Lista implements Serializable {
private ArrayList<String> valores;
public Lista() {
valores = new ArrayList<>();
}
public void agregar(String s) {
valores.add(s);
}
public void mostrar() {
String cadena = "La lista contiene:\n";
for (String s : valores) {
cadena += s + "\n";
}
EntradaSalida.mostrarString(cadena);
}
public Lista deSerializar(String nomArch) throws IOException,ClassNotFoundException {
ObjectInputStream o =
new ObjectInputStream(new BufferedInputStream(new FileInputStream(nomArch)));
Lista j = (Lista) o.readObject();
o.close();
return j;
}
public void serializar(String nomArch) throws IOException {
ObjectOutputStream o =
new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(nomArch)));
o.writeObject(this);
o.close();
}
}
-157-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package serializacion;
import javax.swing.JOptionPane;
Para leer datos desde este tipo de archivos, puede decorarse un objeto de la clase
................................................. mediante los decoradores ..................................................
(para mejorar la eficiencia) y ........................................................ (para poder usar el
método ...................................................................................).
Para grabar datos en este tipo de archivos, puede decorarse un objeto de la clase
................................................ mediante los decoradores ...................................................
(para mejorar la eficiencia) y ......................................................... (para poder usar el
método ...................................................................................).
Para garantizar que los datos se graben en los archivos, éstos deben cerrarse
mediante ...............................................................................................................................
Para que el estado de un objeto pueda ser grabado, la clase del objeto debe
implementar la interfaz .................................. . Ésta no declara métodos ni atributos,
sólo indica que los objetos de las clases que la implementen podrán ser serializados (sus
estados serán grabados) y deserializados (sus estados serán leídos).
-158-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package trescapas;
import java.io.*;
public class Main {
public static void main(String[] args) throws FileNotFoundException, IOException {
BackEnd backEnd = new BackEnd();
FrontEnd frontEnd = new FrontEnd(backEnd);
frontEnd.arrancar();
}
}
package trescapas;
import java.io.*;
import java.util.*;
public class FrontEnd {
private BackEnd backEnd;
public FrontEnd(BackEnd backEnd) {
this.backEnd = backEnd;
}
public void arrancar() throws FileNotFoundException, IOException {
Collection<CarreraDTO> carreras = backEnd.obtenerCarreras();
for (CarreraDTO elem : carreras) {
System.out.println(elem.getCodigo() + " - " + elem.getNombre());
}
System.out.print("Elija una carrera:");
int codCarrera = new Scanner(System.in).nextInt();
Collection<AlumnoDTO> alumnos = backEnd.obtenerAlumnos(codCarrera);
for (AlumnoDTO elem : alumnos) {
System.out.println(elem.getLegajo() + " - " + elem.getApellido() +
", " + elem.getNombres());
}
}
}
-159-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package trescapas;
import java.io.*;
import java.util.Collection;
public class BackEnd {
public Collection<CarreraDTO> obtenerCarreras()
throws FileNotFoundException, IOException {
CarrerasDAO cDAO = new CarrerasDAO();
return cDAO.cargarCarreras();
}
public Collection<AlumnoDTO> obtenerAlumnos(int codCarrera)
throws FileNotFoundException, IOException {
AlumnosDAO aDAO = new AlumnosDAO();
return aDAO.cargarAlumnos(codCarrera);
}
}
package trescapas;
public class CarreraDTO {
private int codigo;
private String nombre;
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
package trescapas;
import java.io.*;
import java.util.*;
public class CarrerasDAO {
public Collection<CarreraDTO> cargarCarreras()
throws FileNotFoundException, IOException {
Collection<CarreraDTO> carreras = new ArrayList<>();
BufferedReader br=new BufferedReader(new FileReader("src/trescapas/carreras.txt"));
String renglon;
while ((renglon = br.readLine()) != null) {
CarreraDTO ca = new CarreraDTO();
ca.setCodigo(Integer.parseInt(renglon));
renglon = br.readLine();
ca.setNombre(renglon);
renglon = br.readLine();
carreras.add(ca);
}
return carreras;
}
}
-160-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package trescapas;
public class AlumnoDTO {
private int legajo;
private String apellido;
private String nombres;
public int getLegajo() {
return legajo;
}
public void setLegajo(int legajo) {
this.legajo = legajo;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
public String getNombres() {
return nombres;
}
public void setNombres(String nombres) {
this.nombres = nombres;
}
}
package trescapas;
import java.io.*;
import java.util.*;
public class AlumnosDAO {
public Collection<AlumnoDTO> cargarAlumnos(int codCarrera)
throws FileNotFoundException, IOException {
Collection<AlumnoDTO> alumnos = new ArrayList<>();
BufferedReader br=new BufferedReader(new FileReader("src/trescapas/alumnos.txt"));
String renglon;
while ((renglon = br.readLine()) != null) {
if (Integer.parseInt(renglon) == codCarrera) {
AlumnoDTO alu = new AlumnoDTO();
renglon = br.readLine();
alu.setLegajo(Integer.parseInt(renglon));
renglon = br.readLine();
alu.setApellido(renglon);
renglon = br.readLine();
alu.setNombres(renglon);
renglon = br.readLine();
alumnos.add(alu);
} else {
renglon = br.readLine();
renglon = br.readLine();
renglon = br.readLine();
renglon = br.readLine();
}
}
return alumnos;
}
}
-161-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Observe que para que el código del ejemplo no quedara demasiado largo y
complejo, no fueron tratadas las excepciones, sólo se las declaró. Como CarrerasDAO
debe acceder al archivo src/trescapas/carreras.txt y AlumnosDAO debe acceder a
src/trescapas/alumnos.txt, es necesario colocar los archivos en esas ubicaciones, o
modificarlas en el código.
carreras.txt alumnos.txt
1 60 1 60
2 Informática Aplicada 2 11440
3 3 Rossi
4 61 4 Pablo Ricardo
5 Control Eléctrico y Accionamientos 5
6 6 65
7 62 7 12787
8 Mecánica, Automotores y Máquinas Térmicas 8 Alvarez
9 9 Tomás Pedro
10 63 10
11 Automatización y Robótica 11 62
12 12 13797
13 64 13 Cáceres
14 Electrónica 14 María Celeste
15 15
16 65 16 63
17 Química y Química Aplicada 17 13998
18 18 Vilas
19 66 19 Luis Pablo
20 Física y Física Aplicada 20
21 21 60
22 67 22 14247
23 Diseño Tecnológico 23 Liao
24 24 Martín Adrián
25 68 25
26 Profesorado en Disciplinas Industriales 26 60
27 27 14478
28 69 28 Lee
29 Inglés e Inglés Técnico 29 Analía Rita
30 30
31 70 31 61
32 Matemática y Matemática Aplicada 32 14500
33 Quito
34 Esteban
-162-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Un escáner puede leer texto desde cualquier objeto cuya clase implemente la
interfaz Readable (por ejemplo: BufferedReader, FileReader, InputStreamReader,
LineNumberReader, etc.), desde instancias de File y de las subclases de InputStream,
e incluso desde objetos de la clase String, entre otros. En este caso, System.in es el
flujo de entrada estándar (el mismo cumple la condición is-a con la clase InputStream).
El texto leído es dividido en partes (tokens) mediante un separador determinado (por
defecto, un carácter de espacio en blanco, como por ejemplo: el espacio propiamente
dicho, el carácter de tabulación o el de salto de línea), y estos tokens pueden convertirse
en valores de distintos tipos usando los métodos next correspondientes (nextInt,
nextDouble, nextShort, nextLong, etc.). Los métodos hasNext (hasNextInt,
hasNextDouble, etc.) sirven para verificar si el flujo contiene más tokens del tipo
correspondiente (lo cual tiene sentido cuando el escáner está siendo usado para leer
texto desde un archivo).
EJERCICIO Nº 4
En Java, existe más de una manera de obtener datos desde archivos XML. Una
de las formas más sencillas consiste en cargar el documento en memoria usando un
parser y luego acceder a su contenido mediante la API (Application Programming
Interface) denominada DOM (Document Object Model). En el siguiente ejemplo, para
referirse al documento contenido en el archivo src/trescapas/datos.xml se utiliza la
variable .............. del tipo ...................................... (es una interface).
-163-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package trescapas;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XML {
public static void main(String[] args) {
Map<String, String> carreras = new HashMap<>();
try {
DocumentBuilderFactory factoriaDeParsers = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = factoriaDeParsers.newDocumentBuilder();
Document doc = parser.parse(new File("src/trescapas/datos.xml"));
Element raiz = (Element) doc.getElementsByTagName("datos").item(0);
NodeList nodosCarrera = raiz.getElementsByTagName("carrera");
for (int i = 0; i < nodosCarrera.getLength(); i++) {
Element e = (Element) nodosCarrera.item(i);
String codigo = e.getElementsByTagName("codigo").item(0).getTextContent();
String denominacion = e.getElementsByTagName("denominacion").item(0).getTextContent();
carreras.put(codigo, denominacion);
}
NodeList nodosAlumno = raiz.getElementsByTagName("alumno");
for (int i = 0; i < nodosAlumno.getLength(); i++) {
Element e = (Element) nodosAlumno.item(i);
System.out.println("Apellido: " +
e.getElementsByTagName("apellido").item(0).getTextContent());
System.out.println(" Nombres: " +
e.getElementsByTagName("nombres").item(0).getTextContent());
System.out.println(" Legajo: " +
e.getElementsByTagName("legajo").item(0).getTextContent());
System.out.println(" Carrera: " + carreras.get(
e.getElementsByTagName("cod_carrera").item(0).getTextContent()));
System.out.println();
}
} catch (ParserConfigurationException | SAXException | IOException ex) {
System.err.println(ex);
}
}
}
En DOM, todos los documentos tienen una estructura de árbol. Los nodos del
árbol pueden ser, entre otros, elementos de XML (implementan la interfaz ...................)
o nodos de texto. Cuando un elemento de XML recibe un mensaje ...................................
con un argumento de la clase .........................., devuelve una lista (que implementa
.........................) con los nodos cuyos nombres coinciden con el argumento. Esta lista
devuelve su longitud cuando recibe el mensaje ................................, y para obtener un
nodo debe enviársele a la lista el mensaje ........................................ usando el índice del
nodo como argumento. Cuando un nodo recibe el mensaje .............................................,
devuelve un objeto de la clase ............................... con el texto que contiene.
-164-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 9
Interfaces Gráficas de Usuario
Una interfaz gráfica o GUI (Graphical User Interface) es un conjunto de
componentes gráficos (ventanas, botones, áreas de texto, etiquetas, etc.) que facilitan la
interacción entre el usuario y la aplicación. En Java existen varias API que podemos
utilizar para desarrollar interfaces gráficas (AWT, Swing, SWT, etc.). En este capítulo
utilizaremos Swing, una de las más populares.
-165-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo1;
public class Main {
public static void main(String[] args) {
Vista v = new Vista();
}
}
-166-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo1;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class Vista {
private JButton b;
private JFrame f;
private JMenu m;
private JMenuBar mB;
private JMenuItem mI;
public Vista() {
b = new JButton();
mI = new JMenuItem();
m = new JMenu();
mB = new JMenuBar();
f = new JFrame();
b.setText("Clique aquí"); // Establece el texto del botón
mI.setText("Salir"); // Establece el texto del ítem del menú
m.setText("Archivo"); // Establece el texto del menú
m.add(mI); // Agrega, en el menú, el ítem del menú
mB.add(m); // Agrega, en la barra de menús, el menú
f.getContentPane().setBackground(Color.CYAN); // Establece el fondo del panel
f.getContentPane().setLayout(new FlowLayout()); // Establece el layout del panel
f.getContentPane().add(b); // Agrega, en el panel, el botón
f.setJMenuBar(mB); // Agrega, en el JFrame, la barra de menús
f.setTitle("Demo 1"); // Establece el título del JFrame
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Al cerrar el JFrame, salir
f.setSize(300, 150); // Establece el tamaño del JFrame
f.setLocationRelativeTo(null); // Centra el JFrame en la pantalla
f.setResizable(false); // Impide que se cambie el tamaño del JFrame
f.setVisible(true); // Muestra el JFrame
}
}
-167-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
OBSERVACIÓN
Por una cuestión de conveniencia, la clase JFrame sobrescribe los tres métodos
add, remove y setLayout para que automáticamente obtengan una referencia al panel
de contenido e invoquen a sus métodos homónimos. Por ello, las líneas de código:
f.getContentPane().setBackground(Color.CYAN); // Establece el fondo del panel
f.getContentPane().setLayout(new FlowLayout()); // Establece el layout del panel
f.getContentPane().add(b); // Agrega, en el panel, el botón
Aquí solamente trataremos los layout managers que están marcados en negrita.
Los demás casi no son usados cuando se programa una GUI a mano, pero algunas
herramientas de diseño (por ejemplo, el GUI Builder de NetBeans) sí los utilizan:
-168-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo2;
public class Main {
public static void main(String[] args) {
Vista v = new Vista();
}
}
package demo2;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Vista {
private JButton b1, b2, b3, b4, b5;
private JFrame f;
public Vista() {
b1 = new JButton("PAGE_START (NORTH)");
b2 = new JButton("LINE_START (WEST)");
b3 = new JButton("PAGE_END (SOUTH)");
b4 = new JButton("LINE_END (EAST)");
b5 = new JButton("CENTER");
f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(b1, BorderLayout.PAGE_START);
f.getContentPane().add(b2, BorderLayout.LINE_START);
f.getContentPane().add(b3, BorderLayout.PAGE_END);
f.getContentPane().add(b4, BorderLayout.LINE_END);
f.getContentPane().add(b5, BorderLayout.CENTER);
f.setTitle("Demo 2");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 150);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
-169-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo3;
package demo3;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public Vista() {
f.getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
f.getContentPane().add(b1);
f.getContentPane().add(b2);
f.getContentPane().add(b3);
f.setTitle("Demo 3");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 150);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
-170-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo4;
package demo4;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public Vista() {
f.getContentPane().setLayout(new BoxLayout(f.getContentPane(),BoxLayout.Y_AXIS));
f.getContentPane().add(b1);
f.getContentPane().add(b2);
f.getContentPane().add(b3);
f.setTitle("Demo 4");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 150);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
-171-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo5;
public class Main {
public static void main(String[] args) {
Vista v = new Vista();
}
}
package demo5;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Vista {
private JButton b[];
private JFrame f;
public Vista() {
b = new JButton[12];
f = new JFrame("Demo 5");
for (int i = 0; i < 12; i++) {
b[i] = new JButton(Character.toString("123456789*0#".charAt(i)));
}
f.getContentPane().setLayout(new GridLayout(4, 3));
for (int i = 0; i < 12; i++) {
f.getContentPane().add(b[i]);
}
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 220);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
-172-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
El último layout manager que veremos aquí es GridBagLayout, uno de los más
flexibles y complejos. Éste divide el panel de contenido en una grilla, y cada elemento
que se vaya agregando debe ir acompañado de una instancia de GridBagConstraints,
en cuyos atributos se indica dónde y cómo mostrar el elemento, el cual, por ejemplo,
puede ocupar más de una celda, estirándose o no, alinearse hacia cualquiera de los
bordes de la celda, etc. La siguiente figura muestra algunas de las posibilidades que
permite este potente layout manager:
En este ejemplo, se utiliza una grilla de dos filas y tres columnas, cuyas celdas
tienen las siguientes coordenadas:
b[0]
b[1] b[2]
gridheight = 2
b[3]
gridwidth = 2
El tercer botón agregado (b[2]) es el único que no debe estirarse para llenar
su celda en ambas direcciones (porque no usa fill = GridBagConstraints.BOTH).
Debido a ello, queda de un tamaño menor que la celda que lo contiene, y entonces se lo
centra usando anchor = GridBagConstraints.CENTER.
package demo6;
-173-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo6;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public Vista() {
b = new JButton[4];
c = new GridBagConstraints[4];
f = new JFrame("Demo 6");
c[0].gridx = 0;
c[0].gridy = 0;
c[0].gridheight = 2;
c[0].fill = GridBagConstraints.BOTH;
c[0].weightx = 1.0;
c[0].weighty = 1.0;
c[1].gridx = 1;
c[1].gridy = 0;
c[1].fill = GridBagConstraints.BOTH;
c[1].weightx = 1.0;
c[1].weighty = 1.0;
c[2].gridx = 2;
c[2].gridy = 0;
c[2].anchor = GridBagConstraints.CENTER;
c[2].weightx = 1.0;
c[2].weighty = 1.0;
c[3].gridx = 1;
c[3].gridy = 1;
c[3].gridwidth = 2;
c[3].fill = GridBagConstraints.BOTH;
c[3].weightx = 1.0;
c[3].weighty = 1.0;
f.getContentPane().setLayout(new GridBagLayout());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
-174-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Hasta aquí hemos visto cómo los layout managers permiten determinar la
distribución de los componentes en un contenedor 1. Ahora veremos un ejemplo donde se
utilizan los contenedores y componentes más comunes.
package demo7;
public class Main {
public static void main(String[] args) {
Vista v = new Vista();
}
}
package demo7;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.tree.DefaultMutableTreeNode;
public class Vista {
private JFrame frame;
private JTabbedPane tabbedPane;
private JPanel panel;
private JScrollPane scrollPane;
private JCheckBox cB1;
private JCheckBox cB2;
private JCheckBox cB3;
private ButtonGroup bG;
private JRadioButton rB1;
private JRadioButton rB2;
private JRadioButton rB3;
private JRadioButton rB4;
private JPanel panelCheckBoxes;
private JPanel panelRadioButtons;
private JPanel panelPanelRadioButtons;
private JPanel panelButton;
private JButton jB;
private JPasswordField passwordField;
private JTextArea textArea;
private JTextField textField;
private JPanel panelConBorde;
private JLabel nombre;
private JLabel antecedentes;
private JLabel contraseña;
private JPanel panelTextField;
private JPanel panelScrollPane;
private JPanel panelPasswordField;
private JPanel panelPanelScrollPane;
private JSplitPane splitPane;
private JPanel panelSuperior;
private JPanel panelInferior;
private BasicArrowButton basicArrowButton;
private JLabel ingresoPorSeleccion;
1En todos los ejemplos vistos, se utilizó el layout manager del panel de contenido del JFrame. Sin embargo,
algunos componentes (por ejemplo, JPanel) son considerados contenedores y, por lo tanto, utilizan su
propio layout manager independiente.
-175-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
public Vista() {
frame = new JFrame("Demo 7");
tabbedPane = new JTabbedPane();
panel = new JPanel();
panel.setLayout(new BorderLayout());
cB1 = new JCheckBox("Java");
cB2 = new JCheckBox("C#");
cB3 = new JCheckBox("Python");
cB1.setMaximumSize(new Dimension(80, 25));
cB2.setMaximumSize(new Dimension(80, 25));
cB3.setMaximumSize(new Dimension(80, 25));
cB1.setBackground(Color.CYAN);
cB2.setBackground(Color.CYAN);
cB3.setBackground(Color.CYAN);
cB1.setAlignmentX(Component.CENTER_ALIGNMENT);
cB2.setAlignmentX(Component.CENTER_ALIGNMENT);
cB3.setAlignmentX(Component.CENTER_ALIGNMENT);
panelCheckBoxes = new JPanel();
panelCheckBoxes.setLayout(new BoxLayout(panelCheckBoxes, BoxLayout.Y_AXIS));
panelCheckBoxes.setBackground(Color.CYAN);
panelCheckBoxes.add(cB1);
panelCheckBoxes.add(cB2);
panelCheckBoxes.add(cB3);
-176-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-177-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-178-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-179-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-180-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
ayudaMenu.add(soloTextoMenuItem1);
ayudaMenu.add(soloTextoMenuItem2);
menuBar.add(verMenu);
menuBar.add(separator);
menuBar.add(ayudaMenu);
frame.setJMenuBar(menuBar);
frame.getContentPane().add(tabbedPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.pack();
// frame.setResizable(false);
frame.setVisible(true);
}
}
EJERCICIO Nº 5
Identifique, en las siguientes figuras, las clases a que pertenecen los contenedores y
componentes señalados. A continuación, complete las explicaciones.
-181-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-182-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
A diferencia de JTextArea, que sólo permite trabajar con texto plano, en Swing
también existen las clases .............................................. y ...............................................
(no mostradas en el ejemplo anterior) las cuales soportan contenidos en RTF y HTML.
La ventaja de esta última es que, además, es capaz de contener a otros componentes,
como por ejemplo, audios e íconos.
EJERCICIO Nº 6
..............................................
..............................................
..............................................
..............................................
-183-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo8;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class Vista {
private JFrame frame;
private JPopupMenu popupMenu;
private JMenuItem elegirArchivo;
private JMenuItem elegirColor;
private JMenuItem acercaDe;
public Vista() {
frame = new JFrame("Demo 8: Haga clic con el botón derecho!");
elegirArchivo = new JMenuItem("Elegir archivo");
elegirColor = new JMenuItem("Elegir color");
acercaDe = new JMenuItem("Acerca de...");
elegirArchivo.addActionListener(new EscuchaElegirArchivo());
elegirColor.addActionListener(new EscuchaElegirColor());
acercaDe.addActionListener(new EscuchaAcercaDe());
popupMenu = new JPopupMenu();
popupMenu.add(elegirArchivo);
popupMenu.addSeparator();
popupMenu.add(elegirColor);
popupMenu.addSeparator();
popupMenu.add(acercaDe);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(380, 150);;
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.getContentPane().addMouseListener(new EscuchaFrame());
}
private class EscuchaElegirArchivo implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.showOpenDialog(frame);
}
}
private class EscuchaElegirColor implements ActionListener {
public void actionPerformed(ActionEvent e) {
Color c = JColorChooser.showDialog(frame, "Colores", Color.BLUE);
}
}
private class EscuchaAcercaDe implements ActionListener {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Programado por DC.", "Acerca de",
JOptionPane.INFORMATION_MESSAGE);
}
}
private class EscuchaFrame extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
}
-184-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package demo8;
public class Main {
public static void main(String[] args) {
Vista v = new Vista();
}
}
Modelo
Invocación de métodos
Eventos
Vista Controlador
-185-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package mvc;
import java.awt.event.*;
public class Controlador {
private Modelo m;
private Vista v;
public Controlador(Modelo m, Vista v) {
this.m = m;
this.v = v;
}
public void start() {
m.setNombreAplicacion("MVC - " + m.getNombreAplicacion());
v.mostrar();
v.addCalcularListener(new CalcularListener());
}
private class CalcularListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object cociente = m.dividir(v.leerDividendo(), v.leerDivisor());
if (cociente != null) {
v.mostrarCociente(cociente);
}
}
}
}
package mvc;
import java.awt.event.*;
public abstract class Modelo {
private ActionListener listener;
private String nombreAplicacion;
public Modelo(String nombre) {
nombreAplicacion = nombre;
}
public String getNombreAplicacion() {
return nombreAplicacion;
}
public void setNombreAplicacion(String nombreAplicacion) {
this.nombreAplicacion = nombreAplicacion;
}
public abstract Object dividir(Object dividendo, Object divisor);
protected void reportException(String exception) {
if (listener != null) {
ActionEvent evt = new ActionEvent(this, 0, exception);
listener.actionPerformed(evt);
}
}
public void addExceptionListener(ActionListener listener) {
this.listener = listener;
}
}
-186-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package mvc;
public class ModeloEntero extends Modelo {
public ModeloEntero(String nombre) {
super(nombre);
}
public Object dividir(Object dividendo, Object divisor) {
Integer cociente = null;
try {
cociente =
Integer.parseInt(dividendo.toString())/Integer.parseInt(divisor.toString());
} catch (Exception e) {
reportException(e.getMessage());
}
return cociente;
}
}
package mvc;
public class ModeloPuntoFlotante extends Modelo {
public ModeloPuntoFlotante(String nombre) {
super(nombre);
}
public Object dividir(Object dividendo, Object divisor) {
Double cociente = null;
try {
cociente =
Double.parseDouble(dividendo.toString())/Double.parseDouble(divisor.toString());
} catch (Exception e) {
reportException(e.getMessage());
}
return cociente;
}
}
package mvc;
import java.awt.event.*;
public abstract class Vista {
private Modelo m;
public Modelo getM() {
return m;
}
public Vista(Modelo m) {
this.m = m;
this.m.addExceptionListener(new ExceptionListener());
}
public abstract void mostrar();
public abstract Object leerDividendo();
public abstract Object leerDivisor();
public abstract void mostrarCociente(Object cociente);
public abstract void mostrarException(String exception);
public abstract void addCalcularListener(ActionListener al);
private class ExceptionListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
mostrarException(event.getActionCommand());
}
}
}
-187-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package mvc;
import java.awt.event.ActionListener;
import javax.swing.*;
public VistaSwing(Modelo m) {
super(m);
marco = new JFrame();
marco.setResizable(false);
marco.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-188-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package mvc;
import java.awt.event.*;
import java.util.Scanner;
public VistaConsola(Modelo m) {
super(m);
}
private static final Scanner ENTRADA = new Scanner(System.in);
-189-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package mvc;
import javax.swing.*;
public class Main {
public static void main(String args[]) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
Modelo m = null;
int opcion = JOptionPane.showOptionDialog(
null, "Que modelo desea usar?", "MVC",
JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null,
new String[]{"Entero", "Punto flotante", "Salir"}, "Entero");
switch (opcion) {
case 0:
m = new ModeloEntero("Cociente Entero");
break;
case 1:
m = new ModeloPuntoFlotante("Cociente Punto flotante");
break;
case 2:
System.exit(0);
}
Vista v = null;
opcion = JOptionPane.showOptionDialog(
null, "Que vista desea usar?", "MVC",
JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null,
new String[]{"Consola", "Swing", "Salir"}, "Consola");
switch (opcion) {
case 0:
v = new VistaConsola(m);
break;
case 1:
v = new VistaSwing(m);
break;
case 2:
System.exit(0);
}
Controlador c = new Controlador(m, v);
c.start();
}
}
-190-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 10
Acceso a Bases de Datos
Una base de datos relacional no es más que un conjunto de datos organizados en
forma de tablas relacionadas entre sí. Toda fila de una tabla es un registro que contiene
un dato cargado en cada columna o campo. Esta organización permite gestionar
información de manera eficiente mediante un SGBD (Sistema Gestor de Base de Datos
o, en inglés, DBMS, Data Base Management System) como, por ejemplo, MySQL, el cual
forma parte de la pila de soluciones XAMPP (que incluye, además, el servidor web
Apache e intérpretes de los lenguajes PHP y Perl, entre otras soluciones). A través del
panel de control de XAMPP, es posible administrar MySQL usando un simple navegador
web, por medio de phpMyAdmin 1.
1 Con XAMPP, para poder usar phpMyAdmin, el servidor Apache debe estar en ejecución.
-191-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-192-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-193-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package agenda;
package agenda;
import java.awt.event.*;
import java.sql.*;
public Modelo() {
tituloApp = "Mi Agenda";
tituloSalida = "Resultado de la operación";
tituloExcepcion = "Error!";
tituloNombre = "Nombre: ";
tituloTelefono = "Teléfono: ";
tituloNuevoTelefono = "Nuevo teléfono: ";
tituloBotonAlta = "Alta";
tituloBotonBaja = "Baja";
tituloBotonModificacion = "Modificación";
tituloBotonConsulta = "Consulta";
tituloRegistros = "Cantidad de registros actualizados: ";
tituloConsultaVacia = "No se ha encontrado a ";
-194-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
driver = "com.mysql.cj.jdbc.Driver";
prefijoConexion = "jdbc:mysql://";
ip = "127.0.0.1"; // Dirección IP donde está corriendo el SGBD
usr = ""; // Usuario
psw = ""; // Password
bd = "agenda"; // Base de datos que contiene la tabla a usar
tabla = "datos"; // Tabla de la agenda
campoNombre = "nombre"; // Campo que contiene el nombre
campoTelefono = "telefono"; // Campo que contiene el teléfono
}
-195-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-196-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
package agenda;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Vista {
private Modelo m;
private JFrame f;
private JPanel p;
private JPanel panelLabelAlta;
private JPanel panelAlta;
private JPanel panelLabelModificacion;
private JPanel panelModificacion;
private JLabel labelNombreAlta;
private JLabel labelTelefonoAlta;
-197-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-198-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-199-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
botonAlta.addActionListener(al);
}
package agenda;
import java.awt.event.*;
private Modelo m;
private Vista v;
-200-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
@Override
public void actionPerformed(ActionEvent event) {
m.darDeAlta(v.getNombreAltaStr(), v.getTelefonoAltaStr());
v.mostrarCantidadDeRegistros(m.getCantidadRegistros());
}
}
@Override
public void actionPerformed(ActionEvent event) {
m.darDeBaja(v.getNombreBajaStr());
v.mostrarCantidadDeRegistros(m.getCantidadRegistros());
}
}
@Override
public void actionPerformed(ActionEvent event) {
m.modificar(v.getNombreModificacionStr(), v.getTelefonoModificacionStr());
v.mostrarCantidadDeRegistros(m.getCantidadRegistros());
}
}
@Override
public void actionPerformed(ActionEvent event) {
m.consultar(v.getNombreConsultaStr());
v.mostrarCadena(m.getResultadoConsulta());
}
}
}
Al arrancar el controlador, éste le envía a la vista los objetos que escucharán los
clics en los botones. Estos objetos listeners pertenecen a clases internas del controlador
y, por lo tanto, tienen acceso al modelo y a la vista (a través de los atributos m y v,
respectivamente). Así, cuando el usuario, por ejemplo, hace clic en el botón de Alta, el
controlador puede obtener los datos de la vista y pasárselos al modelo para que los
guarde en la base de datos, y luego puede obtener del modelo los resultados y pasárselos
a la vista para que ésta los muestre.
-201-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
...............................................................................................................................
................................................................................................................................................
...............................................................................................................................
................................................................................................................................................
...............................................................................................................................
................................................................................................................................................
...............................................................................................................................
................................................................................................................................................
..........................................................................................................................................
................................................................................................................................................
..........................................................................................................................................
................................................................................................................................................
...............................................................................................................................
................................................................................................................................................
...............................................................................................................................
................................................................................................................................................
..........................................................................................................................................
................................................................................................................................................
..........................................................................................................................................
................................................................................................................................................
-202-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 11
Aplicaciones Web
Las aplicaciones web son unas herramientas informáticas que están basadas en
la arquitectura cliente-servidor y que se pueden utilizar mediante un navegador
web (como, por ejemplo, Chrome, Firefox o Safari) a través de Internet o de una
intranet. Son populares debido, entre otros motivos, a la practicidad de usar un
navegador web como cliente ligero, a la independencia del sistema operativo y a la
facilidad de su mantenimiento sin necesidad de distribuir e instalar software a los
usuarios. Los webmails y las tiendas en línea son ejemplos de aplicaciones web.
Básicamente, el navegador web (o sea, el cliente) tiene dos funciones:
1. recibir una petición (request) del usuario, darle formato según el protocolo de
transferencia que se usará (generalmente HTTP - HyperText Transfer Protocol)
y enviársela al servidor;
2. recibir una respuesta (response) del servidor. Si ésta contiene una página web,
procesarla con un motor de renderizado (por ejemplo, Blink, Gecko o WebKit) y
mostrarla. De lo contrario, seguir el protocolo (por ejemplo, descargar un archivo).
El servidor también tiene básicamente dos funciones:
1. recibir la petición (request) que fue enviada desde el cliente e intentar localizar
un recurso para responderla (si es una petición estática, el recurso será algún tipo
de archivo como, por ejemplo, una página web, una imagen o un video; si es una
petición dinámica, el recurso a localizar será un script o un programa);
2. darle formato, según el protocolo de transferencia que se usará, a la respuesta
(response) a entregar o, si el recurso no se pudo localizar, a una página indicadora
del error (por ejemplo, HTTP Error 404), y enviársela al cliente.
Del lado del servidor, es posible encontrar:
• Servidores web (también conocidos como servidores HTTP): son sistemas que,
usando HTTP, reciben peticiones y entregan páginas web como respuesta, tanto
estáticas (las páginas son archivos) como dinámicas (las páginas son generadas
por un script escrito en un lenguaje cuyo intérprete está integrado en el servidor,
como, por ejemplo, PHP o Perl). Los más ampliamente utilizados son Apache
HTTP Server y Nginx (pronunciado en inglés “engine X”).
• Contenedores web (también conocidos como contenedores de servlets): son
sistemas que proveen un entorno de ejecución para servlets (programas escritos
en Java para recibir peticiones/requests y entregar respuestas/responses)
y que administran el ciclo de vida de éstos (en lugar del método main, los servlets
poseen los métodos init, service y destroy, que son invocados, cuando
corresponde, por el contenedor). Los más ampliamente usados son Apache Tomcat
y Eclipse Jetty, los cuales también poseen la funcionalidad de servidor web.
• Servidores de aplicaciones: son sistemas ubicados entre los servidores del
back-end (aplicaciones de negocios o bases de datos) y los clientes del front-end
(los cuales no necesariamente usan HTTP). Si bien existen servidores de
aplicaciones específicos para casi todos los lenguajes de programación (para
Python, por ejemplo, existe Zope), la mayoría corren aplicaciones escritas en Java
(EE), como es, por ejemplo, el caso de Glassfish, el cual también posee las
funcionalidades de contenedor web y de servidor web.
-203-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Settings
Web Server: <No server selected>
Java EE Version: Java EE 7 Web
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.34.v20201102</version>
<configuration>
<stopKey>stop</stopKey>
<stopPort>8081</stopPort>
</configuration>
</plugin>
Por último, en las propiedades del proyecto, se deben editar dos de las Actions
predeterminadas (Run project y Debug project) y cargar una nueva acción personalizada
(Stop project, creada en Add Custom..). En Run project debe completarse, en Execute
Goals, la meta jetty:run; en Debug project, además de completarse en Execute Goals
la meta jetty:run, debe cambiarse jpda.listen=true por jpda.listen=maven, y en
Stop project debe completarse, en Execute Goals, la meta jetty:stop. De esta manera,
Jetty arrancará al correr la aplicación; para detenderlo existirá la acción Stop project en
Run Maven (al hacerle clic derecho al proyecto), y además podrá depurarse el proyecto
(con las opciones del menú Debug de NetBeans).
-204-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-205-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Página de inicio
-206-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
La tabla cervezas deberá poder ser consultada por cualquier usuario anónimo:
Para poder usar la base de datos, en Dependencies debe agregarse el driver JDBC para
MySQL. Esto puede hacerse colocando la dependencia en el archivo pom.xml o mediante
el siguiente asistente que se abre en Dependencies → Add Dependency
-207-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Veremos tres arquitecturas posibles para desarrollar esta aplicación web: usando
sólo servlets (como se hacía antes del lanzamiento de JSP / JavaServer Pages en 1998),
usando sólo JSP (arquitectura JSP Modelo-1) y usando ambos para seguir el patrón
MVC (arquitectura JSP Modelo-2). Las dos primeras sólo tienen valor ilustrativo e
histórico y, siempre que sea posible, se recomienda evitarlas, ya que utilizar la
arquitectura JSP Modelo-2 (MVC) ofrece múltiples ventajas.
La primera variante utiliza una página web estática (en HTML), desde la cual se
envía una petición que será atendida por el servlet CerveceriaServlet.
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<h1>Menú de cervezas</h1>
<form method="post" action="CerveceriaServlet">
<p>
Seleccione el color:
<select name="color" size="1">
<option value="rubia"> rubia </option>
<option value="roja"> roja </option>
<option value="negra"> negra </option>
<option value="verde"> verde </option>
</select>
<input type="hidden" name="dirIP" value="localhost">
<input type="hidden" name="nomBD" value="cerveceria">
<input type="submit" value ="Enviar">
</p>
</form>
</body>
</html>
CerveceriaServlet.java
package prog2.cerveceria;
import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet(name = "CerveceriaServlet", urlPatterns = {"/CerveceriaServlet"})
public class CerveceriaServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
-208-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
String ip = request.getParameter("dirIP");
String bd = request.getParameter("nomBD");
String c = request.getParameter("color");
response.setContentType("text/html;charset=UTF-8");
String jdbcDriver = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://" + ip + "/" + bd;
PrintWriter out = response.getWriter();
try {
out.println("<!DOCTYPE html>");
out.println("<html>\n<head>");
out.println("<meta http-equiv=\"Content-Type\" "
+ "content=\"text/html; charset=utf-8\">");
out.println("<link rel=\"stylesheet\" "
+ "type=\"text/css\" href=\"css/estilo.css\">");
out.println("<link rel=\"icon\" "
+ "type=\"image/x-icon\" href=\"img/favicon.ico\">");
out.println("<link rel=\"shortcut icon\" "
+ "type=\"image/x-icon\" href=\"img/favicon.ico\">");
out.println("<title>Menú de cervezas</title>");
out.println("</head>\n<body>");
try {
Class.forName(jdbcDriver);
Connection con = DriverManager.getConnection(url, "", "");
Statement stmt = con.createStatement();
stmt.execute("SELECT marca, color, foto FROM cervezas WHERE color='" + c + "';");
ResultSet rs = stmt.getResultSet();
if (rs.isBeforeFirst()) {
out.println("<h1>Marcas de cerveza " + c + " recomendadas</h1>");
while (rs.next()) {
out.println("<p>Pruebe: " + rs.getString(1) + "<br><img src=\"img/"
+ rs.getString(3) + "\" alt=\"" + rs.getString(1) + "\"</p>");
}
rs.close();
} else {
out.println("<h1>ERROR</h1><p>Lamentablemente, no hay ninguna cerveza "
+ c + " en nuestra base de datos.</p>");
}
stmt.close();
con.close();
} catch (ClassNotFoundException | SQLException ex) {
out.println("<h1>ERROR</h1><p>" + ex.getMessage() + "</p>");
}
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}
}
-209-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
h1{
font-family:Arial, "Trebuchet MS", Helvetica, sans-serif;
font-size:32px;
color:#000080;
}
p{
font-family:Arial, "Trebuchet MS", Helvetica, sans-serif;
font-size:18px;
color:#0040C0;
}
-210-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
procesar.jsp
<%@ page session="false" %>
<jsp:useBean id="cerveceriaBean" scope="page" class="prog2.cerveceria.CerveceriaBean" />
<%
if (cerveceriaBean.esCargarOK(request.getParameter("dirIP"),
request.getParameter("nomBD"),
request.getParameter("color"))) {
request.setAttribute("cervezas", cerveceriaBean.getResultado());
request.setAttribute("colorElegido", request.getParameter("color"));
request.getRequestDispatcher("resultados.jsp").forward(request, response);
} else {
request.setAttribute("mensajeError", cerveceriaBean.getMensajeError());
request.getRequestDispatcher("error.jsp").forward(request, response);
}
%>
resultados.jsp
<%@page import="java.util.ArrayList"%>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<%
if (request.getAttribute("cervezas") == null) {
request.setAttribute("mensajeError",
"Lamentablemente, no hay ninguna cerveza "
+ request.getAttribute("colorElegido")
+ " en nuestra base de datos.");
request.getRequestDispatcher("error.jsp").forward(request, response);
} else {
%>
<h1>
Marcas de cerveza
<% out.print(request.getAttribute("colorElegido")); %>
recomendadas
</h1>
<%
ArrayList<prog2.cerveceria.CervezaBean> cervezas =
(ArrayList<prog2.cerveceria.CervezaBean>) request.getAttribute("cervezas");
for (int i = 0; i < cervezas.size(); i++) {
%>
<p>
Pruebe: <% out.print(cervezas.get(i).getMarca()); %><br>
<img src="img/<% out.print(cervezas.get(i).getFoto()); %>"
alt="<% out.print(cervezas.get(i).getMarca()); %>">
</p>
<%
}
}
%>
</body>
</html>
-211-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
error.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<h1>ERROR</h1>
<p>
<% out.println(request.getAttribute("mensajeError")); %>
</p>
</body>
</html>
CervezaBean.java
package prog2.cerveceria;
-212-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
CerveceriaBean.java
package prog2.cerveceria;
import java.sql.*;
import java.util.ArrayList;
-213-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<h1>Menú de cervezas</h1>
<form method="post" action="Controlador">
<p>
Seleccione el color:
<select name="color" size="1">
<option value="rubia"> rubia </option>
<option value="roja"> roja </option>
<option value="negra"> negra </option>
<option value="verde"> verde </option>
</select>
<input type="hidden" name="dirIP" value="localhost">
<input type="hidden" name="nomBD" value="cerveceria">
<input type="submit" value ="Enviar">
</p>
</form>
</body>
</html>
Controlador.java
package prog2.cerveceria;
import java.awt.event.*;
import java.util.logging.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.*;
HttpServletRequest request;
HttpServletResponse response;
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
this.request = request;
this.response = response;
String ip = request.getParameter("dirIP");
String bd = request.getParameter("nomBD");
String color = request.getParameter("color");
-214-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
@Override
public void actionPerformed(ActionEvent event) {
String exception = event.getActionCommand();
request.setAttribute("mensajeError", exception);
RequestDispatcher vista = request.getRequestDispatcher("vistaError.jsp");
try {
vista.forward(request, response);
} catch (ServletException | IOException ex) {
Logger.getLogger(Controlador.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
CervezaBean.java
package prog2.cerveceria;
-215-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Modelo.java
package prog2.cerveceria;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.*;
import java.util.ArrayList;
-216-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
vistaError.jsp
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<h1>ERROR</h1>
<p>
${mensajeError}
</p>
</body>
</html>
vistaResultados.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="css/estilo.css">
<link rel="icon" type="image/x-icon" href="img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.ico">
<title>Menú de cervezas</title>
</head>
<body>
<c:choose>
<c:when test="${empty cervezas}">
<h1>ERROR</h1>
<p>
Lamentablemente, no hay ninguna cerveza ${colorElegido} en nuestra base de datos.
</p>
</c:when>
<c:otherwise>
<h1>
Marcas de cerveza ${colorElegido} recomendadas
</h1>
<c:forEach var="cerveza" items="${cervezas}" >
<p>
Pruebe: ${cerveza.marca} <br>
<img src="img/${cerveza.foto}" alt="${cerveza.marca}">
</p>
</c:forEach>
</c:otherwise>
</c:choose>
</body>
</html>
Como se puede observar en las dos últimas vistas, el uso de EL permitió eliminar
la necesidad de utilizar scriptlets.
-217-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
La etiqueta <c:forEach> permite recorrer una colección (en este caso, cervezas
es un ArrayList). Como la clase CervezaBean, a la que pertenecen los objetos extraídos
de la colección en la variable cerveza, es un JavaBean (y, por lo tanto, sus atributos son
accesibles mediante getters y setters que siguen una convención de nomenclatura
estándar), sus atributos pueden obtenerse mediante el operador punto (.), por ejemplo,
así:
${cerveza.foto}
-218-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Unidad 12
Frameworks
La siguiente figura representa la cuestión mencionada en la última pregunta de
la unidad anterior:
Casos-uso
Alta
Controlador Modelo
(servlet)
Baja
-219-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
En este diseño se basa Spring Web MVC. Este framework 1 actúa como una
enorme Factory que instancia los objetos de aquellas clases decoradas mediante ciertas
annotations, porque ofrece inversión de control (Inversion of Control o IoC), y usa un
front controller de la clase org.springframework.web.servlet.DispatcherServlet.
1 Un framework es una colección de interfaces y clases diseñadas para trabajar juntas en el tratamiento
de un tipo de problema en particular. En el caso de Spring Web MVC, el problema en cuestión son las
aplicaciones web. El principal objetivo de un web application framework (WAF) es facilitar a los
programadores el desarrollo y mantenimiento de aplicaciones web complejas.
-220-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Settings
Web Server: <No server selected>
Java EE Version: Java EE 7 Web
-221-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Allí se debe escribir spring-webmvc en Query y elegir la versión que se desea agregar:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version></version>
<type>jar</type>
</dependency>
En el caso anterior, la versión está vacía para que aparezca un menú emergente al situar
el cursor entre las etiquetas XML <version> y </version>, en el que deberá elegirse la
versión RELEASE más reciente que esté disponible en los repositorios de Maven.
-222-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Paso 3: Crear las carpetas y agregar en ellas los archivos del proyecto
Si bien los archivos de recursos (imágenes, estilos) son los mismos que los del proyecto
original, en la cantidad de vistas habrá un cambio, pues estas serán ahora solo las dos
siguientes:
index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="/resources/css/estilo.css">
<link rel="icon" type="image/x-icon" href="/resources/img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="/resources/img/favicon.ico">
<title>Cervecería Spring Web MVC</title>
</head>
<body>
<h1>Elija un color:</h1>
<form method="post" action="/consulta">
<p>
<select name="color">
<c:forEach var="color" items="${colores}" >
<option value="${color}">${color}</option>
</c:forEach>
</select>
<input type="submit" value="Enviar">
</p>
</form>
</body>
</html>
-223-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
resultado.jsp
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="/resources/css/estilo.css">
<link rel="icon" type="image/x-icon" href="/resources/img/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="/resources/img/favicon.ico">
<title>Cervecería Spring Web MVC</title>
</head>
<body>
<h1>
Marcas de cerveza ${colorElegido} recomendadas
</h1>
<c:forEach var="cerveza" items="${cervezas}" >
<p>
Pruebe: ${cerveza.marca} <br>
<img src="/resources/img/${cerveza.foto}" alt="${cerveza.marca}">
</p>
</c:forEach>
</body>
</html>
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context =
new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.setServletContext(servletContext);
ServletRegistration.Dynamic servlet =
servletContext.addServlet("dispatcher", new DispatcherServlet(context));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
}
-224-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@ComponentScan("prog2.corsi.cerveceria")
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(final ResourceHandlerRegistry reg) {
reg.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setViewClass(JstlView.class);
vr.setPrefix("/vistas/");
vr.setSuffix(".jsp");
return vr;
}
@Bean(name = "dbName")
public String getDBName() { return "cerveceria"; }
@Bean(name = "dbURL")
public String getDBURL() { return "127.0.0.1"; }
@Bean(name = "dbUser")
public String getDBUser() { return ""; }
@Bean(name = "dbPswd")
public String getDBPswd() { return ""; }
Los beans decorados en la clase de configuración mediante las annotations @Bean podrán
ser "inyectados" en las clases que dependan de ellos.
-225-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
La clase que define el controlador donde se procesan las distintas peticiones (requests)
HTTP (dado que se trata de un controlador multi-action) y a la cual se le "inyecta" un
DAO (mediante la annotation @Autowired) es la siguiente:
Controlador.java
package prog2.corsi.cerveceria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class Controlador {
@Autowired
private CervezasDAO cervezasDAO;
@GetMapping("/")
public String mostrarInicio(Model model) {
model.addAttribute("colores", cervezasDAO.getColores());
return "index";
}
@PostMapping("/consulta")
public String mostrarRespuesta(
Model model,
@RequestParam(value = "color", required = true) String color) {
model.addAttribute("colorElegido", color);
model.addAttribute("cervezas", cervezasDAO.getCervezasByColor(color));
return "resultado";
}
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
-226-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
@Repository
public class CervezasDAO {
@Autowired
public CervezasDAO(
@Qualifier("dbName") String dbName,
@Qualifier("dbURL") String dbURL,
@Qualifier("dbUser") String dbUser,
@Qualifier("dbPswd") String dbPswd) {
dbFullURL = "jdbc:mysql://" + dbURL + "/" + dbName;
this.dbUser = dbUser;
this.dbPswd = dbPswd;
}
-227-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Como puede observarse, esta clase es un JavaBean, por lo que puede aprovecharse la
versión del proyecto original prácticamente sin cambios.
-228-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
OBSERVACIÓN
EJERCICIO Nº 7
Utilizando Spring Web MVC, transforme la aplicación presentada en la pág. 193
en una aplicación web:
-229-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Framework: ........................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
Library (Ej: Lightweight Java Game Library [lwjgl.org]): ............................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
SDK (Ej: GWT [gwtproject.org]): .....................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
API (Ej: YouTube Data API [developers.google.com/youtube/v3]): ............................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
Engine (Ej: Apache Spark [spark.apache.org]): .............................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
................................................................................................................................................
-230-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-231-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-232-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-233-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
-234-
2.601 / 2.603 - Programación II
Prof. Dr. Diego Corsi / Prof. Matías Ávalos
Bibliografía
Barker, J. (2005): Beginning Java Objects: From Concepts to Code, 2ª ed., Apress
Basham, B. et al. (2008): Head First. Servlets and JSP, 2ª ed., O'Reilly Media
-235-