Subido por Ricardo Triviño

ejemplo Televisor

Anuncio
Creación de un Televisor estándar
Paso 1: Operativa
Crear la Clase Televisor
En este caso, como esta Clase no va a contener ningún método main(...)
•
•
elegimos New | Java Class... en vez de New | Java Main Class...
y le damos el nombre Televisor
Este es el código creado por el asistente
package paqtvestandar;
public class Televisor {
}
•
package paqtvestandar;
o de momento, saber que esta primera línea es necesaria para indicar que la Clase Televisor
pertenece al paquete paqtvestandar
Añadir código a la Clase Televisor
A esta Clase le vamos a añadir una variable de instancia llamada canal que va a ser de tipo int
package paqtvestandar;
public class Televisor {
int canal;
}
•
int canal;
o los datos o variables definidos en una clase se llaman variables de instancia o atributos , porque
cada Instancia de una Clase, es decir, cada Objeto de la Clase contiene una copia propia de estas
variables
o imaginemos una fábrica de coches que tiene un molde de un modelo de coche (una Clase) del
cual se fabrican nuevos coches (Instancias u Objetos).
un ejemplo de variables de instancia o atributos de estos coches que están en circulación
(Objetos) podrían ser
la matrícula
el número de bastidor
el color
o en nuestro caso, tenemos que añadir esta línea para indicar que la Clase Televisor tiene una
variable de instancia de tipo int que se llama canal
Compilar la Clase Televisor
Ahora vamos a crear la Clase Aplicacion.
1
Nota: Recordemos que esta Clase al tener un método main(...) la vamos a tener que crear eligiendo New | Java
Main Class...
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
// TODO code application logic here
}
}
•
public static void main(String[] args) {
o de momento, saber que este es el método que invoca la MVJ (Máquina Virtual Java) cuando
tiene que ejecutar esta o cualquier otra aplicación
o más adelante ya veremos las palabras reservadas que rodean a este método
Añadir código a la clase Aplicación
Para que la clase Aplicación pueda interactuar con un Objeto de tipo Televisor vamos a añadir el siguiente
código al método main(...)
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
tv.canal = 3;
System.out.println("El canal seleccionado es el: " + tv.canal);
}
}
•
•
•
•
Televisor tv;
o en este punto estamos declarando una variable de referencia tv. De momento su valor es null ya
que todavía no apunta a ninguna Instancia u Objeto
tv = new Televisor();
o el operador new nos indica que se acaba de crear un nuevo Objeto, que es una Instancia de la
Clase Televisor
o ahora la variable de referencia tv contiene la dirección de memoria de dicha Instancia
tv.canal = 3;
o como ya tenemos una referencia al Objeto de tipo Televisor, ahora hablamos con su variable de
instancia canal y le decimos que le pasamos el valor 3
System.out.println("El canal seleccionado es el: " + tv.canal);
o mostramos un mensaje indicando el número del canal del Objeto de tipo Televisor
Ahora compilamos las Clases Aplicacion y seguidamente la ejecutamos.
2
Paso 2: Funcionalidad
En este paso vamos a hacer que el usuario pueda subir y bajar el canal del televisor
•
tipoValorDevuelto
o especifica el tipo de dato devuelto por el método
o será void, si el método no devuelve ningún valor
o los métodos que devuelven un valor utilizan la sentencia return
A la Clase Televisor le vamos a añadir los métodos subirCanal(), bajarCanal() y getCanal()
package paqtvestandar;
public class Televisor {
int canal;
public void subirCanal() {
canal = canal + 1;
}
public void bajarCanal() {
canal = canal - 1;
}
public int getCanal() {
return canal;
}
}
•
•
public void subirCanal() {
o como este método no devolverá ningún valor , utilizamos la palabra reservada void.
o canal = canal + 1;
incrementamos en una unidad la variable de instancia o atributo canal.
public int getCanal() {
o delante del método getCanal() indicamos que este método retornará un dato de tipo int.
o return canal;
utilizamos la palabra clave return para indicar que queremos que este método retorne un
dato.
en este caso retorna el valor de la variable de instancia canal
Y seguidamente compilamos la Clase Televisor
Invocar métodos
Para invocar los métodos de un Objeto, tenemos que tener primeramente una referencia a ese objeto y después
escribir un punto "." y finalmente el nombre del método que queremos llamar.
3
•
Nota: A pesar de que a priori puede parecer más rápido y cómodo copiar y pegar el código de ejemplo
que estamos siguiendo, te sugiero que lo escribas tú mismo en el entorno de NetBeans para que te vayas
familiarizando con éste
o verás que cuando por ejemplo escribes tv. entonces te aparece un menú emergente indicándote
cuales son los métodos que puedes invocar y el tipo de datos que retorna
o si por ejemplo has escrito tv.su y quieres que también te aparezca el menú emergente, entonces
lo que tienes que hacer es presionar la tecla Control y seguidamente la barra espaciadora
o trabajando de esta forma consigues no cometer fallos de escritura, porque es el mismo entorno
quien va escribiendo el código
Y aquí está el código que tenemos que añadir a la Clase Aplicacion
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
tv.canal = 3;
System.out.println("El canal seleccionado es el: " + tv.canal);
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
}
}
•
•
tv.subirCanal();
o como tenemos la variable de referencia tv que apunta a una instancia de tipo Televisor, entonces
nos es posible hablar con el método subirCanal()
System.out.println("El canal seleccionado es el: " + tv.getCanal());
o el método getCanal() nos devuelve un int indicándonos el número actual del canal del televisor
Paso 3: Funcionalidad
En este paso vamos a crear dos televisores utilizando diferentes Constructores y vamos a subir y bajar el canal
de los mencionados televisores.
Polimorfismo a través de la sobrecarga de método
Los Constructores se declaran de la siguiente forma
nombreDelConstructor(listaDeParámetros){
cuerpoDelConstructor
}
•
tipoValorDevuelto
4
un Constructor no devuelve ningún valor, ni siquiera void
de hecho lo que devuelve es la posición de memoria dónde residirá el objeto que se acaba de
crear
pero esta posición de memoria a nivel numérico interno (@FF00AC) está destinada a la
MVJ y no al desarrollador
de esta forma Java consigue que sus aplicaciones sean más fiables y seguras ya
que no nos permite posicionarnos en una dirección de memoria directamente, si
no que nos obliga a acceder a estas direcciones a través de las variables de
referencia devueltas por los Constructores al crear un objeto
nombreDelConstructor
o el nombre del Constructor es el mismo que el nombre de la Clase. Y siguiendo la convención de
nombres en Java, este nombre tendría que tener
la primera letra de la primera palabra compuesta en mayúsculas
la primera letra de la segunda y restantes palabras compuesta en mayúsculas
o por tanto una forma rápida de distinguir un Constructor de un método es por la primera letra de
la palabra compuesta
como ya sabemos en el Constructor esta letra será mayúscula y en el caso de un método
será minúscula
o
o
•
Nota: para ver también la convención de nombres en los Constructores podemos seguir el vínculo Apéndice Convenciones Java y UML - Mayúsculas y minúsculas (II)
A la Clase Televisor le vamos a añadir un Constructor sobrecargado
package paqtvestandar;
public class Televisor {
int canal;
public Televisor(int valorCanal) {
canal = valorCanal;
}
public void subirCanal() {
canal = canal + 1;
}
public void bajarCanal() {
canal = canal - 1;
}
public int getCanal() {
return canal;
}
}
Como podemos observar después de haber añadido el Constructor sobrecargado en la Clase Televisor, aparece
el siguiente error en la Clase Aplicacion
5
•
cannot find symbol
symbol: constructor Televisor()
location: class paqtvestandar.Televisor
Create Constructor Televisor() in paqtvestandar.Televisor
Este error nos indica que no encuentra el Constructor sin argumentos de la Clase Televisor. Por tanto, nos
vemos obligados a escribir el Constructor de la Clase Televisor aunque no tenga contenido
•
•
•
si la Clase sólo tiene un Constructor sin argumentos y no queremos implementarlo
o no hace falta que éste esté escrito dentro de la Clase
si por el contrario, la Clase tiene al menos un Constructor sobrecargado como por ejemplo Televisor(int
valorCanal) y no queremos implementar el Constructor sin argumentos
o el Constructor sin argumentos tiene que estar escrito dentro de la Clase aunque no tenga
contenido
en cualquiera de los dos casos anteriores, el runtime de Java se crea una especie de
Constructor interno que se denomina Constructor por defecto que utiliza para inicializar
todas las variables de instancia de la Clase con unos valores por defecto
dependiendo del tipo de datos de las variables de instancia, el Constructor interno
asignará unos valores por defecto u otros:
tipos de datos primitivos
son aquellos datos que no son Clases, como por ejemplo boolean,
int, float, double...
sus valores por defecto son
boolean: false
int: 0
float: 0.0
double: 0.0
tipos de datos no primitivos
son todas las Clases
el valor por defecto de las Clases es null
es decir, son variables de referencia que todavía no apuntan
a ningún objeto
public Televisor(int valorCanal) {
o a este Contructor le hemos añadido un argumento
o el hecho de que ahora tengamos dos Constructores con una signatura diferente (uno sin
argumento y el otro con un argumento de tipo int) se le llama Sobrecarga de método y es una de
las tres formas de implementar el Polimorfismo
o canal = valorCanal;
la variable de instancia canal se actualizará cuando se crea una instancia de la Clase
Televisor
Ahora añadimos un Constructor sin argumentos a la Clase Televisor
6
package paqtvestandar;
public class Televisor {
int canal;
public Televisor() {}
public Televisor(int valorCanal) {
canal = valorCanal;
}
public void subirCanal() {
canal = canal + 1;
}
public void bajarCanal() {
canal = canal - 1;
}
public int getCanal() {
return canal;
}
}
Crear dos objetos del mismo tipo de forma diferente
En ocasiones nos puede convenir inicializar una o más variables de instancia de un objeto en el mismo
momento que lo estamos creando
•
si es esto lo que queremos, lo que haremos será pasar los valores de las variables de instancia que
queremos inicializar como argumento de un Constructor
Ahora vamos a crear dos objetos de tipo Televisor. Uno de ellos estará referenciado por una variable de
referencia llamada tv y el otro por otra variable de referencia llamada televisor
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
System.out.println("El canal por defecto es el: " + tv.canal);
tv.canal = 3;
System.out.println("El canal seleccionado es el: " + tv.canal);
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
7
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor televisor = new Televisor(6);
System.out.println("El canal del segundo televisor es el: "
+
televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
}
}
•
•
•
•
System.out.println("El canal por defecto es el: " + tv.canal);
o en este caso el mensaje de salida nos dirá que el número de canal es el 0, porque ha sido por un
lado declarado por nosotros como variable de instancia y por el Constructor inicializado con su
valor por defecto
Televisor televisor = new Televisor(6);
o hasta el momento, siempre declarábamos primero la variable de referencia y después creábamos
el objeto por motivos didácticos
lo habitual es que esto se escriba en una sola línea
o en este caso el Runtime de Java sabe que tiene que llamar al Constructor que tiene un argumento
de tipo int
System.out.println("El canal del segundo televisor es el: " + televisor.getCanal());
o el segundo televisor que hemos creado nos indica el canal 6
System.out.println("El canal seleccionado es el: " + tv.getCanal());
o y el primer televisor que hemos creado nos indica el canal 3
Paso 4: Funcionalidad
En este paso no vamos a permitir la creación de un televisor con un valor de canal negativo
Si el valor de un canal es menor que 0, entonces el canal asignado será el 0.
Sentencia de control de flujo if-else
La sentencia if se utiliza para dirigir la ejecución de un programa hacia dos caminos diferentes.
Esta es la sintaxis de un control de flujo if-else
if (condición) {
sentencia1;
sentencia2;
}
else {
sentencia3;
sentencia4;
}
•
if (condicion) {
o después de la palabra reservada if, siempre tenemos que abrir un paréntesis para incluir dentro
una condición
o condición
8
en la siguiente tabla podemos ver los distintos operadores utilizados en las condiciones:
Operador
==
!=
>
<
>=
<=
&&
||
•
Significado
Igual a
Distinto de
Mayor que
Menor que
Mayor o igual
que
Menor o igual
que
AND
OR
else {
o
si no se cumple la condición anterior, entonces realizamos otras sentencias
Si sólo hubiera una sentencia, es decir una sola línea de código nos podríamos ahorrar los corchetes
if (condición)
sentencia1;
else
sentencia3;
•
if (condición)
o personalmente prefiero utilizar siempre los corchetes aunque sólo haya una sentencia porque
si más adelante decido añadir una nueva sentencia, los corchetes ya están allí y
también me ayudan a tener una legibilidad de código más clara
También podemos utilizar sentencias if-else-if múltiples que se basan en una sentencia de if anidados
Esta es la sintaxis de un control de flujo if-else-if
if (condición) {
sentencia1;
sentencia2;
}
else if (condición) {
sentencia3;
sentencia4;
}
else {
sentencia5;
}
9
•
•
Las sentencias if se ejecutan de arriba a abajo de tal forma que tan pronto como una de las condiciones
de control del if sea verdadera, la sentencia o sentencias asociadas se ejecutan y el resto no se tiene en
cuenta
si ninguna de las condiciones es verdadera, entonces se ejecuta la sentencia else final, que actúa como
condición por defecto
o si no hay sentencia else final y todas las otras condiciones son falsas, entonces no se ejecuta
ninguna sentencia
Vamos a crear una pequeña aplicación con varias sentencias if-else-if múltiples para saber en qué estación se
encuentra un mes en particular.
package paqsentenciascontrol;
public class EstacionesIfElseIf {
public EstacionesIfElseIf() {
}
public static void main(String[] args) {
int mes = 10;
String strEstacion;
if(mes == 12 || mes == 1 || mes == 2){
strEstacion = "invierno";
}
.
.
.
else {
strEstacion = "desconocido";
}
System.out.println("El mes " + mes + " es " + strEstacion);
}
}
Sentencia de ramificación múltiple switch
La sentencia switch nos proporciona una forma fácil de ir a partes diferentes del código en función del valor de
una expresión.
•
las expresiones tienen que ser de tipo int, short, char o byte
No cabe duda que es la mejor alternativa a una larga serie de sentencias if-else-if.
Esta es la sintaxis de una ramificación múltiple switch
10
switch (expresión) {
case valor1:
sentencia1;
sentencia2;
break;
case valor2:
sentencia3;
sentencia4;
break;
case valor3:
sentencia5;
sentencia6;
break;
default:
sentencia7;
}
•
el mecanismo de la sentencia switch es el siguiente
o el valor de la expresión se compara con cada uno de los valores (valor1, valor2, valor3) de las
sentencias case
si coincide con alguno
se ejecuta el código que sigue a la sentencia case
si no coincide con ninguno
se ejecuta la sentencia default
la sentencia default es opcional. Por tanto si ningún case coincide y no hay
una sentencia default entonces no se hace nada
o la sentencia break se utiliza en sentencias switch para finalizar una secuencia de sentencias
es decir, cuando se encuentra una sentencia break la ejecución salta a la primera línea de
código que sigue a la sentencia switch completa. Esto tiene el efecto de salirse del switch
Vamos a crear una pequeña aplicación con una sentencias switch para saber en qué estación se encuentra un
mes en particular.
Y este es parte del código de la Clase EstacionesSwitch teniendo en cuenta que los meses pertenecen a las
siguientes estaciones
•
•
•
•
•
invierno: 12, 1 y 2
primavera: 3, 4 y 5
verano: 6, 7 y 8
otoño: 9, 10, 11
desconocido: cualquier número que no esté entre el número 1 y el 12
11
package paqsentenciascontrol;
public class EstacionesSwitch {
public static void main(String[] args) {
int mes = 14;
String strEstacion;
switch (mes){
case 12:
case 1:
case 2:
strEstacion = "invierno";
break;
.
.
.
}
System.out.println("El mes " + mes + " es " +
strEstacion);
}
}
Ahora vamos a ahondar un poco más en la sentencia break. Si añadimos el siguiente código
public static void main(String[]
args) {
int mes = 12;
String strEstacion;
switch (mes){
case 12:
System.out.println("case 12");
case 1:
System.out.println("case 1");
case 2:
System.out.println("case 2");
strEstacion = "invierno";
break;
.
.
.
}
12
Como podemos observar en la salida al inicializar la variable mes a valor 12, se han ejecutado las sentencias
correspondientes al case 12, 1 y 2 hasta que se ha encontrado con la sentencia break del case 2 y entonces se ha
salido de la sentencia switch.
Paso 4: Sin canal negativo
En nuestro caso, no vamos a permitir que se construya una instancia de tipo Televisor con un valor de canal
negativo. Para ello vamos a añadir el siguiente código a la Clase Televisor
package paqtvestandar;
public class Televisor {
int canal;
public Televisor() {
}
public Televisor(int valorCanal) {
if (valorCanal < 0){
canal = 0;
}
else {
canal = valorCanal;
}
}
public void subirCanal() {
canal = canal + 1;
}
public void bajarCanal() {
canal = canal - 1;
}
public int getCanal() {
return canal;
}
}
•
•
if (valorCanal < 0){
o si el canal es negativo, entonces ejecutamos la sentencia asociada a esta condición
canal = 0;
o en este caso decimos que la variable de instancia canal toma como valor el número 0
o Nota: Más adelante ya veremos las excepciones, que son un mecanismo muy útil para resolver
situaciones de este tipo.
En la Clase Aplicacion vamos a pasarle un valor negativo al Constructor del segundo Televisor
13
package paqtvestandar;
public class Aplicacion {
public Aplicacion() {
}
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
System.out.println("El canal por defecto es el: " + tv.canal);
tv.canal = 3;
System.out.println("El canal seleccionado es el: " + tv.canal);
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor televisor = new Televisor(-5);
System.out.println("El canal del segundo televisor es: "+
televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
}
}
•
•
Televisor televisor = new Televisor(-5);
o como podemos ver, al constructor de Televisor le pasamos un valor de canal negativo
System.out.println("El canal del segundo televisor es el: " + televisor.getCanal());
o y como cabía esperar, el mensaje del canal asignado en este momento es el 0
Paso 5: Funcionalidad Encapsulación
En este paso vamos a impedir que los televidentes puedan acceder directamente a los canales del Televisor.
Si lo intenten tan se encontrarán con errores como éste
•
canal has private access in paqtvestandar.Televisor
A través de la Encapsulación se puede controlar que partes de un programa pueden acceder a los miembros
(variables de instancia y métodos) de un Objeto
•
•
controlando el acceso se pueden prevenir malas actuaciones sobre los datos
la Encapsulación se basa en el control de acceso o ámbito.
o para empezar vamos a conocer los ámbitos private y public. Más adelante conoceremos los
ámbitos friendly y protected
o public
puede ser accedido por cualquier parte del programa
o private
14
sólo puede ser accedido por otros miembros de su Clase
El hecho de que hasta ahora accediéramos al atributo canal de la instancia de tipo Televisor de esta forma
tv.canal = 3; tiene varios inconvenientes. De momento vamos a ver uno de ellos
•
no hay validación del valor asignado
o es decir si un usuario del televisor decide que el canal que quiere ver es el -4, no tenemos ningún
mecanismo para defendernos y "ya nos han colado un gol"
A la Clase Televisor le tenemos que hacer las siguientes modificaciones
package paqtvestandar;
public class Televisor {
private int canal;
public Televisor() {
}
public Televisor(int valorCanal) {
setCanal(valorCanal);
}
public void subirCanal() {
setCanal(canal + 1);
}
public void bajarCanal() {
setCanal(canal - 1);
}
public int getCanal() {
return canal;
}
public void setCanal(int valorCanal) {
if (valorCanal < 0){
canal = 0;
}
else {
canal = valorCanal;
}
}
}
•
private int canal;
o al indicar que el ámbito es private estamos diciendo que sólo desde dentro de la Clase Televisor
se puede acceder al atributo canal
15
•
•
•
public Televisor(int valorCanal) {
o no tendríamos un código optimizado si tuviéramos que filtrar los datos en dos sitios distintos.
por este motivo, en vez de filtrar el dato correspondiente al canal en el Constructor, lo
haremos solamente en el método setCanal()
o setCanal(valorCanal);
invocamos a este método por las razones expuestas anteriormente
setCanal(canal + 1);
setCanal(canal - 1);
o cuando el usuario baja el canal, se podría dar el caso que llegara al canal 0 y si sigue bajando el
canal llegaría al canal -1.
para evitar esto invocamos al método setCanal(...) pasándole como argumento el
resultado de la operación de bajar el canal.
o para seguir una coherencia de código realizamos lo mismo en el caso de que el usuario quisiera
subir el canal
si más adelante nos dijeran que por ejemplo el canal más alto no puede superar el número
99, simplemente tendríamos que ampliar el filtro en el método setCanal(...)
public void setCanal(int valorCanal) {
o al tener este método un ámbito public, este método podrá ser llamado
tanto desde el Constructor de su Clase
como desde cualquier otra parte del programa que estuviera interesada en conversar con
una instancia de tipo Televisor para cambiarle el canal
Ahora compilamos la Clase Televisor y seguidamente abrimos la Clase Aplicacion. Como podemos observar el
compilador nos da un error indicándonos que la variable de instancia canal tiene acceso privado y que por
consiguiente la Clase Aplicacion no se puede compilar
Para evitar estos errores tenemos que realizar unos pequeños cambios en la Clase Aplicacion
Aquí tenemos el código de la Clase Aplicacion
package paqtvestandar;
public class Aplicacion {
public Aplicacion() {
}
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
System.out.println("El canal por defecto es el: " + tv.getCanal());
// tv.canal = 3;
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor televisor = new Televisor(-5);
16
+
System.out.println("El canal del segundo televisor es el: "
televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
}
}
•
tv.setCanal(3);
o a diferencia del código de los pasos anteriores tv.canal = 3;
ahora la Clase Televisor no permite que se acceda directamente a su variable de instancia
canal debido a que esta es privada, pero sí permite el acceso a su método setCanal()
porque éste es público
Paso 6: Diagrama de Clases UML
UML (Unified Modeling Language, Lenguaje Unificado de Modelado) es un estándar auspiciado por el OMG
(Object Management Group) para realizar las labores de Análisis y Diseño como antesala al desarrollo de
cualquier aplicación escrita en un lenguaje Orientado a Objetos.
•
una de las labores de Diseño es la creación de los Diagramas de Clases de Diseño
o estos diagramas son muy útiles porque muestran la estructura de las Clases que después van a
estar escritas en un lenguaje de programación Orientado a Objetos
Estas son las dos Clases que hemos estado utilizando hasta ahora
•
•
Televisor
o una Clase se divide en tres partes. En la primera parte tenemos
el nombre de la Clase.
nuestra Clase se llama Televisor
(from paqtvestandar)
aquí nos está indicando que la Clase Televisor pertenece al package
(paquete) paqtvestandar
-canal: int
o una Clase se divide en tres partes. En la segunda parte tenemos
las variables de instancia o atributos
nuestra Clase de momento tiene un atributo que se llama canal y es del tipo
primitivo int
17
•
•
•
•
•
•
para indicar que su ámbito es private, se pone el signo menos delante del nombre
del atributo.
+Televisor():
o una Clase se divide en tres partes. En la tercera parte tenemos
los métodos u operaciones de la Clase
a los Constructores también se les considera métodos
pero como ya sabemos que un Constructor no devuelve ningún valor,
después de los dos puntos no aparece la palabra reservada void ni ningún
tipo de dato
para indicar que su ámbito es public se pone el signo más delante del nombre del
método
+Televisor(valorCanal:int):
o en este caso este Constructor está sobrecargado con el nombre de una variable llamada
valorCanal de tipo int
+subirCanal():void
o en el interior de los paréntesis este método no tiene nada, porque no tiene argumentos
o como este método no devuelve ningún valor, utilizamos la palabra reservada void para indicarlo
+getCanal():int
o como este método retorna un valor de tipo entero, lo indicamos con la palabra reservada int
detrás de los dos puntos ":"
Aplicacion
o este es el nombre de la segunda Clase que hemos creado y que pertenece al mismo paquete
+main(args:String[]):void
o de momento saber que está subrayado porque es static
•
o
o
esta flecha es una Relación de Dependencia que nos indica que la Clase Aplicacion depende
puntualmente de la Clase Televisor para poder realizar una serie de acciones
<<instantiate>>
UML utiliza los estereotipos para agregar información tanto a las Relaciones como a los
Elementos (un Elemento puede ser por ejemplo una Clase)
además de los estereotipos estándar de UML, los usuarios de UML podemos
añadir nuevos estereotipos y así hacer que UML sea extensible
en nuestro caso concreto estamos diciendo que una de las acciones que puede realizar la
Clase Aplicacion sobre la Clase Televisor es crear instancias (<<instantiate>>) de tipo
Televisor
De esta forma y siguiendo la convención de los Constructores explícitos e implícitos tenemos el siguiente
Diagrama de Clases de Diseño con un Constructor por defecto y otro Constructor sobrecargado.
18
Paso 7: Ventajas de la Encapsulación
En este paso vamos a aumentar las prestaciones de nuestro televisor subiendo y bajando la intensidad del color
de la pantalla y modificando el volumen del televisor.
Ahora que ya sabemos conceptualmente que es la Encapsulación, vamos a estudiar más a fondo sus ventajas
Validación de los datos
Ya comentamos la gran ayuda que nos ofrece la Encapsulación para filtrar datos no deseados
Facilidad de uso de la Clase
Imaginemos que creamos un Objeto de tipo Coche y que un futuro comprador de este coche se acerca a un
concesionario y lo quiere probar antes de comprarlo
•
gracias a que la operativa básica de conducción es relativamente sencilla, el futuro comprador se sentará
en el asiento del conductor y utilizará métodos públicos como arrancar(), frenar(), girar(sentido, ángulo)
y acelerar().
o los ingenieros que han creado la Clase Coche han tenido que implementar muchas más
operativas que las básicas ya mencionadas
por ejemplo, cuando el futuro comprador ha invocado el método acelerar(), éste método a
su vez ha llamado a otros métodos como inyectarGasolina(), moverPistones(),
transmitirFuerzaAlPalier() y moverRuedas()
o Si este futuro comparador fuera un desarrollador de software que tiene que interactuar con una
instancia de la Clase Coche, éste podría seguir un Diagrama de Clases como este:
19
o
En vez de seguir este otro en el que se muestra un atributo llamado suministradorCombustible1
que es de tipo Carburador y los métodos inyectarGasolina(), moverPistones(),
transmitirFuerzaAlPalier() y moverRuedas() relacionados con el hecho de querer aumentar la
velocidad del coche invocando al método acelerar().
o
en la vida real sucede lo mismo, todas las cosas que nos rodean son Objetos con sus atributos y
operativas para que podamos interactuar con ellos.
si no pudiéramos tener diferentes niveles de abstracción con las cosas que nos rodean,
tendríamos que ser expertos en casi todo con el fin de poder utilizarlo
en este ejemplo sólo hemos imaginado los métodos que un ingeniero tendría que implementar
para poder acelerar, pero sigamos imaginando y pensemos en la cantidad de métodos que todavía
se tendrían que añadir para por ejemplo realizar giros, frenar o arrancar el coche
gracias a la Encapsulación, pueden haber desarrolladores especializados en crear Clases de bajo
nivel con una gran complejidad intrínseca, pero que su utilización es extremadamente sencilla
de esta forma utilizamos atributos y métodos de ámbito private para realizar tareas de
bajo nivel (todo lo que se encuentra debajo del capó) que sólo pueden ser invocadas
desde el interior del propio Objeto y métodos de ámbito public para que puedan ser
invocados tanto desde el interir del propio Objeto como desde otro Objeto
o
o
Documentación menos extensa y por lo tanto más ágil
La documentación entregada al desarrollador o desarrolladores que quieren interactuar con los Objetos de tipo
Coche será evidentemente mucho menos extensa que la que utilizan los desarrolladores que han creado esta
Clase
•
todos los atributos y métodos de ámbito private no se muestran ni se explican en dicha documentación
Flexibilidad en el cambio de versiones
Finalmente ya hemos cumplido con nuestro objetivo de crear la Clase Coche y ya hay muchos usuarios que
desde sus aplicaciones interactúan con las instancias de nuestro coche
•
•
pero la gran mayoría de los usuarios opinan que nuestro coche les está arruinando sus bolsillos cada vez
que tienen que repostar
debido a esto, nos vemos obligados a realizar una serie de cambios importantes en nuestra Clase para
que los usuarios no se tengan que gastar tanto dinero en combustible
20
•
por tanto creamos la versión 2.0, la cual ya no funciona con un Carburador sino que funciona con una
BombaInyectora propia de los coches Diesel, e intercambiamos el método inyectarGasolina() por el
método inyectarGasoil().
Entregamos la nueva versión a nuestros usuarios, y les indicamos que la operativa del coche sigue siendo
exactamente la misma que en la versión 1.0.
•
esto es posible debido a que ellos nunca llegaron a interactuar con el atributo
suministradorCombustible1 ni con el método inyectarGasolina(), y por lo tanto
o nosotros como desarrolladores de la Clase Coche
hemos tenido total libertad y flexibilidad para realizar los cambios necesarios
o y los usuarios
no han tenido que modificar ni una sola línea de código de los Objetos que interactúan
con nuestra Clase
Paso 7: Operativa
Subir y bajar la intensidad del color del televisor
Ahora queremos que el Televisor estándar además de ofrecer la operativa de cambiar los canales, también nos
permita aumentar y disminuir la intensidad del color. Para ello vamos a crear cuatro nuevos métodos:
21
Este es la implementación de la Clase Televisor
package paqtvestandar;
public class Televisor {
private int canal;
public Televisor() {
}
public Televisor(int valorCanal) {
setCanal(valorCanal);
}
public void subirCanal() {
setCanal(canal + 1);
}
public void bajarCanal() {
setCanal(canal - 1);
}
public int getCanal() {
return canal;
}
public void setCanal(int valorCanal) {
if (valorCanal < 0){
canal = 0;
}
else {
canal = valorCanal;
}
}
public void subirColor(){
System.out.println("Televisor - subirColor(): estoy subiendo el
color");
subirColorAyuda();
}
private void subirColorAyuda() {
System.out.println("Televisor - subirColorAyuda(): sigo subiendo el
color");
}
public void bajarColor(){
System.out.println("Televisor - bajarColor(): estoy bajando el
22
color");
bajarColorAyuda();
}
private void bajarColorAyuda() {
System.out.println("Televisor - bajarColorAyuda(): sigo bajando el
color");
}
}
•
•
public void subirColor(){
o System.out.println("Televisor - subirColor(): estoy subiendo el color");
este método empieza a subir la intensidad del color del televisor
o subirColorAyuda();
pero llega un momento que tiene la necesidad de invocar a otro método que hará trabajos
de más bajo nivel y que sólo será invocado desde dentro del Objeto de tipo Televisor
porque este método es de ámbito private
private void subirColorAyuda() {
o System.out.println("Televisor - subirColorAyuda(): sigo subiendo el color");
mostraremos este tipo de mensaje y otros parecidos a medida que el flujo del programa
vaya pasando por ellos
Compilamos la Clase Televisor y seguidamente modificamos la clase Aplicacion
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
System.out.println("El canal por defecto es el: " + tv.getCanal());
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor televisor = new Televisor(-5);
System.out.println("El canal del segundo televisor es el: "
+ televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirColor();
// tv.subirColorAyuda();
}
}
23
Al ejecutar la Clase Aplicacion vemos los mensajes correspondientes a los métodos subirColor() y
subirColorAyuda()
Si descomentamos la invocación al método subirColorAyuda() apuntado por la variable de referencia tv, vamos
a poder ver que el compilador se queja (y con razón!) porque estamos intentando acceder a un método de
ámbito private
Subir y bajar el volumen del televisor
Según el siguiente Diagrama de Clases de Diseño
Vamos a implementa las Clases Televisor y Aplicacion de tal forma que cuando creemos una instancia de
Televisor, ésta ya tenga el volumen por defecto en posición 5 sin que se lo tengamos que indicar desde la Clase
Aplicacion de forma explícita
Por lo que respecta a la implementación de los métodos subirVolumen() y bajarVolumen(), no tendremos en
cuenta los valores negativos ni tampoco el valor máximo del volumen, así que tendremos que implementar estos
métodos con el siguiente código
volumen = volumen + 1
volumen = volumen - 1
Desde la Clase Aplicacion tenemos que
•
•
•
•
•
crear una instancia de Televisor
seguir cambiando los canales y subiendo el color como en el ejemplo anterior
subir el volumen una posición
mostrar un mensaje diciendo que "La posición del volumen es: "....
Aquí tenemos el código del televisor estándar
24
package paqtvestandar;
public class Televisor {
private int canal;
private int volumen = 5;
public Televisor() {}
public Televisor(int valorCanal) {
setCanal(valorCanal);
}
public void subirCanal() {
setCanal(canal + 1);
}
public void bajarCanal() {
setCanal(canal - 1);
}
public int getCanal() {
return canal;
}
public void setCanal(int valorCanal) {
if (valorCanal < 0){
canal = 0;
}
else {
canal = valorCanal;
}
}
public void subirColor(){
System.out.println("Televisor - subirColor(): estoy subiendo el color");
subirColorAyuda();
}
private void subirColorAyuda() {
System.out.println("Televisor - subirColorAyuda(): sigo subiendo el color");
}
public void bajarColor(){
System.out.println("Televisor - subirColor(): estoy bajando el color");
bajarColorAyuda();
}
private void bajarColorAyuda() {
System.out.println("Televisor - bajarColorAyuda(): sigo bajando el color");
}
public void subirVolumen() {
volumen = volumen + 1;
}
public void bajarVolumen() {
volumen = volumen - 1;
25
}
public int getVolumen() {
return volumen;
}
}
•
Y este es el código modificado de la Clase Aplicacion
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
Televisor tv;
tv = new Televisor();
System.out.println("El canal por defecto es el: " + tv.getCanal());
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor televisor = new Televisor(-5);
System.out.println("El canal del segundo televisor es el: "
+
televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirColor();
tv.subirVolumen();
System.out.println("La posición del volumen es: " + tv.getVolumen());
}
}
Paso 8: Herencia a través de las relaciones de Generalización
En este paso vamos a crear un nuevo televisor con las mismas prestaciones que el televisor original.
El nuevo televisor nos permite desconectar el sonido sin tener que modificar el volumen.
Después de mucho trabajo y esfuerzo hemos conseguido crear un televisor estándar con una funcionalidad
suficiente para poder ser utilizado.
A partir de este televisor estándar, vamos a crear un nuevo televisor que tendrá una funcionalidad añadida
•
para ponerlo en práctica, nos vamos a crear un televisor que lo llamaremos TelevisorVirtual y que nos
va a permitir desconectar el sonido en caso de que por ejemplo recibiéramos una llamada telefónica y no
quisiéramos bajar el volumen paulatinamente,
o es decir invocando al método bajarVolumen() n veces seguidas
26
Este es su Diagrama de Clases de Diseño correspondiente
•
o
este tipo de flecha en UML representa una relación de Generalización
este tipo de relación
nos indica que la Clase hija, en este caso TelevisorVirtual, hereda todos los
atributos y métodos de la Clase padre, en este caso Televisor
es muy conveniente cuando queremos crear una Clase muy afín a otra u otras
Clases , pero que no se ajusta cien por cien a nuestras necesidades
entonces lo que hacemos es implementar los nuevos métodos de nuestra
Clase y nos ahorramos el tener que escribir o copiar-y-pegar código que ya
ha sido escrito anteriormente. Este código que reutilizamos puede ya haber
sido escrito
por nosotros mismos,
por otra persona de nuestro departamento o de nuestra empresa
o por cualquier otra persona u organización que no pertenezca a
nuestra empresa
gracias a la herencia, se puede crear una Clase general que define características comunes
a un conjunto de elementos relacionados. Esta Clase puede ser heredada por otras Clases
27
más específicas, añadiendo a cada una de estas Clases específicas aquellas cosas que son
particulares a ellas
la Clase general recibe el nombre de Superclase o Clase padre
a las Clases más específicas se les llama Subclases o Clases hijas
una Subclase puede ser a su vez una Superclase de una Subclase y así
sucesivamente
Ahora vamos a crear una nueva Clase llamada TelevisorVirtual que va a heredar de la Clase Televisor
Este es el código de la Clase TelevisorVirtual
package paqtvestandar;
public class TelevisorVirtual extends Televisor {
private boolean sonido = true;
public void setSonido(boolean valorSonido){
sonido = valorSonido;
}
public boolean isSonido(){
return sonido;
}
}
•
•
•
•
public class TelevisorVirtual extends Televisor {
o en Java, cuando una Clase hereda de otra se indica con la palabra reservada extends
private boolean sonido = true;
o declaramos una variable booleana y hacemos que tome el valor verdadero cuando se cree una
instancia de la Clase
public void setSonido(boolean valorSonido){
o si algún televidente decide ver las imágenes pero no escuchar el televisor, entonces lo que tiene
que hacer es
invocar este método con la variable valorSonido con valor falso
public boolean isSonido(){
o de la misma forma que existe la convención de utilizar los getters y setters para acceder a las
variables de instancia, también es muy habitual utilizar el prefijo is para saber el estado de una
variable de instancia booleana
Compilamos la Clase TelevisorVirtual.
Para que la Clase Aplicacion pueda interactuar con la Clase TelevisorVirtual, vamos a tener que realizar
bastantes cambios en la Clase Aplicacion.
•
para que podamos mantener el código de lo que hemos estado haciendo hasta ahora, vamos a realizar
una copia de las Clases Aplicacion y Televisor
o el IDE NetBeans a la copia de estas Clases las llamará Aplicacion_1 y Televisor_1.
28
nosotros seguiremos trabajando sobre las Clases Aplicacion y Televisor y dejaremos las
Clases copiadas como clases históricas para poderlas repasar en el futuro siempre que
queramos
Ahora vamos a hacer que la Clase Aplicacion_1 interactúe con la Clase Televisor_1.
package paqtvestandar;
public class Aplicacion_1 {
public static void main(String[] args) {
Televisor_1 tv;
tv = new Televisor_1();
System.out.println("El canal por defecto es el: " + tv.getCanal());
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.bajarCanal();
System.out.println("El canal seleccionado es el: " + tv.getCanal());
Televisor_1 televisor = new Televisor_1(-5);
System.out.println("El canal del segundo televisor es el: "
+
televisor.getCanal());
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirColor();
// tv.subirColorAyuda();
}
}
Modificación por completo de la Clase Aplicacion
Este es el nuevo código que sustituye al código anterior de Clase Aplicacion
package paqtvestandar;
public class Aplicacion {
public static void main(String[] args) {
TelevisorVirtual tv = new TelevisorVirtual();
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirColor();
tv.setSonido(false); // Desactivamos el sonido
System.out.println("El televisor tiene el sonido: " + tv.isSonido());
}
}
•
TelevisorVirtual tv = new TelevisorVirtual();
o con el operador new creamos una instancia de la Clase TelevisorVirtual
29
•
•
•
tv.setCanal(3);
o si nos fijamos, la Clase TelevisorVirtual no contiene el método setCanal(int valorCanal), pero sí
que lo tiene su Clase padre Televisor
como TelevisorVirtual hereda de Televisor, la variable de referencia tv puede invocar
tanto a métodos propios de la Clase TelevisorVirtual como a métodos propios de la Clase
Televisor
tv.setSonido(false);
o desactivamos el sonido
System.out.println("El telvisor tiene el sonido: " + tv.isSonido());
o verificamos que el sonido está desactivado
Paso 9: Polimorfismo a través de la sobre escritura de método
En este paso el nuevo televisor conecta el sonido automáticamente al subir o bajar el volumen.
Al ejecutar la Clase Aplicacion vemos como el sonido se ha activado por el hecho de haber subido el volumen
Cuando al nuevo televisor virtual le desconectamos el sonido, éste se queda en silencio.
Para volver a conectar el sonido simplemente tenemos que invocar al método setSonido(true)
•
pero ganaríamos en flexibilidad si diéramos al televidente la posibilidad de conectar el sonido de forma
automática si éste decidiera invocar los métodos subirVolumen() o bajarVolumen()
o pero estos métodos ya están implementados en la Clase Televisor. Entonces ¿Qué podemos
hacer? Este es el Diagrama de Clases de Diseño actualizado para esta nueva situación:
30
•
+subirVolumen():void
o la segunda forma de implementar el Polimorfismo es a través de la sobreescritura de método. (La
primera forma de implementarlo fue a través de la sobrecarga de método)
la sobre escritura de método consiste en volver a implementar en la Subclase uno o más
métodos que ya están implementados en la Superclase
esta técnica nos es extremadamente útil cuando queremos heredar de una
Superclase pero la operativa de uno o más métodos heredados de dicha Superclase
no se ajusta a la operativa que nosotros queremos aplicar a la nueva Subclase
Este es el código ampliado de la Clase Televisor Virtual
package paqtvestandar;
public class TelevisorVirtual extends
Televisor {
private boolean sonido = true;
public TelevisorVirtual() {
}
public void setSonido(boolean
valorSonido){
sonido = valorSonido;
}
public boolean isSonido(){
return sonido;
}
public void subirVolumen() {
super.subirVolumen();
if (this.isSonido() == false){
setSonido(true);
}
}
public void bajarVolumen() {
super.bajarVolumen();
if (!isSonido()){
setSonido(true);
}
}
}
•
public void subirVolumen() {
o en este caso la sobreescritura de método es posible porque la signatura de método del método
subirVolumen() de la Subclase coincide con la signatura de método del método
subirVolumen()de la Superclase
31
para que dos métodos tengan la misma signatura, éstos tienen que tener
el mismo nombre de método
el mismo número de argumentos
y que estos argumentos coincidan con el mismo tipo de datos
dentro de este método tenemos que escribir nuestro nuevo código
super.subirVolumen();
la palabra clave super, se utiliza para indicar que queremos invocar un método que se
encuentra en la Superclase y así poder distinguirlo del mismo método que se encuentra en
la Subclase.
en nuestro caso, la funcionalidad de bajarVolumen() de la Superclase Televisor se
ajusta a nuestras necesidades y por tanto no la volvemos a escribir. De esta forma
nos ahorramos tener que volver a escribir o cortar-y-pegar el mismo
código
y sobre todo evitamos tener el mismo código diseminado por toda la
aplicación, y con el consiguiente engorro y el riesgo de error de tener que
actualizarlo en "800" sitios si se tuviera que modificar dicho código
if (this.isSonido() == false){
la palabra clave this, se utiliza para indicar que queremos invocar a un método que se
encuentra en la propia Clase.
en nuestro caso, invocamos al método this.isSonido() para saber si el sonido está
conectado o desconectado
if (!isSonido()){
la ausencia de la palabra clave this, también se utiliza para indicar que queremos invocar
a un método que se encuentra en la propia Clase.
si el código lo genera un Asistente o Wizard del entorno de desarrollo, éste lo que
acostumbra hacer al escribir código por nosotros es poner de forma explícita la
palabra clave this.
si el código lo escribimos nosotros, lo habitual es no utilizar la palabra clave this,
porque sin ella ya se sobreentiende que estamos invocando a métodos de nuestra
propia Clase
(!isSonido())es equivalente a (isSonido() == false)
setSonido(true);
activamos el sonido
o
o
o
o
o
Después de haber añadido el nuevo código compilamos la Clase TelevisorVirtual.
Y este es el código de la Clase Aplicacion que interactúa con una instancia de tipo TelevisorVirtual
package paqtvestandar;
public class Aplicacion {
public Aplicacion() {
}
public static void main(String[] args) {
TelevisorVirtual tv = new TelevisorVirtual();
tv.setCanal(3);
System.out.println("El canal seleccionado es el: " + tv.getCanal());
tv.subirColor();
32
tv.setSonido(false); // Desactivamos el sonido
System.out.println("El telvisor tiene el sonido: " + tv.isSonido());
tv.subirVolumen();
System.out.println("La posición del volumen es: " + tv.getVolumen());
System.out.println("El televisor tiene el sonido: " + tv.isSonido());
}
}
•
•
tv.subirVolumen();
o desde la Clase Aplicacion nos es totalmente transparente el hecho de que ahora el método
subirVolumen() esté sobrescrito en la Clase TelevisorVirtual, desde aquí no percibimos ninguna
diferencia
System.out.println("El televisor tiene el sonido: " + tv.isSonido());
o comprobamos que el sonido se ha activado sin que se lo hayamos indicado de forma explícita
con el método setSonido(true)
Al ejecutar la Clase Aplicacion vemos como el sonido se ha activado por el hecho de haber subido el volumen
33
Descargar