Acesso a métodos de um objeto

13 respostas
v1c70r_f

Bom dia pessoal!

Gostaria de quando chamar um método de uma classe eu pudesse chamar sequencialmente outro método da mesma classe. (Acho que ficou um pouco confuso de entender neh xD)

Ex. Quando uso a classe Criteria do Hibernate eu posso usar assim:

criteria.add(Restrictions.("id.codigo", codigo))
             .add
.
.
.
             .list();

Todos esses métodos que eu acessei são da mesma instância e eles sempre retornam o próprio criteria utilizado, já vi também casos que quando instancio um objeto já posso ir setando as suas propriedades chamando seus métodos´, ou seja sem utilizar o método construtor.

Ex.

MeuObjeto obj = new MeuObjeto()
    .setPropriedade('teste');
    .setPropriedade2('teste');

Queria poder faser isso em uma classe com métodos estáticos e que se eu setar uma propriedade quando chamo um determinado método, esse método comporte-se de forma diferente.
Tudo bem que eu poderia passar valor por parâmetro, mas gostaria de implementar igual no caso do Criteria.
Esse tipo de implementação segue algum padrão ou especificação ou algo do tipo?
Como posso fazer isso?

Vlw, abraço.

13 Respostas

T

Ora, isso é muito fácil. Nunca ouviu falar de “return this”?

Em vez desse feijão-com-arroz:

public Class HaHaHa {
    private String propriedade;
    public void setPropriedade (String propriedade) {
        this.propriedade = propriedade;
    }
}

use isto aqui (talvez dê até para você customizar o Eclipse 3.2 ou posterior para ele gerar os setters deste jeito, não sei):

public Class HaHaHa {
    private String propriedade1;
    private int propriedade2;
    public HaHaHa setPropriedade1 (String propriedade) {
        this.propriedade1 = propriedade;
        return this;
    }
    public HaHaHa setPropriedade2 (int propriedade) {
        this.propriedade2 = propriedade;
        return this;
    }
}
v1c70r_f

puuuuutzzzz xD

nem tinha me ligado…

vlwwwww… abraçow

ViniGodoy

Só de curiosidade. Essa técnica chama-se Invocation Chaining. E como this só tem sentido em objetos, não pode ser usada com métodos estáticos. :wink:

v1c70r_f

xD é hehe…

meu caso é justamente por ser uma classe de métodos estáticos que eu não estava conseguindo utilizar uma implementação para isso…

onde posso encontrar esses tipos de técnicas utlizadas na manipulação de objetos?

existe algo parecido quando falamos de métodos estáticos?

abraçow

ViniGodoy

Algumas classes do Java implementam essa técnica:
ByteBuffer e StringBuilder são exemplos delas.

Eu acho muito prático também para classes matemáticas, como por exemplo, de vetores matemáticos.

Não me lembro de ver nada assim para métodos estáticos. Pelo menos, não de maneira trivial.
Aliás, na prática um bom sistema deve ter poucos métodos estáticos. Será que a tal classe não poderia ser reescrita de outra forma?

v1c70r_f

ViniGodoy:

(…)
Aliás, na prática um bom sistema deve ter poucos métodos estáticos. Será que a tal classe não poderia ser reescrita de outra forma?

Um bom sistema deve ter poucos métodos estáticos??? Pq?

Pode sim ser reescrita.

A classe na verdade é um copy properties de um ORM para um VO, criei ela pq vi que na aplicação várias classes do nosso sistema utilizavam métodos privados para fazer a cópia de propriedades e que várias classes utilizavam os mesmos objetos.

Nessa classe que eu criei as cópias são feitas em cascata, ou seja, se dentro de um ORM tem outro ORM ele da um get e transforma também para VO, inicialmente pensei em deixar ela assim e quem quisesse fazer a cópia apenas do wrappers do ORM utilizasse o BeanUtilsBean e quem quisesse que fosse feita a cópia em modo ‘cascata’, utilizasse essa classe CopyProperties.

Justamente por poder ser utilizada em muitos locais diferentes e com grande frequencia pensei em utilizar métodos estáticos, a coloquei em um pacote ‘util’ e vários EJBs de modelo a utilizam.

Pensei em colocar uma propriedade .cascade(), assim se não fosse informado nada faria a cópia simples e se fosse chamado o tal .cascade(), faria do jeito que está hoje.

Seria melhor se estivesse de forma genérica, mas nem pensei ainda em fazer um copyProperties utilizando refletion e tals, sei lah, sempre que fiz isso achei muito lento e desempenho é um fator crítico nessa aplicação.

Na verdade esse querer implementar esse ‘.cascade()’ é mais querer aprender do que necessidade na solução.

ViniGodoy

Métodos estáticos não podem ser sobrescritos e não sofrem polimorfismo.

Por consequencia:
Não é possível fazer mocks para métodos estáticos, o que prejudica testes unitários.
Não é possível fazer proxies de métodos estáticos, o reduz possibilidades na manutenção;
Não é possível obter um comportamento polimórfico desses métodos, o que pode dificultar a implementação.
Não é possível fazer métodos estáticos com Invocation Chaining :slight_smile:

Você pode criar uma classe com métodos não estáticos, e obte-la através de seu framework de dependency injection.

v1c70r_f

:thumbdown:

putz, eu fiz uma porrada de sobrecarga, e putz, a maioria das coisas que vc flw eu nem o que é pra falar a verdade…

mocks?
proxie de métodos?

carinha, agora você me deixou confuso…

nesse caso, que implementação você sugeriria?

ViniGodoy

Imagina a seguinte interface:

public interface Fabrica {
   MeuObjeto createObject();
}

Você tem uma implementação dela assim:

public void FabricaImpl implements Fabrica {
    public MeuObjeto createObject() {
            MeuObjeto.loadFromDB();
    }
}

