Archive for the 'Design' Category

Passagem de Parâmetros em Python

Dentre os temas que geram discussões intermináveis, apaixonadas, e na maioria das vezes, completamente inúteis, a questão de tipagem de dados em linguagens de programação ocupa um espaço não desprezível.

Tipagem forte, tipagem fraca, tipagem estática, tipagem dinâmica. Existem prós e contras para cada lado, e os defensores de cada campo passam horas e horas tentando provar que o esquema alheio é péssimo. Tendo trabalhado com todos, programando em Java, Python, Javascript e C, chego à conclusão de que é possível ser feliz e produtivo com qualquer uma delas.

Mas nem tudo é perfeito, e em algumas situações, o jardim do vizinho é mais verde…

Sinto falta em alguns momentos de poder tipificar parâmetros de funções em Python, por dois motivos. O primeiro é para evitar que parâmetros de tipos errados sejam passados inadvertidamente: fazer verificação de tipo no início de um método ou função é simples, mas as vezes consome tempo de programação inútil.

O outro que mais me incomoda é que a falta desta tipagem impede que se utilize um recurso muito útil e elegante que é a sobrecarga de método. Basicamente, em Python não é possível criar vários métodos com o mesmo nome e com assinaturas diferentes. O jeito Python de resolver isso é verificando o tipo dentro do método, ou então criando um mecanismo de dispatch, usando os famosos dicionários (que são extremamente eficientes. Ousam dizer por aí que é a implementação mais eficiente que existe….).

Ambas funcionam, mas não são tão elegantes como a solução oferecida pela sobrecarga. Aproveito, pergunto: existe alguma forma mais elegante de se fazer isso em Python?

Fica aqui a minha sugestão. HEY, GUIDO, TIPIFICA OPCIONALMENTE PARÂMETRO DE MÉTODO AÍ!

Identificação de charsets

Hoje, para variar, vou falar sobre outra coisa que me incomoda. Aliás, que me incomodava.

Sempre fiquei muito intrigado com um fato que recorrentemente eu presenciava: precisava ler um arquivo de “texto puro” mas, quando abria o arquivo, lá estavam vários caracteres esquisitos ou pontos de interrogação ou qualquer coisa que não deveria estar lá. Ou seja, o programa que eu estava usando não conseguia reconhecer adequadamente o charset do arquivo e, quando tentava mostrar os caracteres usando o charset padrão do programa, um punhado de coisa ficava zuada.

Ficava sempre pensando que alguma coisa idiota devia estar acontecendo, afinal de contas, como um programa não conseguia reconhecer um charset? Tantos anos de evolução na computação e uma coisa simples como esta ainda não funcionava direito? Tentava de várias formas bolar uma solução para isso mas, qualquer coisa que eu pensava, não era uma solução que eu achasse viável. Afinal, a maneira mais fácil de identificar um charset seria colocar uma espécia de meta-dado no arquivo, como é (deveria ser) feito com páginas web (mas nem sempre funciona também por falhas nos programas!). E aí o arquivo deixaria de ser um arquivo de “texto puro”.

Enfim, não tinha uma solução para este “problema” e, pelo jeito, via que ninguém mais tinha, afinal o problema continuava existindo. Mas ele continuava me incomodando de tempos em tempos.

Até que li este texto do Joel. E aquietei meu espírito em relação a este problema. O texto inteiro é bom, especialmente a parte final The Single Most Important Fact About Encodings. E o que resume tudo é esta frase: There Ain’t No Such Thing As Plain Text. Ou, numa tradução livre: “Não existe algo como texto puro”.

Ou seja, tudo aquilo que é mostrado pelo programa que abre arquivos de “texto puro” é simplesmente uma abstração. Esqueça os caracteres. Eles são apenas interpretação dos dados reais binários. Qualquer representação dada a eles é apenas resultado de algum processamento por trás que tenta ou não identificar qual foi o charset usado para criar o arquivo.

Por mais óbvio que isso seja e por mais que eu soubesse disso, é engraçado como eu não ligava este simples fato à minha inquietudade em relação ao processamento de arquivos textos pelos programas.

Feito isso, com a ajuda do Joel, problema resolvido!

Design OO, Design E-R, e o tal do ORM.

Vira e mexe aparece algum problema que pode ser um exercício interessante, um Kata para programador que eu gostaria de colocar aqui para ser analisado, discutido e implementado pelo pessoal que visita o blog. Além disso, tem algum tempo também que eu quero escrever sobre os conflitos entre o design Orientado a Objeto e o desenvolvimento de aplicações usando banco de dados relacionais, e sobre como as ferramentas que fazem ORM tentam, mas não conseguem resolver esse conflito de uma forma definitiva.

Como não estou com tempo para fazer tudo isso, vou fazer um mix e ver o que acontece. Resumidamente, vou explicar um pequeno problema que eu tenho envolvendo a implementação de um sistema usando o ORM do Django, e gostaria de saber se alguém vê uma solução correta e elegante.

