jemluz / java-basic

A starter java project to deal with basic concepts
1 stars 0 forks source link

POO (Encapsulamento) #5

Open jemluz opened 8 months ago

jemluz commented 8 months ago

Encapsulamento

Quando criamos uma classe, dentro dela haverá a declaração dos atributos e métodos necessários para nutrir e manipular os objetos que serão criados a partir dessa classe.

No entanto, se alguns desses atributos ou métodos forem facilmente visíveis e modificáveis, isso pode dar liberdade para que alterações sejam feitas, resultando em efeitos colaterais imprevisíveis.

Rótulos public and private

Na POO, um atributo ou método que não é visível de fora do próprio objeto é chamado de "privado" e quando é visível, é chamado de "público".

Quando um atributo/método for definido como privado, dizemos que ele está encapsulado.

Esse encapsulamento de atributos e métodos impede o chamado vazamento de escopo, onde um atributo ou método é visível por alguém que não deveria vê-lo, como outro objeto ou classe.

Isso evita a confusão do uso de variáveis globais [citada na issue anterior] no programa, deixando mais fácil de identificar em qual estado cada variável vai estar a cada momento do programa, já que a restrição de acesso nos permite identificar quem consegue modificá-la.

Getters

São métodos focados em fornecer o valor de um atributo privado. Você não mais consultará o atributo diretamente, até por quê o rotulo de private impedirá, ao invés disso tu chama um get (que terá rótulo público) e ele sim irá consultar a propriedade para você. image

Como um cliente que chama um garçon para perguntar a ele se na cozinha tem algum ingrediente especifico. O cliente não irá levantar e verificar por ele mesmo.

Setters

São métodos focados em alterar o valor de um atributo privado. Você não mais consultará o atributo diretamente, até por quê o rotulo de private impedirá, ao invés disso tu chama um set (que terá rótulo público) passando para esse método o novo valor desejado ele irá realizar a operação de definir esse novo valor.

Como quem chama o garçon para fazer uma alteração no pedido que já foi solicitado. O cliente não irá levantar e fazer isso por ele mesmo.

Exemplo

Criamos uma classe de conta bancaria, com 2 métodos, depositar e sacar.

public class Account {

  String number;
  String holder;
  double balance;

  void deposit(double value) {
    if (value > 0) {
      balance =+ value;
      System.out.println("Your deposit of " + value + " has been fulfilled! Your current balance is " + balance);
    } else {
      System.out.println("The deposit value is invalid! Try with another one.");
    }
  }

  void withdraw(double value) {
    if (value > 0 && value <= balance) {
      balance =- value;
      System.out.println("Your deposit of " + value + " has been fulfilled! Your current balance is " + balance);
    }
  }
}

Nesse caso eu tenho a liberdade para alterar as propriedades de um obj da classe diretamente.

package bank;

public class TestAccount {
  public static void main(String[] args) {
    Account accountObject1 = new Account();

    // without encapsulation
    // data can be accessed and changed easily, there is no control over the confidenciality and security of the system, this is dangerous!
    accountObject1.number = "000000-1";
    accountObject1.holder = "John Doe";
    accountObject1.balance = 100;

    accountObject1.deposit(50);
    accountObject1.withdraw(100);

    accountObject1.balance = 20;
  }
}

Abaixo encapsularemos a classe conta, adicionando os rótulos private e public aos seus atributos e métodos. Além também de adicionar os Getters e Setters.

public class Account {

  private String number;
  private String holder;
  private double balance;

  public Account() {
    balance = 0;
  }

  public double getBalance() {
    return balance;
  }

  public String getHolder() {
    return holder;
  }

  public String getNumber() {
    return number;
  }

  public void setBalance(double balance) {
    this.balance = balance;
  }

  public void setHolder(String holder) {
    this.holder = holder;
  }

  public void setNumber(String number) {
    this.number = number;
  }

  void deposit(double value) {
    if (value > 0) {
      balance =+ value;
      System.out.println("Your deposit of " + value + " has been fulfilled! Your current balance is " + balance);
    } else {
      System.out.println("The deposit value is invalid! Try with another one.");
    }
  }

  void withdraw(double value) {
    if (value > 0 && value <= balance) {
      balance =- value;
      System.out.println("Your deposit of " + value + " has been fulfilled! Your current balance is " + balance);
    }
  }
}

Após o encapsulamento todo o acesso e manipulação de um objeto da classe é feito através de getters e setters.

package bank;

public class TestAccount {
  public static void main(String[] args) {
    Account accountObject1 = new Account();

    // with encapsulation
    accountObject1.setNumber("00000-1");
    accountObject1.setHolder("John Doe");

    accountObject1.deposit(100);
    accountObject1.withdraw(50);
    accountObject1.getBalance();
  }
}

