Gets and Sets Erradication

Se esta classe te retorna cópias de seus atributos e faz cópia dos parametros dos métodos Sets. Isso te impede que vc altere os atributos sem passar pelos métodos da classe. Isso lhe mostra que vc ainda sim tem encapsulamento e esta mantendo sua composição.

A chave disso tudo é a cópia! Se vc alterar fora do objeto de origem só vai esta alterando a cópia e não o objeto de onde vc o extraiu!

Derrepente nos dois artigos eles não quiseram se aprofundar…[/quote]

A questão de encapsulamento, ao meu ver agora, é vc nao permitir que as classes de fora sequer “vejam” os atributos que é de conhecimento apenas da própria classe, e que as demais classes nao devem nem sequer saber da existência desses atributos.

1- Inicialmente utilizamos GETS e SETS, para que objetos externos não dependam diretamente da estrutura interna de outros objetos. Nos permitindo alterar a estrutura interna a qualquer momento sem afetar os objetos externos;

Codigo ruim com dependencia interna


class A {
 public int idade;
}

class UsaA {
 A a;

 public void usaA(){
    a.idade = 1;
 }
}

Tendo como base o código acima. Como eu faço agora que quero guardar a idade em uma string? Vou ter que alterar tanto o objeto A quano o que usa A.

Codigo com um pouco de encapsulamento


class A {
 private int idade;

 .... setIdade(int x){
  idade = x;
 }
}

class UsaA {
 A a;

 public void usaA(){
    a.setIdade(1);
 }
}

Tendo como base o código acima. Como eu faço agora que quero guardar a idade em uma string? Simples so alterar o set! Não preciso mais alterar o UsaA pois ele não depende mas da estrutura interna de A.

Solução só altero A


class A {
 private String idade;

 .... setIdade(int x){
  idade = x.toString;
 }
}

Este é o primeiro ganho que temos com a utilização de gets e sets!

Outro ganho é manter o estado de um objeto que podemos discutir depois....

Encapsulamento não é esconder informação em si, mas sim esconder a forma com essas informações são mantidas!

Oi pessoal

Vamos com calma. Nao quis dizer que voce nunca deve usar getter e setter, mas que voce nao deve coloca-los sem haver necessidade. Ate pode existir uma classes com 10 atributos e com 10 getters e setters, mas tem de ter um motivo excepcionalmente forte.

O exemplo da idade é na verdade um contra exemplo. Nao faz sentido voce ter um setIdade, ja que o ideal seria guardar a data de nascimento, e deixar o getIdade calculando a idade baseado nesse Calendar… nao haveria setters.

Claro, em alguma modelagem pode fazer sentido existir uma pessoa que mude de data de nascimento, que seria para corrigir erros.

No mundo das entidades, getters e setters vao aparecer com mais frequencia. Quanto mais logica de negocio e responsabilidade voce colocar neles, essa proporcao vai diminuir, pois eles vao parar de ser fantoches, como mostra o Phillip.

Sobre o getter ser mais lento, eh uma maneira do Phillip dizer. Porque no fundo, se precisar, o JIT da JVM vai fazer inline desse metodo e ele passara a ter exatamente a mesma performance do que se o atributo fosse publico…

Aliás, deve existir algum artigo por aí dizendo “setters considered harmful”, mostrando como é boa a ausência de setters e as vantagens de ter objetos imutáveis.

Rodrigo,

acho que estamos num Forum técnico, e os posts de muitas pessoas que ‘dizem o que querem’ me fizeram entender bastante coisas de OO. cuidado com esses comentarios[/quote]
quando eu falei isto eu quiz dizer que muitas pessoas falam o que querem isto é formação de opinião, e muitoas pessoas aceitam, pq são pessoas que precisam saber algo e se apoiam a primeira resposta.