Usando exemplos reais: estou trabalhando em um projeto que visa analisar registros médicos eletrônicos e identificar se certos casos registrados devem ser notificados ao CDC. Mais especificamente, o que eu quero é analisar o registro de vacinas que a pessoa recebeu e verificar se houve alguma reação adversa. Uma reação adversa pode ser identificada por algo comum como febre, ou algo mais complexo como uma hemorragia ou por um exame de laboratório mostrando uma variação em valores de seus componentes (Hemoglobina, Creatina, Sódio, Potássio, etc).

“Falando” OO, o que eu tenho é a classe AdverseEvent, da qual derivam FeverEvent, DiagnosticsEvent e LabResultEvent. Na hora de construir os relatórios, é necessário obter a lista dos eventos e obter também os atributos específicos de cada subclasse. Até aí OO funciona. E antes que the-one-who-can’t-be-named grite “Design Patterns”, eu vou falar que estamos lidando com a implementação de uma Factory: dada uma lista de chaves que identifique uma lista de AdverseEvents, a Factory instancia o objeto adequado a partir da id fornecida.

O “problema”: esses objetos ficam em um banco de dados, e como mapear isso para um banco de dados relacional e fazer uma factory que construa o objeto adequado com uma única consulta?

Vamos ao código. A primeira solução seria uma implementação que tenta ser puramente OO. O código abaixo é parte de um típico módulo de definição dos seus modelos quando usando Django.

 

class AdverseEventManager(models.Manager):

    def by_id(self, key):
        for klass in [FeverEvent, DiagnosticsEvent, LabResultEvent]
            try:
                obj = klass.objects.get(id=key)
                return obj
            except: pass
        return None

class AdverseEvent(models.Model):
    # Managers
    objects = models.ModelManager()
    manager = AdverseEventManager()

    # Atributos que realmente são de interesse e vão para o BD.
    patient = models.ForeignKey(Demog)
    immunization = models.ForeignKey(Immunization)
    matching_rule_explain = models.CharField(max_length=200)
    category = models.CharField(max_length=20, choices=ADVERSE_EVENT_CATEGORIES)
    created_on = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)



# Classes filhas. NO BD, são mapeadas para tabelas que contém os campos 
# específicos da classe, e referencia a tabela da classe-pai
class FeverEvent(AdverseEvent):
    temperature = models.FloatField('Temperature')
    encounter = models.ForeignKey(Enc)

class DiagnosticsEvent(AdverseEvent):
    encounter = models.ForeignKey(Enc)
    icd9 = models.ForeignKey(Icd9)


class LabResultEvent(AdverseEvent):
    lab_result = models.ForeignKey(Lx)

 

 

O Factory method é by_id. Para os que não conhecem Django, ModelManager.get() faz a consulta no banco de dados e retorna um único objeto da classe que o contém, ou a exceção DoesNotExist se não existir objetos com os parâmetros indicados. Como a chave pertence a classe-pai, podemos ter certeza que não há haverá objetos das classes filhos com o mesmo id. Isso permite que encontremos o objeto desejado. O problema com essa solução é que o número de consultas é proporcional ao número de sub-classes

O segundo esquema é “menos OO”. Para desespero de alguns puristas, nós podemos criar um dicionário no módulo que mapeia as sub classes possíveis e a factory faz sempre duas consultas para obter o objeto apropriado.

 

ADVERSE_EVENT_CLASSES = {'fever':FeverEvent,
                         'diagnostics':DiagnosticsEvent,
                         'lab_result': LabResultEvent
                         }

class AdverseEventManager(models.Manager):  
    def by_id(self, key):
        try:
           ev = AdverseEvent.objects.get(id=key)
           return ADVERSE_EVENT_CLASSES[ev.event_type].objects.get(id=key)
        except:
            return None

class AdverseEvent(models.Model):
    # Managers
    objects = models.ModelManager()
    manager = AdverseEventManager()

    # Atributos que realmente são de interesse e vão para o BD.
    patient = models.ForeignKey(Demog)
    immunization = models.ForeignKey(Immunization)
    matching_rule_explain = models.CharField(max_length=200)
    category = models.CharField(max_length=20, choices=ADVERSE_EVENT_CATEGORIES)
    created_on = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)

    # Adicionamos esse campo para identificar para saber qual é a subclasse 
    event_type = models.CharField(max_length=20, choices=tuple(ADVERSE_EVENT_CLASSES.items()))

 

 

Enfim, só estou expondo as duas soluções que eu conheço para lidar com algo trivial. Se alguém tiver alguma idéia diferente, por favor deixe registrado nos comentários.

Com relação a crítica ao ORM: parte da minha bronca são essas incongruências que existem, o ORM não conseguindo fazer uma boa ponte entre o modelo OO e o modelo E-R. É por detalhes assim que estamos começando a ver uma série de projetos de banco de dados não-relacionais, do qual espero falar mais adiante.

Outra coisa interessante: se você perceber, um banco de dados XML não teria esse problema, entretanto parece que ninguém nunca pensou em implementar o BDB-XML como backend para uma datastore.

