Saltar a contenido

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:

Herencia

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:

Encapsulacion

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

encapsulación

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