Gets and Sets Erradication

56 respostas
fabim

Boa tarde guj,

Li o artigo “Fantoches”, do pcalcado no wikipedia. Faz muito sentido.

Entretanto, uma frase me deixou um pouco confuso. Ele pergunta o seguinte no artigo: “… (afinal, o que são get e set além de um jeito mais lento de fazer um atributo público?)…”

Nao concordo muito com essa afirmação. Por exemplo, eu poderia ter o seguinte método na minha classe “Individuo”:

public void setDataNascimento(Timestamp data) throws Exception {

     if ( data.after( Utilities.dataAtual() ) {
          throw new Exception("Data nao pode ser superior a data de Hoje.");
     }

}

que é um dos propósitos de metodos set, na minha opiniao.

O que eu axo totalmente desnecessario mesmo seria algo do tipo:

public void setDataNascimento( Timestamp data ) {

     this.dataNascimento = data;

}

:roll:

56 Respostas

rodrigopmatias

hehe muita gente diz o que quer e muitas pessoas aceitam. boa!

vanzella

muito legal o topico, mas mesmo o mais basico como:

public void setDataNascimento( Timestamp data ) {
 
      this.dataNascimento = data;
 
 }

não seria inutil, não consigo ver outra forma de encapsular isso. Ja imaginou a zona que seria se os atributos fossem publicos, todo mundo metendo a mão. Bom sei lá, fala ai galera…

rodrigopmatias

para linguagens que não tem refletion para ser utilizado em automatizadores como por exemplo em dispositivos que serializam informação, mas uso mais como um padrão de desenvolvimento e as vezes fasso coisas sem sentido como foi exposto pelo amigo.

esb

vanzella:
muito legal o topico, mas mesmo o mais basico como:

public void setDataNascimento( Timestamp data ) {
 
      this.dataNascimento = data;
 
 }

não seria inutil, não consigo ver outra forma de encapsular isso. Ja imaginou a zona que seria se os atributos fossem publicos, todo mundo metendo a mão. Bom sei lá, fala ai galera…

E com o getDataNascimento() eu não meto a mão?

vanzella

nos métodos da pra usar synchronizer, rsrs.

Na verdade uso sem saber o real motivo tbm.
Uma padronização da sun, que nunca parei pra analisar.

esb:
vanzella:
muito legal o topico, mas mesmo o mais basico como:

public void setDataNascimento( Timestamp data ) {
 
      this.dataNascimento = data;
 
 }

não seria inutil, não consigo ver outra forma de encapsular isso. Ja imaginou a zona que seria se os atributos fossem publicos, todo mundo metendo a mão. Bom sei lá, fala ai galera…

E com o getDataNascimento() eu não meto a mão?

Paulo_Silveira

Nao é bem isso que o Phillip quer dizer.

Existem casos que fazem sentido o setter, mesmo sem validacao dentro. Porque a validacao pode vir a ser necessaria DEPOIS.

Mas o ruim de getters e setters é que muitas vezes eles sao criados sem necessidade alguma! Poderiamos dar mais logica ao objeto de uma série de maneiras.

fabim

Amazing… vlw paulo pelo artigo…

agora comprendo perfeitamente que so se deve “abrir” o acesso aos atributos que realmente sao essencias à interface publica da classe. O que é de conhecimento apenas da propria classe, e que as demais classes nem devem saber que existe, fica eternamente privado, e ISSO SIM quer dizer encapsulamento.

agora, uma pergunta…

pq que RAIOS ferramentas como o eclipse, JDev, tem a tal da “Generate getters and setters”? Agora entendo perfeitamente que isso é um veneno, e devia ter seu nome trocado pra “Quebrar o encapsulamento da classe”.

Duende_Macabro

Nessas ferramentas voce pode escolher de quais atributos e se set e/ou get.

:slight_smile:

Wolf_X

adorei o artigo !!!
mto bom mesmo Paulo
vlw
ainda mais pra quem ta começando a dominar OO como eu , eh uma mao na roda :smiley:

ViniGodoy

Fora o fato de se fazer o setter sem pensar. Por exemplo:

public class Semaforo {
   enum Cor {VERMELHO, AMARELO, VERDE};

   private Cor cor;

   public void setCor(Cor cor) { 
      this.cor = cor;
   }

   public Cor getCor() {
      return Cor;
   }
}

Um set desses é claramente danoso. Um semáforo tem uma série de estados. Você não pode simplesmente passar do verde para o vermelho, sem ir para o amarelo. Nem tem qualquer sentido passar do vermelho para o amarelo. Poderíamos colocar essas regras usando exceptions, como descrito ali em cima, mas ainda teríamos problemas. O maior dele é que estaríamos delegando a implementação das trocas de estado para fora da classe, e não dentro, como deveria ser. Isso é uma violação do encapsulamento, não dos atributos, mas sim, da implementação.

O ideal então é eliminar o set e deixar apenas o método "proximaCor", que faria a troca correta de estado.

fabim
pcalcado

Sem contexto essa frase não faz sentido (e provoca uns comentários de quem não leu o artigo mas quer opinar mesmo assim, como vimos em resposta ao seu post).

Dentro do artigo ela está colocara logo após uma demonstração de que é possível ter exatamente o mesmo ‘encapsulamento’ (que [b]não é encapsulament/b]) provido por get/set com uma struct C, por isso que o texto afirma que get/set é um atributo público mais lento.