Doc. Job4Dev: Integração com Twitter

Job4Dev deu nascimento ao projeto Open Source Jobboard, hospedado no GitHub e que está sendo levado adiante pelo Raphael. É um ótimo momento portanto para destrinchar e documentar alguns aspectos da arquitetura do sistema. Vou começar pela integração com o Twitter.

Por que integrar com o Twitter?

Sendo rápido e objetivo, decidi usar o Twitter por achar que seu design e seu modo de funcionamento se adequavam perfeitamente àquilo que eu estava buscando: a diversificação dos canais de divulgação das vagas. Esta integração foi possível graças ao fato do microblog oferecer uma API bastante bem feita e bem documentada. Junte a isso a minha vontade de brincar com a plataforma, e temos um cenário perfeito!

E a API do Twitter, como funciona?

Ela é baseada em REST, e permite fazer boa parte (se não tudo) do que um usuário humano poderia fazer. Eu explico sucintamente o seu funcionamento no artigo Conversando com o Twitter. A documentação da API pode ser encontrada em http://apiwiki.twitter.com/.

Mas chega de papo, vamos ao design da coisa…

A integração do Job4Dev com o Twitter começou com as seguintes premissas

  • Baixo impacto sobre o tempo de resposta do site no momento do cadastro de uma nova vaga
  • Garantia de que TODAS as vagas seriam publicadas no Twitter
  • Simplicidade da arquitetura
Resumindo, e usando as palavras da moda, a nova funcionalidade deveria ser ROBUSTA, ESCALÁVEL e CONFIÁVEL.

A chave para se conseguir os dois primeiros ítens é o uso de um sistema assíncrono. Ou seja: a ação de gravação de uma nova vaga na base de dados deve notificar o sistema de que uma nova mensagem deve ser enviada ao Twitter, e o envio deve ser executado em background. Isso evita que problemas na rede ou no próprio microblog (bastante comuns na época do desenvolvimento da funcionalidade) afetem o tempo de resposta.

Para garantir que todas os anúncios fossem enviados, deveria existir um mecanismo que persistisse os tweets em caso de problemas de rede, permitindo envio posterior. Finalmente, para garantir a confiabilidade e a simplicidade, decidi usar o máximo de recursos do sistema operacional. Afinal o Linux para mim é o melhor servidor de aplicações que existe!

E como tudo isso foi implementado?

Eu cogitei gerar a mensagem diretamente a partir da base de dados, uma vez que as informações são gravadas em uma tabela. Mas para garantir que uma vaga fosse enviada apenas uma vez, e permitir que uma mensagem fosse reenviada em caso de problemas de comunição, o esquema das tabelas teria que ser modificado para guardar o estado de processamento do canal Twitter.

Para evitar esta modificação, ao meu ver intrusiva, e aplicando a fundo o conceito K.I.S.S, resolvi utilizar o sistema de arquivos: ao submeter uma vaga, o sistema gera um arquivo texto em um diretório específico, contendo a mensagem a ser enviada. Para garantir que não haja conflito, o nome do arquivo é a URL da vaga.

O envio das mensagens é efetuado por uma tarefa em Python lançada pelo CRON, de 15 em 15 minutos. O algoritmo é simples: verifica no diretório apropriado se existem arquivos de mensagens. Caso existam, abre cada um, lê o conteúdo, formata uma mensagem e efetua a requisição de envio para o Twitter. Caso a mensagem seja enviada com sucesso, remove o arquivo. Caso contrário, registra o erro em um arquivo de log e deixa o arquivo no diretório para uma execução futura.

Os mais curiosos poderão ver o código do módulo Twitter aqui. Este módulo pode ser livremente utilizado para implementação de uma funcionalidade semelhante. Basta passar por parâmetro o nome do diretório, o nome de usuário no microblog e a senha.

A nova ortografia e o redesenho de software

Faz tempo que eu não escrevo por aqui… não é que minhas ideias andem sem acento. Ideias não faltam. Falta o algo extra que as coloca no papel. Ou, no caso, no blog.

Para mim, uma das coisas que mais marcarm a virada do ano no Brasil foi a entrada em vigência da nova ortografia da língua portuguesa falada por aqui. Dizem que isso foi para unificar melhor a língua portuguesa falada em diversos países. Sinceramente, sempre tive dúvida em relação a argumentos como este. Sempre escutei na escola que a língua é um “ser” em constante mutação e evolução causada pelo seu uso pelo povo. Se isso é verdade, até quando a ortografia vai ser comum em todos os países de língua portuguesa? Até os políticos aprovarem outra lei para unificarem novamente as línguas que se mudaram com o tempo? Enfim… não sou especialista neste assunto e este não é um blog sobre línguas. Então vou deixar este assunto de lado um pouco.

