Recentemente venho dedicando boa parte do meu tempo na implementação da minha dissertação de mestrado. O meu trabalho consiste em modelar/desenvolver um middleware para redes de sensores sem fio. Apesar de ser um pythonista de carteirinha não consegui achar nenhuma plataforma de sensores baseadas em python então a minha escolha para o mestrado ficou com o SunSPOT. Para os desavisados, o SunSPOT trabalha com j2me. Dessa forma, precisei deixar o python de lado e voltar a programar em “Java”.
O middleware que venho implementando tem como principal requisito ser extremamente flexível. O objetivo é tornar o middleware bastante customizável, permitindo que os interessados possam modificá-lo. Além disso, outra característica dele é permitir a troca on-the-fly de alguns componentes visando atender os requisitos da aplicação. No intuito de solucionar o problema da troca de componentes uma das escolhas realizadas foi a de utilizar o padrão proxy. Quando um componente é solicitado como requisito de outro componente, o middleware entrega um proxy para a implementação real, assim um componente sempre tem uma referência opaca ao componente. Através da utilização de proxies, o middleware pode realizar a troca de componentes ajustando apenas a referencia do proxy e todos os objetos que fazem uso dele terão a referência ao novo componente automaticamente. Assim, o uso do padrão ajuda a controlar as referências para um determinado componente que precisa ser trocado.
No entanto, um “problema” da abordagem adotada é que eu necessito implementar um novo proxy para cada interface dos componentes. O middleware em questão possui vários componentes que podem ser modificados de acordo com a escolha da aplicação, por exemplo, gestores de bateria, roteamento e etc. Nesse cenário, para cada componente que é permitido a troca, é necessário ao programador desenvolver pelo menos 3 classes/interfaces. A interface do componente, a implementação real e o proxy.
Olhando a definição do padrão contida na wikipedia isso fica mais claro ao leitor. Podemos ver a definição da interface, da implementação real e do proxy.
import java.util.*;
interface Image {
public void displayImage();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
System.out.println("Loading " + filename);
}
public void displayImage() {
System.out.println("Displaying " + filename);
}
}
class ProxyImage implements Image {
private String filename;
private Image image;
public ProxyImage(String filename) {
this.filename = filename;
}
public void displayImage() {
if (image == null)
{
image = new RealImage(filename);
}
image.displayImage();
}
}
Agora imagine que você tenha umas 15 interfaces para fazer isso ? Começa a ficar tedioso. Esse é o tipo de código em que um proxy em si não é código duplicado mas sua lógica sim, eles fazem sempre a mesma coisa, possuem uma referência ao objeto real e delegam todos os seus métodos para o objeto. Atualmente as IDEs for Java já permitem gerar esse tipo de código automaticamente, o que me facilitou muito o trabalho. Inicialmente eu passei mais tempo pedindo ao netbeans para produzir código do que de fato programando. Porém, apesar da IDE gerar o código, a sua manutenção ainda fica por conta do programador. O que acontece quando um método novo entra na interface? Outro precisa ser renomeado? Mudança de parâmetros e etc. Todos nós sabemos, mudanças na interface afetam todas as implementações, nesse caso os proxies também.
Como programador Python, pensei desde o princípio: “esse é o tipo de código que pode ser feito de forma muito mais inteligente em Python do que me Java”. Em Python eu posso definir um único proxy para todas as interfaces. Ou seja, onde eu teria 15, 20 implementações eu passo a ter só uma. Isso é redução de código válida, não estamos falando de one-liners. Dessa forma é menos código para manter, um único ponto para consertar, refatorar, testar. Quanta mágica se precisa para definir esse proxy genérico em Python?
class Proxy(object):
def __init__(self, obj):
self.obj = obj
def __getattr__(self, attr):
return getattr(self.obj, attr)
Pronto, ai está nosso “incrível” “super” proxy. Isso teria me poupado mais de uma centena de linhas de código. Como esse código funciona? Simples, em python nós temos um método, denominado de __getattr__, o qual é invocado quando não é encontrado um determinado atributo na instância. O nosso Proxy implementa o __getattr__ na linha 4, e na 5 nós definimos que quando um atributo não for encontrado dentro do proxy deve ser procurado na referência que o mesmo detém. Pequeno e elegante. Outro detalhe é que não estamos amarrados a nenhuma interface e não precisamos escrever vários métodos apenas delegando tarefas.
Os programadores rails vão achar semelhante ao method_missing do ruby. A idéia é a mesma, porém funciona também para atributos. Não sei dizer se ruby possui isso para acesso a atributos também, acredito que sim.
A princípio, a sua solução elegante em Py pode ser obtida usando-se Reflections em Java. Nao sei se vc já chegou a utilizar… Mas… Através de Java Reflections vc pode trabalhar em cima da estrutura da classe em tempo de execução, ou seja, vc é capaz de destrinchar todo o objeto, Atributos, Métodos o que você quiser. Só que tem um porém: dá um pouquinho de trabalho mas o resultado é fascinante. Use Reflections se vc quer trabalhar polimorfismo em tempo de execução!!!Se eu viajei mto é pq vivo Java e sei que a coisa mais poderosa depois da Google é a Plataforma Java… Se alguém te perguntar se algo é possível, lembre da Plataforma Java e diga sim é possível!!!
Boolean isPossible;
if(isPossible) {
java.Possible.letsTryToDoIt();
}else {
try{
use.Java.tryAgain();
}catch(Exception e) {
use.your.head.use.Java.andTryAgain();
}finally{
with.java.everything.is.Possible.doItNow();
}
}
Caro Valmir, conheço sim a API reflections de Java mas não tenho idéia em como simular isso, na minha opinião não é possível, pois a estrutura da classe em si não é dinâmica. Já que você conhece e diz que é possível poderia mostrar como ?
Post interessante. De fato em java isso não seria simples, visto que uma chamada a um método não existente resultaria em uma exceção. Tava imaginando aqui se você não conseguiria implementar seu middleware em python e utilizar a engine de linguages de script do java, você conseguiria atráves do python manipular a API java e acessar os sensores. Faço isso com Ruby e funciona muito bem. Parabéns.
o/
Mude seu mestrado: construa uma plataforma de sensores baseadas em python. Com certeza deve ter muita coisa em C ou C++, aí manda um SWIG e corre pro abraço!
Olá,
não sou tão expert no assunto, mas como alguns amigos disseram, creio que daria para fazer em java de forma melhor que você descreveu, agora não sei se ficaria melhor do que em Python. Você disse que precisaria implementar a interface do componente, a referência real e o proxy, só que em Java o padrão proxy já está implementado, sendo necessário definir somente a forma que você utilizará ele, utilizando por exemplo InvocationHandler que irá descrever como você executa o método na referência real, fica bem flexível, mas como disse não sei se fica mais produtivo do que em Python
Sim, sim, vocês estão certos, no Java Desktop tem o padrão proxy implementado, que funciona através da geração de bytecode da classe. No entanto, eu utilizava a versão ME, a qual não possui.