com o java o conceito de copia e referencia perdeu meio que o sentido, isto e muito mais aparente quando se mexe com C ou C++ se vc for programar nestas linguagens o conceito de encapsulamento pede mesmo os getters e setters, e até que alguem prove que isto não é necessário eu continuo sabendo que os get e set são ferramentas de encapsulamento, agora se vc implementa uma logica de leitura e escrita o problema e todo seu.

Acho que o maior problema esta em dizer que o uso de GETS e SETS acaba com o encapsulamento! O que na maioria das vazes esta errado!

Até aquele uso mais escroto dos gets e sets trazem seus beneficios! Aqueles que nem sabem para que serve gets e sets e saem colocando em tudo quanto é lugar estão sendo beneficiados por encapsular a estrutura interna das suas classes! Podem até estar fazendo objetos sem responsabilidades, mas que estão encapsulando a estrutura interna das suas classes estão!

rs… engraçado isso aqui… Eu não disse que fazia sentindo. Só quiz exemplificar uma forma de dependencia interna que os gets e sets (métodos em geral) nos ajudam a eliminar. Encapsulando a estrutura interna da classe!

Dai vem o bom e velho concelho: Programa para interface. Que a maioria hj pensa que é sair tacando inteface em java para td quanto é lado. E na verdade eu acho que queria dizer programe com base nos métodos que um objeto te fornece e não com base nos dados de um objeto!

[quote=Paulo Silveira]
No mundo das entidades, getters e setters vao aparecer com mais frequencia. Quanto mais logica de negocio e responsabilidade voce colocar neles, essa proporcao vai diminuir, pois eles vao parar de ser fantoches, como mostra o Phillip.[/quote]

Não é por um objeto ter GETS e SETS que ele é dito fantoche!

Um o objeto pode ter zero de encapsulamento e mesmo assim não ser utilizado como um fantoche!

O que torna um objeto um fantoche é a forma de como vc o utiliza!

Posso ter um objeto com zero de encapsulamento e que possua toda sua responsabilidade definida! Se eu quiser me aproveitar da falta de encapsulamento eu posso utiliza-lo como fantoche agora se eu não quiser me aproveitar dessa falta de encapsulamento posso utiliza-lo sem ser como fantoche!

Agora tudo bem se um objeto não possui encapsulamento vamos ter tendencia em utiliza-lo como fantoches! Mas não necessariamente o faremos…

Concordo plenamente! Os métodos estão ai para nos ajudar a encapsular as coisas… mas não necessariamente vc vai implementa-los de forma que este encapsulamento aconteça!

A meu ver métodos em si (pode até incluir os gets e sets) podem nos proporcionar 3 tipos de encapsulamento.

1- Encapsulamento da estrutura interna da classe;
2- Encapsulamento da logica de manipulação dos dados da classe;
3- Encapsulamento do estado da classe.

Deixa eu ver se eu entendi: gets e sets improprios são somente aqueles que modificam um atributo de uma classe que na verdade não deveria alterar.

tipo:

public class Calculos {
	
	private double pi = 3.14159265;
	
	private double valorTotal;

	public double getPi() {
		return pi;
	}

	public void setPi(double pi) {
		this.pi = pi;
	}
	
	public double getValorTotal(){
		return pi + 10;
	}
}

No caso o set do pi é improprio, pois o mesmo não deve ser alterado, é uso exclusivo da classe, ninguem deveria ter o conhecimento que ele existe, certo?

Então gets e sets que alteram valores que são mutaveis, ou seja que são definidos pelo programador pra serem alterados mesmo que mais basicos como:

public void getDataNascimento() {
       // mesmo sem fazer nada aqui
        return dataNascimento;
  
  }

são validos, para evitar o alto acoplamento que o brunohansen explicou.

no caso do PI esta erado também a forma como vc declarou ele, pq ele é uma constante e em momento algum deve ser modificado entao pi deveria ser declarado assim:

[code]public class Math
{
public static final double PI = 3.1415678;

 ...

}[/code]