Por outro lado, no final do ano, durante as minhas férias, quando em geral eu me afasto consideravelmente dos computadores, uma pessoa próxima e com mais que o dobro da minha idade veio até mim reclamar que o UOL tinha mudado a interface do seu webmail e agora ela estava perdida. A questão é que apesar da idade, esta pessoa que eu conheço é bem interessada em relação à tecnologia de uma forma geral e mexe no computador com certo traquejo. Aliás, a reclamação em relação ao webmail era justamente porque ela usa esta aplicação como ferramenta de trabalho diária. Como eu estava por perto acabei indo ajudá-la com as dúvidas em relação ao novo webmail do UOL. Como eu tenho conta no BOL (que usa o mesmo engine de webmail que o UOL) eu sabia como era a antiga ferramenta. Já era um bom começo, imaginei eu. Mas confesso que, após ver o novo webmail, fiquei frustrado. Eu ainda conseguia utilizá-lo, mas ele tinha ficado mais difícil. Isso tudo para que ele fosse, “finalmente”, Web 2.0! Grande coisa! Mas, vá lá… eu conseguia usá-lo. Mas a pessoa que estava ali do meu lado estava com sérias dificuldades. E com razão! Para se ter uma idéia a lista de contatos era mostrada por ordem alfabética de… e-mail. Ou seja, se eu tivesse cadastrado nos meus contatos alguém com nome Miguel mas que tivesse o e-mail frances@algumlugar.com o nome Miguel aparceria na letra F!!!

Depois de ver isso e outras coisas toscas (tudo em nome da famigerada Web 2.0!) fiquei  pensando na frustração da pessoa que veio me pedir ajuda. Fiquei tentando imaginar como era esta frustração porque, como eu disse, apesar de eu achar difícil, vá lá, eu ainda conseguia usar o webmail.

Depois de refletir durante um tempo eu acabei achando algo que, para mim, parecia ser equivalente àquela sensação de frustração: a tal mudança ortográfica do português.

Talvez o que realmente me incomode e fruste com esta nova ortografia seja a mudança em si. Eu fui alfabetizado e escrevo há mais de 10 anos segundo as normas da velha ortografia (que, diga-se de passagem, valerá até 2012; não misturá-las até lá é que vai ser difícil). Mudanças sempre incomodam e ter que reaprender a escrever vai ser um pouco chato. Mas, sinceramente, nada muito complicado neste caso, eu acho. Mais cedo ou mais tarde vou me acostumar (ainda mais com os corretores ortográficos de hoje em dia :) ). Mas fico imaginando: se eu já estou achando chato ter que reaprender o português, imagina a geração dos meus pais que está passando por isso pela segunda vez depois que foram alfabetizados. E a dos meus avôs que já passaram por isso três ou quatro vezes? Não sei como isso funciona em outros países, mas acho que estas mudanças políticas no Brasil não fazem muito sentido… ainda mais nesta quantidade. Como será que as pessoas mais velhas se sentem? Talvez já nem estejam se importando mais em aprender direito a nova ortografia se isso não impactar diretamente a vida delas.

Imagino que o sentimento de algumas pessoas em relação aos sistemas computacionais seja parecido. Apesar de as pessoas se acostumarem com eles e aprenderem a utilizá-los, de uma hora para outra, alguém decide que é necessário pegar um sistema que funciona razoavelmente bem e alterá-lo apenas para seguir as tendências da moda Web 2.0, prejudicando imediatamente um incontável número de pessoas que não estavam assim tão interessadas no modismo. Depois de algum tempo não seria de se estranhar que algumas dessas pessoas “desistissem” de tentar reaprender a utilizar o sistema.

Vejam que não sou contra mudanças! Desde que elas sejam necessárias, bem pensadas, planejadas, e de que venham para melhorar efetivamente a vida das pessoas, facilitando as tarefas. Sou contra mudanças políticas: como me parece ser a mudança ortográfica e como me pareceu ser a alteração do webmail do UOL (para citar só um exemplo na computação).

Por isso caros leitores, a mensagem que eu gostaria de deixar nesta primeira mensagem que escrevo este ano é uma que já foi tratada aqui neste blog outras vezes: quando você estiver desenhando um sistema ou redesenhando uma funcionalidade já existente, pergunte-se sempre se você está tomando decisões que realmente agregam valor e funcionalidade às suas alterações. Fujam das alterações políticas e burocráticas e, se necessário, fuja dos modismos também. Saiba utilizar as ferramentas e as tecnologias com aquilo que elas melhor tem a lhe oferecer e não vire escravo das novas siglas que aparecem o tempo todo.

Micro introdução sobre orientação a aspectos (AspectJ)

Eu estava pensando em escrever sobre algumas coisas que têm me interessado ultimamente e que têm alguma relação com o post sobre vazamento de memoria em Java, mas eu percebi que não teria como, sem fazer uma pequena introdução sobre programação orientada a aspectos (aspect oriented programming, AOP). A gente já discutiu um pouco sobre isso no post sobre decorators em python, mas não fizemos nada mais introdutório (se você já tem uma noção do assunto, provavelmente não vai achar esse artigo muito útil). Então nesse post eu vou falar sobre alguns conceitos de desenvolvimento orientado a aspectos(AOSD), mas com ênfase descarada em AOP na forma como implementada em AspectJ. Eu não pretendo cobrir em profundidade o assunto, pretendo somente dar uma idéia da motivação e discutir alguns pontos sobre o paradigma. Além disso, eu vou usar na maioria a terminologia em inglês, pois eu acho estranho traduzir termos tecnológicos. Se alguem tiver interessado em termos em português, o pessoal do workshop em AOP (WASP’04) fez um catálogo de termos.

