SubClasses não! Decorators

May 16, 2013, by Aurélio A. Heckert - No comments yet

Imagine: Você está fazendo um sistema que deve apresentar diversos formatos de dados similares e derivados. O que você faz?

Classes e Subclasses! Yeah!
Não... Pare.

Eu também sempre fui por aí, o projeto Noosfero também começou assim, mas qual é o problema com isso?

Imagine que você quer apresentar arquivos em uma página web. Temos a classe File e sabemos que imagens devem ser apresentadas com a tag <img>, enquanto outros arquivos serão apresentados apenas com o link para download. O que você faz?

Podemos fazer duas subclasses estendendo File: Image e GenericFile — ou — colocamos um "if" na visualização (Ugh... mas é mais econômico).

Ok... Mas agora você decidiu que as imagens serão editáveis e você precisa de um modo de edição diferenciado para bitmaps (PNG, JPG) e vetores (SVG). Depois você decide que vai adicionar um player para áudio (OGG), para vídeos html5 (OGV, WebM) e vídeos flash (FLV, MP4). Vai continuar usando "if"?

Agora não dá para fugir de uma representação com classes especializadas. A primeira idéia seria:

  • File
    • Image
      • ImageBitmap
      • ImageVector
    • Playable
      • Audio
      • Video
        • VideoHTML5
        • VideoFlash
    • GenericFile

No caso de uma aplicação Rails e de tantos outros fameworks (se não todos), a classe que representa o objeto persistido em banco (Model), contendo ou não referência para o sistema de arquivos, é igualmente registrada em banco, frequentemente tendo uma tabela exclusiva para sí. Isso torna a busca por itens do tipo X muito mais fácil e rápido.

Qual o problema com isso? Imagine que agora você decide fazer o preview de documentos de texto, como PDFs e ODFs. Ao atualizar sua aplicação será necessário fazer uma correção no banco onde alguns registros dos tipos em questão serão convertidos para o novo tipo.

"Migrations existem para isso. Não é?" Sim, mas você não quer adicionar complexidade (código extra e espalhado) a sua modificação. Você quer tranquilidade. O admin quer tranquilidade. Atualização simples, admin feliz → Admin feliz, usuário seguro. :-)

Ok, esse argumento ainda não convenceu, então imagine que sua aplicação suporta plugins e alguns deploys irão usar um ou outro plugin para um tipo não suportado no core e, para piorar, o plugin pode ser habilitado e desabilitado a qualquer momento. O que fazer? Você faria com que o método que habilita e desabilita plugins rodasse a migration nesses momentos? (Se você respondeu "Er... Sim." eu tenho medo de você.)

Bem, acho que agora eu convenci que é possível chegar em um ponto onde a criação de subclasses para apresentar tipos derivados pode te levar a calvície prematura ou internação em instituição psiquiátrica. Mas qual é a solução?

Não registre tipos derivados em novas classes! Crie decorators.

Você sabe o que é um decorator? Eu também não estudei padrões de projeto com afinco, mas é bom que você tenha ao menos uma idéia do que isso significa.

No livro Objects on Rails, Avdi Grimm descreve o que ele chama de exhibit, um decorator especializado em apresentar models. Perfeito! Inspirado nele eu trabalhei numa solução para a exibição de arquivos no Noosfero. A idéia é o seguinte:

Pegue aquele diagrama de herança de classes que coloquei no início do texto, não derive File, crie uma árvore independente de exhibits:

  • File (sem subclasses)
  • FilePresenter (o nome que dei ao meu exhibit abstrato)
    • Image
      • ImageBitmap
      • ImageVector
    • Playable
      • Audio
      • Video
        • VideoHTML5
        • VideoFlash
    • GenericFile

Quando você quiser apresentar um arquivo não use render_page_to(some_file), use:

fp = FilePresenter.for(some_file)
render_page_to(fp)

Quando quiser extrair uma informação específica ao tipo do arquivo não use some_file.icon(), isso exigiria uma coleção de ifs neste método, use:

fp = FilePresenter.for(some_file)
fp.icon

A princípio pode parecer que precisaremos de muito código extra coletando o FilePresenter de File antes de qualquer exibição, mas não, veja o meu patch para o Noosfero. Poucos locais precisam usar o método FilePresenter.for() e a tal instância é repassada adiante para outros usos. Essa implementação apenas coloca o (praticamente) mesmo código em locais diferentes, tornando "tudo" mais maleável. Com isso os sérios problemas mostrados antes estão solucionados. Você pode, por exemplo, adicionar e remover um plugin que define um presenter para qualquer tipo específico, a qualquer momento, e tudo funcionará de imediato, sem maiores preocupações (se você não usar POG).