[quote=rodrigopmatias]no caso do PI esta erado também a forma como vc declarou ele, pq ele é uma constante e em momento algum deve ser modificado entao pi deveria ser declarado assim:

[code]public class Math
{
public static final double PI = 3.1415678;

 ...

}[/code][/quote]

Sim, até sei, mas coloquei somente como exemplo pra ficar obvio, mas pode ser qualquer atributo ou metodo que as outras classes não precisem saber que existe e nem mesmo pensar em usar.

Pensando de uma maneira OO:
Minha classe não sabe como um metodo de outra classe tratara internamente, o importante é saber o parametro que passarei (se passar) e o que eu espero de resposta.

Muito interessante este tópico…
Mas ficou uma duvida…

Um classe Pessoa que tem o atributo dtNascimento, e esta classe está mapeada para o hibernate, tem algum problema dela ter get e set para o dtNascimento ? O Hibernate precisa dos get e set dos atributos, não é ?

[quote=cleriston]Muito interessante este tópico…
Mas ficou uma duvida…

Um classe Pessoa que tem o atributo dtNascimento, e esta classe está mapeada para o hibernate, tem algum problema dela ter get e set para o dtNascimento ? O Hibernate precisa dos get e set dos atributos, não é ?[/quote]

O Hibernate só precisa de um construtor vazio na classe.

O Hibernate utiliza os gets e sets para poder ler e escrever os dados dos atributos.

Se o atributo for private, é impossível uma outra classe (como, por exemplo, o controlador do hibernate) acessar seus dados, nem mesmo usando Reflection (receberiamos algo como IllegalAccessException).

Não li o tópico do link fornecido mas estava lendo os posts do pessoal e vi que muitos não tem conhecimento do que eh OO realmente (pelo menos no que diz respeito apenas ao encapsulamento, já que OO não se resume a isso e ficaria muito grande - mais do que já está - falar sobre isso agora). Sugerir que seja feito acesso direto a atributos de uma classe é algo inaceitável. Fornecer apenas get/set não garante o total encapsulamento de uma classe, mas, como já foi dito, pelo menos encapsula a estrutura interna da classe. Quando outras classes só tem acesso aos métodos de uma classe, ela não sabe nem mesmo se realmente existe um atributo (poderia ter um metodo getIdade() sem nem sequer ter um atributo idade, por exemplo)

Encapsulamento não é algo a ser aplicado apenas a uma classe… muitos pensam apenas em relação a classe… vendo o encapsulamento de uma classe, seria simplesmente vc manter o que diz respeito a classe visível a ela apenas. Mas tb existe encapsulamento de módulos… dependendo da modelagem, é possível termos classes com membros de acesso default (sem modificador de acesso) para que os mesmos sejam acessados por outras classes de um mesmo pacote… isso varia de projeto pra projeto (embora o grande uso na prática seja do encapsulamento de classe apenas)

Na verdade o fato de tornar membros privados e fornecer metodos publicos de acesso não é nem encapsulamento em si… isso é apenas o conceito de information hiding, onde escondemos a estrutura interna da classe, como o proprio nome sugere.

Para de fato encapsularmos a classe é necessário disponibilizarmos para fora apenas métodos que façam parte do serviços que a classe oferece. Assim sendo, a primeira coisa a se fazer eh esconder os atributos. A segunda coisa é só criar metodos de acesso para aqueles atributos que faça sentido serem visíveis para fora da classe.

Um exemplo clássico da desvantagem de utilizar os atributos como public foi o “bug do milênio”, onde os anos eram representados por uma estrutura de 2 digitos… essa informacao da estrutura da classe estava disponivel para todo mundo… todos acessavam o atributo ano que tem 2 digitos… quando o ano 99 estava para virar 00 se mantivesse do jeito q estava, seria como se fosse o ano 1900, entao tornou-se necessario mudar o tipo do dado para pelo menos 4 digitos… com essa mudanca, como tudo ao redor tinha conhecimento da implementacao, todos os aplicativos foram atingidos e por isso teve uma grande preocupacao para nao quebrar outros pontos de diversas aplicacoes…