Motivação e Exemplo

No meu ponto de vista, AOSD não é algo que veio revolucionar o desenvolvimento de software, mas sim um avanço incremental do paradigma de orientação a objetos (OO). Todo bom programador sabe que se deve tentar distribuir as responsabilidades (concerns) do programa em módulos, de forma que cada módulo seja fácil de implementar, entender e reusar. Em C, por exemplo, funções podem ser consideradas módulos. Pra qualquer programa não trivial, você não vai criar somente uma função main(), vai separar a funcionalidade em muitas funções que se chamam pra realizar o objetivo do software. Já OO prega a modularização do software em uma hierarquia de classes e conta com polimorfismo, herança, etc. A motivação de AOSD é que existem algum concerns que entrecortam a hierarquia, ou seja, que mesmo criando essa hierarquia de classes, você não tem como separar dois concerns que deveriam ser separados (para serem melhor implementados, entendidos e reusados).

Pra não ficar somente no abstrato, vamos à um exemplo bem simples, que é utilizado constantemente pra apresentar AOP. Imagine que você tem uma classe que faz o controle do acesso ao banco de dados. O código abaixo é um esboço de como uma classe dessas seria implementada em Java. Você tem um gerente de transações e, em todos os métodos de acesso ao banco de dados, você chama o gerente pra iniciar uma transação e fazer commit se tudo deu certo, ou rollback se deu algum problema. Note que hoje em dia ninguém faria um código assim, já que existem soluções melhores do que ficar escrevendo SQL no seu código (como Hibernate), mas é bem ilustrativo do problema.

public class DB { TransactionManager manager = ...;

public void storeUser(User u) {
    manager.start();
    try {
        // write some sql code storing u into the DB
        manager.commit();
    } catch (DBException e) {
        manager.rollback();
    }
}
public void storeAccount(Account a) {
    manager.start();
    try {
        // write some sql code storing a into the DB
        manager.commit();
    } catch (DBException e) {
        manager.rollback();
    }
}
...

}

Claramente dá pra perceber que eu tô misturando dois concerns na mesma classe. Um é persistência (salvar o User e a Account no banco de dados) e outro é controle de transações. Em OO, não tem um jeito elegante de separar esses concerns. Você pode até tentar fazer uma interface com uma callback pra tentar minimizar a repetição de código, como no código abaixo, mas não fica ideal.

