segunda-feira, agosto 27, 2007

"Pseudo" encapsulamento

Junto com "herança" e "polimorfismo", "encapsulamento" é o terceiro pilar da programação orientada a objetos. Aliás, tentar fazer da programação um aglomerado de "caixas pretas" é uma coisa que tentamos desde que inventaram o Assembly. A idéia sempre foi ocultar detalhes para que se possa modificar o programa facilmente ou, no mínimo, não esquentar a cabeça com detalhes. Um dos melhores patterns que já vi sobre o assunto faz parte de um catálogo de patterns do Brian Foote chamado "The Selfish Class". Ele não foca exatamente sobre mudança, mas sobre facilidade de uso. Mas no fim da contas, fazer muito através de uma interface mínima é o melhor ponto sobre encapsulamento. O que vemos muito por aí é encapsulamento "pra inglês ver". Só funciona na teoria, porque na prática, é igual tentar se esconder tampando os olhos, no melhor estilo: "se eu não o vejo, ele também não me vê". Ao longo do tempo, comecei a encarar alguns items como bad smell de "encapsulamento pra inglês ver" e gostaria de compartilhar eles: Métodos de acesso : Talvez o bad smell mais comum e o mais polêmico. Métodos de acesso (gets e sets) quebram encapsulamento. Não sou o único a pensar assim (procure no google, você vai achar mais alguns doidos, inclusive com muito mais cacife que eu). Basicamente a idéia é a seguinte: você está expondo seus dados internos, mesmo que seja atráves de um método. Isso não é muito problema, desde que, em 90% do tempo, você trate o objeto como um todo ao invés de ficar cutucando seus dados (do objeto, não os seus) Mas quando você começa a pegar os dados de uma tela web e jogar campo a campo no seu objeto, isso significa que você quebrou o encapsulamento. Uma maneira "caixa preta" de fazer isso seria jogar os dados todos pra dentro do objeto e ele pegar o que precisa OU fazer a atribuição via reflexão. O ponto é o seguinte: se você alterar um campo e tiver que mexer em algum lugar que não seja tela, banco ou regra de negócio, então seu encapsulamento está quebradão. Caso típico é quando você precisa ficar"repassando" os campos. Não concorda? Não tem problema, é um feeling meu (e de alguns outros doidos). Coloca o seu aí, então, ué :) Delegate : Delegates são uma ótima forma de diminuir o número de objetos que você tem que trabalhar. Mas quando você começa a delegar TUDO, ou então os mesmo dados (parâmetros e retorno) são simplesmente passados sem alteração ou qualquer outro comportamento por 2 ou 3 métodos... hmmm... tem algo estranho. Já vi isso sendo usado como uma forma de manter as coisas no lugar quando se está em uma arquitura com muitas camadas. Mas sinceramente, isso é só uma maneira muito esquisita de violar camadas, não de preservá-las. Se você precisa de algo lááá do fundo em uma camada da frente, sem alterações, melhor repensar sua arquitetura nesse ponto. Iteradores externos : (essa vai fazer alguém querer comer meu fígado... sem tempero!!!) Essa viola a máxima do "Tell, don't ask" sem dó nem perdão. A moral da história é a seguinte, ao invés de pedir para as coleções trabalharem para você, somos obrigados a pedir licença, pegar tudo o que elas têm e ficar item a item vendo o que precisa ser feito. A solução para isso seriam iteradores internos, implementados lindamente com closures (viva Smalltalk, 30 anos atrás já fazia isso!). À primeira vista, quem não conhece vai achar que dá na mesma, só quando você olha mais de perto vê que várias operações básicas que fazemos todos os dias podem ser encapsuladas com isso. Tais como "selecionar o primeiro que satisfaz uma condição", "fazer um 'de->para' com os objetos da coleção", "acumular valores segundo uma fórmula qualquer", e por aí vai. "Data Transfer Objects" ou "Value Objects" : Essa viola o "objetos devem ter comportamento". Veja bem, um Objeto (com "O" maíusculo) é justamente a junção de DADOS + FUNCIONALIDADE (comportamento), se você tem dados em um objeto, então a funcionalidade que atua sobre esse objetos está em outro lugar... separando dados e funcionalidades voltamos a programação procedural (ou funcional). E no que isso viola encapsulamento? Viola porque outros objetos precisam conhecer os detalhes do seu DTO para poderem trabalhar com ele. Trocando em miúdos, esse "pseudo-objeto" está com suas estranhas completamente expostas e a mercê de outros objetos malignos que querem comer seu coração (do objeto, não o seu). Okay, como tudo na vida, existem lugares onde DTOs são legais e bemvindos. Mas quando eles começam a aparecer demais, aí dá pra começar a ficar desconfiado. Se a gente realmente caprichar no uso dos DTOs, vamos acabar com um sistema OO com a cara de um ASP dos antigos, mas com mais camadas... No geral, são esses meus bad smells de encapsulamento. E são realmente bad smells, ou seja, pode ser que não seja nada e que o uso esteja correto, mas não custa dar uma verificada. Outra coisa é que encapsulamento é legal, mas não é nenhuma vaca sagrada (é?). Violo ela com muito gosto e uma pitada de sal se for para deixar meu sistema mais simples. Infelizmente, normalmente é o contrário... :)

sexta-feira, agosto 10, 2007

Linguagens do futuro

Um colega meu ("O Francês") me apontou alguns links que o pessoal aposta no Erlang como próximo sucessor do java. Isso me deu vontade e exercitar minha "veia futuróloga". Não que eu seja um bom futurólogo, mas é que é divertido ficar fazendo extrapolações. Na minha humilde opinião, acho que existe um buraco a ser preenchido para a "Próxima Grande Linguagem". Explico: Nos anos recentes, ando observando uma crescente valorização das linguagens de script, acho que principalmente por causa do Javascript, Ruby e Python (não descartando PHP) que vêm ganhando atenção. Com isso, vi que certas coisas, como "closures" ganharem bastante importância conforme o pessoal começou a perceber que isso facilita muito a programação. Essas funcionalidades todas, até onde percebo, são justamente as que facilitam a criação de DSLs (Domain Specific Languages). Nota pessoal: um bom sistema Orientado a Objeto é praticamente uma DSL. As boas práticas de programação OO parecem levar inadvertidamente a uma DSL. Se esse pressuposto for correto, a próxima grande linguagem deveria ter funcionalidades boas para gerar DSLs. As melhores que conheço hoje são Lisp e depois Ruby. Para o Lisp virar a "Próxima Grande Linguagem", me parece que faltam duas coisas: uma sintaxe mais digerível para os desenvolvedores que estão acostumados com linguagens de sintaxe "C-like" e uma "killer application" (ou deveria dizer "killer framework") para web. Digo isso porque aplicações web são o que está quente hoje, com tendência a ficar mais quente :) Para o Ruby virar a "Próxima Grande Linguagem", sinto que falta uma boa máquina virtual, talvez com compilação para alguma linguagem intermediária. Aí entra a competência do YARV ou do JRuby (ou IronRuby também). Conclusão: acho que existe um buraco aí que poderia ser preenchido por algo que compilasse em bytecode de Lisp, mas que tivesse uma sintaxe mais similar ao que a grande maioria dos desenvolvedores está acostumado. Eu estava trabalhando em algo na linha, só por farra, mas minhas habilidades em compiladores ainda é meio crua e vai demorar um bom tempo até aparecer algo que valha a pena brincar :) Vamos ver no que dá.