fabim

pcalcado:
fabiocsi:

Entretanto, uma frase me deixou um pouco confuso. Ele pergunta o seguinte no artigo: “… (afinal, o que são get e set além de um jeito mais lento de fazer um atributo público?)…”

Sem contexto essa frase não faz sentido (e provoca uns comentários de quem não leu o artigo mas quer opinar mesmo assim, como vimos em resposta ao seu post).

Dentro do artigo ela está colocara logo após uma demonstração de que é possível ter exatamente o mesmo ‘encapsulamento’ (que [b]não é encapsulament/b]) provido por get/set com uma struct C, por isso que o texto afirma que get/set é um atributo público mais lento.

sim…

Realmente só depois que o Paulo postou que eu consegui realmente enteder o siginficado dessa sua frase. btw excelente o artigo, me fez entender ‘finalmente’ o que é encapsulamento. Estou lendo mais da fragmental.

confesso que algumas das respotas foram um tanto meio que… rs… nada a ver

thx

fabim

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

vanzella

boa tarde fabiocsi,
Essa coisa de utilizar um padrão de projeto ou qualquer outra coisa sem saber a finalidade acontece com todo mundo, tenho certeza que todos já usaram alguma coisa sem saber o real motivo rsrs, mas o que difere o bom profissional é buscar saber o porque de estar usando, qual a finalidade.
Isso acontece muito, principalmente em Frameworks, muitos utilizam sem saber o que realmente acontece.
O uso de gets e sets é tão rotineiro que as vezes não fundamentamos o por que usar.

fabim

vanzella:
boa tarde fabiocsi,
Essa coisa de utilizar um padrão de projeto ou qualquer outra coisa sem saber a finalidade acontece com todo mundo, tenho certeza que todos já usaram alguma coisa sem saber o real motivo rsrs, mas o que difere o bom profissional é buscar saber o porque de estar usando, qual a finalidade.
Isso acontece muito, principalmente em Frameworks, muitos utilizam sem saber o que realmente acontece.
O uso de gets e sets é tão rotineiro que as vezes não fundamentamos o por que usar.

verdade :wink:

brunohansen

vanzella:
muito legal o topico, mas mesmo o mais basico como:

public void setDataNascimento( Timestamp data ) {
 
      this.dataNascimento = data;
 
 }