Resumindo a historia do bug, se ao inves de termos algo equivalente a
*** nesse exemplo, não estou usando sintaxe de java… os colchetes não estão representando um array…
*** imagine que é um tipo numérico e entre colchetes é informado o tamanho de dígitos.

public int[2] ano;

tivéssemos:

private int[2] ano;

public int[2] getAno(){ ... }
public void setAno(int[2] ano){ ... }

quando houve a necessidade da mudanca, soh precisariamos ter feito a seguinte alteracao:

private int[5] ano; //mudou aqui, mas isso fica interno e nao afeta nada la fora.

//mantém o método que todas as aplicações estavam chamando
public void setAno(int[2] ano){ 
    //inclui logica pra converter o ano de 2 digitos pra 5 digitos
    //ex. algo como this.ano = Integer.parseInt("019" + ano);
}

//mantém o método que todas as aplicações estavam chamando
public int[2] getAno(){  
    //inclui logica pra converter o ano de 5 digitos pra 2 digitos
}

//método a ser chamado após o ano 2000
public int[5] getAno(){ ... }

//método a ser chamado após o ano 2000
public void setAno(int[5] ano){ ... }

Tb é importante ressaltar que não é qualquer validação que deve ser colocada dentro dos sets. Apenas aquelas que são internas à classe. O ideal é fazer certos tipos de validação em um outro nível. Algo como se alguém chegou a chamar o setXXX é porque alguma camada acima já validou os dados e eu devo simplesmente atualizar o valor (mas muitas vezes isso é uma decisão de projeto).

[quote=RafaelVS]
Tb é importante ressaltar que não é qualquer validação que deve ser colocada dentro dos sets. Apenas aquelas que são internas à classe. O ideal é fazer certos tipos de validação em um outro nível. Algo como se alguém chegou a chamar o setXXX é porque alguma camada acima já validou os dados e eu devo simplesmente atualizar o valor (mas muitas vezes isso é uma decisão de projeto).[/quote]

Parabéns pela explicação… muito boa!
A respeito da citação acima, onde ficaria as validações? Apenas me de pistas, q eu procuro…
Vlw

Oi, Cleriston.

Em uma arquitetura em camadas, por exemplo, vc tem:

  1. GUI
  2. fachada
  3. classes de negócio (ex.: CadastroConta)
  4. classes repositorio (ex. RepositorioContaBDR)

Ortogonalmente, temos as classes basicas (ex. Conta), que são usadas nas demais camadas.

As camadas que estão nos níveis superiores chamam serviços das camadas abaixo.

Então, imagine que Conta tenha duas operações: creditar e debitar. Ambas atualizam o atributo saldo, uma incrementando uma valor e outra decrementando. Além disso, a operação debitar precisa validar se a conta tem saldo suficiente.

A) Dependendo do projeto, podemos na classe Conta criar simplesmente um método setSaldo(double valor) que atualizará o saldo com o valor informado, sem fazer nenhuma validação.

Na classe que trata das regras de negócio (aqui chamada de CadastroConta) deverá ter os métodos creditar() e debitar(). O método creditar() simplesmete somaria o valor e chamaria o setSaldo(novoValor) da conta. O método debitar() faz a validação, verificando se a conta tem saldo suficiente para debitar o valor informado e chama o método setSaldo(novoValor), onde o novoValor já é o valor final, após diminuir o saldo.

B) Outra opção, seria na própria classe Conta ter os métodos creditar() e debitar(), que funcionam como um “setSaldo” só que já contém implementação de regras de validação, se necessário. Aqui simplificaríamos os métodos na classe CadastroConta, que não precisariam fazer validação e simplesmente delegar a operação para a classe básica, que fará a validação.

