No es un bug, es una característica no documentada

21/3/15

Programación. Clases, objetos y paquetes (II)

19:06 Posted by Inazio , No comments

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