Exemplos baseados no artigo: Encapsulamento, herança e polimorfismo: as principais características da POO

jemluz commented 8 months ago

Outros exemplos de encapsulamento

Se alguns desses atributos ou métodos forem facilmente visíveis e modificáveis, como o mecanismo de aceleração do carro, isso pode dar liberdade para que alterações sejam feitas, resultando em efeitos colaterais imprevisíveis. Nessa analogia, uma pessoa pode não estar satisfeita com a aceleração do carro e modifica a forma como ela ocorre, criando efeitos colaterais que podem fazer o carro nem andar, por exemplo.

Como sabemos como o nosso carro acelera? É simples: não sabemos. Nós só sabemos que para acelerar, devemos pisar no acelerador e de resto o objeto sabe como executar essa ação sem expor como o faz. Dizemos que a aceleração do carro está encapsulada, pois sabemos o que ele vai fazer ao executarmos esse método, mas não sabemos como - e na verdade, não importa para o programa como o objeto o faz, só que ele o faça.

Não sabemos como o carro sabe qual velocidade mostrar no velocímetro ou como ele calcula sua velocidade, mas não precisamos saber como isso é feito. Só precisamos saber que ele vai nos dar a velocidade certa. Ler ou alterar um atributo encapsulado pode ser feito a partir de getters e setters (colocar referência).

Exemplo de encapsulamento aplicado a uma classe Carro, em java:

public class Carro {
    private Double velocidade;
    private String modelo;
    private MecanismoAceleracao mecanismoAceleracao;
    private String cor;

    /* Repare que o mecanismo de aceleração é inserido no carro ao ser construído, e
        não o vemos nem podemos modificá-lo, isto é, não tem getter nem setter.
        Já o "modelo" pode ser visto, mas não alterado. */
    public Carro(String modelo, MecanismoAceleracao mecanismoAceleracao) {
        this.modelo = modelo;
        this.mecanismoAceleracao = mecanismoAceleracao;
        this.velocidade = 0;
    }

    public void acelerar() {
        this.mecanismoAceleracao.acelerar();
    }

    public void frear() {
        /* código do carro para frear */
    }

    public void acenderFarol() {
        /* código do carro para acender o farol */
    }

    public Double getVelocidade() {
        return this.velocidade
    }

    private void setVelocidade() {
        /* código para alterar a velocidade do carro */
        /* Como só o próprio carro deve calcular a velocidade, 
            esse método não pode ser chamado de fora, por isso é "private" */
    }

    public String getModelo() {
        return this.modelo;
    }

    public String getCor() {
        return this.cor;
    }

    /* podemos mudar a cor do carro quando quisermos */
    public void setCor(String cor) {
        this.cor = cor;
    }
}

Exemplo de encapsulamento aplicado a uma classe Carro, em python:

class Carro:
    def __init__(self, modelo, mecanismoAceleracao):
        self.__modelo = modelo;
        self.__velocidade = 0
        self.__mecanismoAceleracao = mecanismoAceleracao

    def acelerar(self):
        mecanismoAceleracao.acelerar()

    def frear(self):
        # Codigo para frear o carro

    def acenderFarol(self):
        # Codigo para acender o farol do carro

    def getVelocidade(self):
        return self.velocidade

    def __setVelocidade(self):
        # Codigo para alterar a velocidade por dentro do objeto

    def getModelo(self):
        return self.modelo

    def getCor(self):
        return self.cor

    def setCor(self, cor):
        self.cor = cor
jemluz commented 8 months ago

Rótulo estatic

Em métodos

Quando utilizamos a palavra static em um método de uma classe, permitimos que esse método seja chamado sem precisar criar uma instancia da classe.

Por isso sempre que rodamos a função main para executar nosso programa, tomamos o cuidado de declarar ela como static, do contrário precisariamos instanciar a classe do programa antes.

Em variáveis

Se usado em variáveis, é como se fixasse o valor. Se você não coloca static nas variáveis, elas ficam com uma cópia diferente pra cada objeto (o que é o mais usual).

Blocos de inicialização

Blocos de inicialização são bloquinhos com coisas que você quer que executem quando a classe for carregada na virtual machine, o que acontece apenas uma vez (é bem pouco usado, é mais só pra saber que existe).

Por exemplo:

static {
    System.out.println("O classloader carregou essa classe!!");
}

Classes internas

E classes internas são classes dentro de classes, que também podem ser static (também pouco usado, mas saiba que existe). Por exemplo:

class Externa {

    static class Interna {

    }

}