Mas como funcionaria o FilePresenter.for()?

Sim, esta é umas das diferenças da minha implementação para a proposta de Avdi (boa para este caso, mas considere a proposta de Avdi mais abrangente), o método de classe deve perguntar a cada subclasse se ele aceita exibir o arquivo e com qual prioridade.

Por exemplo, se temos uma imagem JPG o FilePresenter.GenericFile deve responder positivamente com um valor baixo, o FilePresenter.ImageBitmap deve responder positivamente com um valor alto e o FilePresenter.Audio deve responder positivamente com um valor nulo. Depois de coletar todas as respostas, o método FilePresenter.for(meu_jpg) criará uma instância da classe que respondeu com maior prioridade, encapsula o objeto File meu_jpg.

Como a subclasse de FilePresenter "responde" se aceita ou não encapsular a instancia de File? Toda subclasse deve implementar o método accepts?(file) como método de classe.

Tudo junto (uma visão simples):

class FilePresenter
  def self.for(f)
    # Intera para cada subclasse, ordenando por prioridade
    klass = FilePresenter.subclasses.sort_by {|class_name|
      # Pergunta se a subclasse aceita o arquivo e sua prioridade
      class_name.constantize.accepts?(f) || 0
    }.last.constantize  # Pega a subclasse de maior prioridade
    # Retorna a instância da subclasse, encapsulando o objeto do arquivo
    klass.new(f)
    # Existem outros detalhes a serem considerados, mas isso basta para o exemplo.
  end

  # Isso fará esse decorator funcionar como uma instância de File
  def method_missing(m, *args)
    @file.send(m, *args)
  end

  ...
end

class FilePresenter::Image < FilePresenter
  def initialize(f)
    @file = f  # encapsula a instância de File
  end

  def self.accepts?(f)
    # retorna alta prioridade para imagens
    f.content_type[0..4]=='image' ? 10 : nil
  end

  ...
end


My JavaScript Style Badge

January 8, 2013, by Aurélio A. Heckert - No comments yet

+------------------+
|sn?b.             |
|p+?*              |
|=C1               |
|+?                |
|1          #  ##  |
|           # #    |
|           #  ##  |
|        #  #    # |
|         ##   ##  |
+------------------+
 jsstyle.github.com 



Olha mãe! Eu que fiz.

November 7, 2012, by Aurélio A. Heckert - No comments yet

Contribuir com o Software livre trás sensações boas em cascata e inclui uma massagem no ego que poucas profissões podem prover. A coisa acontece mais ou menos assim:

  • Você pega um software ou um serviço que um monte de gente usa, executa uma funcionalidade costumeira, e pensa:
    • Essa é uma funcionalidade legal, e percebe:
      • Pô! Tá funcionando melhor! E lembra:
        • Puts! Fui eu que fiz!
          • Sinta a ocitocina na veia.

A minha última sensação do tipo "puts! eu que fiz" foi essa aqui:

imagens no histórico de atividades do noosfero

Uma porra de um bloquinho que lista de uma forma chuchuzinha (no histórico de atividades) as imagens que você postou.

Quer sentir isso? Existem milhares de projetos de SL precisando da sua mão. Quer uma forcinha? Participe do HackingDay!



HackingDay - Um dia para Hackear, aprender e FAZER!

November 7, 2012, by Aurélio A. Heckert - No comments yet

Neste domingo (11/11) faremos a segunda versão do encontro HackingDay Salvador, na sede da Colivre, das 13:30 às 19:00 horas.

Logo do HakingDay SalvadorO HackingDay é um dia dedicado a solução de um bug ou implementação de uma feature em um projeto de SL, usando a metodologia Coding Dojo, onde todos aprendem, mas o objetivo central é contribuir com um projeto e solucionar um problema do mundo real.

O primeiro encontro (v0.0) aconteceu no dia 27 do mês passado, no CPD da UFBA. Veja o relato e fotos.

Para o encontro deste Domingo, 11/11 já temos até a quitutes de festa garantidos. Serão produzidos pela cooperativa Rango Vegan, graças ao apoio da professora Debora Abdala e do projeto TecCiência.

Se estiver em Salvador, venha participar localmente. Se estiver fora, siga o HackingDay Salvador no Face, Twitter ou Identi.ca e pegue o convite para o Hangout.

...e venha codar com a gente.



Senhores da Vida e da Morte

October 23, 2012, by Aurélio A. Heckert - No comments yet

Não estou falando de nenhum grupo em especial, esse não é um post sobre os Iluminati.

Estou falando de você.