não seria inutil, não consigo ver outra forma de encapsular isso. Ja imaginou a zona que seria se os atributos fossem publicos, todo mundo metendo a mão. Bom sei lá, fala ai galera…

Os métodos GETS e SETS, a meu ver, vieram para prover 3 formas de encapsulamento:

1- Encapsula a manipulação de um objeto antes dele ser setado em outro objeto ou deixa um gancho para que esta manipulação seja feita posteriormente;

public void setDataNascimento( Timestamp data ) {
      // faz algo em data
      this.dataNascimento = data;
 
 }

2- Encapsula a manipulação de um objeto antes dele ser recuperado de outro objeto ou deixa um gancho para que esta manipulação seja feita posteriormente;

public void getDataNascimento() {
      // faz algo em data
       return dataNascimento;
 
 }

3- Encapsula o PROPRIO ATRIBUTO do objeto podendo ser adicionada manipulações. Em outras palavras aplica o conceito de composição!

public void setDataNascimento( Timestamp data ) {
      // faz algo em data
      this.dataNascimento = data.CLONE;
 
 }

public void getDataNascimento() {
      // faz algo em data
       return dataNascimento.CLONE;
 
 }

[]s


Está aberta a temporada de caça ao Shoes! O que essa galera viajante tem pego no seu pé heim rappa! Daqui a pouco vc esta igual ao Luca sendo apedrejado por paradas que não tem nada a vê!

fabim

Acho que vc precisa ler o artigo dele, e o do paulo, pq encapsulamento pelo que eu entendo agora nao tem nada a ver com get e set’s.

se vc tem 1 classe com 10 atributos, e pra todos os atributos tu tem set e get, entao o encapsualmento da sua classe é 0.

brunohansen

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…

fabim

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…

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.

brunohansen

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....
brunohansen

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

Paulo_Silveira

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…

Fabio_Kung

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.

rodrigopmatias

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
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.

brunohansen

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!

brunohansen

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!

brunohansen

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.

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…

brunohansen

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.
vanzella

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.

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:

public class Math
{
     public static final double PI = 3.1415678;
     
     ...
}
vanzella
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:
public class Math
{
     public static final double PI = 3.1415678;
     
     ...
}

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.

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 é ?

A

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 é ?

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

R

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).

cleriston

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).

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

R

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.

cleriston

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]

R

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

Rodrigo_Manhaes

É perfeitamente possível que uma classe acesse atributos privados de outra usando a API de Reflection. Quando você mapeia seus atributos usando JPA, por exemplo, é exatamente isto que acontece.

Na verdade, não. Imagine que você tem uma classe com um atributo numérico, digamos, salário, do tipo double e você cria um método

public double getSalario() { return this.salario; }

e descobre que o tipo double, por exemplo, não lhe fornece precisão suficiente e é necessário o uso de BigDecimal, não há como fazer isto sem quebrar o contrato da classe.

Dieval_Guizelini

Desculpem senhores,

eu li todos os post e como não vi nenhuma ocorrência, acho que posso estar fazendo coisas erradas.

Um dos motivos que utilizo os métodos gets/sets é para “criar” notificações para objetos monitores sobre a modificação de determinados atributos, como ocorre em vários componentes da Swing. O que é muito similar aos “eventos” da programação por evento.

logo ficou a dúvida, existe outra forma de identificar a mudança de conteúdo (ou estado) de uma propriedade no exato momento em que ela ocorre e em alguns casos antes que ela ocorra?

exemplo de método:

public void setNascimento(Date nasc) { for(AtributoModificadoNotificar aviso : listeners ) aviso.before... this.nascimento = nasc; for(AtributoModificadoNotificar aviso : listeners ) aviso.after... }

vw

Ps: Eu sei que posso criar uma thread que fica lendo o conteúdo de uma propriedade e comparando com um valor interno e quando ocorrer a mudança notificar outro objeto, mas não é o caso.

R

É perfeitamente possível que uma classe acesse atributos privados de outra usando a API de Reflection. Quando você mapeia seus atributos usando JPA, por exemplo, é exatamente isto que acontece.

