Hola
Encapsular simplemente consiste en ocultar los miembros de una clase (atributos, métodos...) de forma que no se pueda acceder a ellos desde "fuera" de la propia clase.
Esto lo hacemos con los modificadores de acceso: public, private, protected...
Es decir, cuando en una clase declaramos sus atributos como private, pues ya estamos encapsulando.
Por lo general, los atributos vamos a querer que sean privados y los métodos querremos que sean públicos.
De hecho, usaremos métodos públicos (getters/setters) para dar acceso desde el exterior a los atributos privados.
Pero, ¿entonces para que hacemos atributos privados si luego los "desprotegemos" dando acceso mediante métodos públicos?
La cuestión es que si hacemos los atributos públicos, no tendremos forma de controlar que valor reciben.
En cambio, dando acceso mediante métodos, aquí si que podemos poner controles, si queremos.
Por ejemplo, supón que creamos una clase Persona donde hacemos
public un atributo que representa la
edad.
Puede que alguien que esté usando nuestra clase haga un programa sin un control de "calidad de datos" mínimo y ocurran cosas como que el atributo edad reciba el valor 1500, o el valor -45, es decir, valores que son absurdos para la edad de una persona.
Si ese programador es descuidado, no es culpa nuestra. Pero podemos poner de nuestra parte para que nuestra maravillosa clase Persona, sea más segura y fiable.
Así que lo que haremos será "encapsular" el atributo
edad para que no se pueda acceder directamente a él.
Escribiremos un método para dar acceso al atributo, uno llamado
setEdad(int edad);, pero dentro del método pondremos nosotros un control de "calidad de datos".
Con un if podemos comprobar si la edad recibida entre paréntesis, es un valor apropiado.
Si es un valor negativo o es demasiado alto, lo rechazaremos y mostraremos mensaje de aviso en pantalla.
Y si es un valor aceptable, pues entonces sí se lo asignaremos al atributo
edad que tenemos "encapsulado" y protegido.
De esta manera, nuestra clase ya impone ciertas reglas y límites para los valores de los atributos. Y esto hace que el programador que use nuestra clase, tenga que ser más cuidadoso con su código ya que no podrá registrar "personas" si no se ocupa de controlar mejor la calidad de los datos que vaya a recoger.
Bien, tras aclarar que es el encapsulamiento y cuál es su propósito, vamos a ver el ejercicio.
Piden clases, encapsulamiento, herencia y control de excepciones. Es un ejercicio sencillo donde la herencia no parece necesaria, pero bueno, podemos "forzarla" un poco para incluirla.
Mi propuesta sería crear una clase
Usuario y una clase
Mantenimiento.
Esta última, sería la clase madre de otras tres subclases:
Reparacion,
Calibracion y
OtrosAsí ya tenemos herencia.
Esta podría ser la clase
Usuario:
public class Usuario {
private int numCliente;
private String nombre;
private String celular;
public Usuario(int numCliente, String nombre, String celular) {
this.numCliente = numCliente;
this.nombre = nombre;
this.celular = celular;
}
public int getNumCliente() {
return numCliente;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getCelular() {
return celular;
}
public void setCelular(String celular) {
this.celular = celular;
}
@Override
public String toString() {
return String.format("Nº Cliente: %d\nNombre: %s\tCelular: %s", numCliente, nombre, celular);
}
}
Esta la clase
Mantenimiento, que como está destinada a ser clase "madre", la podemos declarar como abstracta. Esto es porque luego en el programa, no vamos a querer crear objetos de esta clase madre, si no que vamos a querer crear solo objetos de sus clases hijas.
public abstract class Mantenimiento {
protected Usuario usuario;
protected String equipo;
public Mantenimiento(Usuario usuario, String equipo) {
this.usuario = usuario;
this.equipo = equipo;
}
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
public String getEquipo() {
return equipo;
}
public void setEquipo(String equipo) {
this.equipo = equipo;
}
@Override
public String toString() {
return String.format("Datos Usuario: %s\n\t---------------\nEquipo: %s\n\t---------------\n",
usuario, equipo);
}
}
Fíjate que aquí los atributos están como protected y no como private.
Podrían ser private, pero cuando creamos clases abstractas, muchas veces vamos a querer que sus clases hijas puedan acceder directamente a los atributos que están heredando de su clase madre.
Con protected, damos acceso solo a las clases hijas. Siguen estando encapsulados, pero le abrimos las puertas a los "legítimos herederos".
Vamos a ver estas clases hijas.
Esta sería
Calibracion, que es casi idéntica a su clase madre, solo varía su método toString().
public class Calibracion extends Mantenimiento {
public Calibracion(Usuario usuario, String equipo) {
super(usuario, equipo);
}
@Override
public String toString() {
return String.format("%sTipo Manteminiento: Calibracion", super.toString());
}
}
La clase
Reparacion si añade un nuevo atributo para indicar que pieza es la que requiere ser reparada:
public class Reparacion extends Mantenimiento {
private String piezaFalla;
public Reparacion(Usuario usuario, String equipo, String piezaFalla) {
super(usuario, equipo);
this.piezaFalla = piezaFalla;
}
public String getPiezaFalla() {
return piezaFalla;
}
public void setPiezaFalla(String piezaFalla) {
this.piezaFalla = piezaFalla;
}
@Override
public String toString() {
return String.format("%sTipo Manteminiento: Reparacion\nPieza que falla: %s",
super.toString(), piezaFalla);
}
}
Y la clase
Otros, que usa un atributo extra para pedir una descripción del servicio que se necesita:
public class Otros extends Mantenimiento {
private String descripcion;
public Otros(Usuario usuario, String equipo, String descripcion) {
super(usuario, equipo);
this.descripcion = descripcion;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
@Override
public String toString() {
return String.format("%sTipo Mantenimiento: Otros\nDescripcion: %s", super.toString(), descripcion);
}
}
Con esto, ya podemos hacer una clase main donde usar un ArrayList de tipo
Mantenimiento, donde registraremos objetos
Calibracion,
Reparacion y
OtrosComo todas son hijas de
Mantenimiento es posible juntar objetos de esas tres clases en esa colección.
Es un ejemplo muy sencillo, un menú muy básico para registrar mantenimientos y mostrar un listado:
import java.util.ArrayList;
import java.util.Scanner;
public class ManteminientoEquipos {
private static ArrayList<Mantenimiento> mantenimientos = new ArrayList<Mantenimiento>();
private static Scanner teclado = new Scanner(System.in);
public static void main(String[] args) {
int opcion = 0;
do {
System.out.println("\n\t\tMANTENIMIENTO EQUIPOS");
System.out.println("\t\t------------- -------\n");
System.out.println("[1] --- Registrar nuevo Mantemimiento");
System.out.println("[2] --- Listar Mantenimientos registrados");
System.out.println("[3] --- TERMINAR PROGRAMA");
System.out.print("Elija opcion: ");
try {
opcion = Integer.parseInt(teclado.nextLine());
switch(opcion) {
case 1:
nuevoManteminiento();
break;
case 2:
listarMantenimientos();
break;
case 3:
System.out.println("\n\t\tFIN DE PROGRAMA");
break;
default:
System.out.println("Opcion inexistente");
}
}
catch(NumberFormatException nfe) {
System.out.println("ERROR: Introduzca solo números");
opcion = 0;
}
}while(opcion != 3);
}
private static void nuevoManteminiento() {
//Pedimos datos cliente
System.out.println("\nDATOS DEL CLIENTE:");
System.out.print("Nombre Completo: ");
String nombre = teclado.nextLine();
System.out.print("Celular: ");
String celular = teclado.nextLine();
//Numero cliente se genera según longitud del arraylist
int numCliente = 1000 + mantenimientos.size();
//Preguntamos equipo a mantener
System.out.print("\nEQUIPO A MANTENER: ");
String equipo = teclado.nextLine();
//Preguntamos tipo mantenimiento
int tipo = 0;
while (tipo < 1 || tipo > 3) {
try {
System.out.println("\nTIPO DE MANTEMIENTO:");
System.out.println("[1] -- Calibracion\n[2] -- Reparacion\n[3] -- Otros");
System.out.print("Elija: ");
tipo = Integer.parseInt(teclado.nextLine());
switch (tipo) {
case 1:
mantenimientos.add(new Calibracion(new Usuario(numCliente, nombre, celular), equipo));
break;
case 2:
System.out.print("Pieza que falla: ");
String pieza = teclado.nextLine();
mantenimientos.add(new Reparacion(new Usuario(numCliente, nombre, celular), equipo, pieza));
break;
case 3:
System.out.print("Descripcion del mantenimiento: ");
String descripcion = teclado.nextLine();
mantenimientos.add(new Otros(new Usuario(numCliente, nombre, celular), equipo, descripcion));
break;
default:
System.out.println("Tipo inexistente");
}
}
catch(NumberFormatException nfe) {
System.out.println("ERROR: Introduzca solo números");
tipo = 0;
}
}//Fin del while
//Mostramos datos del mantenimiento registrado
System.out.println("\nMANTENIMIENTO REGISTRADO:\n");
System.out.println(mantenimientos.get(mantenimientos.size() - 1));
}
private static void listarMantenimientos() {
System.out.println("\nLISTADO DE MANTENIMIENTOS");
System.out.println("------- -- --------------\n");
for (Mantenimiento mant: mantenimientos)
System.out.println("\n" + mant + "\n");
}
}
Fíjate que al listar los objetos del ArrayList, aunque están todos
guardados como Mantenimiento, luego al imprimirse en pantalla cada objeto se comporta como un
Calibracion, o un
Reparacion o un
Otros, según corresponda.
Esto se llama
polimorfismo, otro término más que hay que conocer en POO
MANTENIMIENTO EQUIPOS
------------- -------
[1] --- Registrar nuevo Mantemimiento
[2] --- Listar Mantenimientos registrados
[3] --- TERMINAR PROGRAMA
Elija opcion: 2
LISTADO DE MANTENIMIENTOS
------- -- --------------
Datos Usuario: Nº Cliente: 1000
Nombre: Lucas Ramirez Celular: 657908742
---------------
Equipo: Impresora de tinta HP900C
---------------
Tipo Manteminiento: Calibracion
Datos Usuario: Nº Cliente: 1001
Nombre: Sara Pelaez Celular: 634561290
---------------
Equipo: Laptop Lenovo CN2000
---------------
Tipo Manteminiento: Reparacion
Pieza que falla: Pantalla
Datos Usuario: Nº Cliente: 1002
Nombre: Jorge Morales Celular: 65533990
---------------
Equipo: Smartphone Samsung G300
---------------
Tipo Mantenimiento: Otros
Descripcion: Sustituir batería por una de 6500mAh
Pregunta si algo no ha quedado claro.
Un saludo.