Legal, não? Agora vamos supor que você precisasse gerar um log todas as vezes que esse método fosse chamado. Mas não sempre, só de vez enquanto. Aliás, só quando você (programador) fosse avaliar o seu programa. Isso não deveria ir para cliente. Como organizar a classe para isso?

Você poderia fazer um proxy. Ele implementa a interface e delega os métodos para classe principal. O proxy é possível pois ele implementa a mesma interface.

public void FabricaProxyComLog implements Fabrica {
   private Fabrica fabrica;

   public FabricaProxyComLog(Fabrica umaFabrica) {
        this.fabrica = umaFabrica;
   }

   public MeuObjeto createObject() {
       logar();                                 //Fazemos o log
       return fabrica.createObject(); //Agora sim, passamos para a fábrica de verdade
   }

   public void logar() {
       //Loga isso num arquivo qualquer.
   }
}
Agora, no seu código, bastaria ir na declaração da sua classe de fábrica e trocar isso:
Fabrica f = new FabricaImpl();
Por isso:
Fabrica f = new FabricaProxyComLog(new FabricaImpl());
Reconhece esse padrão? Se você já usou a Collections framework, já deve ter visto que é possível fazer:
List<Integer> lista = new Collections.unmodifiableList(umaListaQualquer);
O que o new Collections.unmodifiableList faz? Ele encapsula a sua lista num proxy, que lança exceção sempre que tentarem modifica-la. Ele não cria uma cópia de sua lista. A mesma coisa para o Collections.synchronizedList. Se já usou os buffers, deve ter entrado em contato com um tipo especial de proxy, chamado Decorator. Você provavelmente tinha um InputStream bruto (como o FileInputStream). Daí, vc precisou coloca-lo num buffer para aumentar a performance. O que você fez?
BufferedInputStream bis = new BufferedInputStream(algumFileInputStream);
E se você precisasse ler objetos lá de dentro?
ObjectInputStream ois = new ObjectOutputStream(bis);

Cada decorator adicionou uma pequena funcionalidade na classe original. Sacou?
Esse é o poder que você ganha com proxies.

E agora, o que é um mock? Um mock é uma classe que finge ter uma funcionalidade. É muito usado em JUnits. A grande vantagem do Mock é que vc programa ele para se comportar como você quiser.

Por exemplo, vamos supor que você esteja escrevendo um JUnit para a classe MeuObjeto. Mas a única fábrica do seu programa real é uma que pega lá do Hibernate, num servidor remoto, que está em algum lugar da web. Acho que você não gostaria de configurar um ambiente pesado como esse só para testar a classe meu objeto. Então, o que você faz? R: Um mock.

public class FabricaMock implements Fabrica {
   public MeuObjeto createObject() {
          return new MeuObjeto(); //Cria um objeto padrão qualquer.
   }
}

Agora você poderia implementar um JUnit facilmente:

public class Teste {
   @Test
   public void testaToString() {
           Fabrica f = new FabricaMock(); //Sem o stress de hibernate
           MeuObjeto mo = f.createObject();
           Assert.assertEquals("default", meuObjeto.toString());
   }
}

O legal é que você poderia programar o seu Mock para gerar casos de erro. Por exemplo, para lançar a exceção de que o objeto não existe. E ver como outras classes no seu programa respondem a isso.

Por exemplo:

public class TestaIndexador {
   @Test
    public void teste() {
         //A classe indexador criaria indices de meus objetos
         FabricaMock fabrica = new FabricaMock()
         fabrica.setFingirNaoTerObjetoNenhum(true);
         Indexador indexador = new Indexador(fabrica);
         indexador.indexar(); //Não deve dar erro nenhum, se der é problema do indexador!
    }

   @Test(expected=UnableToIndexException.class)
    public void teste2() {
         //agora vamos testar se o indexador gera um erro caso a fábrica também gere
         FabricaMock fabrica = new FabricaMock()
         fabrica.setGerarObjetosNaoIndexaveis(true);
         Indexador indexador = new Indexador(fabrica);
         indexador.indexar(); //Deve gerar uma UnableToIndexException
    }
}

Note que esses métodos setFingirNaoTerObjetoNenhum e setGerarObjetosNaoIndexaveis só existem no Mock. Foram feitos especialmente para testar o Indexador e classes similares, que venham a usar a fábrica. Ele permite que coloquemos situações especiais da fábrica e testemos como os objetos (nesse caso o Indexador) devem se comportar nessas situações.

O que eu sugiro? Pense em ter uma classe concreta, sem métodos estáticos.

Agora, se o seu programa é muito simples, só para faculdade, então permaneça com os statics, pq são mais simples de implementar.

ViniGodoy

Na pressa acabei escrevendo o código da FabricaProxy ali em cima com erro.
Agora sim, dá para entender qual é a idéia do proxy.

Mais uma curiosidade. O java tem uma classe chamada Proxy, que permite a criação de proxies dinamicamente, em tempo de execução.

v1c70r_f

Vini, SHOW DE BOLA!

Muito obrigado pela explicação e atenção carinha!!!

:smiley:

Abraçooo!

:thumbup:

foxpv

Vini, sei que o tópico é antigo, mas posso fazer uma pergunta?

Em que esse FabricaProxyComLog está ajudando o programado a realizar log apenas quando ele quer? Já que estamos instanciando um Objeto Fabrica com a implementação de FabricaProxyComLog, todas as vezes que chamarmos o método createObject o log seria realizado né não?

Abraços.

ViniGodoy

Sim. Toda vez. Mas o programa pode decidir quando usar ou não a versão com Log.

Criado 25 de outubro de 2007
Ultima resposta 26 de mai. de 2010
Respostas 13
Participantes 4