Na verdade, não. Imagine que você tem uma classe com um atributo numérico, digamos, salário, do tipo double e você cria um método

public double getSalario() { return this.salario; }

e descobre que o tipo double, por exemplo, não lhe fornece precisão suficiente e é necessário o uso de BigDecimal, não há como fazer isto sem quebrar o contrato da classe.

Continuo achando que eh impossivel acessasr os atributos privados… no maximo quando vc mapeia seus atributos usando JPA, vc consegue porque vc tb cria os get/set que sao public… por Reflection vc nao consegue ler/escrever em um atributo privado… se isso fosse possivel, toda a “seguranca” de manter um atributo privado seria jogada fora.

No caso da necessidade de usar um BigDecimal, para nao quebrar o contrato da classe vc precisaria manter o public double getSalario(); e adicionar um public BigDecimal getSalarioNovo();… em seguida, tornar o antigo getSalario deprecated.

L

RafaelVS:

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.

Não é por nada não. Mas o argumento que você usou pra opção A é pouquíssimo convincente. Você acredita que se deva fazer a separação entre uma classe com métodos (CadastroConta) e uma classe com propriedades (Conta), pois a primeira pode variar entre bancos, e a segunda seria estático. Ora, quem garante que as propriedades de Conta também não mudam entre bancos? Isso não faz sentido!

Talvez um jeito de garantir reúso seja a utilização de design patterns como o Template Method, do jeito que você falou, o reúso não acontece.

R

Mas a idéia da opção A não eh GARANTIR o reuso… é AUMENTAR A POSSIBILIDADE de reuso. Se vc coloca validação no método vc vai estar amarrando a implementação… se não fizer isso, aumenta a possibilidade de reusar a classe em outro banco.

Se a conta de um banco A tem as mesmas propriedades da conta de um banco B mas possuem regras de validação diferentes, eu poderia reusar aquela classe se a validação não estiver na classe Conta… Mas se as contas possuem propriedades diferentes, aí eu n poderia reusar… mas pelo menos eu tenho a chance de reusar no primeiro caso.

E esse exemplo que eu dei foi bem simples, que fica facil de entender… tem casos onde as propriedades das classes mudam menos do que as regras de negócio.

Além disso, estou dando exemplos que motivem a implementação usando uma arquitetura em camadas (mantendo as camadas: classes basicas, repositorio, regras de negocio, fachada, gui) e separa codigo de negocio da classe basica eh uma boa opcao para aumentar a possibilidade de reuso.

L

RafaelVS:
Continuo achando que eh impossivel acessasr os atributos privados… no maximo quando vc mapeia seus atributos usando JPA, vc consegue porque vc tb cria os get/set que sao public… por Reflection vc nao consegue ler/escrever em um atributo privado… se isso fosse possivel, toda a “seguranca” de manter um atributo privado seria jogada fora.

No caso da necessidade de usar um BigDecimal, para nao quebrar o contrato da classe vc precisaria manter o public double getSalario(); e adicionar um public BigDecimal getSalarioNovo();… em seguida, tornar o antigo getSalario deprecated.

Por increça que parível, a JPA é capaz sim de acessar um atributo privado sem get/set. Só não sei como, mas faz. Eu já fiz esse teste e funciona. Faça você também e verá. Tudo bem que a “segurança” seria jogada fora, mas por mim, seria por uma boa causa.

Agora, quanto a criar um novo método getSalarioNovo() e fazer getSalario() deprecated é uma idéia ridícula! Normalmente, um método deprecated é simplesmente marcado como tal, e não se mexe mais nela. Mas nesse caso não, ou você: a) cria tanto o atributo float e BigDecimal dentro da classe e se vira pra sincronizá-los, ou b) você troca float pra BigDecimal e reimplementa um método que imediatamente vai virar deprecated e não terá serventia nenhuma. Além do mais, você não evita a reescrita de outras classes, apenas posterga para uma data indefinida.

