33
1 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.

Ejemplo Televisor

Embed Size (px)

DESCRIPTION

JAVA ORIENTADO A OBJETOS

Citation preview

1

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.

2

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.

3

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.

4

• 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

5

o un Constructor no devuelve ningún valor, ni siquiera void o 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

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

6

• 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

7

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();

8

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

9

� 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; }

10

• 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

11

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

12

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; . . . }

13

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

14

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

15

� 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

16

• 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);

17

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

18

� 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 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 o <<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.

19

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:

20

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

o 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

o 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

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

21

• 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:

22

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

23

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(); } }

24

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

25

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;

26

} 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

27

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

28

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.

29

� 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

30

• 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:

31

• +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

32

� 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

o dentro de este método tenemos que escribir nuestro nuevo código o 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

o 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 o 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) o setSonido(true);

� activamos el sonido

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();

33

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