Uma vantagem em tomar a decisão A é que vc está aumentando a possibilidade de reuso da classe Conta… já que ela não tem nenhum tipo de validação… por exemplo, se vc colocar o sistema em um outro banco onde a validação para a operação debitar é verificar se tem saldo com algum limite especial, a sua classe Conta não seria afetada… vc só precisaria mudar no método da classe CadastroConta. Uma desvantagem é que vc poderia, sem querer, através de algum método chamar diretamente o setSaldo(valor) podendo passar um valor sem fazer validação (mas se vc tomar a decisão de poder reusar a classe Conta, vc terá que tomar esse cuidado de não utilizar o método de maneira indevida)

Uma vantagem em tomar a decisão B é que ficará mais seguro… a partir de qualquer lugar vc pode chamar o método debitar() passando um valor inválido que o método irá validar corretamente e só atualizará os dados se tal operação for válida. Uma desvantagem é que se vc precisar reutilizar essa classe em um banco que usa uma política diferente para validação (como a que citei acima) vc precisaria escrever uma nova classe Conta para aquele banco.

A mesma idéia serve para o método creditar()… se ele é definido no CadastroConta e eu precisar reusar a classe Conta em um sistema que tem uma política diferente para creditar (por exemplo, se o saldo a ser creditado for maior do que X, será dado algum bônus para o cliente da conta) eu poderei fazer isso. Pois a classe Conta tem apenas um método setSaldo() sem validação e eu posso usa-lo tanto para creditar quanto para debitar (independente da regra envolvida)

Por isso falei que isso é uma questão de decisão de projeto… não tem a forma certa ou errada, vc é quem decide, nesse caso, se vai querer integridade dos dados ou se vai querer maior possibilidade de reuso.

RafaelVS muito obrigado por sua explanação…
Só mais uma dúvida…
No caso B qual seria a função da classe CadastroConta (já que ela nao irá fazer as validações) ?

Desde já fico grato

[color=red]Obs : Editei o nome “RafalVS” para “RafaelVS”[/color]

A função dela seria apenas possibilitar a extensibilidade… se vc um dia quisesse adicionar alguma regra de validação, vc nao tocaria na classe básica. Você apenas adicionaria a validação na classe ContaCadastro.

Com a implementação atual, ela simplesmente faria conta.debitar()… mas futuramente poderia fazer if(nova_validacao){ conta.debitar(); } e o método debitar de conta nao seria atingido.

É a mesma idéia que o pessoal falou sobre ter sets para atualziar valores de um atributo, mesmo que não faça nada atualmente… já pensando em alguma necessidade de modificação futura… só que aqui estamos pensando em um nível mais alto… não estando pensando mais em manipulação de atributos, mas sim em manipulação de classes.

Além disso, a idéia da arquitetura em camadas é de modularizar o sistema… separando tudo que é estrutura do objeto em uma camada, tudo que está relacionado a forma de armazenamento em outra, tudo que está relacionado a regras de negócio em outra, tudo que está relacionado a distribuição em outra, tudo que está relacionado a interface grafica em outra, e por aih vai… Com isso, seu sistema vai ficar bem “plug and play”, isto é, se vc quiser colocar uma nova validação, vc o faz sem afetar qualquer codigo de classe basica, de repositorio, de interface, etc… é apenas tirar a implementacao antiga e colocar a nova…

Então, na opção B, mesmo que a validação estivesse na classe básica (o que de certo modo estaria ferindo a idéia da arquitetura em camadas), ainda seria útil existir CadastroConta, pois isso é uma maneira de vc dizer que existe uma forma de validação (embora atualmente não faça validação, existe o espaço para vc colocar a validação sem afetar o restando do sistema).

Se quiser mais informações, sugiro que vc pesquise sobre arquitetura em camadas… não pesquisei agora, mas acredito que na internet tenha muitas fontes sobre isso.

[]'s