Nessa aula, continuaremos a explorar os princípios de encapsulamento através da ocultação total do estado dos objetos.
Ao final da aula, saberemos como dedicar métodos ao acesso e controle do estado de objetos.
Sabemos que ocultar os detalhes de implementação de uma classe nos traz certas vantagens.
Vamos agora expandir esse princípio, e considerar que o estado interno de qualquer objeto deve sempre estar oculto.
Ou seja, apenas o próprio objeto pode saber e manipular o seu próprio estado.
Isso implica em todos os atributos de uma classe serem privados (campos private).
Logo, no restante do programa, o estado de um objeto só pode ser alterado através de métodos públicos.
Fica então a cargo do objeto emitir informações baseadas em seu estado, e manipular o mesmo através de seus métodos públicos.
O padrão mais simples pelo qual esse princípio é aplicado são os métodos getter e setter, que emitem e alteram respectivamente, dados sobre o objeto.
Eles são tipicamente escritos para os atributos da classe os quais deseja-se tornar acessíveis para outras partes do programa, mas ainda sobre controle do objeto.
Funcionam assim:
Se quisermos oferecer acesso de leitura ao atributo cpf da classe Pessoa, criamos um método getCpf() que oferece acesso controlado ao atributo.
No caso mais simples, o atributo é retornado diretamente, mas podemos aplicar alguma transformação, como formatação da string.
Se quisermos oferecer acesso de escrita ao ao atributo cpf da classe Pessoa, criamos um método setCpf() que altera o valor do atributo conforme um parâmetro (argumento do método).
public class Pessoa {
private String cpf;
...
}
public class Pessoa {
private String cpf;
...
public String getCpf() {
return cpf;
}
}
public class Pessoa {
private String cpf;
...
public String getCpf() {
return formataCpf(cpf);
}
}
public class Pessoa {
private String cpf;
...
public String getCpf() {
return formataCpf(cpf);
}
public void setCpf(String cpf) {
this.cpf = cpf;
}
}
Para garantir que o estado de um objeto será sempre válido, podemos verificar os parâmetros passados ao método setter, lançando e capturando exceções, ou fazendo ajustes antes de mudar efetivamente um campo.
Esse processo é conhecido como validação de parâmetros, e o valor efetivo do campo pode ser completamente distinto de sua representação publica através dos métodos getter e setter.
Por exemplo, o campo cpf da classe pessoa poderia ser mudado para o tipo int[] e ainda sim mantermos o getter como public String getCpf() e o setter como public void setCpf(String cpf), fazendo as transformações apropriadas.
public class Pessoa {
private String cpf;
private ValidadorCpf validador;
...
public void setCpf(String cpf) {
try {
this.cpf = validador.validarCpf(cpf);
}
catch (CpfFormatException e) {
...
}
}
}
Os métodos getter e setter também podem ser usados para disparar ações, por exemplo informando a outros objetos que o seu estado foi modificado.
Esse tipo de ação é conhecida como trigger (gatilho).
public class Pessoa {
private String cpf;
private ValidadorCpf validador;
...
public void setCpf(String cpf) {
try {
String novoCpf = validarCpf(cpf);
validador.informaMudanca(this.cpf, novoCpf);
this.cpf = novoCpf;
}
catch (CpfFormatException e) {
...
}
}
}
Agora entendemos um pouco mais sobre os princípios envolvidos no encapsulamento e como aplicá-los às nossas classes usando os métodos de acesso getters e setters.
Também pudemos ver que o controle de acesso ao estado interno dos objetos nos traz novas possibilidades, como a verificação de parâmetros e triggers.
Que tipo de verificação faríamos ao construir métodos para fazer uma transferência bancária em nosso sistema bancário simples?
Perguntas:
Exercício:
Até a próxima aula!