La ocultación y el encapsulamiento
Si hemos
dicho que los objetos deben tener un estado coherente y nos esforzamos
escribiendo un código que intenta garantizarlo, ¿qué ocurre si hacemos lo
siguiente?
Bombilla b = new Bombilla(100,0);
for(i=0;i<=1000;i++){
b.encender();
b.apagar();
}
if (b.fundida == true)
b.fundida
= false;
Consultamos y cambiamos una propiedad que define el estado del objeto
saltándonos todos los controles.
No queremos que se puedan realizer ese tipo de manipulaciones externas
del objeto, para ello debemos ocultar las propieades de las clases utilizando
el modificador de visibilidad private.
public class Bombilla{
//
Propiedades OCULTAS
private
int potencia;
private
int numEncendidos;
private
Boolean encendida, fundida;
}
Ya no podríamos hacer b.fundida = false;
Pero tampoco podríamos consultar el valor de ninguna propiedad, como por
ejemplo if(b.numEncendidos <100)
Para solucionar este problema hacemos lo siguiente:
1.
Todas las propiedades se declaran como private
2.
Para las propiedades cuyo valor quiero que pueda
ser consultado creo un método public que devuelve el valor:
public int obtenerNumEncendido(){ // Getter
return numEncendidos;
}
3.
Para aquellas propieades cuyo valor quiero que
pueda ser modificado creo un método public que reciba el nuevo valor y haga las
comprobaciones pertinentes
public void establecerPotencia(int nuevaPotencia){
if (nuevaPotencia > 0)
potencia = nuevaPotencia:
// Setter
}
De esta forma se dice que las propiedades están ocultas y que su acceso
externo está encapsulado en uno o más métodos.
Todos los objetos deben cumplir este doble principio de ocultación y
encapsulamiento si se quiere garantizar su buen funcionamiento.
En inglés:
è
Los métodos de consulta reciben el nombre de
getters (obtenedores) ya que se nombre como getNombrePropiedad
è
Los modificadores reciben el nombre de setters
(establecedores) ya que son de la forma setNombrePropiedad
Eclipse es capaz de generar automáticamente los getters y setters de
las propiedades de una clase.
Para ello nos vamos a Source à Generate
Getters and Setters
El objeto en primera persona. Referencia this
¿Qué ocurre si escribimos el siguiente código?
public void establecerPotencia(int potencia){
if
(potencia > 0)
potencia
= potencia;
}
¿Cómo distinguimos la propiedad del parámetro potencia?
Podemos cambiar el nombre del parámetro para que sea distinto del
nombre de la propiedad pero también podemos usar la propiedad this.
This es una
referencia al objeto actual, al objeto con el que estamos trabajando en ese
momento. Desde esa referencia podemos accede a todas las propiedades y métodos
del objeto.
Usando this en el caso anterior, quedaría:
public void establecerPotencia(int potencia){
if
(potencia > 0)
this.potencia
= potencia;
}
Se usa normalmente para resolver conflictos de nombre o aclarar a qué
objeto pertenece una propiedad o un método.
Se puede utilizar en métodos y constructores.
Sobrecarga de métodos
Java permite que una clase tenga varias versiones de un método o
constructor.
De esta forma evitamos memorizer demasiados nombres de métodos.
Las reglas a seguir para sobrecargar métodos o constructores son las
siguientes:
è
Los métodos tienen el mismo nombre y tipo de
retorno
è
Los métodos se distinguien por el número y tipo de
los parámetros de entrada
è
La visibilidad (private, public…) de los métodos no
sirve para distinguirlos entre sí
Ejemplo:
Tengo dos versiones del constructor de clases y dos del método apagar.
Sería erroneo intentar añadir el método int apagar(), no lo
diferenciaría.
El modificador static
Si en una casa tenemos ocho objetos Bombilla todas de 100W y hay cuatro
encendidas, ¿cuál es la potencia consumida por el conjunto? 400W
La potencia consumida por el conjunto de las bombillas es un valor que no
podemos relacionar con un solo objeto, sino que es del conjunto.
El modificador static aplicado a una propiedad de una clase hace
que dicho campo sea considerado como “de grupo” o “de clase”. Ejemplo:
public class Bombilla{
private
static int consumoTotal = 0; // Es obligatorio inicializar aquí
private
boolean encendida;
private
int potencia;
private
int numEncendidos;
public
Bombilla(int potencia, int numEncendidos){
//
Si ponemos aquí consumoTotal = 0; cada vez que
//
creemos un objeto pondremos a 0 el consume global
//
del grupo
this.potencia
= potencia;
this.numEncendidos
= numEncendidos;
}
public
void encender(){
encendida
= true;
numEncendidos++;
consumoTotal
= consumoTotal+this.potencia;
}
public
int getConsumoTotalBombilla(){
return
consumoTotal;
}
}
El modificador static aplicado a un método (no constructores) hace que
dicho método sea considerado como “de clase”, pudiendo ser llamado sin crear
ningún objeto de la clase.
La sintaxis de la llamada sería NombreClase.nombreMetodoEstatico(…);
Si retocamos el ejemplo anterior añadiendo static al método
getConsumoTotalBombillas:
public static int
getConsumoTotalBombillas(){
return consumoTotal;
}
Ahora podríamos ejecutar el siguiente código:
System.out.print(“Sin bombillas
el consumo es: ”);
System.out.println(Bombilla.getConsumoTotalBombillas()+”W”);
Bombilla b1 = new
Bombilla(100,5);
Bombilla b2 = new
Bombilla(60,1);
b1.encender();
b2.encender();
Sytem.out.println(Bombilla.getConsumoTotalBombillas());
El término static viene porque Java organiza la memoria en zonas:
è
Zona de
memoría estática: Se establece en tiempo de compilación
è
Zona de
memoria dinámica: Se establece en tiempo de ejecución
è
Pila del
programa (b1 y b2)
Como un método estático puede desde la clase, sin que existan objetos,
cuando escribamos el código de un método estático solo podremos utilizar
propiedades estáticas y llamar a métodos estáticos.
En nuestro ejemplo de las bombillas, el método estático
getConsumoTotalBombillas no puede accede a encendida, numEncendidos,
potencia…
Aunque el modificador static parece poco útil se emplea mucho. Algunos
ejemplos prácticos:
Clase Math de la
librería estándar de Java:
Esta clase no tiene constructors con lo que no podremos crear un objeto
de ella. Sin embargo todas sus propiedades y métodos son públicos y estáticos.
Ejemplo de uso:
longitud = 2 * Math.PI * radio;
solucion1 = (-b + Math.sqrt
(b*b-4*a*c))/(3*a);
tiradaDado = Math.floor(1 + 6 *
Math.random());
System.out.println:
Cada vez que escribimos System.out.println(…) estamos accediendo al
método estático println contenido en la propiedad pública y estática out, que
está contenida en la clase System.
Concepto y notación en UML
Si escribimos un conjunto que interactúan resolviendo un problema común
sería interesante poder agruparlas y tratarlas como grupo.
El paquete es la estructura que Java proporciona para agrupar y
gestionar grupo s de clases. Además un paquete puede contener a otros paquetes.
Es parecido al concepto de librería en C
Notación UML y
ejemplo
Declaración de un paquete
Para indicar que en una clase se incluye un paquete solo hay que poner
en la primera línea de su código fuente la sentencia
package nombreDelPaquete;
En el ejemplo anterior los ficheros Bombilla.java, Televisor.java,
Video.java y MandoUniversal.java deben tener como primera linea
package electrodomesticos;
Reglas de nombrado
de un paquete
è
Se escribe entero en minúsculas y sin espacios
è
Debe comenzar con una letra, ‘$’ o ‘_’
è
No puede ser una palabra reservada del lenguaje
(int, float, class)
è
Si un paquete está contenido en otro paquete, el
nombre del paquete contenedor precede al nombre del contenido y se separan por ‘.’
Ejemplos:
Correctos
utilidades gestionClientes;
utilidades graficos utilidas.sonidos;
Incorrectos
12meses;
static;
gestionBajas;
future-soft;
double.number;
El espacio de nombres
Un paquete define un espacio de nombres que actúa como prefijo del
nombre de las clases que contiene.
Así dos clases se pueden llamar igual si pertenecen a paquetes
distintos. Ejemplo:
Paquetes
En un Mercado en el que muchas empresas desarrollan clases y las
reutilizan, los paquetes nos ayudan a resolver conflictos de nombres.
Las empresas suelen crear una estructra de paquetes basada en su
dirección de dominio web pero invertida.
Ejemplos:
·
www.endesa.com à com.endesa.servicios
com.endesa.facturacion
·
www.sadiel.es à es.sadiel.servicios
es.sadiel.facturacion
Si unan clase no se declara dentro de un paquete, el compilador la
añade al paquete predeterminado o por defecto
La estructura de carpetas asociada
Los paquetes son contenedores de clases o de otros paquetes.
Por otro lado los sistemas operativos organizan sus ficheros en
carpetas. De modo que una carpeta es un contenedor de ficheros o de otras
carpetas.
Java establece una relación directa entre paquete – carpeta y clase –
fichero.
Ejemplo
Esta organización es la que el compilador espera, así que para cambiar
una clase de paquete hay que organizarla correctamente.
Clases visibles dentro y fuera de un paquete
Hasta ahora todas las clases que hemos escrito comenzaban con public
class … Esto significa que la clase será visible y / o utilizable por las
clases de dentro y fuera del paquete.
Sin embargo si una clase que pertenece a un paquete se declara sin el
modificador public, sólo será visible por las clases que comparten el
paquete con ella.
Esto permite escribir clases auxiliaries o de apoyo a las clases
visibles o públicas.
El modificador de visibilidad del paquete
Los paquetes son contenedores de clases que guardan una relación con
ellas.
Si las calses de un paquete necesitan cooperar entre sí puede ser
interesante que algunas propiedades o métodos tengan un nivel de ocultación
intermedio entre public y private, de manera que se comparta información.
Este nivel de visibilidad recibe el nombre de visibilidad de paquete o
amigable (friendly).
Un miembro (método o propiedad) de una clase se declara como amigable
simplemente al no poner ni public ni private. Ejemplo:
int codigoCliente;
boolean propietario;
void imprimeCliente(){…}
Su forma de denotarlo en UML consiste en no poner ni el + (public) ni
el – (private).
Ejemplo
De la clase video:
è
Los métodos public cargar y play pueden ser usados
en todas las clases
è
La propiedad privada format sólo puede usarla la
clase video
è
Los miembros con visibilidad del paquete puntosX,
puntosY y setFormato pueden ser utilizados por la clase iagen pero no por audio
o teclado.
Cuando escribo una clase que necesita interactuar con una o más clases
agrupadas en un paquete necesitamos escribir una sentencia de importación para
que el compilador puede econtrar dichos elementos.
import utilidades.Teclado; // Importa sólo la
clase Teclado
import utilidades.graficos.*; // Importa
todas las clases públicas del paquete utilidades.graficos
Se pueden importer tantas clases como se desee.
Las sentencias import se escriben justos después de la sentencia
package (si existe).
Vamos a ojear el paquete java.lang definido en la librería estándar de
java:
La librería estándar contiene más de 200 paquetes que agrupan a más de
3500 clases.
Versión con traducción no oficial al español:
-
Podemos conectar Eclipse con la documentación estándar
de Java de manera que nos ofrezca información contextual de cada clase, método
o propiedad que utilicemos.
0 comments:
Publicar un comentario