Tema 8: Relaciones entre clases - Tema 8: Relaciones
entre clases
8.1. Asociación
Es una conexión entre clases, que implica la existencia de una relación estructural
entre objetos de esas clases.
Diremos que dos o más clases tienen una relación de asociación cuando una de
ellas tenga que requerir o utilizar alguno de los otros atributos o métodos de las
otras clases.
La asociación expresa una relación (unidireccional o bidireccional) entre las
instancias, a partir de las clases conectadas.
No requieren que creemos un objeto nuevo a partir de otros objetos, sino
únicamente que los objetos interactúen entre sí. La creación o desaparición de uno
de ellos implica únicamente la creación o destrucción de la relación entre ellos y
nunca la creación o destrucción del otro.
Un ejemplo de relación de asociación es la relación “perfil – foto”. Podemos definir
una clase “Perfil” y una clase “Foto” que nos permitan representar esta relación.
Implementación del ejemplo anterior en Java
Creación de la clase Foto
Creación de la clase Perfil
Creación de la clase principal
El gráfico anterior UML representa una relación de asociación. Las relaciones de
asociación se representan en UML como una línea que une las dos clases siendo
relacionadas.
En el ejemplo, además vemos que esta relación se ha declarado de multiplicidad
indefinida en ambos extremos, lo que quiere decir que una persona puede tener en
su perfil una o más fotos.
8.2. Agregación
Es una relación que se derivó de la asociación, por ser igualmente estructural, es
decir, que contiene un atributo que en todos los casos será una colección, es decir,
un Array, Vector, entre otros, además indica que una clase es parte de otra clase.
Los componentes pueden ser compartidos por varios compuestos (de la misma
asociación de agregación o de varias asociaciones de agregación distintas). La
destrucción del compuesto no conlleva la destrucción de los componentes.
La agregación se representa en UML mediante un diamante de color blanco
colocado en el extremo en el que está la clase que representa el “todo”.
Veamos un ejemplo de agregación.
• Tenemos una clase Aula.
• Tenemos una clase Alumnos.
• Tenemos una clase Escritorios.
• Un aula agrupa a varios.
Implementación del ejemplo anterior en Java
Creación de la clase DatosP, que almacenará los datos personales de cada
persona.
Creación de la clase Alumno
Creación de la clase Escritorio
Creación de la clase Aula
Creación de la clase principal
8.3. Composición
Es una relación estructural, pero se le suma un método de destrucción de los
objetos.
Composición es una forma fuerte de composición donde la vida de la clase
contenida debe coincidir con la vida de la clase contenedor. Los componentes
constituyen una parte del objeto compuesto. De esta forma, los componentes no
pueden ser compartidos por varios objetos compuestos. La supresión del objeto
compuesto conlleva la supresión de los componentes.
La programación es la misma para las composiciones y las agregaciones, la
diferencia es meramente conceptual entre una y otras.
El símbolo de composición es un diamante de color negro colocado en el extremo
en el que está la clase que representa el “todo” (Compuesto).
Veamos un ejemplo de composición.
• Tenemos una clase Equipo.
• Un objeto Equipo está a su vez compuesto por uno o varios objetos del tipo
Jugador.
• El tiempo de vida de los objetos Jugador depende del tiempo de vida de
Equipo, ya que si no existe un Equipo no pueden existir sus jugadores.
Implementación del ejemplo anterior en Java
Creación de la clase Carrera
Creación de la clase Universidad
Creación de la clase principal
Diferencias entre agregación y composición
Características Agregación Composición
Varias asociaciones comparten los
Sí No
componentes.
Destrucción de los componentes al
No Sí
destruir el compuesto.
Cardinalidad a nivel compuesto Cualquiera 0..1 o 1
Rombo
Representación Rombo negro
transparente
8.4. Modelado e implementación
Asociación
Para entender la implementación en la agregación, como se ha dicho antes, hay
que tomar en cuenta que esta relación no es de contención. Tomando el ejemplo
antes mencionado, un aula no contiene a estudiantes.
Para comprender de una mejor manera cómo se comporta o trabaja esta clase, es
necesario darle sentido a la frase que va a contener a la agregación “Usa un”.
• Un cliente usa tarjeta de crédito.
• Un estudiante usa libros.
Código
Composición
En caso contrario, la composición es un tipo de relación dependiente en donde un
objeto más complejo es conformado por objetos más pequeños. En esta situación,
la frase “Tiene un”, debe tener sentido.
• Una biblioteca tiene libros.
• La portátil tiene un teclado.
Representaremos la relación: La portátil tiene un teclado.
Código
8.5. Dependencia: implementación
Es la relación menos importante, debido a que simplemente refleja que la
implementación de una clase depende de otra.
Es una relación de uso entre dos clases (una usa a la otra). Esta relación es las más
básica entre clases y comparada con los demás tipos de relación, la más débil.
Representación UML
Se representa con una flecha discontinua que parte desde una clase y apunta a
otra. El sentido de la flecha nos indica quién usa a quién.
Una clase A usa una clase B cuando no contiene atributos de la clase B pero, o bien
utiliza alguna instancia de la clase B como parámetro en alguno de sus métodos
para realizar una operación, o bien accede a sus atributos (clases con
métodos amigos).
Del diagrama anterior podemos observar que:
• La Persona usa una Cédula.
• La Persona depende de la Cédula.
• Dada la dependencia, todo cambio en la Cédula podrá afectar a la Persona.
En la práctica este tipo de relación se interpreta como que la Persona hace uso de
la Cédula ya sea instanciándola directamente, o
bien, recibiéndola como parámetro de entrada en uno de sus métodos.
Modelamiento e implementación
Supongamos lo siguiente:
1. Tenemos una clase.
2. Tenemos una clase Cédula con sus respectivos atributos.
3. La clase Persona necesita de su documento cédula.
Para esto generamos una relación de dependencia.
Traduciendo a código: (Java)
Clase Cedula
Clase Persona
En funcionamiento
Autoevaluación
1. La figura representa:
A. Composición.
B. Asociación.
C. Agregación.
2. La figura representa:
A. Asociación unidireccional.
B. Asociación con multiplicidad 1.1.
C. Dependencia.
3. En el caso Reloj -> Display, ¿de qué tipo de asociación se trata?
A. Composición.
B. Agregación.
C. Dependencia.
4. La figura representa:
A. Composición.
B. Asociación simple.
C. Herencia simple.
5. ¿Cuál es la cardinalidad de la composición?
A. Cualquiera.
B. 0.1 o 1. n
C. 0.1 o 1
6. ¿Cuál de las siguientes relaciones entre clases es más débil?
A. Agregación.
B. Dependencia.
C. Composición.
7. La dependencia se puede definir como:
A. Una clase que es parte de otra clase (composición débil).
B. Una relación de uso entre dos clases (una usa a la otra).
C. Un objeto más complejo que es conformado por objetos más pequeños.
8. Cuando la destrucción del objeto compuesto no conlleva la destrucción de
los componentes se denomina:
A. Polimorfismo.
B. Multiplicidad simple.
C. Agregación.
9. La figura representa:
A. Asociación unidireccional.
B. Dependencia.
C. Asociación.
10. En el caso Persona -> Cerebro, ¿de qué tipo de asociación se trata?
A. Composición.
B. Agregación.
C. Herencia.
Tema 9 : Generalización/especialización y cód - Tema
9 : Generalización/especialización y cód
9.1. Generalización/especialización
9.1.1. Herencia
9.1.2. Tipos de herencia
9.1.3. Constructores super () y this ()
9.1.4. Ventajas de la herencia
9.1.5. Modelado
9.1.6. Implementación
9.1.1. Herencia
La herencia es específica de la programación orientada a objetos, donde una clase
nueva se crea a partir de una clase existente. La herencia (a la que habitualmente
se denomina subclase) proviene del hecho de que la subclase (la nueva clase
creada) contiene los atributos y métodos de la clase primaria. La principal ventaja
de la herencia es la capacidad para definir atributos y métodos nuevos para la
subclase, que luego se aplican a los atributos y métodos heredados.
Ventajas
• Una vez que el método ha sido definido en la superclase, esto es heredado
automáticamente por las superclases.
• De esta manera el método solo se escribe una vez y no una vez por cada
subclase.
• Una clase y sus hijos comparten un conjunto común de propiedades.
• Una subclase hereda todo lo public y lo protected (propiedades o métodos)
de su padre sin importar en qué paquete está la subclase.
Nomenclatura
Partiendo del siguiente ejemplo se procederá a explicar cómo funcionan
las palabras clave para reconocer cuando se habla de una herencia. Si definimos
la clase Coche a partir de la clase Vehículo se dice que:
• "Coche" hereda las variables y métodos de "Vehículo".
• "Coche" extiende de "Vehículo".
• "Coche" es subclase de "Vehículo".
o clase derivada
o clase hija
• "Vehículo" es superclase de "Coche".
o clase base.
o clase padre.
• La herencia realiza la relación es-un – Un coche es-un vehículo; un perro
es-un mamífero, etc.
Dentro de Java se indica usando la palabra reservada extends class.
Punto3D extends Punto2D
Visibilidad
• Los miembros privados de la superclase no son visibles desde la subclase.
• Los miembros públicos de la superclase son visibles y siguen siendo
públicos en la subclase.
- Se puede acceder a los miembros de la superclase usando la palabra reservada
super.
- Si una clase se declara como final no se puede heredar de ella.
- En Java, todas las clases heredan implícitamente de la clase Object.
Reglas
• Una interfaz puede heredar de otra interfaz.
• Una clase (abstracta o no) puede heredar de una interfaz.
• Una interfaz NO puede heredar de una clase.
• 9.1.2. Tipos de herencia
• Herencia simple
• La herencia simple consiste en que una clase hereda a una clase hijo, y a
solo una le hereda sus atributos. Es igual al concepto general de herencia,
con la limitante de solo poder heredar de una clase padre a una clase hijo, y
solo a una clase hijo
Clase Derivada Clase Derivada
Clase Derivada
H. Pública H. Pública
H. Pública
No direct. No direct. No direct.
PRIVATE
Accesible accesible accesible
PROTECTED Protected Protected Private
PUBLIC Public Protected Private
Ejemplo
Métodos en las clases derivadas
En la clase derivada se puede:
• Añadir nuevos métodos/atributos propios de la clase derivada.
• Modificar los métodos heredados de la clase base.
Refinamiento
Se añade comportamiento nuevo antes y/o después del comportamiento
heredado. (Simula, Beta) (se puede simular en C++, Java)
C++, Java: Constructores y destructores se refinan.
• Reemplazo. El método heredado se redefine completamente, de forma que
sustituye al original de la clase base.
Constructor en herencia simple
Los constructores no se heredan.
• Siempre son definidos para las clases derivadas.
• Creación de un objeto de clase derivada: Se invoca a todos los
constructores de la jerarquía.
• Orden de ejecución de constructores: Primero se ejecuta el constructor de
la clase base y luego el de la derivada.
Esto implica que la clase derivada aplica una política de refinamiento: añadir
comportamiento al constructor de la clase base.
• Ejecución implícita del constructor por defecto de clase base al invocar a
un constructor de clase derivada.
• Ejecución explícita de cualquier otro tipo de constructor en la zona de
inicialización (refinamiento explícito). En particular, el constructor de copia.
Herencia múltiple
Consiste en la utilización de las propiedades de una clase a varias clases más, lo
que significa que en esta propiedad una sola clase padre puede heredarle atributos
u objetos de esta a varias clases hijo sin ninguna limitación entre ellas.
Nota
Dentro de lenguajes como Java la herencia múltiple no puede ser implementada.
De implementación. La implementación de los métodos es heredada. Puede
sobrescribirse en las clases derivadas.
De interfaz. Solo se hereda la interfaz, no hay implementación a nivel de clase
base (interfaces en Java, clases abstractas en C++).
9.1.3. Constructores super () y this ()
super ()
Las dudas con los constructores aparecen ligadas a las jerarquías de clases y a la
palabra super(). Supongamos que tenemos la siguiente jerarquía:
En este caso podemos tener dos clases con el siguiente código por simplificar al máximo:
Como podemos ver, todos los constructores llaman por defecto al constructor de la
clase superior a través de una llamada a super() (en este caso al constructor por
defecto). Esto es debido a que los constructores no se heredan entre jerarquías de
clases; por lo tanto, la palabra super() siempre es la primera línea de un constructor
e invoca al constructor de la clase superior que comparta el mismo tipo de
parametrización.
Aunque nosotros no pongamos la palabra super() esta siempre será añadida salvo que
nosotros la añadamos. Por ejemplo, si nuestros constructores tienen parámetros, las
cláusulas super que deberemos construir serán las siguientes entre Persona y Deportista
para que el código compile:
Ya que sino el compilador añadirá super() por defecto y el código no compilará al
carecer la clase Persona de un constructor por defecto.
this ()
La otra posibilidad a super() es el uso de this() en la primera línea de un constructor.
Esto lo que hace es invocar a otro constructor que esté en la misma clase y que
soporte el conjunto de parámetros que le pasamos.
9.1.4. Ventajas de la herencia
1. Evitar duplicidad y favorecer la reutilización de código (las subclases
utilizan el código de superclases).
2. Facilitar el mantenimiento de aplicaciones. Podemos cambiar las clases que
usamos fácilmente.
3. Facilitar la extensión de las aplicaciones. Podemos crear nuevas clases a
partir de otras existentes.
9.1.5. Modelado
Dentro de UML se representa a la herencia con una flecha que va desde el hijo
hacia el padre, como se puede ver en el siguiente ejemplo:
Donde se puede ver que tanto CANINO como FELINO al ser hijos, su relación de
herencia (en este caso con el método rugir), se ve expresada con una flecha que sale
desde ellos y termina en el padre que es la clase Animal.
9.1.6. Implementación
Aquí se puede ver un ejemplo sobre la base del diagrama anterior.
9.2. Código limpio
9.2.1. Estándares de implementación, buenas prácticas de programación
9.2.2. Atributos de calidad de código
9.2.3. Buenas prácticas de programación para tener un código limpio
9.2.1. Estándares de implementación, buenas prácticas de
programación
Cuando hablamos de un código limpio, nos referimos al estilo de desarrollo centrado
en el lector, un software que sea fácil de leer, escribir y mantener. El código limpio
es uno fácil de entender y cambiar.
Para que un código sea fácil de cambiar, se debe tener en cuenta: que las clases y
los métodos sean pequeños y tengan una única responsabilidad, que las clases
tengan una API concisa y clara, que las clases y los métodos sean predecibles y
trabajen como lo esperado, un código fácil de testear y tenga pruebas unitarias, que
los test sean fáciles de entender y cambiar, etc.
Solid, cinco principios básicos de diseño de clases
Solid es un acrónimo inventado por Robert C. Martin para establecer los cinco
principios básicos de la programación orientada a objetos y diseño. Este acrónimo
tiene bastante relación con los patrones de diseño, en especial, con la alta
cohesión y el bajo acoplamiento.
El objetivo de tener un buen diseño de programación es abarcar la fase de
mantenimiento de una manera más legible y sencilla así como conseguir crear
nuevas funcionalidades sin tener que modificar en gran medida código antiguo. Los
costes de mantenimiento pueden abarcar el 80 % de un proyecto de software, por
lo que hay que valorar un buen diseño.
S - Responsabilidad simple (Single responsibility)
Este principio trata de destinar cada clase a una finalidad sencilla y concreta. En
muchas ocasiones estamos tentados a poner un método reutilizable que no tiene
nada que ver con la clase, simplemente porque lo utiliza y nos pilla más a mano. El
problema surge cuando tenemos la necesidad de utilizar ese mismo método desde
otra clase. Si no se refactoriza en ese momento y se crea una clase destinada para
la finalidad del método, nos toparemos a largo plazo con que las clases realizan
tareas que no deberían ser de su responsabilidad.
Con la anterior mentalidad nos encontraremos, por ejemplo, con un algoritmo de
formateo de números en una clase destinada a leer de la base de datos porque fue
el primer sitio donde se empezó a utilizar. Esto conlleva a tener métodos difíciles de
detectar y encontrar, de manera que el código hay que tenerlo memorizado en la
cabeza.
Ejemplo
Modo incorrecto
Modo correcto
O - Abierto/Cerrado (Open/Closed)
Principio atribuido a Bertrand Meyer quien habla de crear clases extensibles sin
necesidad de entrar al código fuente a modificarlo. Es decir, el diseño debe ser
abierto para poderse extender pero cerrado para poderse modificar. Lo complicado
es predecir por dónde se debe extender y que no tengamos que modificarlo.
El uso más común de extensión es mediante la herencia y la reimplementación de
métodos. Existe otra alternativa que consiste en utilizar métodos que acepten
una interface de manera que podemos ejecutar cualquier clase que implemente
esa interface. En todos los casos, el comportamiento de la clase cambia sin que
hayamos tenido que tocar código interno.
Ejemplo
Modo incorrecto
Modo correcto
L - Sustitución Liskov (Liskov substitution)
Este principio fue creado por Barbara Liskov y habla de la importancia de crear
todas las clases derivadas para que también puedan ser tratadas como la propia
clase base. Cuando creamos clases derivadas debemos asegurarnos de no
reimplementar métodos que hagan que los métodos de la clase base no
funcionasen si se tratasen como un objeto de esa clase base.
Ejemplo
Modo incorrecto
Modo correcto
I - Segregacion de la interface (Interface segregation)
Este principio fue formulado por Robert C. Martin. Cuando se definen interfaces
estos deben ser específicos a una finalidad concreta. Por ello, si tenemos que definir
una serie de métodos abstractos que debe utilizar una clase a través de interfaces,
es preferible tener muchos interfaces que definan pocos métodos que tener una
interface con muchos métodos.
El objetivo de este principio es principalmente poder reaprovechar los
interfaces en otras clases. Si tenemos una interface que compara y clona en la
misma interface, de manera más complicada se podrá utilizar en una clase que solo
debe comparar o en otra que solo debe clonar.
Ejemplo
Modo incorrecto
Modo correcto
D - Inversión de dependencias (Dependency inversion)
También fue definido por Robert C. Martin. El objetivo de este principio es
conseguir desacoplar las clases. En todo diseño siempre debe existir un
acoplamiento, pero hay que evitarlo en la medida de lo posible. Un sistema no
acoplado no hace nada, pero un sistema altamente acoplado es muy difícil de
mantener.
El objetivo de este principio es el uso de abstracciones para conseguir que una clase
interactúe con otras clases sin que las conozca directamente. Es decir, las clases
de nivel superior no deben conocer las clases de nivel inferior. Dicho de otro modo,
no debe conocer los detalles.
Un código limpio debería cumplir con la regla DRY (Don't Repeat Yourself, o No Te
Repitas). Cuando se aplica correctamente el principio DRY, la modificación de un
único elemento del sistema no requiere cambios en otros elementos que no tengan
relación lógica.
Ejemplo
Modo incorrecto
Modo correcto
9.2.2. Atributos de calidad de código
1. Los nombres de paquetes deberían estar totalmente en minúsculas. Esto se
basa en la convención especificada por Sun para la nomenclatura de paquetes.
mypackage, [Link]
El nombre inicial de dominio del paquete debe estar totalmente en minúsculas.
2. Los nombres que representan tipos deben ser sustantivos y escribirse con
mayúsculas y minúsculas, iniciando con mayúscula.
Line, AudioSystem
Esta es una práctica común en la comunidad de desarrollo en Java e incluso la
convención de nomenclatura de tipos que utiliza Sun en los paquetes predefinidos
de Java.
3. Los nombres de variables deben utilizar mayúsculas y minúsculas iniciando con
minúscula.
line, audioSystem
Es una práctica común en la comunidad de desarrollo Java y también la convención
de nomenclatura de nombres de variables utilizada por Sun para los paquetes
predefinidos de Java. Esta hace que las variables sean fáciles de distinguir de los
tipos y resuelve de manera efectiva las posibles colisiones de nombre como en la
declaración Line line;
4. Los nombres que representan constantes (variables finales) deben estar
totalmente en mayúsculas utilizando subrayados (guion bajo) para separar las
palabras.
MAX_ITERATIONS, COLOR_RED
5. Los nombres que representan métodos deben ser verbos y escribirse con
mayúsculas y minúsculas iniciando con minúscula.
getName(), computeTotalWidth()
Es una práctica común en la comunidad de desarrollo en Java y también la
convención de nomenclatura de nombres utilizada por Sun para los paquetes
predefinidos de Java. Es idéntico al caso de los nombres de variables, pero los
métodos en Java ya se distinguen de las variables por su forma particular.
6. Las abreviaturas y acrónimos no deberían estar con mayúsculas cuando se usan
como nombre.
exportHtmlSource(); //NOT: exportHTMLSource();
openDvdPlayer(); //NOT: openDVDPlayer();
Utilizar todo en mayúsculas para el nombre base entraría en conflicto con las
convenciones anteriores. Una variable de este tipo debería nombrarse dVD, hTML,
etc., lo cual obviamente no es legible. Otro problema se ilustra en el ejemplo
anterior. Cuando el nombre se conecta a otro, se reduce seriamente la legibilidad.
La palabra a continuación del acrónimo no queda como debería.
9.2.3. Buenas prácticas de programación para tener un código
limpio
Evitar la creación innecesaria de objetos, Lazy Initialitation
La creación de objetos en Java es una de las operaciones más costosas en términos
de uso de memoria e impacto en el performance. Esto es evitable creando o
inicializando objetos solo en el momento en que serán requeridos en el código.
Nunca hacer variables de instancia públicas
Hacer una variable de instancia pública puede ocasionar problemas en un
programa. Por ejemplo, si se tiene una clase MiCalendario, esta clase contiene un
arreglo de cadenas diasDeLaSemana. Pero es un arreglo público y a este puede
ser accedido por cualquiera. Se debe asumir que este arreglo contiene siempre los
siete nombres de los días de la semana. Alguien por error puede cambiar el valor e
insertar un error.
Trata siempre de minimizar la mutabilidad de las clases
Hacer una clase inmutable es hacerla inmodificable. La información de la clase se
preservará durante el tiempo de vida de la clase. Las clases inmutables son simples
y fáciles de manejar.
No obstante, crear objetos inmutables puede golpear significativamente el
rendimiento de una aplicación. Así que se elige cuidadosamente si se quiere que
una clase sea o no inmutable.
Para hacer una clase inmutable se puede definir su constructor de forma privada y
luego crear un método estático para inicializar al objeto y devolverlo.
Siempre que sea posible trata de usar tipos primitivos en lugar de las
clases Wrapper
Las clases Wrapper son buenas, pero también son lentas. Los tipos primitivos son
como clases, sin embargo, las clases wrapper almacenan la información completa
acerca de una clase. Algunas veces un programador puede agregar un error en el
código usando una wrapper por un descuido. Por ejemplo:
Autoevaluación
1. El principio de responsabilidad simple se refiere a:
A. Conseguir desacoplar las clases.
B. Destinar la inmutabilidad de una clase.
C. Destinar cada clase a una finalidad sencilla y concreta.
2. ¿Cuál de los siguientes métodos está escrito correctamente?
A. exportHtmlSource ();
B. openDvDPlayer ();
C. openDVDPlayer ();
3. El principio de segregación de interfaces señala:
A. Al definir interfaces, estos deben implementarse dentro de las clases.
B. Al definir interfaces, estos deben ser compartidos por las clases.
C. Al definir interfaces, estos deben ser específicos a una finalidad concreta.
4. ¿Quién fue el inventor del acrónimo SOLID?
A. Robert C. Martin.
B. Rubira Jans.
C. Bertrand Meyer.
5. ¿Qué significa hacer a una clase inmutable?
A. Hacerla inmodificable.
B. Hacerla modificable.
C. Hacerla dependiente de otra clase.
6. Es conveniente usar datos primitivos en lugar de clases:
A. Object.
B. Wrapper.
C. Rapper.
7. El principio sustitución, Liskov hace referencia a:
A. Crear todas las clases derivadas para acceder a todos los atributos.
B. Crear todas las clases derivadas para que puedan tratarse como la clase base.
C. Derivar clases para que sean más entendibles y accesibles.
8. El principio de inversión de dependencias hace referencia a:
A. Conseguir modelar adecuadamente una clase.
B. Conseguir acoplar las clases.
C. Conseguir desacoplar las clases.
9. ¿Cuál de las siguientes variables está bien escrita?
A. audiosystem.
B. audioBot.
C. audiOSystem.
10. El principio de abierto/cerrado se refiera a:
A. Crear clases extensibles sin necesidad de entrar al código fuente a modificarlo.
B. Crear clases extensibles entrando al código fuente a modificarlo ya que este es
entendible.
C. Crear clases extensibles sin necesidad de entrar al código fuente de método
específico.
Tema 10: Gestion de defectos - Tema 10: Gestion de
defectos
10.1. Definición
El testing es una actividad desarrollada para evaluar la calidad del producto y para
mejorarlo al identificar defectos y problemas.
El testing de software consiste en la verificación dinámica del comportamiento de
un programa sobre un conjunto finito de casos de prueba, apropiadamente
seleccionados a partir del dominio de ejecución que usualmente es infinito, en
relación con el comportamiento esperado.
Es una técnica dinámica en el sentido de que el programa se verifica poniéndolo en
ejecución de la forma más parecida posible a como se ejecutará cuando esté en
producción –esto se contrapone a las técnicas estáticas las cuales se basan en
analizar el código fuente.
Si uno de los casos de prueba detecta un error el programa es incorrecto, pero si
ninguno de los casos de prueba seleccionados encuentra un error no podemos decir
que el programa es correcto. Esos casos de prueba son elegidos siguiendo alguna
regla o criterio de selección. Se determina si un caso de prueba ha detectado un
error o no comparando la salida producida con la salida esperada para la entrada.
Como sugiere la figura 1, un sistema complejo suele testearse en varias etapas que por lo
general se ejecutan siguiendo una estrategia bottom-up, aunque el proceso general es
iterativo. Se debe volver a las fases anteriores cada vez que se encuentra un error en la
fase que está siendo ejecutada.
10.2. Verificación y validación
La verificación y validación es el nombre que se da a los procesos de comprobación
y análisis que aseguran que el software que se desarrolla está acorde a su
especificación y cumple las necesidades de los clientes.
Es un proceso de ciclo de vida completo. Inicia con las revisiones de los
requerimientos y continúa con las revisiones del diseño y las inspecciones del
código hasta la prueba del producto. Existen actividades en cada etapa del proceso
de desarrollo del software. Boehm (1979) expresó la diferencia entre ellas de forma
sucinta.
10.2.1. Verificación
¿Estamos construyendo el producto correctamente?
El papel de la verificación comprende comprobar que el software está de acuerdo
con su especificación. Se comprueba que el sistema cumple los requerimientos
funcionales y no funcionales que se le han especificado.
10.2.2. Validación
¿Estamos construyendo el producto concreto?
La validación es un proceso más general. Se debe asegurar que el software cumple
las expectativas del cliente. Va más allá de comprobar si el sistema está acorde con
su especificación, para probar que el software hace lo que el usuario espera a
diferencia de lo que se ha especificado.
En la figura 1 se muestra el lugar que ocupan las inspecciones y las pruebas dentro
del proceso de desarrollo de software. Las flechas indican las fases del proceso en
las que se utilizan las técnicas. Las inspecciones de software se pueden utilizar en
todas las etapas del proceso, mientras que las técnicas de prueba solo cuando está
disponible un prototipo o código ejecutable.
Las técnicas de inspección incluyen inspección de programas, análisis
automatizado de código fuente y verificación formal. Sin embargo, las técnicas
estáticas solo pueden comprobar la correspondencia entre un programa y su
especificación mas no pueden probar que el software es de utilidad operacional y,
mucho menos, que las características no funcionales del software son las correctas.
Por lo tanto, para validar un sistema de software, siempre se requieren llevar a cabo
ciertas pruebas. Aunque en la actualidad las inspecciones se utilizan ampliamente,
las pruebas de los programas son aún la técnica de verificación y validación
predominante.
10.3. Pruebas vs. depuración
10.3.1. Pruebas
10.3.2. Depuración
10.3.1. Pruebas
Las pruebas que se aplican al programa son de diversa índole y generalmente
dependen del tipo de problema que se está resolviendo. Comúnmente se inicia la
prueba de un programa introduciendo datos válidos, inválidos e incongruentes y
observando cómo reacciona en cada ocasión.
Los resultados pueden ser los siguientes:
• La lógica del programa está bien, pero hay errores sencillos, los cuales los
corregimos eliminando o modificando algunas instrucciones o incluyendo
nuevas.
• Hay errores ocasionados por fallas en la lógica, lo que nos obliga a regresar
a las fases de Diseño y Codificación para revisión y modificación del
diagrama.
• Hay errores muy graves y lo más aconsejable es que regresemos a la fase 2
para analizar nuevamente el problema y repetir todo el proceso.
• No hay errores y los resultados son los esperados. En este caso, guardamos
el programa permanentemente en un medio de almacenamiento.
Si se está automatizando alguna tarea manual, es común poner a funcionar por un
tiempo y de forma paralela ambas alternativas, a fin de comparar las salidas de las
dos y adquirir confianza en la solución automatizada.
10.3.2. Depuración
No existe un proceso sencillo para la depuración de programas. Los mejores
depuradores buscan patrones en los resultados de las pruebas donde el defecto se
detecta, y para localizar el defecto utilizan el conocimiento que tienen sobre el tipo
de defecto, el patrón de salida, así como el lenguaje y proceso de programación. El
conocimiento del proceso es importante. Los depuradores conocen los errores de
los programadores comunes (como olvidar incrementar un contador, errores de
direccionamiento de punteros en lenguaje C, etc.) y los comparan contra los
patrones observados.
En la figura 2 se explica que después de que se descubre el origen del fallo en el
programa, este debe corregirse y entonces reevaluar el sistema. Esto implica repetir
de nuevo las pruebas anteriores (pruebas de regresión). Estas pruebas se hacen
para comprobar que los cambios introducidos resuelven definitivamente el fallo y no
introducen nuevos fallos. La estadística muestra que la reparación de un fallo
frecuentemente es incompleta y además introduce nuevos fallos.
10.4. Pruebas de unidad
Comprueban si el código funciona y cumple con las especificaciones necesarias
para su desempeño óptimo. Se focalizan en verificar cada módulo con lo que mejora
el manejo de la integración de lo más básico a los componentes mayores.
Las pruebas unitarias se enfocan en demostrar que la lógica existente en el código
está en buen estado y que funcionará en todos los casos en los que se los pueda
llegar a ejecutar, además aporta fiabilidad al código. Entre algunos de los beneficios
están:
• Aumentan la legibilidad del código y ayudan a los desarrolladores a entender
el código base, lo que facilita hacer cambios más rápidamente.
• Los test unitarios bien realizados sirven como documentación del proyecto.
• Se realizan en pocos milisegundos, por lo que podrá realizar cientos de ellas
en muy poco tiempo.
• Las unit testing le permiten al desarrollador refactorizar el código más
adelante y tener la garantía de que el módulo sigue funcionando
correctamente. Para ello se escriben casos de prueba para todas las
funciones y métodos, para que cada vez que un cambio provoque un error,
sea posible identificarlo y repararlo rápidamente. (¿Qué son las pruebas
unitarias y cómo llevar una a cabo?, s. f.)
• La calidad final del código mejorará ya que, al estar realizando pruebas de
manera continua al finalizar el código será limpio y de calidad.
• Como las pruebas unitarias dividen el código en pequeños fragmentos, es
posible probar distintas partes del proyecto sin tener que esperar a que otras
estén completadas.
Para la elaboración de pruebas unitarias en Java, por ejemplo, se puede utilizar el
JUNIT y CACTUS.
10.4.1. Instalación de JUNIT
1. Se ingresa a la siguiente url a través de cualquier navegador
([Link]
4. No se necesita hacer ninguna otra modificación, se hace clic a Finish para concluir
la agregación del jUnit.
10.4.2. El proyecto
Creamos un proyecto que llamaremos «Prueba de Cobertura», agregamos un
paquete «example» y las librerías arriba mencionadas. Para finalizar, creamos una
clase «[Link]».
10.4.3. El código
El código que probaremos, lo estudiamos en un post anterior [LINK] donde
realizamos una prueba manual del algoritmo. El algoritmo corresponde al problema
de detectar el número mayor dados tres números.
Pasamos del algoritmo a código y tenemos:
10.4.4. Paquete de prueba
Ya que tenemos nuestra clase de prueba lista, procedemos a crear las clases de
prueba. Clic derecho sobre nuestro proyecto -> New -> Other… En Categoría
seleccionamos «Unit Tests» y en File Types, seleccionamos «Test for Existing
Class» y presionamos el botón siguiente.
A continuación, presionando el botón «browse…» buscamos y seleccionamos la
clase que testearemos («Miclase»), dejamos todas las opciones tal cuales están y
finalizamos presionando el botón «Terminar».
Se creará otra clase en la sección «Paquete de prueba» como se ve a continuación:
10.4.5. JUnit
Antes de realizar la prueba con JUnit, debemos estudiar un poco el código
generado. En la parte inferior de la clase MiClaseTest encontramos el siguiente
método:
Donde:
1) @Test: Los métodos marcados con esta anotación, le indican a JUnit que es
código que queremos que se ejecute. Son estos métodos donde se implementa el
código de pruebas.
2) Las variables del método que probaremos.
3) Instancia a muestra de prueba.
4) El resultado que esperamos obtener si la prueba tiene éxito.
5) La llamada al método de prueba, el resultado se almacena en otra variable.
6) assertEquals: Es uno de varios métodos con los que cuenta JUnit. Este método
en particular compara si dos objetos son iguales, de no ser así, lanzará una
excepción y la prueba se detiene.
7) fail: Este método hace que la prueba termine con fallo.
Como vimos en un post anterior [Caja blanca: Prueba del camino básico], este
algoritmo consta de cuatro caminos posibles, eso quiere decir que debemos
implementar cuatro métodos de prueba.
Tomando como base el método de prueba arriba mencionado, creamos cuatro
métodos con sus respectivos valores de entrada y valores esperados, es decir:
Para ejecutar el test con JUnit, damos clic derecho sobre la clase
[Link]
-> run file
Observamos que pasamos las cuatro pruebas con éxito.
Si tuviésemos algún error, este aparecería en color rojo.
Ejemplo
Realice una calculadora básica y haga todos los test de prueba necesarios para
tener una cobertura del 100 % del programa.
Creamos clases de los objetos que existan. En este caso creamos una clase
(figura 17) del objeto principal, un número.
Al tener la clase Número, procedemos a la creación de la clase Operación (figura 18)
donde vamos a colocar las operaciones en cada método.
Una vez creados los métodos, vamos a testearlos con ayuda de jUnit como se ve en las
figuras 19 y 20.
Compilaremos la clase OperacionTest para evaluar los métodos en la figura 21.
Autoevaluación
1. El testing permite:
A. La detección de errores con una gran posibilidad de recuperación del programa
por sí mismo.
B. Corregir los errores del sistema operativo, con la finalidad de evitar daños en el
sistema.
C. Verificación dinámica del comportamiento de un programa.
2. ¿Cuál es el orden correcto del proceso de testing?
A. Testing de unidad, Testing de módulos, Testing de subtemas, Testing de
sistema, Testing de aceptación.
B. Testing de subtemas, Testing de sistema, Testing de aceptación, Testing de
unidad, Testing de módulos.
C. Testing de unidad, Testing de subtemas, Testing de módulos, Testing de
sistema, Testing de aceptación.
3. ¿Cuál es la diferencia entre validación y verificación según Boehm?
A. La construcción del producto concreto y correcto.
B. Su duda de que construye.
C. El uso y la confirmación.
4. Las técnicas de inspección incluyen inspección de programas como:
A. Inspección de programas, análisis automatizado de código fuente y verificación
formal.
B. Inspección de programas, codificación y propuesta del programa.
C. Prototipo, verificación formal, diseño de arquitectura.
5. ¿Qué se obtendrá al realizar una prueba al programa introduciendo datos
válidos, inválidos e incongruentes?
A. Hay errores muy graves y lo más aconsejable es que regresemos a la fase 2
para analizar nuevamente el problema, y repetir todo el proceso.
B. La aplicación compila y lanza una excepción de tipo IOException al momento
de ingresar datos.
C. Hay errores muy graves en la codificación.
6. ¿Cuál es el proceso de depuración correcta?
A. Localiza y corrige errores, pruebas de regresión.
B. Pruebas de regresión y finaliza.
C. Localiza error, corrige, finaliza.
7. Los beneficios de las pruebas unitarias son:
A. Aumentan la legibilidad del código y ayudan a los desarrolladores a entender el
código base, lo que facilita hacer cambios más rápidamente.
B. La calidad final del código mejorará ya que, al estar realizando pruebas de
manera continua, al finalizar el código será limpio y de calidad.
C. No dañar el sistema operativo con algún error en el código.
8. ¿Para qué sirve la verificación y validación?
A. Procesos de comprobación y análisis que aseguran que el software que se
desarrolla cumple las necesidades.
B. Proceso de comprobación y análisis de una sola clase para conseguir errores
en el proyecto.
C. Proceso de análisis de un proyecto para ver su funcionalidad.
9. ¿Qué hace la verificación?
A. Comprueba que el sistema cumpla con los requerimientos funcionales y no
funcionales que se le especificó.
B. Comprueba que una sola clase funcione correctamente.
C. Comprueba que el sistema tenga los parámetros en todas las clases.
10. La opción que representa una técnica de inspección de software es:
A. Codificación del programa.
B. Análisis automatizado de código fuente.
C. Análisis de requerimientos.
Tema 11: Encapsulamiento - Tema 11:
Encapsulamiento
11.1. Definición
La encapsulación es un mecanismo que consiste en organizar datos y métodos de
una estructura, conciliando el modo en que el objeto se implementa, es decir,
evitando el acceso a datos por cualquier otro medio distinto a los especificados; por
lo tanto, la encapsulación garantiza la integridad de los datos que contiene un
objeto.
Encapsulamiento significa que los datos y las acciones se combinan en una sola
entidad (es decir, un objeto de la clase) y se ocultan los detalles de la
implementación. La programación orientada a objetos (POO) encapsula datos
(atributos) y métodos (comportamientos) en objetos. Los objetos tienen la propiedad
de ocultación de la información. Esta propiedad significa que, aunque los objetos
pueden conocer cómo comunicarse unos con otros a través de interfaces bien
definidas, no pueden conocer cómo están implementados otros objetos (los detalles
de la implementación están ocultos dentro de los propios objetos).
Esta es una situación muy frecuente del mundo real. Es posible conducir con
eficacia un automóvil sin conocer los detalles internos de cómo funciona el motor,
el tren de engranajes o el sistema de frenos. En Java se tiene un gran control sobre
el encapsulamiento de una clase y un objeto. Se consigue aplicando modificadores
a clases, variables de instancia y de clases, y métodos. Algunos de estos
modificadores se refieren al concepto de paquete, que se puede considerar
básicamente como un grupo de clases asociadas.
Modificadores
Se pueden realizar modificaciones de:
• Clases
• Variables
• Métodos
1. Modificadores de clase
Se puede cambiar la visibilidad de una clase utilizando una palabra reservada,
modificador, antes de la palabra reservada “class” en la definición de la clase. Por
ejemplo:
Una clase pública se define dentro de su propio archivo y es visible en cualquier
parte. Una clase que es local a un paquete específico no tiene modificador y se
puede definir dentro de un archivo que contiene otras clases. Como máximo, una
de las clases de un archivo puede ser una clase pública.
2. Modificadores de variables
La cantidad de encapsulamiento impuesta por una clase se establece a discreción
del programador. Puede permitirse el acceso completo a todo el interior dentro de
la clase o se pueden imponer diversos niveles de restricciones.
En particular, se puede controlar cuándo es el acceso a otra clase que tiene la
instancia y las variables de una clase. Se consigue esta acción utilizando un
modificador, palabra reservada, antes del tipo de la variable. Por ejemplo:
Normalmente es una buena idea imponer tanto encapsulamiento como sea posible.
En consecuencia, debe ocultarse todo lo que se pueda, excepto lo que se desea
hacer visible a otras clases, en cuyo caso se debe permitir la cantidad mínima de
visibilidad.
Lista de un modificador de variable o método
Nota
El modificador "Protected” es más débil que el uso de ningún modificador. No se
debe utilizar ningún modificador con preferencia a “protected”.
3. Modificadores de métodos
Se puede limitar también el acceso de otras clases a métodos. Esta acción se realiza
utilizando una palabra reservada (modificador), antes del tipo de retorno del método.
Los modificadores son los mismos que para las variables.
11.2. Clases internas
Java permite definir clases e interfaces dentro de otras clases e interfaces.
Una clase que no se anida dentro de otra se denomina clase de nivel superior.
Esto significa que prácticamente todas las clases utilizadas hasta ahora son clases
de nivel superior.
Las clases internas se definen dentro del ámbito de una clase externa (o de nivel
superior). Estas clases internas se pueden definir dentro de un bloque de
sentencias o (anónimamente) dentro de una expresión.
Ejemplos
La clase Empleado tiene dos clases internas que representan una dirección y un
sueldo. El código fuente de la clase Empleado y sus dos clases internas Dirección
y Sueldo son:
El resultado de la ejecución de esta aplicación es:
Las clases internas no se pueden utilizar fuera del ámbito. Por consiguiente, las
clases externas de la clase Empleado no pueden acceder a la clase interna (a
menos que sean subclases o estén en el mismo paquete, dependiendo de la
visibilidad en la definición de la clase).
En la práctica, la clase de nivel superior actúa como un paquete de objetos que
contiene cero o más clases internas. Esto es particularmente útil en desarrollo de
software orientado a objetos. Por otra parte, la capacidad para definir un código
determinado que se puede crear y pasar donde se necesite es muy importante.
Sintaxis: C posee punteros a funciones. El lenguaje Smalltalk utiliza objetos que
representan código (objetos bloque). Java posee clases internas.
Los tipos de clases internas son:
• Estáticas Internas
• Clase Interna Miembro
• Clases Internas de Método
• Clases Internas Anónimas
Estáticas Internas
Este tipo de clase interna tiene las siguientes normas:
• Se declaran igual que una clase normal, con la diferencia de que le
añadimos la palabra reservada "static" a la clase.
static class NombreClase{}
• Pueden acceder a métodos y variables de la clase "externa" directamente,
siempre que estos sean estáticos (incluidos privados).
• En caso de que los atributos o métodos no sean estáticos, podemos seguir
accediendo a ellos, pero deberemos de instanciar su clase (crear un objeto
de la clase).
• Para crear una instancia de la clase estática interna lo haremos de la
siguiente manera:
[Link] nombre=new
[Link]();
Clase Interna Miembro
La Clase Interna Miembro tiene las siguientes normas:
• Este tipo de clase es considerada un elemento más de la clase externa, con
lo cual puede tener libre acceso a métodos y atributos de su clase externa
(incluso privados).
• No podemos definir variables ni métodos estáticos dentro de este tipo de
clase, a no ser que sea alguna variable de tipo constante.
• Se declara como una clase normal.
class ClaseInterna{};
• Para acceder a métodos y atributos de esta clase interna desde la clase
externa deberemos de instanciar la clase Interna.
[Link] nombre=new
[Link]();
Clases Internas de Método
Para seguir el funcionamiento de este tipo de clase seguiremos las siguientes
normas:
• La clase solo podrá ser instanciada dentro del mismo método.
• Una clase interna de método puede acceder a las variables locales del
método SOLO si son FINAL, pero puede acceder con normalidad al resto de
variables de la clase.
• Los modificadores permitidos por este tipo de clase son "abstract" y "final".
Clases Internas Anónimas
Las reglas a seguir con este tipo de clases son:
• Siempre debe ser una subclase de otra clase ya existente o bien
implementar alguna interfaz.
ClasePadre cp=new ClasePadre(){ //Definición de la clase anónima...};
• La definición de la clase anónima se lleva en una linea de código; por lo tanto,
se debe añadir punto y coma ";" al final de la declaración.
• Solamente podrá acceder a los métodos de la clase que se hayan
heredado, sobrescrito o implementado.
• Es posible encontrarse una clase anónima como argumento de un método.
11.3. Paquetes
Los paquetes son una forma de organizar grupos de clases. Un paquete contiene
un conjunto de clases relacionadas bien por finalidad, por ámbito o por herencia.
Los paquetes resuelven el problema del conflicto entre los nombres de las clases.
Al crecer el número de clases crece la probabilidad de designar con el mismo
nombre a dos clases diferentes.
Las clases tienen ciertos privilegios de acceso a los miembros dato y a las funciones
miembro de otras clases dentro de un mismo paquete. En el Entorno Integrado de
Desarrollo (IDE) JBuilder de Borland, un proyecto nuevo se crea en un subdirectorio
que tiene el nombre del proyecto. A continuación, se crea la aplicación, un archivo
.java que contiene el código de una clase cuyo nombre es el mismo que el del
archivo. Se pueden agregar nuevas clases al proyecto, todas ellas contenidas en
archivos .java situadas en el mismo subdirectorio. La primera sentencia que
encontramos en el código fuente de las distintas clases que forman el proyecto
es package o el nombre del paquete.
La palabra reservada import
Para importar clases de un paquete se usa el comando import. Se puede importar
una clase individual import [Link]; o bien, se puede importar las clases
declaradas públicas de un paquete completo, utilizando un asterisco (*) para
reemplazar los nombres de clase individuales import [Link].*;
Para crear un objeto fuente de la clase Font podemos seguir dos alternativas
1. import [Link];
2. Font fuente=new Font("Monospaced", [Link], 36);
O bien, sin poner la sentencia import: [Link] fuente=new
[Link]("Monospaced", [Link], 36);
Normalmente, usaremos la primera alternativa, ya que es la más económica en
código, si tenemos que crear varias fuentes de texto.
Se pueden combinar ambas formas, por ejemplo, en la definición de la clase
BarTexto
import [Link].*;
public class BarTexto extends Panel implements [Link]{
//...
Panel es una clase que está en el paquete [Link], y Serializable es una interface
que está en el paquete [Link] (2).
Los paquetes estándar
Paquete Descripción
Contiene las clases necesarias para crear applets que se
[Link]
ejecutan en la ventana del navegador.
Contiene clases para crear una aplicación GUI independiente
[Link]
de la plataforma.
[Link] Entrada/Salida. Clases que definen distintos flujos de datos.
Contiene clases esenciales, se importa implícitamente sin
[Link]
necesidad de una sentencia import.
Se usa en combinación con las clases del paquete [Link]
[Link]
para leer y escribir datos en la red.
java. Útil Contiene otras clases útiles que ayudan al programador.
Figura 12. Paquetes java.
Fuente: Pablo Parra
Declaración de un paquete
Cada clase de Java pertenece a un paquete. La clase se añade al paquete cuando
se compila. Un paquete explícito se define por la palabra reservada “package” en el
comienzo del archivo en el que se definen una o más clases o interfaces. Para poner
una clase en un paquete específico se necesita añadir la siguiente línea como la
primera sentencia no comentario:
Los nombres de los paquetes deben ser únicos para asegurar que no haya
conflictos de nombres. Java impone un convenio de nombres por el que un
nombre de paquete se construye por un número de componentes separados por
un punto (separador). Estos componentes corresponden a la posición de los
archivos.
En el caso siguiente,
Los archivos del paquete están en un directorio llamado dibujos dentro de un
directorio llamado pruebas.
Por otra parte, si los archivos de un paquete específico están en un directorio
llamado concurso, dentro de un directorio llamado pruebas, el nombre del paquete
es:
Observe que esto supone que todos los archivos asociados con un único paquete
están en el mismo directorio. Cualquier número de archivos se puede convertir en
parte de un paquete, sin embargo, un archivo solo se puede especificar en un único
paquete.
Nota
Un paquete es una colección de clases relacionadas e interfaces que proporcionan
protección de acceso y gestión de espacio de nombres.
Autoevaluación
1. Un encapsulamiento es:
A. Un mecanismo que consiste en organizar datos y métodos de una estructura.
B. Un mecanismo que no consiste en organizar datos y métodos de una estructura.
C. Un mecanismo que no garantiza la integridad de los datos que contiene un objeto.
2. Un paquete es:
A. Una forma de organizar grupos de clases.
B. Una forma de desorganizar grupos de clases.
C. Código, memoria, entrada, salida.
3. [Link] contiene:
A. Clases para crear una aplicación GUI independiente de la plataforma.
B. Clases necesarias para crear applets que se ejecutan en la ventana del navegador.
C. Clases esenciales, se importan implícitamente sin necesidad de una sentencia
import.
4. [Link] contiene:
A. Clases para crear una aplicación GUI independiente de la plataforma.
B. Clases necesarias para crear applets que se ejecutan en la ventana del navegador.
C. Clases esenciales, se importan implícitamente sin necesidad de una sentencia
import.
5. [Link] contiene:
A. Clases para crear una aplicación GUI independiente de la plataforma.
B. Clases necesarias para crear applets que se ejecutan en la ventana del navegador.
C. Clases esenciales, se importan implícitamente sin necesidad de una sentencia
import.
6. [Link]:
A. Contiene clases útiles que ayudan al programador.
B. Se usa en combinación con las clases del paquete [Link] para leer y escribir datos
en la red.
C. Contiene clases para crear una aplicación GUI independiente de la plataforma.
7. [Link] contiene:
A. Clases para crear una aplicación GUI independiente de la plataforma.
B. Clases necesarias para crear applets que se ejecutan en la ventana del navegador.
C. Entrada/Salida. Clases que definen distintos flujos de datos.
8. [Link] contiene:
A. Clases útiles que ayudan al programador.
B. Clases para crear una aplicación GUI independiente de la plataforma.
C. Entrada/Salida. Clases que definen distintos flujos de datos.
9. Las variables de tipo referencia:
A. Se declaran como una clase normal.
B. La clase solo podrá ser instanciada dentro del mismo método.
C. Es posible encontrarse una clase anónima como argumento de un método.
10. Una norma de la clase interna anónima es:
A. Se declara como una clase normal.
B. La clase solo podrá ser instanciada dentro del mismo método.
C. Es posible encontrarse una clase anónima como argumento de un método.
Tema 12: Polimorfismo - Tema 12: Polimorfismo
12.1. Definición y ventajas
Definición
Polimorfismo quiere decir "un objeto y muchas formas". Esta propiedad permite que
un objeto presente diferentes comportamientos en función del contexto en que se
encuentre. Por ejemplo, un método puede presentar diferentes implementaciones
en función de los argumentos que recibe, recibir diferentes números de parámetros
para realizar una misma operación y realizar diferentes acciones dependiendo del
nivel de abstracción en que sea llamado.
Ventajas
• El polimorfismo permite a los programadores separar las cosas que cambian
de las que no cambian, y de esta manera hacer más fácil la ampliación, el
mantenimiento y la reutilización de los programas.
• El polimorfismo puede hacerse con referencias de superclases abstract,
superclases normales e interfaces.
• Por su mayor flexibilidad y por su independencia de la jerarquía de clases
estándar, las interfaces permiten ampliar muchísimo las posibilidades del
polimorfismo.
• El polimorfismo está basado en utilizar referencias de un tipo más “amplio”
que los objetos a los que apuntan.
Las ventajas del polimorfismo son evidentes, pero hay una importante limitación: el
tipo de la referencia (clase abstracta, clase base o interface) limita los métodos
que se pueden utilizar y las variables miembro a las que se pueden acceder.
Tipos de polimorfismo
• Polimorfismo de sobrecarga.
Ocurre cuando las funciones del mismo nombre existen, con función similar, en
clases que son completamente independientes unas de otras.
• Polimorfismo paramétrico
Capacidad para definir varias funciones utilizando el mismo nombre, pero usando
parámetros diferentes (nombre y/o tipo). El polimorfismo paramétrico selecciona
automáticamente el método correcto a aplicar en función del tipo de datos
pasados en el parámetro.
• Polimorfismo de subtipado
En realidad es el verdadero polimorfismo (o "polimorfismo puro"), donde las clases
derivadas redefinen los métodos y/o propiedades heredados mediante la
sobreescritura (override). Se resuelve en tiempo de ejecución.
12.2. Sobrecarga de métodos
Es una herramienta que nos ofrece la programación orientada a objetos, ya que nos
permite modelar y definir el comportamiento de los objetos como queramos, es
decir, de acuerdo con unos parámetros de entrada, ejecutar uno u otro método.
La sobrecarga de métodos consiste en poner varios métodos con el mismo nombre
en la misma clase, pero siempre que su lista de argumentos sea distinta, es
decir, no puede haber dos métodos que se llamen igual con la misma lista de
argumentos, aunque devuelvan datos de distinto tipo. El compilador sabría a cuál
de todas las sobrecargas nos referimos por los argumentos que se le pasen en la
llamada, pero no sería capaz de determinar cuál de ellas debe ejecutar si tienen la
misma lista de argumentos.
Un ejemplo de sobrecarga de métodos tenemos:
12.3. Sobrecarga escritura de métodos
La sobreescritura de métodos nos permite redefinir un método que heredamos para
que este funcione de acuerdo con nuestras necesidades y no a lo definido en la
superclase. Cuando en un objeto llamamos a un método, el compilador comprueba
si el método existe en nuestro objeto; si existe, lo usa y si no existe en nuestro
objeto, entonces lo busca en la superclase. Esto ocurre así hasta que el compilador
encuentra el método definido. El compilador busca el método de abajo a arriba.
12.3.1. Conceptos relacionados
Sobrecarga
• La sobrecarga representa diferentes maneras de realizar una misma acción.
• En los programas se usa el mismo nombre en diferentes métodos con
diferentes firmas [número, orden y tipo de los parámetros].
• El código de programación asociado a cada sobrecarga puede variar.
Ejemplo
• Contratar(“Juan”, “Ventas”, 2500);
• Contratar(“Juan”);
• Contratar(“Juan”, 2500);
Herencia
La herencia es el mecanismo de implementación mediante el cual elementos más
específicos incorporan la estructura y comportamiento de elementos más generales
(Rumbaugh 99).
Una clase se dice que hereda o extiende a otra clase antecesora. La palabra
reservada extends sirve para indicar que una clase extiende a otra. La clase que
extiende a otra hereda todos los atributos y métodos de la clase antecesora que no
sean privados (private). La clase antecesora puede extender a su vez otra clase.
• Sobreescritura
Los constructores son métodos invocados en el momento de la creación de
instancias. Como cualquier otro método, se puede sobrescribir en el momento de la
extensión. Si el constructor de una clase que extiende a otra necesita invocar a un
constructor de su clase antecesora, lo debe hacer antes de llamar a cualquier otro
método. Cuando se crea una instancia de una clase que extiende a otra, de alguna
manera se debe construir una instancia de la clase antecesora. Si no se especifica
lo contrario, al crear una instancia de una clase se invoca al constructor por defecto
de la clase antecesora, por lo que este constructor debe estar definido en la clase
que se extiende.
Ejemplo
Autoevaluación
1. ¿Cuál es un tipo de polimorfismo?
A. Sobrecarga.
B. Overflow.
C. Sobrecargado.
2. ¿Qué quiere decir polimorfismo?
A. Un objeto una sola forma.
B. Un objeto muchas formas.
C. Muchos objetos una forma.
3. ¿Qué polimorfismo es considerado como el verdadero polimorfismo?
A. Polimorfismo paramétrico.
B. Polimorfismo de sobrecarga.
C. Polimorfismo de subtipado.
4. ¿En qué lista de herramientas de la programación no puede haber dos
métodos que se llamen igual con la misma lista de argumentos?
A. Sobrecarga.
B. Sobreescritura.
C. Ninguna de las opciones.
5. El polimorfismo permite:
A. Separar las cosas que cambian de las que no cambian.
B. Separar las cosas que cambian.
C. Separar las cosas que no cambian.
6. Las limitantes del polimorfismo son:
A. Tipo de referencia, método y variables.
B. Método y variables simples.
C. Método y variables.
7. El polimorfismo universal se divide en:
A. Heredado.
B. Configuración y heredado.
C. Destordamiento.
8. El polimorfismo ad hoc se divide en:
A. Heredado.
B. Destordamiento.
C. Configuración y heredado.
9. ¿Cómo busca el compilador un método?
A. De abajo hacia arriba.
B. De arriba hacia abajo.
C. Desde la mitad.
10. ¿Qué método nos permite redefinir un método que heredamos para que
este funcione de acuerdo con nuestras necesidades y no a lo definido en la
superclase?
A. Sobreescritura.
B. Sobrecarga.
C. Las dos opciones.
Tema 13: Abstracción, interfaces de programac - Tema
13: Abstracción, interfaces de programac
13.1. ¿Qué es abstracción?
La abstracción radica en seleccionar una representación de datos de un conjunto
grande de los mismos, para mostrar solo los detalles relevantes del objeto. Esto
permite reducir el esfuerzo y la complejidad en la programación, lo cual se logra
usando clases e interfaces abstractas. Es uno de los conceptos más importantes de
la Programación Orientada Objetos (POO u OOP). ([Link], 2019).
Entre los principales conceptos están:
Clase abstracta
• Una clase abstracta es aquella que contiene métodos, pero al menos uno de
ellos debe ser abstracto, es decir, no tiene cuerpo y su implementación está
en otra clase. (Martin Sierra, 2008)
• No puede instanciar objetos.
• Se utiliza solo como superclases.
• Sirve para proporcionar una superclase apropiada a partir de la cual heredan
otras clases.
Método abstracto
Es un método sin cuerpo. El método abstracto nunca será definitivo porque la clase
abstracta debe implementar todos los métodos abstractos. (SlideShare, 2012).
13.1.1. Sintaxis de un método y una clase abstracta
Según Martin Sierra (2008), la sintaxis para la creación de un método y una clase
abstracta es la siguiente:
public abstract class nombreClase
public abstract tipo nombreMetodo (argumentos);
//otros método
}
13.1.2. Ventajas de la abstracción
Las ventajas que se tiene al utilizar la abstracción son:
• El usar una clase abstracta permite agrupar varias clases relacionadas.
• La abstracción ayuda a reducir la complejidad del diseño y del proceso de
implementación del software. ([Link], 2019).
• 13.1.3. Modelado de la abstracción
• A continuación, se modelará un ejemplo generalizado de las clases
abstractas como se referencia en la figura 2.
• Ejemplo
• Se requiere modelar con clases y métodos abstractos un ser vivo y sus
métodos serán lo más generales posibles, en este caso se desea modelar la
función de alimentación de los seres vivos.
En el siguiente ejercicio se requiere modelar una clase abstracta para ser vivo y su
método alimentarse().
Diagrama de clases del ejemplo
Se pude entender de mejor manera al traducirlo al lenguaje de Java con el IDE
NetBeans:
• Para crear un nuevo proyecto:
Dar clic en la pestaña File, como se referencia en la figura 4.
Elegir New Project…
Dar clic en el icono semejante a una taza que lleva por
etiqueta JavaApplication y nombrar el proyecto, dar clic en“finish” y ya se
puede trabajar en el código.
Figura 6. Creación de un proyecto en NetBeans.
Fuente: Pablo Parra
• Para crear una clase del ejemplo 1:
Figura 7. Creación de una clase en NetBeans.
Fuente: Pablo Parra
Crear una clase llamada SerVivo
Figura 8. Creación de una clase en NetBeans.
Fuente: Pablo Parra
Si se declara un método abstracto en una clase que no está declarada como
abstracta, se lanzará un error.
Figura 9. Error típico al trabajar con clases y métodos abstractos.
Fuente: Pablo Parra
Al definir la clase como abstracta con la palabra reservada abstract se quitará el
error.
Figura 10. Corrección del Error típico al trabajar con clases y métodos abstractos.
Fuente: Pablo Parra
Crear una nueva clase que heredará el método abstracto de la superclase
SerVivo
Figura 11. Creación de clase Planta.
Fuente: Pablo Parra
La clase se llamará Planta.
Figura 12. Nombramiento de la clase Planta.
Fuente: Pablo Parra
Se implementa el método alimentarse() heredado de la superclase SerVivo.
Figura 13. Implementación del método abstracto.
Fuente: Pablo Parra
En el warning , que aparece en la línea 12 donde se encuentra la
implementación del método abstracto, dice que se debe añadir @Override para
poder sobrescribir en el método del padre.
Figura 14. Aviso por sobre escritura en el método de la superclase desde una clase hija. Autoría
Propia.
Figura 15. Corrección de aviso por sobreescritura en el método de la superclase desde una clase
hija.
Fuente: Pablo Parra
De la misma forma se implementan los demás métodos abstractos de las clases
que hereden de la superclase.
Creamos una clase principal donde instanciaremos nuestros objetos:
Figura 16. Clase principal.
Fuente: Pablo Parra
Corrida
Figura 17. Salida de resultados.
13.2. ¿Qué es la interfaz?
La palabra “interfaz” puede referirse a múltiples cosas, en muchos ámbitos, incluso
en programación puede tener varios significados.
• En programación orientada a objetos (POO), interfaz o interfaces (también
llamada protocolo), podría hacer referencia al conjunto de métodos que tiene
un objeto para poder trabajar con este. Esos conjuntos de métodos
constituyen la interfaz del objeto en POO.
• También, interfaz podría hacer referencia a un API. Un API es una interfaz
de programación de aplicaciones (del inglés Application
Programming Interface - API), es el conjunto de funciones y procedimientos
(o métodos, en la programación orientada a objetos), que ofrece cierta
biblioteca para ser utilizado por otro software como una capa de abstracción.
Son usados generalmente en las bibliotecas.
• Interfaz podría hacer referencia también a la parte gráfica en la que se
programa. (Alegsa, 1998)
En nuestro caso, se considera el primer significado, el cual es un medio común para
que los objetos no relacionados se comuniquen entre sí. Estas son definiciones de
métodos y valores sobre los cuales los objetos están de acuerdo para cooperar.
13.2.1. Sintaxis de una interfaz
Según Martin Sierra (2008), la sintaxis para una interfaz se define mediante la
palabra interface, utilizando la siguiente sintaxis:
public interface nombreInterfaz
tipo nombreMetodo (argumentos);
//otros métodos
Ejemplo
public interface Operaciones
{
void rotar ( );
String serializar();
//otros métodos
Al crear una interfaz se debe tener en cuenta las siguientes consideraciones:
• Todos los métodos definidos en una interfaz, son públicos y abstractos.
• En una interfaz es posible definir constantes.
• Una interfaz no es una clase debido a que las interfaces tan solo pueden
contener constantes y métodos abstractos, no es posible crear objetos de
una interfaz.
13.2.2. Ventajas de la interfaz
Entre las principales ventajas de utilizar interfaz están:
• El principal beneficio de usar una clase abstracta es que permite agrupar
varias clases relacionadas.
• La abstracción ayuda a reducir la complejidad del diseño y el proceso de
implementación del software. ([Link], 2019)
• Obligar a que ciertas clases utilicen los mismos métodos (nombres y
parámetros).
• Establecer relaciones entre clases que no estén relacionadas.
• Al diseñar por interfaces se garantiza un mínimo de acoplamiento y un
máximo re-uso.
• El principal uso de las interfaces es que puedes SIMULAR la herencia
múltiple; recuerda que Java solo soporta herencia simple.(Scribd, 2019)
• 13.2.3. Modelado de la interfaz
• Ejemplo del modelado de interfaces (figura 18)
13.2.4. Ejercicio con una interfaz
Utilizando como IDE el Netbeans, realizar un programa en Java que permita
imprimir los datos del usuario para lo cual se utilizará una interface denominada
Impresión, como se muestra en el Diagrama de Clases UML, figura 19, además
utilizar POO.
Programación de la interface denominada Impresión
La Corrida del mencionado programa se referencia en la figura 26, en la cual se
puede ver los datos del cliente y del administrador.
13.2.5. Resumen de una interfaz
• La clase que implementa la interfaz necesita proporcionar funcionalidad para
los métodos declarados en la interfaz.
• Todos los métodos en una interfaz son implícitamente públicos y abstractos.
• Una interfaz no puede ser instanciada.
• Una referencia de interfaz puede señalar objetos de sus clases de
implementación.
• Una interfaz puede extenderse desde una o varias interfaces. Una clase
puede extender solo una clase, pero implementar cualquier cantidad de
interfaces. ([Link], 2019)
Diferencias entre una interfaz y una clase abstracta
Tabla 1
Diferencias entre interfaz y clase abstracta
Interface Clase abstracta
Las interfaces soportan herencia
Las clases abstractas no soportan herencia múltiple.
múltiple.
La interfaz no contiene atributos. La clase abstracta contiene atributos.
La interfaz no contiene constructores. La clase abstracta contiene constructores.
Una interfaz contiene solo miembros Una clase abstracta contiene miembros incompletos y
incompletos. completos.
Una interfaz no puede tener
Una clase abstracta puede contener modificadores de acceso
modificadores de acceso por defecto,
para las funciones, propiedades, etc.
todo es asumido como público (public).
Los miembros de una interfaz no pueden Solo el miembro completo de la clase abstracta puede ser
ser estáticos (Static). estático.
Autoevaluación
1. ¿Qué es abstracción?
A. Es una representación de datos de un conjunto grande de los mismos, para
mostrar solo los detalles relevantes del objeto.
B. Es un sitio web que ofrece a los usuarios la posibilidad de subir y compartir en
público o en privado solo presentaciones de diapositivas.
C. En programación orientada a objetos (POO), interfaz o interfaces (también
llamada protocolo) podría hacer referencia al conjunto de métodos que tiene un
objeto para poder trabajar con este. Esos conjuntos de métodos constituyen la
interfaz del objeto en POO.
2. ¿Cuál es la palabra reservada que se utiliza para las interfaces?
A. Interfaz.
B. Interface.
A. Abstract.
3. ¿Cuál es la línea que permite identificar qué es una abstracción?
A. Línea de herencia continua.
B. Línea de herencia entrecortada.
C. Línea de herencia doble.
4. ¿Cuál es la línea que permite identificar que está utilizando interfaces?
A. Línea de herencia entrecortada.
B. Línea de herencia continua.
C. Línea de herencia doble.
5. ¿Qué es una interfaz?
A. Es una representación de datos de un conjunto grande de los mismos, para
mostrar solo los detalles relevantes del objeto.
B. Es un sitio web que ofrece a los usuarios la posibilidad de subir y compartir en
público o en privado solo presentaciones de diapositivas.
C. En programación orientada a objetos (POO), podría hacer referencia al
conjunto de métodos que tiene un objeto para poder trabajar con este.
Tema 14: Componentes y objetos gráficos - Tema 14:
Componentes y objetos gráficos
14.1. Widgets (componentes gráficos)
Un widget es una pequeña aplicación o programa, usualmente presentado en
archivos o ficheros pequeños, que es ejecutada por un motor de widgets o Widget
Engine. Entre sus objetivos están los de dar fácil acceso a funciones
frecuentemente usadas y proveer de información visual.
¿Qué es un Widget de Java?
Java es un lenguaje de programación orientado a objetos que utilizan los
programadores para desarrollar aplicaciones para entornos web y de escritorio. En
el desarrollo de aplicaciones de escritorio, el programador necesita a menudo para
proporcionar una interfaz gráfica de usuario (GUI) con la que puede interactuar el
usuario. Esto significa la creación de ventanas que contienen diferentes campos,
botones y funciones. Esto también significa que el programador tendrá que crear
"widgets" o un pequeño elemento funcional dentro de una ventana de la GUI de
Java.
En la programación Java, un "widget" representa una pieza funcional de una
interfaz gráfica de usuario con la que puede interactuar un usuario. Esto puede
tomar la forma de un botón o un campo de texto.
Bibliotecas Widget
Dos bibliotecas comunes son la biblioteca Swing y la biblioteca SWT. La librería
Swing se origina a partir de Sun, el desarrollador que creó Java, como un
constructor de interfaz gráfica de usuario funcional completa con una biblioteca de
widgets.
El Standard Widget Toolkit (SWT) se origina a partir de un toolkit, anteriormente
proporcionado con Java y permite los programas de acceso a las funciones de
programación de interfaz gráfica de usuario de bajo nivel. Esto significa que los
programadores pueden ampliar la funcionalidad de aspecto "natural" del sistema
operativo host para aplicaciones de aspecto más integradas.
AWT y Swing: ¿Cuáles son sus características básicas?
• Los componentes de Swing tienen nombres que comienzan con J. Ejemplo
de ello es Button en AWT y JButton en Swing.
• Los componentes de AWT están en el paquete awt, mientras que los de
Swing los encontramos en [Link].
¿Qué es una clase de contenedor?
Las clases de contenedor son clases que pueden tener otros
componentes. Entonces, para crear una GUI, se necesita al menos un objeto
contenedor. Hay tres tipos de contenedores.
• Panel. Es un contenedor puro y no es una ventana en sí misma. El único
propósito de un panel es organizar los componentes en una ventana.
• Marco. Es una ventana en pleno funcionamiento con su título e iconos.
• Diálogo. Se puede considerar como una ventana emergente que aparece
cuando se debe mostrar un mensaje. No es una ventana completamente
funcional como el marco.
Componentes gráficos principales en Java
• JButton
La función de un JButton es crear un botón visible dentro de una aplicación de
interfaz gráfica de usuario en Java. Usando métodos adicionales dentro del lenguaje
Java, los programas también pueden proporcionar respuestas detalladas a la
interacción del usuario con un JButton.
Los programas de Java pueden crear JButtons y añadirlos a elementos visibles de
la interfaz de usuario como JFrames y JPanels. A menudo las declaraciones de
clase para GUIs de Java declaran elementos JButton como variables de instancia
antes del método constructor de la clase, como se muestra a continuación.
private JButton pressButton;
A continuación, esta clase de Java puede instanciar la variable JButton dentro del
método constructor como se muestra a continuación:
pressButton = new JButton("Press Me");
El parámetro constructor determina el texto que aparecerá en el botón visible, por
lo que debe ajustarse al propósito del botón dentro de la aplicación.
Métodos del JButton
• void setText (String s); Se utiliza para configurar el texto especificado en el
botón.
• String getText (); Se utiliza para devolver el texto del botón.
• void setEnabled (boolean b); Se utiliza para habilitar o deshabilitar el botón.
• void setIcon (Icon b); Se utiliza para establecer el icono especificado en el
botón.
JTextField
Un JTextField o campo de texto es un componente Java de la clase swing que crea
un input utilizado para la captura de datos. A continuación, se muestra cómo se crea
un JTextField:
JTextField campo=new JTextField ();
Esto crea un campo de texto vacío.
JTextField campo=new JTextField(String texto);
Esta instrucción crea un campo de texto con un texto especificado.
JTextField campo=new JTextField(String texto, int columnas);
Esto nos crea un campo de texto con un texto especificado y el número de
columnas.
Métodos del JTextField
• void setText (String s); Se utiliza para configurar el texto especificado en el
campo de texto.
• String getText (); Se utiliza para devolver el texto del campo de texto.
• void setEnabled (boolean b); Se utiliza para habilitar o deshabilitar el
campo de texto.
• Void setEditable (boolean b); Inicializar el campo de texto para no permitir
cambios por parte del usuario.
JLabel
Un Label o etiqueta puede mostrar texto plano, una imagen o una imagen con un
texto. A continuación, se muestra cómo se crea un JLabel.
JLabel etiqueta=new JLabel();
Esto crea una etiqueta vacía, sin texto y sin imagen.
JLabel etiqueta=new JLabel(Icon imagen);
Esto crea una etiqueta con una imagen especificada.
JLabel etiqueta=new JLabel(String texto);
Esto crea una etiqueta con un texto especificado.
JLabel etiqueta=new JLabel(String texto, Icon imagen, int alineacion);
Esto crea una etiqueta con imagen y texto especificado con una alineación
horizontal.
Métodos del JLabel
• void setText (String s); Se utiliza para configurar el texto especificado en el
campo de texto.
• String getText (); Se utiliza para devolver el texto del campo de texto.
• void setEnabled (boolean b); Se utiliza para habilitar o deshabilitar el
campo de texto.
• Void setIcon (Icon i); Define qué icono mostrará el componente.
JPanel
Entenderemos un JPanel como un contenedor en el cual podemos colocar y
acomodar elementos (dentro del panel) y así luego se nos facilitará mover todos
esos componentes relacionados de un lugar a otro solo moviendo el JPanel y no
cada uno de los elementos que este contiene.
JPanel panel=new JPanel();
Esto nos crea un panel vacío.
Métodos del JPanel
• String paramString (); Devuelve una representación de cadena de este
JPanel.
• void updateUI (); Restablece la propiedad UI con un valor del aspecto actual.
• add (Component c); Agrega un componente a un contenedor especificado.
• 14.2. Formularios
• Para ejemplo, se creará un formulario en Java en el IDE NetBeans. Para
crearlo, se seleccionará un paquete creado anteriormente, y luego se
dará clic derecho sobre este; aparecerá un menú flotante en el cual se
ubicará el puntero del mouse sobre New, inmediatamente aparecerá una
serie de elementos de los cuales se dará clic sobre "JFrame Form":
Aparecerá la ventana "New JFrame Form". En la parte de "Class Name" se colocará el
nombre para el formulario (aquí se tendrá en cuenta las mismas recomendaciones que
cuando le damos un nombre a un proyecto); en este caso se llamará Formulario1 y se
dará clic en el botón "Finish".
Ahora se podrá ver en la ventana "Projects" que el formulario fue creado; sin embargo, se
puede observar que automáticamente el IDE Netbeans abre el formulario creado en la
ventana central.
Al lado izquierdo de la ventana de Netbeans, hay una paleta de elementos (cajas de
texto, etiquetas, botones, etc.…), los cuales se usarán para darle forma al formulario
Ahora se podrá dar clic sobre el elemento que se quiera colocar en el formulario, y
luego dirigir el puntero del mouse al JFrame o formulario, y dar clic en este en la
parte donde se desea colocar el elemento.
Luego de colocar todos los elementos que se dese al formulario, se tendrá algo como lo
siguiente:
Para poder cambiar el tamaño de un elemento, solo basta con seleccionarlo y ubicarse en
uno de sus extremos (superior, inferior, derecho, izquierdo o esquinas) y se verá que el
puntero del mouse toma una forma de flecha doble (bidireccional), en ese momento, se
dará clic sostenido y se arrastrará el mouse hacia la dirección correcta y hasta la
distancia requerida.
Ahora se cambiará el texto a los elementos; en este caso se seleccionará el
elemento y luego se dará clic derecho sobre este; se desplegará un menú flotante
en el cual se dará clic sobre "Edit Text", y luego se escribirá el texto que se desea.
Luego de modificar cada uno de los elementos del formulario, se obtendrá el siguiente
resultado:
Ejemplo de aplicación
Para el siguiente ejemplo, se solicita crear tres formularios: el primer formulario
tendrá dos botones, cada botón permitirá moverse entre los distintos formularios
que se crearán; el segundo formulario permitirá ingresar un estudiante, en donde se
pedirá el número de cédula del estudiante, sus nombres, apellidos y en qué curso
se encuentra; en el tercer formulario, se deberá ingresar el número de cédula de un
estudiante y, por medio de un botón, se desplegará toda la información almacenada
de dicho estudiante.
El problema será resuelto en el IDE NetBeans para Java y se seguirán los siguientes
pasos:
1. Previa a la creación del nuevo proyecto, se creará un nuevo paquete con el
nombre “Ventanas”, y en este se crearán los formularios con los siguientes
nombres:
2. En el formulario “Principal”, colocaremos dos botones que nos servirán para
acceder a los otros formularios, se les asignará el nombre de Ingreso y Consulta.
Figura 18. Botones agregados en el formulario “Principal”.
Fuente: Pablo Parra
3. Ahora en el formulario “IngresoEstudiante”, colocaremos etiquetas (JLabel),
campos de texto (JTextField), una lista desplegable (JComboBox) y dos botones,
y luego los editaremos de la siguiente manera:
Figura 19. Formulario “IngresoEstudiante” personalizado.
Fuente: Pablo Parra
4. Para modificar el contenido de la lista desplegable, haremos clic derecho encima
y seleccionaremos propiedades, y en el campo “model” colocaremos el contenido
que deseemos que despliegue, como se muestra a continuación.
Figura 20. Modificando el contenido de un JComboBox.
Fuente: Pablo Parra
5. En el formulario “Consulta”, agregaremos etiquetas, campos de texto y tres
botones como se muestra a continuación:
Figura 21. Formulario “Consulta” personalizado.
Fuente: Pablo Parra
6. Una vez creados los formularios, crearemos una clase llamada Estudiante en el
paquete principal. En esta clase, crearemos el objeto de tipo Estudiante que
quedará de la siguiente manera (se recuerda al estudiante agregar los
métodos getter y setter).
Figura 22. Clase Estudiante.
Fuente: Pablo Parra
7. Para este ejemplo, se creará una clase llamada Alumnado que tendrá las
siguientes características:
Figura 23. Clase Alumnado.
Fuente: Pablo Parra
Figura 24. Métodos de la clase Alumnado.
Fuente: Pablo Parra
En la figura 23 se puede apreciar que se aplicó el patrón Singleton, este patrón de
diseño se encarga de que una clase determinada únicamente pueda tener un único
objeto. El objetivo de este patrón es el de garantizar que una clase solo tenga una
instancia (o ejemplar) y proporcionar un punto de acceso global a ella. En este
ejemplo se ha hecho uso de este patrón ya que el formulario IngresoEstudiante y el
formulario Consulta deben conocer a la clase Alumnado que contiene a los
estudiantes.
8. En el formulario principal, agregaremos las siguientes líneas de código:
Figura 25. Formulario principal.
Fuente: Pablo Parra
Figura 26. Métodos del formulario principal.
Fuente: Pablo Parra
9. En el formulario IngresoEstudiante, agregaremos las siguientes líneas de
código:
Figura 27. Formulario IngresoEstudiante.
Fuente: Pablo Parra
Figura 28. Métodos del formulario IngresoEstudiante.
Fuente: Pablo Parra
10. En el formulario Consulta, agregaremos las siguientes líneas de código:
Figura 29. Formulario Consulta.
Fuente: Pablo Parra
Figura 30. Métodos del formulario Consulta.
Fuente: Pablo Parra
A continuación, se muestra la ejecución del programa con sus respectivos
formularios:
14.3. Menús y tablas
Menús
Java ofrece varias clases para poner menús en una ventana.
• JMenuBar
• JMenu
• JMenuItem
• JCheckBoxMenuItem
• JRadioButtonMenuItem
Un JFrame o JApplet puede guardar una barra de menú donde se cuelgan menús
desplegables.
Los menús tienen elementos de menú que puede seleccionar el usuario.
Las barras de menús se pueden contemplar como una estructura que soporta
menús.
Una ventana (frame) solo puede tener una barra de menús (objeto MenuBar), que se
sitúa en la parte de arriba del mismo.
Los submenús son botones JMenu.
Los elementos de los menús son botones JMenuItem.
Cuando se activa un menú se despliegan automáticamente las opciones del menú.
Ejemplo
Código
Tablas
Las tablas son uno de los elementos más utilizados dentro de cualquier lenguaje de
programación, se las puede utilizar para desplegar una lista de datos similares
ordenados de una forma específica. Su uso es amplio pues es bastante versátil.
Tablas en Java
Dentro de Java tenemos el componente gráfico conocido como jTable. Es una clase
que permite organizar una determinada información en tabla, esta difiere de una
base de datos normal porque al utilizar jTable se puede visualizar esta tabla,
brindándole al usuario organización de información, oportunidades de editar y
cambiar el tamaño de las columnas entre otras.
• Inicios
En principio se creó la clase Jtable para constituir una interfaz ligada a bases de
datos a través de "Java Database Connectivity" (JDBC), y así evita la complejidad
que existía para el manejo de datos, dando así al programador mucha más facilidad
a la hora de trabajar con este tipo de información.
• Edición
La tabla se puede manipular en modo diseño, se especifican los nombres y cantidad
de columnas haciendo clic derecho sobre la tabla y accediendo a “Contenidos de
tabla (Table Contents)”. Para el combo agregamos “Sí” y “No” en modo diseño
desde la propiedad del objeto y en el apartado “model”.
• Implementación
Una de las formas más fáciles de utilizar un jTable es la siguiente manera,
instanciar como modelo de datos un DefaultTableModel y luego
un JTable, pasándole el modelo en el constructor.
14.4. Gestión de eventos
Cuando el usuario de un programa o applet mueve el ratón o hace un clic o usa el
teclado, genera un Evento. En Java los eventos, como cualquier otra cosa, se
representan como instancias u objetos de alguna clase. Para programar una
interfaz gráfica es necesario aprender a utilizar los eventos.
Cuando el usuario interactúa sobre los diversos componentes del awt, estos
generan eventos. La siguiente tabla muestra los eventos que cada tipo de
componente puede generar y cuándo los genera.
Tabla 1
Componentes de interacción
Tipo de componente Evento generado Hechos que lo generan
Button ActionEvent El usuario hace un clic sobre el botón.
El usuario selecciona o deselecciona el
Checkbox ItemEvent
interruptor (Checkbox).
El usuario selecciona o deselecciona el
CheckboxMenultem ItemEvent
interruptor (Checkbox).
El usuario selecciona o deselecciona un
Choice ItemEvent
elemento de la lista.
El componente se mueve, cambia de
ComponentEvent tamaño, se esconde o se exhibe.
El componente gana o pierde el foco.
Focus Event
Component
El usuario pulsa o suelta una tecla.
KeyEvent
El usuario pulsa o suelta un botón del
ratón, el cursor del ratón entra o sale o
MouseEvent
el usuario mueve o arrastra el ratón.
Se agrega o se quita un componente al
Container ContainerEvent
contenedor.
ActionEvent El usuario hace doble clic en un
elemento de la lista.
List
El usuario selecciona o deselecciona un
ItemEvent elemento de la lista.
El usuario selecciona un elemento del
Menultem ActionEvent
menú.
El usuario mueve la barra de
Scrollbar AdjustmentEvent
desplazamiento.
TextComponent TextEvent El usuario hace un cambio en el texto.
El usuario termina de editar el texto
TextField ActionEvent
(hace un intro).
La ventana se abre, se cierra, se
Window WindowEvent
minimiza, se reestablece o se cierra.
Fuente: [Link]
Para facilitar la tarea del programador se ha creado una serie de interfaces que
deben implementarse cuando se quieren procesar algunos de estos eventos. La
siguiente tabla presenta estas interfaces con sus métodos.
Tabla 2
Interfaces
Interfaz Métodos
ActionListener actionPerformed(ActionEvent)
AdjustmentListener adjustmentValueChanged(AdjustementEvent)
componentHidden(ComponentEvent)
componentMoved(ComponentEvent)
ComponentListener
componentResized(ComponentEvent)
componentShown(ComponentEvent)
componentAdded(ContainerEvent)
ContainerListener
componentRemoved(ContainerEvent)
focusGained(FocusEvent)
FocusListener
focusLost(FocusEvent)
ItemListener itemStateChanged(ItemEvent)
keyPressed(KeyEvent)
KeyListener keyReleased(KeyEvent)
keyTyped(KeyEvent)
mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
MouseListener mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
mouseDragged(MouseEvent)
MouseMotionListener
mouseMoved(MouseEvent)
TextListener textValueChanged(TextEvent)
windowActivated(WindowEvent)
WindowListener windowClosed(WindowEvent)
windowClosing(WindowEvent)
windowDeactivated(WindowEvent)
windowDeiconified(WindowEvent)
windowIconified(WindowEvent)
windowOpened(WindowEvent)
Autoevaluación
1. Uno de los objetivos de los Widgets es:
A. Dar formato a los componentes gráficos.
B. Proveer de información visual.
C. Facilitar el acceso a métodos.
2. Una característica de la biblioteca Swing es:
A. Está en el paquete [Link].
B. Tiene nombres que comienzan con JS.
C. Tiene nombres que comienzan con J.
3. Una clase contenedor es:
A. Tabla.
B. Panel.
C. Campo de texto.
4. ¿Qué método permite habilitar o deshabilitar un JTextField?
A. setEnabled (boolean b);
B. setEditable (boolean b);
C. setEdit (boolean b);
5. Una de las funciones del JPanel es:
A. Colocar tablas para bases de datos.
B. Mostrar y recibir texto en el menú.
C. Poder colocar y acomodar elementos.
6. Una opción de menú que ofrece Java es:
A. JMenuItem.
B. JItem.
C. JItemMenu.
7. El JTable se creó inicialmente para:
A. Manipular en modo diseño.
B. Constituir una interfaz ligada a bases.
C. Un objeto más complejo es conformado por objetos más pequeños
8. ¿Cuál de los siguientes elementos pertenece a los componentes de
interacción?
A. List.
B. JTable.
C. JFrame.
9. ¿Qué es un submenú dentro de Java?
A. Menús de nivel inferior.
B. Botones de JTable.
C. Son botones JMenu.
10. El componente Choice, ¿qué evento genera?
A. ComponentEvent.
B. ItemEvent.
C. MouseEvent.