Encapsulamento - Getters e setters; Validação de parâmetros e triggers

Computação II - Ciência da Computação


Prof.: Danilo S. Carvalho

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:

  1. Há algum problema em retornar campos de tipos complexos através de um método getter? De que forma poderíamos resolver isso?
  2. Porquê não deveríamos alterar o estado do objeto em um método getter?

Exercício:

  1. Adicione o conceito de caixa eletrônico ao sistema bancário simples (caso ainda não tenha feito), e implemente as operações deste sem que incluam conhecimento interno dos outros objetos do sistema.

Até a próxima aula!