Encapsulación¶
El mecanismo que permite restringir el acceso a componentes en los objetos, es decir, ofrece protección a los miembros de la clase de cualquier acceso externo no autorizado es la encapsulación. No estamos hablando de seguridad, hablamos más bien, de restringir el acceso desde fuera al funcionamiento interno de una clase, es decir, ocultar el trabajo interno que se realiza en una clase.
Veamos un ejemplo de una clase que no usa encapsulación y así nos permitirá entender mejor porqué la encapsulación en algo positivo. Luego veremos cómo se haría con encapsulación.
Ejemplo de programa SIN encapsulación¶
Acceso a la clase y modificación de funcionalidad¶
Primero creamos una clase Player
con los siguiente campos:
public class Player {
//propiedades públicas
public String name;
public int health;
public String weapon;
public void applyDamage(int damage) {
this.health -= damage;
if (this.health <= 0 ) {
System.out.println("Player died");
}
}
public int getActualHealth() {
return health;
}
}
Ahora implementamos el main
en otra clase:
public class Main {
public static void main(String[] args) {
Player player = new Player();
player.name = "Patricia";
player.health = 50;
player.weapon = "flamethrower";
int damage = 30;
player.applyDamage(damage);
System.out.println("The actual health is " + player.getActualHealth());
damage = 10;
player.health = 100;
player.applyDamage(damage);
System.out.println("The actual health is " + player.getActualHealth());
}
}
Observamos que podemos inicializar directamente los campos del objeto Player
a través de clase externa porque hemos establecido la visibilidad como public
.
Además, vemos que podemos causar daño al jugador, pero seguidamente podemos darle más vida puesto que tenemos control sobre los atributos del jugador. Por lo que, al poder acceder a esos campos directamente potencialmente estamos abriendo la aplicación y permitiendo cambiar el comportamiento.
Ya que nosotros no queremos que se le pueda dar vida. Solo aplicar daño.
Consecuencias de cambios internos de la clase¶
Imagina que queremos que el name se guarde en mayúsculas siempre, si el campo es público para su asignación, el usuario de la clase no tiene que conocer que las especificaciones de la clase es que se guarde el nombre en mayúsculas.
Player player = new Player();
//error en la especificaciones de la clase
player.name = "pepito";
Importante
La clase tiene que ser responsable del mantenimiento correcto de sus propiedades
Garantizar que los valores cumplan la especificaciones de la clase¶
Como no hemos definido un constructor, puede ser que la clase externa que usa Player
no defina las variables de forma válida. Por tanto, no podemos garantizar que el uso del objeto jugador será correcto.
Imagina que al crear un objeto de la clase Player
nos olvidamos de darle valor al campo de health
:
Al intentar aplicar daño no se aplicará de forma correcta. Por tanto, es conveniente definir un constructor para garantizar que el objeto se construye de forma correcta y además si queremos agregar algún tipo de validación también podríamos realizarla en el constructor.
Ejemplo de programa con encapsulación¶
Vamos a ver cuál es la forma correcta realizar el ejemplo anterior usando encapsulación:
- Las propiedades tienen que ser privadas
- La asignación de las propiedades y la corrección de los datos se realizará mediante setters y constructores
- La lectura de propiedades, si es necesario, se realizará mediante getters
public class Player {
//propiedades privadas
private String name;
//valor por defecto
private int health = 100;
private String weapon;
private String status="vivo";
public Player(String name, int health, String weapon) {
//guardamos el nombre en mayúsculas. Los setter controlan la entrada correcta
setName(name);
if (0<health && health <= 100) {
this.health = health;
}
this.weapon = weapon;
}
public void applyDamage(int damage) {
this.health -= damage;
if (this.health <= 0 ) {
System.out.println("Player died");
status="muerto";
}
}
//getter y setter
public void setName(String name) {
//controlamos que siempre se guarde en mayúsculas
this.name = name.toUpperCase();
}
public int getHealth() {
return health;
}
//podemos saber el estado del personaje, pero no modificarlo directamente
public String getStatus() {
return status;
}
}