public interface TransactionalTask { public void execute(); } public class TransactionManager { TransactionManager manager = ...;

public void executeInTransaction(TransactionalTask task) {
    manager.start();
    try {
        task.execute();
        manager.commit();
    } catch (DBException e) {
        manager.rollback();
    }
}
...

} public class DB { public void storeUser(User u) { manager.executeInTransaction(new TransactionalTask() { public void execute() { // write some sql code storing u into the DB } }); } ... }

A idéia de AOSD é criar um novo módulo (chamado aspecto) em que você possa encapsular esses concerns que não tem como você modularizar com classes. AspectJ é a implementação mais conhecida de AOP, tanto por ser a primeira, quanto por ser bem robusta e contar com apoio de diversas empresas. Em AspectJ, que é uma extensão de Java (todo código Java é código AspectJ válido), nós criaríamos um aspecto pra encapsular controle de transações. O pseudo código abaixo mostra a idéia (eu não testei o código, então não garanto copy-and-paste funcionando).

public aspect TransactionManagerAspect { TransactionManager manager = ...;

around(Object o) : execution(public void DB.*(..)) && args(o) {
    manager.start();
    try {
        proceed(o);
        manager.commit();
    } catch (DBException e) {
        manager.rollback();
    }
}

}

public class DB {

public void storeUser(User u) {
    // write some sql code storing u into the DB
}
public void storeAccount(Account a) {
    // write some sql code storing a into the DB
}
...

}

A classe DB agora contém somente código relativo à persistência e o aspecto é responsável pelo controle de transações. Em terminologia de AOSD, around é um tipo de advice e execution um tipo de pointcut. O que esse código tá dizendo é que sempre que a execução do programa for iniciar um método publico, que retorna void, na classe DB, o método deve ser substituído (por isso “around”) por esse advice aqui. O código do advice vai fazer o controle da transação e vai chamar proceed pra executar o código original. Note que o código do advice é muito parecido com a nossa solução usando callbacks. A grande novidade aqui é que usando pointcuts a gente consegue especificar quando que a “callback” deve ser usada, deixando o código da classe DB bem mais limpo.

Esse exemplo mostra a parte dinâmica da história, isto é, como usar aspectos pra “interceptar” alguns pontos da execução de um programa e inserir (ou remover) funcionalidade (código) nesses pontos. Mas AOSD também trata da parte estática ou “estrutural” da coisa. Por exemplo, imagine que no nosso exemplo nós quiséssemos inserir um controle de concorrência bem primitivo, pra garantir que ninguém modificou o User no tempo entre eu buscar no banco e salvar. Pra isso, eu poderia adicionar um campo na classe User com um contador que é incrementado sempre que ele for salvo no banco. Desta forma, na hora de salvar, eu confiro se o valor do contador no objeto que recebi é o mesmo do banco de dados. Se não for, significa que alguém alterou o objeto e eu recebi um objeto inconsistente. Novamente, o problema é que esse campo não faz parte do concern persistência ou modelo de dados. Portanto, não faria sentido eu declarar o contador na classe User. Pra resolver esse problema, AspectJ usa intertype declarations, que permitem a declaracão de campos e métodos em outras classes. Por exemplo, o código abaixo declara e inicializa um campo counter na classe User, removendo dela o concern tratamento de concorrência.

public aspect ConcurrencyManagerAspect { long User.counter = 0; ... }

Conceitos

Quando eu estava descrevendo o exemplo eu me referi à alguns conceitos de AOSD. Mas acho importante deixar um pouco mais explícito, então seguem algumas definições.

Existem alguns eventos que acontecem durante a execução de um programa que são interessantes de serem interceptados. Esses pontos são chamados join points. Nós vimos que a execução de um método é um ponto interessante quando o objetivo é substituir o método ou introduzir funcionalidade antes ou depois do método. Outros join points, por exemplo, são a chamada de um método (não confundir com a execução), a leitura ou escrita de um campo, ou a inicialização estática de uma classe. Mas esses join points são eventos concretos que acontecem em tempo de execução. Pointcuts são um jeito de especificar de forma abstrata (por exemplo, com regular expressions) quais join points você quer interceptar. AspectJ oferece pointcuts primitivos pros join points mais importantes (como execution, call, get, etc) e outros pointcuts usados pra identificar os objetos envolvidos nos eventos e ligá-los a variáveis. No nosso exemplo, args foi usado pra pegar uma referência ao objeto passado como parâmetro ao método. Outros pointcuts importantes são target (e.g., o objeto que é o destino de uma chamada de método) e this (e.g., o objeto que faz a chamada).

Quando um join point acontece em runtime e ele faz match com um pointcut, então se executa o advice relativo ao pointcut (se tiver algum). Nós vimos como um advice do tipo around funciona. AspectJ também tem advices before e after (que pode ser after throwing ou after returning), que servem pra introduzir código antes ou depois do join point. Intertype declarations também são disponibilizadas por AspectJ pra introduzir novos campos métodos em outras classes, como vimos no exemplo.

Um aspecto é então um módulo usado pra encapsular todas essas construções, do mesmo jeito que uma classe encapsula métodos e campos. O objetivo principal de um aspecto é de modularizar um interesse (concern) que não faz parte da decomposição normal da hierarquia de classes. Esses concerns são chamados de crosscutting concerns, porque eles entrecortam a hierarquia. A implementação de um concern é dita espalhada (scattered) quando ela não está localizada, e entrelaçada (tangled) quando ela está misturada com outros concerns.

Pra quem tiver interesse, o AspectJ Programming Guide é um excelente tutorial com bons exemplos e referências pros diversos tipos de pointcuts, além de explicar alguns problemas que você pode encontrar quando não tiver muita experiência com aspectos.

Discussão

No texto acima eu tratei AOSD e AOP quase como sinônimos, mas eu quero enfatizar que são distintos. Orientação a aspectos surgiu como AOP, pois trazia soluções pra problemas diretamente ligados ao código, como no exemplo sobre controle de transações. Contudo, percebeu-se que seria interessante elevar o nível de abstração e que crosscutting concerns podiam também ser encontrados em design, arquitetura, análise de requisitos, etc. Então AOSD trata não só da programação, mas também de todo o conjunto de metodologias, processos, etc, ligados ao desenvolvimento de software usando a idéia de que crosscutting concerns devem ser tratados como “first class citizens”.

Na minha experiência, AOP como encontrada em AspectJ é utilizada quase que exclusivamente em círculos acadêmicos. Existem algumas empresas que adotam AspectJ para alguns projetos, mas a linguagem está longe de ser adotada em larga escala. Porém, alguns conceitos de AOP são muito usados em alguns frameworks e application servers como Spring e JBoss. Spring, por exemplo, tem o conceito de interceptors, que nada mais são do que pointcuts e advice, mas menos poderosos (não permitem interceptar join points de pouca granularidade, como getters, se não me engano).

Eu acredito que existem atualmente dois grandes impedimentos pra se usar AOP em larga escala. Primeiro é a tecnologia do compilador. Hoje em dia ninguém aceita trabalhar com um compilador que não seja incremental. Quando você usa Eclipse e faz uma alteração no código, o JDT só compila o necessário. O similar para AspectJ, AJDT, tenta fazer o mesmo. Porém, a tarefa é muito mais difícil, pois uma pequena mudança tem que ser verificada perante todos os pointcuts do seu sistema, pois de repente o que você mudou pode agora ser interceptado pelo pointcut. Assim, apesar de ter uma performance relativamente boa, trabalhar com AspectJ usando AJDT não é tao agradável quanto trabalhar com Java usando JDT.

Segundo, AOSD não vai acabar com todos os seus problemas. É difícil pros programadores entenderem os conceitos e leva tempo pra comunidade desenvolver design patterns e entender o que tirar de melhor do paradigma. Pointcuts são atualmente regular expressions, então eles são frágeis e qualquer mudança de nome de um método pode fazer um pointcut não mais interceptar aquele método, e você não tem como perceber que isso aconteceu. Suporte de boas ferramentas é então essencial. Além disso, eu tenho dúvidas de que aspectos sejam bons pra modularizar concerns heterogêneos como “logging”, que foi sempre um dos principais exemplos. O problema é que cada instância de logging é muito específica pro contexto, então não faz sentido você remover todas as chamadas de log e encapsular em um advice, pois o advice teria que ser diferente pra cada ponto que ele interceptasse. Controle de transações é homogêneo, pois o advice sempre faz a mesma coisa, então é um exemplo mais apropriado. Existem estudos que apontam que Mixin Layers são melhores pra concerns heterogêneos como logging, mas isso seria assunto para um outro post.

========================== Nota no dia 5/11

Conversando com o Miguel eu acabei percebendo algo que talvez não tenha ficado muito claro. AspectJ é uma extensão de Java e precisa de um compilador especial que compila as classes como se fossem Java e depois analisa os aspectos do sistema e faz diretamente as modificações no código (ou introduz testes de tempo de execução quando não tem como verificar estaticamente o resultado). Esse último passo, “introduzir os aspectos”, é chamado weaving.

Existem duas alternativas principais de compiladores pra AspectJ. O ajc é parte do AJDT, que é uma extensão do JDT do Eclipse. Como o JDT, o AJDT contém editor, compilador, debugger, e tudo o mais, implementados em plugins pro Eclipse. O principal objetivo do projeto é que a compilação seja rápida pra que você tenha a mesma experiencia que programando em Java, ou seja, ele tenta ser incremental e o código do compilador em si é otimizado (leia-se, não-facilmente extensível).

A outra alternativa é o abc (Aspect Bench Compiler), que é um compilador acadêmico cujo objetivo principal é ser extensível pra que se possa fazer pesquisa com linguagens de programação, otimização de código, análise estática, etc. Dessa forma, compilar um sistema grande com abc, principalmente usando as otimizações disponíveis, é bem demorado. O ideal é programar usando o AJDT e usar o abc pra gerar o bytecode de produção, já que ele é bem mais otimizado que o gerado pelo ajc.

Além disso, AspectJ não é a única linguagem orientada à aspectos derivada de Java. No site da aosd.net, por exemplo, tem uma lista com alguns sistemas (derivados ou não de Java) e algumas ferramentas orientadas a aspectos.

Qual a fonte?

Essa dica é para amantes de design em geral, e fontes e tipografia em particular. Descobri hoje, lendo a revista Mac+, o site What the Font que oferece uma ferramenta que tenta descobrir a fonte utilizada para escrever texto em um arquivo de imagem.

O processo é simples: faça upload ou passe a URL da imagem que deseja analisar, verifique se os caracteres que ele detectou estão corretos e pronto. O resultado é exibido na forma de uma lista de textos em diversas fontes, e o sistema permite a comparação das fontes obtidas com a sua fonte original mostrando a sua imagem sempre no centro da tela.

Utilizei a ferramenta para tentar descobrir as fontes utilizadas pelo Raphael para fazer o logo do job4dev (ele perdeu o arquivo PSD original…), e o resultado obtido está abaixo:

whatthefont.png

A fonte ATCapone-Light parece a correta!

Frase do Dia

“Customers don’t know what they want, much less what they need, until they see it.”

O que tem de tão legal em usar um DVCS?

Contrariamente aos desejos do nosso editor-chefe, este blog ainda não é o seu ponto único de informação sobre tecnologia. Se fosse, você estaria perdendo muita informação interessante que foi produzida em outros lugares. Por exemplo: muitas pessoas estão passando a adotar software para gerenciamento distribuído de código e controle de versão. Tendo em vista que nosso editor-chefe é um usuário de CVS e SVN, não faz muito sentido para ele acompanhar as experiências das pessoas que estão adotando sistemas que se fundamentam em outra arquitetura básica, logo ele terá pouca oportunidade para escrever a respeito.

Esse não é um texto para falar sobre as vantagens técnicas de usar sistemas distribuídos de versão (Distributed Version Control System – DVCS) como o Git, Darcs ou Mercurial. Já existe muito material que você pode encontrar por aí, como por exemplo uma apresentação em vídeo do próprio Linus Torvalds sobre o Git e suas vantagens. O que eu quero é justamente comentar sobre os princípios que levaram Torvalds a desenvolver um sistema completamente novo. De acordo com Linus, sistemas distribuídos não são melhores por questões tecnológicas ou de engenharia avançada, mas são melhores por princípios da arquitetura: “Com sistemas centralizados”, diz ele, “o desenvolvedor tem que lidar com questões que podem se tornar problemas políticos – saber quem pode ter acesso ao sistema; quem pode ter acesso de escrita; quem vai ser o responsável por determinados subsistemas, etc. Em sistemas distribuídos, isso não acontece. Cada desenvolvedor é dono do seu próprio repositório e decide livremente de quem ele irá puxar suas alterações. Não existe um repositório canônico, oficial. O meu repositório de código é tão bom quanto o dos desenvolvedores que estão trabalhando em repositórios de quem eu colaboro.”

Uma outra questão – fundamentalmente importante – é a questão da confiança. Diz Torvalds: “Eu não preciso mais ficar verificando a qualidade de cada patch que entra na minha árvore de código. Eu posso puxar qualquer coisa que venha da árvore de Andrew Morton (desenvolvedor principal da versão 2.6 do kernel do Linux) sem medo de causar prejuízo em meu código, pois eu confio em Andrew.”

Esse é um argumento estranho, mas muito poderoso: nós podemos confiar em uma, duas, dez pessoas. Mas não podemos confiar em centenas ou milhares de pessoas. Confiança não é escalável. Podemos até confiar em alguém desconhecido indiretamente (o amigo do meu amigo…) mas esse critério não resiste por muitas iterações. É esse o ponto que torna sistemas que se baseiam em arquiteturas distribuídas melhores.

Não importa o quão superior ou madura seja a implementação de um sistema centralizado, ele jamais terá “trabalhe apenas com as pessoas em que você confia” como uma feature. É impossível obter isso em um sistema centralizado, por design. A killer feature de um DVCS é a possibilidade de gerenciar seus projetos usando o seu grau de confiança nas pessoas.

ClockingIT

Existe uma ferramenta muito legal para controle de projetos, bugs, releases e afins disponível na web: ClockingIT.

Este projeto tem uma história bem peculiar: foi desenvolvido por um casal norueguês, Erlend e Ellen Simonsen. Erlend é desenvolvedor e é responsável por toda a parte de programação. Ellen é designer e cuida de toda a parte visual.

O ClockingIT segue a mesma receita de muitos projetos de alta qualidade e valor agregado disponíveis por aí: nasceu de uma necessidade do próprio desenvolvedor. Erlend é um consultor de tecnologia independente e desenvolve projetos sob medida para vários clientes, e sentia a necessidade de ter uma boa ferramenta de gerenciamento de projetos. Seguindo a filosofia do Getting Real, resolveu por a mão na massa e começou a desenvolver o ClockingIT usando a plataforma Ruby On Rails.

O sistema permite o cadastro de diversos projetos e colaboradores. Em cada projeto, é possível criar milestones e tarefas (com diversos graus de prioridade, nível de dificuldade, tempo estimado de execução e data de entrega) e designar um colaborador para sua execução. Além disso, o sistema permite a criação de subtarefas e permite registrar o tempo levado para executar cada tarefa, gerando um log de trabalho. Com estes dados, é possível criar relatórios diversos, como timesheets que são extremamente úteis para consultores que ganham por hora. E o sistema ainda oferece vários modos de visualização de todos estes dados: timeline, schedule, lista de tarefas…

Mas o grande diferencial, aquilo que me faz ter vontade de escrever sobre o ClockingIT neste blog, é a qualidade da interface. O design gráfico é simples, limpo e extremamente intuitivo. E o site faz um uso intensivo e inteligente de scripts e recursos assíncronos. Eu destaquei a palavra inteligente porque hoje em dia vemos muitos sites usando Ajax simplesmente por usar, apenas para baterem no peito e gritar “Eu sou uebi doispontuzeru!!!”. Olhando a interface do Clocking IT, talvez muitos resolvem repensas suas visões. A interação como o site é extremamente ágil e leve, exatamente como em software desktop. Qualquer ação efetuada por algum usuário é imediatamente refletida na tela de outros usuários conectados no mesmo projeto. É possível saber em tempo real quem está conectado no sistema, o que está fazendo, a quanto tempo está fazendo. E graças ao chat embutido, é possível até conversar com outros colaboradores.

Last, but not least, o sistema oferece integração com iCal (pode ser integrado com Google Calendar ou com o calendário da Apple por exemplo), sistema de RSS, um wiki, fórum , e uma speedbar (uma janelinha pequena) que permite que o usuário marque o início e fim de uma tarefa (com botões de start, stop e pause e um cronômetro rodando em tempo real).

É, definitivamente o ClockingIT não deixa nada a desejar de ferramentas tradicionais de gerenciamento de projetos. E com certeza ele é uebidoispontuzero.

Next Page »

Switch to our mobile site