O grande nabo do excesso de getters e setters é que muitas vezes a pessoa cria regras de negócio fora da classe, mas não apenas em uma outras classe, mas em várias, e de maneira desorganizada. Muitas vezes, numa equipe média ou grande, ocorre até repetição de código. A colocação dessas regras dentro da classe é eficiente para que esses problemas não ocorram.

L

RafaelVS:
Mas a idéia da opção A não eh GARANTIR o reuso… é AUMENTAR A POSSIBILIDADE de reuso. Se vc coloca validação no método vc vai estar amarrando a implementação… se não fizer isso, aumenta a possibilidade de reusar a classe em outro banco.

Se a conta de um banco A tem as mesmas propriedades da conta de um banco B mas possuem regras de validação diferentes, eu poderia reusar aquela classe se a validação não estiver na classe Conta… Mas se as contas possuem propriedades diferentes, aí eu n poderia reusar… mas pelo menos eu tenho a chance de reusar no primeiro caso.

E esse exemplo que eu dei foi bem simples, que fica facil de entender… tem casos onde as propriedades das classes mudam menos do que as regras de negócio.

Além disso, estou dando exemplos que motivem a implementação usando uma arquitetura em camadas (mantendo as camadas: classes basicas, repositorio, regras de negocio, fachada, gui) e separa codigo de negocio da classe basica eh uma boa opcao para aumentar a possibilidade de reuso.

Reusar atributos é um tipo muito pobre de reúso. E quanto a aumentar ou não a possibilidade de reúso, isso é subjetivo demais. A minha crença é não.

A arquitetura em camadas que você fala não é da maneira que eu imagino e costumo fazer. As regras de negócio não ficam em cima da repositorio e das classes básicas. Ficam justamente dentro das classes básicas! E não há razão de não ser assim, as regras de negócio independem de contexto cliente-servidor, de clusterização ou de qualquer que seja. Agora, persistência, camada de serviço (facade) e gui dependem de contexto específico e merecem ficar em cima das classes básicas.

R

As validações que tratam do estado do a tributo devem ficar dentro da classe basica, mas as validacoes que envolvem outros cadastros/repositorios, devem ficar numa camada separada, acima do repositorio.

Ex.:

Locadora.

Pra fazer uma locacao, preciso informar a midia (atraves do codigo) e um cliente (atraves do cpf). Pra confirmar o cadastro, eh preciso que o codigo e o cpf estejam cadastrados no sistema.

Na classe CadastroLocacao tem o metodo locar(codigoMidia, cpfCliente)… Esse método faz as consultas nos devidos repositorios para saber se os parametros existem no sistema. Caso essa validação tenha sucesso, eu insiro uma Locacao chamo os sets de Midia e Cliente na classe Basica de Locacao (que nao possuem nenhuma validacao) e insiro a Locacao no RepositorioLocacao.

Se essa validação fosse colocada na classe básica Locacao, estaria criando muita dependencia na classe básica e diminuiria a possibilidade de reuso.

Matheus_Tardivo
RafaelVS:
Continuo achando que eh impossivel acessasr os atributos privados... no maximo quando vc mapeia seus atributos usando JPA, vc consegue porque vc tb cria os get/set que sao public...
Pode acreditar: é possível! :D Leia a documentação do Hibernate:
Hibernate Reference Documentation:
You can see that Hibernate can access public, private, and protected accessor methods, as well as (public, private, protected) fields directly.
Faça um teste do tipo:
@Entity
public class Teste {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	
	private String nome;
	
	public Teste() {}
	
	public Teste(String nome) {
		this.nome = nome;
	}
	
	@Override
	public String toString() {
		return String.format("[Teste id: %d, nome: %s]", this.id, this.nome);
	}
}
E depois:
entityManager.persist(new Teste("Teste1"));
entityManager.persist(new Teste("Teste2"));
entityManager.persist(new Teste("Teste3"));