Alguns onívoros de mente mais aberta as vezes dizem:
"Matar animais não é muito legal, mas a vida na fazenda (em algumas delas) é muito melhor que a vida na selva. Lá é luta constante pela vida."

Spot
Spot, minha filha com dificuldade de entender o ditado "a curiosidade matou o gato"

Pois é, sou vegano eu cuido de uma gata e entendo bem o argumento de que conosco a vida deles é bem melhor do que na selva (ou o equivalente "selva de pedra"), ela que o diga (se pudesse) sobre quando decidiu pular no telhado de uma loja e ficar mais de 24 horas pela própria sorte. (hoje ela ronrona muito mais do que antes! :-))

Mas, como você mesmo pontuou, esse benefício só está presente quando se trata de um cuidado realmente interessado, o que exclui muitos "donos" ("cuidadores" ou "tutores" é melhor), muitas empresas e muitas fazendas.

Mas, considerando que todas as fazendas são lindas, como nos comerciais... O fato de (que em alguns casos) damos uma vida melhor ao animal, nos dá o direito de mata-los?

Hoje, é tranquilamente viável ser vegano. Vamos matar seres sencientes simplesmente para o nosso prazer gastronômico? Temos esse direito? Porque? Porque (de um ponto de vista antropocêntrico), na escala de prioridade de direitos, o direito de qualquer animal vem depois do direito dos humanos? Eu até concordo com isso, mas isso valida a morte de um senciente simplesmente para o nosso prazer?

Você acredita que sim? Afinal na escala de prioridades os direitos dos animais seguem após os direitos dos humanos (do nosso ponto de vista), então lhe peço: Posso tirar-lhe um rim? Garanto que vou usar um método indolor. Peço porque um parente meu precisa, um parente muito querido. Veja só:

  1. Essa pessoa está num grau superior ao seu, no meu ponto de vista.
  2. Não peço para suprir um simples prazer, é real necessidade.
  3. Nem vou te matar, só vou encurtar sua vida.

Isso é diferente? Como assim? Falamos em uma escala de prioridade, porque esquecer que existe essa escala entre humanos? Meus familiares e meus amigos estão em níveis superiores aos de desconhecidos, como você.

Não temos esse direito entre humanos? Porque temos esse direito com outros seres sencientes? Quem foi que nos deu esse direito? Papai do céu?

Existem outros porquês além do prazer gastronômico para não ser vegano? Saúde? Mas as estatísticas são favoráveis, já temos médicos e nutricionistas veganos para os inseguros. Temos uma grande bagagem de estudos para quem quer entender por conta própria (eu curto).

O receio está nos idiotas? Brow... se você descobrir como se livrar dos idiotas, me conte. ;-)

Você não vai se convencer agora, tudo bem, afinal você não é nenhum idiota com idéias tão voláteis... mas você já pensou na criação de aves? Não se pode comparar com a criação (brasileira) de bovinos. É desumana (para não dizer extremamente cruel). E aí? Se o argumento era trocar uma boa vida pela morte prematura, o que argumentar nesse ponto? Que tal parar de consumir aves e ovos?

"Mas em todo lugar existe exploração. Teriamos que parar de consumir de industrias que maltratam seus funcionários também."

Ótimo! O argumento de que em todo lugar tem exploração é válido. Mas não devemos considerar também a escala? Em que fábrica os trabalhadores ficam presos em jaulas apertadas por toda sua vida, que ainda é concluída com uma morte prematura que nada tem a ver com eutanásia?

Realmente se importa com isso ou foi só um argumento de fuga? Se você se importa, existe o Comércio Justo e a Economia Solidária precisando de consumidores. Por exemplo, temos a RedeMoinho em Salvador.

Quer ler mais sobre direitos dos animais? Visite a ANDA - Agência de Notícias de Direitos Animais.

Um site que trata de vários temas relacionados. Foque em alguns temas específicos lendo os colunistas. Indico fortemente a Dr. em filosofia Sônia T. Felipe e o jornalista Marcio de Almeida. (não quero fazer injustiça com os outros, então de uma olhada na lista de colunas da página inicial :-))

Quer a opinião de um profissional e informações técnicas/científicas? Visite a Nutriveg - Consultoria em Nutrição Vegetariana.

O site do Dr. em Nutrição George Guimarães. Já li alguns textos dele e assisti a uma de suas palestras sobre direito animal. Ele tem lutado bastante para mudar a visão preconceituosa dos profissionais brasileiros de saúde sobre o vegetarianismo, já que em países como os EUA e Inglaterra (dentre outros) as associações médicas já reconhecem e apoiam o vegetarianismo a muito tempo.