List<Teste> testes = entityManager.createQuery("select t from Teste t").getResultList();
for (Teste teste : testes) {
	System.out.println(teste);
}
Paulo_Silveira

É possivel acessar sim Rafael, usando o Field.setAcessible(true). Da pra bloquear isso com security policy, mas ai o hibernate/jpa pode nao funcionar direito se voce fez anotacoes por atributos em vez de getters. Nao precisa dos getters e setters.

R

Oi, Paulo… estou aqui pesquisando como o Hibernate faz pra acessar o membro private… ainda nao cheguei a uma conclusao definitiva, mas ate agora ta parecendo que isso eh possivel por causa das annotations que colocamos no atributo ou na classe… Se for isso mesmo, entao acho (ja nao tenho mais certeza agora hehe) que um membro private puro (sem intervencao de annotations) não consiga ser acessado fora da classe nem com Reflection.

Talvez seja justificavel o fato do hibernate conseguir acessar o membro private por causa da annotation @Entity ou da @GeneratedValue (que acredito que talvez possam mudar o comportamento, inclusive o nível de acesso dos membros)

Fiz o sequinte teste rapidamente do setAccessible e se o atributo for private não consigo acessá-lo por Reflection:

public Class Teste {

private String aaa = "aaa"; //Assim a linha Field f = Teste.class.getField("aaa"); lança "java.lang.NoSuchFieldException: aaa"
//public String aaa = "aaa"; //Assim exibe o vaor do atributo aaa.

public static void main(String[] args) throws Exception {
  Field f = Teste.class.getField("aaa");
  f.setAccessible(true);
  System.out.println(f.get(new Teste()));
}

}
sergiolopes

Na verdade, o modo como o Hibernate acessa suas propriedades depende de onde está a anotação @Id. Se marcar no atributo ele acessa via atributo, mesmo sendo private.

Aliás, muitos frameworks fazem acesso a atributos private via Reflection

cv1

Use getDeclaredField. E da proxima que o Paulo te disser que eh possivel, nao duvide. :smiley:

R

Use getDeclaredField. E da proxima que o Paulo te disser que eh possivel, nao duvide. :D

Caramba!! hehehe impressionante… testei com getDeclaredField e consegui setar e capturar valores do atributo private hehehe. Será que tb consigo acessar os atributos privados das classes de Java? Ou eles estão protegidos com as políticas que o Paulo comentou?

Valeu.

Rodrigo_Manhaes

As políticas de segurança valem para todas as classes. É possível acessar atributos privados de qualquer classe com as políticas default.

public static void main(String[] args) { String s = "Teste"; try { Field f = String.class.getDeclaredField("value"); f.setAccessible(true); char[] conteudoString = (char[]) f.get(s); for (char ch: conteudoString) System.out.print(ch); } catch (Exception e) { e.printStackTrace(System.err); } }[

Agora, vai acreditar em mim ou vai esperar o Paulo confirmar outra vez? :twisted: :twisted:

Rodrigo_Manhaes

Isto significa que a cada mudança na forma como o atributo é armazenado, ou seja, a cada alteração na estrutura interna da classe, a interface pública da classe é alterada. Se isto não é quebra de encapsulamento, então eu não sei o que seria.

Rodrigo_Manhaes

Um interessante link sobre getters e setters é Why getter and setter methods are evil. O autor, Allen Holub, é bastante radical em muitos de seus pontos de vista. Foi um artigo, porém, que abriu minha mente para muitos aspectos da OO. O JavaWorld, por sinal, como já dito, é uma ótima refrerência.

Aproveito a oportunidade para parabenizar o Paulo Silveira e o Phillip Calçado pelos excelentes textos. Material com esta qualidade em português é algo raro.

Criado 26 de junho de 2007
Ultima resposta 18 de set. de 2007
Respostas 56
Participantes 20