O verdadeiro custo do Paliativo



Quem nunca ouviu uma frase semelhante a essa: "Faz aí uma correção que resolve esse problema rapidamente, pois precisamos entregar isso para o cliente e tem que funcionar. Outra hora revisamos esse código com mais calma".

E assim iniciou-se um ciclo viciante (sim, viciante, essa é realmente a palavra) de pequenas inserções de código que visam boas intenções, obviamente, porém com consequências destrutivas no longo prazo. Se você já passou por isso sabe do que estou falando. São essas pequenas atitudes que fazem com que um projeto comece a ter pequenas amostras de código que poderão futuramente nunca ser substituídas.

Afinal, para que serve um paliativo?

Você já se perguntou? Bom eu vou lhe explicar. Um paliativo segue a ideia de um MVP praticamente, desenvolver o mínimo de valor entregável que gere um retorno e que atenda às expectativas do cliente. Só que um paliativo não pode ser chamado de MVP, ele apenas segue a ideia, aí você adiciona um pouco mais de velocidade ao processo (nesse momento você já deve estar no meio do fogo), e também algumas palavras mágicas devem ser proferidas: "Isso será temporário!".

Ei pera aí! Temporário? Eu acho que tem algo errado aí. Algo que deveria ser temporário acaba permanecendo por muito tempo no sistema. Na maioria das vezes esse paliativo recebe correções e até melhorias. Nesse ponto, significa praticamente dar uma armadura a um monstro que já existia. Você agora está fortalecendo esse monstro.

"O cliente não se importa com o código", muito se ouve falar isso. A única pessoa que se importa com ele é você, pois nem o seu gestor está interessado se aquilo é um paliativo, se é de fato uma implementação permanente, se é a melhor aplicação dos padrões de projeto que existe, ninguém se importa, somente você e agora você tem um monstro com uma armadura em seu sistema. Sabe por quanto tempo ele vai viver?

Por que paliativos são um problema?

Simples, pois eles funcionam. E somente por esse atrativo é que são tão requisitados e aceitos. E então esquecidos. Mas então qual é o problema com eles? Especificamente, o problema não está no paliativo. Se um sistema fosse apenas um paliativo, ele em si seria o sistema, logo não seria um paliativo, nem poderia ser chamado disso. O que estou tentando dizer é que nesse contexto o "paliativo" não é um problema, ele é um sistema. 

Quando o problema é o paliativo, você tem um problema no sistema. Um pequenino e indefeso código lá nos confins daquele bolo de classes e arquivos de configuração, no futuro pode resultar em um comportamento que precise de correção ou melhoria. Nesse contexto, nós temos um problema, mas o que é o problema: o paliativo inocente ou a situação do futuro que precisa mexer nesse paliativo? Perceba que aqui estamos falando de questões como planejamento e segurança: quanto estou disposto a me defender de possíveis anomalias conhecidas do meu sistema?

Bom, você tem um problema quando esse paliativo está em um local incomum, não rastreável, nem por olhos humanos, durante a rotina do dia-a-dia, e nem por sistemas de versionamento. Você também tem um problema com paliativos quando eles não seguem boas práticas como escalabilidade, legibilidade, manutenibilidade, responsabilidade, baixo acomplamento, e tantas outras mais. Aliás, um paliativo que segue essas práticas não pode ser chamado de paliativo.

Não sei se ficou claro, mas o fato de um sistema ser algo dinâmico faz com que você tenha que pensar no futuro. Já falei sobre o futuro logo acima. O paliativo somente será um problema se existir um futuro, e ele existe para a maioria dos sistemas. Partindo dessa premissa, talvez o problema realmente não seja o paliativo, mas sim você que chegou ao ponto de precisar de um paliativo.

Algumas táticas para contornar isso

Fugir dos paliativos é um pouco difícil. Você precisa deles e isso é inevitável por diversas razões: um paliativo de curto prazo é a melhor solução para que um projeto seja entregue, uma demanda seja finalizada ou uma correção em ambiente produtivo mantenha o sistema online. Então nesse caso o que fazer? 

Regra nº 1: Rastreabilidade

Primeiro passo é tornar esse paliativo rastreável, ou seja, você quer que esse paliativo dentro de algum tempo vire algo que seja revisado, e feito de forma correta e permanente. Estamos falando agora de código limpo, performático e fácil de entender. Um código que siga os padrões do Projeto e de Projeto (perceba a diferença entre "de projeto" e "do projeto", um papo para outro post). 

Se a sua empresa utiliza algum controle de issue, o melhor é neste exato momento que você está criando o paliativo também criar uma issue explicando como encontrar o paliativo e o que deve ser feito para que posteriormente alguém atenda a esta solicitação, ou mesmo você, e a correção deste paliativo resulte em uma implementação correta do mesmo. Se um Kanban é utilizado em seu projeto, essa é a hora de fazer um ticket ou story aparecer nele, provavelmente em backlog, o local não importa, mas alguma coisa deve aparecer.

Regra nº 2: Código Sujo

Sim, sujo, é isso mesmo que você leu, uma denominação que indica o oposto de código limpo. Uma importante atitude é não fazer com que esse paliativo seja algo excepcionalmente bom, ou seja, se você é um expert em código não utilize das melhores práticas, o palitivo não pode ser considerado um código fantástico que alguém vai olhar e pensar "Meu Deus, eu não vou conseguir fazer melhor que isso". Se esse código envolve telas faça uma tela com HTML puro as pessoas não devem gostar disto, a própria implementação deve ser um alerta para que as coisas sejam melhoradas.

Outra motivação para que esse paliativo seja sujo, é que fazer um código limpo geralmente demanda mais tempo, pois atender as boas práticas na maioria das vezes requer tempo, análise, uma relembrada dos padrões de código, conduta e convenções utilizadas. Se você está utilizando um paliativo, muito provavelmente esteja em um mal momento, algo está pegando fogo, então por que perder tempo com coisas bonitas?

Se você não tem saída e este paliativo deve ser uma implementação muito boa tanto em front quanto em back, performática e fácil de ler, então você ainda deve lembrar que tem a possibilidade de criar uma issue no sistema para rastreamento futuro. Se tempo não for o problema para você, por que está chamado sua implementação de paliativo?

Regra nº 3: Conhecimento

Eu não poderia deixar de falar sobre isso, pois esse com certeza é o assunto-chave para reduzir drasticamente a capacidade de você gerar um paliativo no sistema. Deixei ele por último, pois quero que esse seja o foco. As regras 1 e 2 são contingências, mas a regra 3 é o que realmente fará a diferença na sua vida.

Você já se perguntou por que chegou ao ponto de precisar de um paliativo? Bom, vou tentar listar alguns pensamentos, ou mesmo afirmações, que podem estar passando pela sua cabeça agora:

A plataforma/framework/lib que estou utilizando possui um bug, e agora preciso de um paliativo. Bom, você identificou um bug, será que consegue documentar esse bug e passar para a equipe de suporte desse recurso de terceiro que você precisa? Esse recurso está encapsulado e é acessível através de uma interface do seu sistema, e pode facilmente ser implementado um novo encapsulamento oferencendo a possibilidade de troca ou alternância de recurso? O ideal aqui é reportar o bug, mas também ter o sistema muito bem protegido para que uma troca de lib, por exemplo, não seja um problema. O acoplamento dessa lib no sistema deveria ser uma interface ou classe do sistema, e só. O nome desse padrão de projeto é Facade, mas nada impede você de utilizar o padrão Adapter, também conhecido como Wrapper, caso o recurso seja minúsculo e precise de adaptações para conexão com o seu sistema.

Outra pessoa da minha equipe gerou esse problema, e agora preciso de um paliativo. Ótimo, você identificou um problema no sistema, identificou o gerador, fez o paliativo. Hora de respirar? Não. Tente entender os motivos que levaram essa pessoa do seu time a criar esse problema para você. Muito provavelmente será falta de conhecimento, e agora você tem uma missão em suas mão: melhorar o processo de desenvolvimento do seu time. Você tem o conhecimento necessário para propagar de maneira controlada a informação de ações ou boas práticas para evitar problemas que geram paliativos no sistema. Um time é tão forte quanto seu integrante mais fraco.

Eu gerei esse problema, e agora preciso de um paliativo. Perfeito, você tem nas mãos um feedback perfeito para tomar uma atitude de mudança em sua vida. Melhore suas práticas de desenvolvimento de software, utilize testes unitários, crie anotações para não esquecer de configurações complexas de plataformas mal documentadas, utilize testes automatizados, explore mais as lógicas do seu código, teste em múltiplos ambientes, converse com outras pessoas do time, peça para alguém mais validar o seu código, peça por sugestões de melhorias, peça por sugestões de testes que podem ser feitos, explore mais as plataformas online como StackOverflow, teste como se sua vida dependesse desse código, teste, Teste, TESTE.

Para você desenvolvedor...

Se você leu até aqui acredito que seja alguém como eu, que deseja insamente melhorar, que seja curioso, que seja inquieto com o código fonte. Espero que meu objetivo tenha sido alcançado com você. Minha visão sobre o paliativo, o motivo de eu considerar isso um problema, como atacar o problema, mas principalmente o que fazer pró-ativamente para que um paliativo nunca seja necessário em seu sistema. 

ATG: RqlStatement com Cache interno


Resolvi por curiosidade estudar o método estático RqlStatement.parseRqlStatement. No Javadoc da classe, pude observar que existem duas formas de utilizar o método com passagem de String para representar o RQL, algo muito comum quando estamos no mundo ATG e seus infindáveis properties de configuração. As duas formas podem ser vistas abaixo:

RqlStatement.parseRqlStatement(rql);

e

RqlStatement.parseRqlStatement(rql, cacheRqlStatement);

Quero focar meus esforços em apresentar a diferença entre esses dois métodos. Quando utilizado RqlStatement.parseRqlStatement(rql), o que realmente acontece é a execução do parse sem manter o objeto convertido em cache. Para deixar claro, esse cache é interno a classe.

Já no segundo método, o Javadoc descreve o processo da seguinte maneira:

"Parses an RqlStatement from the given String. If pCacheRqlStatement is true, then the RqlStatement that is created from this operation will be saved in an internal cache with the String pStatement as the key. The next time this same string is passed to parseRqlStatement, the cached RqlStatement will be returned, so we don't have to re-parse the same string over and over again. Only use this method for statements that you'll definitely be re-using over again, and if the code you're using does NOT cache the statement already".

Isso quer dizer que ao utilizar o segundo método ganha-se a possibilidade de armazenar no cache interno do RqlStatement uma referência do objeto convertido, não sendo necessário efetuar novamente a conversão do mesmo pela própria classe.

Para se ter uma ideia do impacto disso, montei 2 testes com JUnit: 1 sem cache e 1 com cache (informando o segundo parâmetro como true). Na imagem abaixo, pode-se ver como é alarmante a diferença entre ambos os processos quando temos 1 milhão de iterações. Sem cache o processo leva 62 segundos, mas com cache leva apenas 0,843 segundos.



Sendo assim, o melhor caminho seria utilizar RqlStatement.parseRqlStatement(rql, true), favorecendo a performance dos processos, otimizando o tempo em novas chamadas ao parse que possuam o mesmo parâmetro de RQL, não sendo necessário armazenar referências convertidas em atributos de classe, pois a classe RqlStatement faz isso.

Talvez você se sinta inclinado a querer a possibilidade de limpar esse cache quando quizer. Esse geralmente é um controle que Desenvolvedores do mundo ATG gostam de ter. Entretanto, lhe afirmo que esse não é um cache perigoso, daqueles que você realmente precisa limpar, pois algo foi atualizado em banco e agora a aplicação precisa refletir. Não! Esse é um cache interno a classe RqlStatement. Se o RQL for alterado por um properties dinâmico, tudo que a classe irá fazer na próxima iteração é armazenar o novo RQL convertido, sendo que o anterior não será mais utilizado.

Gostou? Compartilhe esse post =)

JavaScript: Notifications API

A API de Notificações permite que páginas web controlem a apresentação de notificações do sistema para o usuário final. Essas notificações estão em um nível acima da janela atual da página web, isso permite que elas sejam apresentadas mesmo que a guia não esteja selecionada, ou que você esteja utilizando um programa/aplicativo diferente. A API foi projetada para possui total compatibilidade com sistemas de notificação existentes nas diferentes plataformas.

Modo de Uso


Em plataformas compatíveis, existem dois passos muito simples para lançar notificações:

1. Solicitar permissão ao usuário;
2. Lançar a notificação.

Solicitando permissão ao usuário

A solicitação de permissão para envio de notificações é um passo muito interessante. Que mundo seria esse se as notificações estivessem livres para uso desenfrado por todos os sites? Seria o carnaval eterno na web (acho que de qualquer forma isso já está acontecendo com alguns sites). Para evitar o problema clássico do uso sem limites, a API foi criada com uma premissa básica: notificações precisam ser ativadas pelo usuário final.

O seguinte código lança a solicitação de permissão, que nada mais é do que um popup, parecido com um tooptip ou popover, onde existe uma pergunta e as opções de resposta: permitir ou bloquear.




Exemplo de solicitação emitida pelo navegador após executar o código citado.


Quando solicitar?

Depende. Gosto de pensar que essa decisão tem a ver com "se colocar no lugar do usuário". A maioria dos sistemas solicita a permissão ao carregar a página, mas tudo vai depender do tipo de conteúdo e do público alvo. Pense comigo, o conteúdo pode chamar mais atenção do usuário que a solicitação e ela acabar sendo ignorada. Quando uma solicitação é ignorada ela mantém o status default, ou seja, notificações não serão lançadas. O público alvo pode resultar no mesmo problema, pois se o seu sistema atinge pessoas que tem pouco ou nenhum conhecimento do que seria uma notificação elas podem simplesmente ignorar ou até bloquear (status "denied") a solicitação. Por padrão, os browsers não efetuam múltiplas solicitações de permissão se o usuário já bloqueou a primeira.

Lançando notificações

Após obter a permissão do usuário, seu sistema está apto a lançar notificações. O status da API está "granted" e criar uma notificação é muito simples:


title: Na notificação, o title fica na parte superior, com mais destaque;
options: Existem diversas opções de configuração, porém as que você realmente precisa conhecer são body e icon. O body recebe um texto mais descritivo, mais longo que o title, e que será apresentado no centro da notificação. Já o icon é uma url de uma imagem para ser apresentada na esquerda da notificação.


Exemplo de Notificação do Queiroz


Isso é o suficiente para sua notificação ser apresentada ao usuário =)

Queiroz: um exemplo real


Na imagem demonstrada acima você pode observar um exemplo de notificação existente no Queiroz.js. Já falei do Queiroz.js aqui no blog. No Queiroz.js, utilizo as notificações para avisar aos usuários de que alguns determinados horários do dia estão próximos de chegar. Isso ajuda consideravelmente o usuário que pode estar efetuando atividades em outras janelas, mas mesmo assim verá a notificação. Esse é um exemplo real, e foi utilizado exatamente da mesma forma que explico acima.

Para refletir


Você deseja que o usuário aceite a permissão, pois ele entende aquilo e sabe do que se trata, ou você simplesmente quer que ele aceite e viva com uma notificação que pode se tornar algo muito estressante e gerar críticas ruins? A maioria dos sites não se importa com a resposta para essa pergunta. Não seja mais um nesse grupo.

Udemy: Cloud Computing com AWS

Encerro hoje meu primeiro curso na Udemy que teve como foco "Cloud Computing com AWS" e gostaria de compartilhar esse conhecimento com você. Dentre os assuntos abordados estavam: AWS com ECS, RDS, Elastic IP e Security Groups. Instalação e configuração de Apache, PHP e Wordpress em uma instância Linux. Google Cloud, Azure, Digital Ocean e Linode.

Andamento


O curso realmente te mostra como iniciar no AWS com ECS, RDS, Elastic IP e Security Groups, de quebra ainda te ensina a instalar Apache, PHP e Wordpress. No ECS configurei uma instância Linux. No RDS uma instância MySQL. Acontece que aquele console da Amazon em questão de produtos, serviços e opções é algo para meses, não apenas 3,5 horas, em compensação as configurações para subir uma instância Linux, configurar um Banco de Dados, instalar um servidor Web, Wordpress e sair rodando é bem rápido.

Durante o curso tive a infelicidade de ser cobrado em $1 dólar, além daquele $1 dólar que a verificação do cartão de crédito me cobrou, por algum motivo que até o momento não sei, pois segui exatamente o que o curso apresentou, porém isso é uma questão de atenção. Se você é aluno como eu aconselho fortemente a ler além do que o curso te oferece antes de colocar a mão na massa mesmo durante o FREE TIER (período de 12 meses).

No fim tudo é estímulo


Estou feliz com o resultado, e sei que boa parte do aprendizado também depende de mim. Ao final, falar sobre Google Cloud, Azure, Digital Ocean e Linode fecharam com chave de ouro o super portifólio de conhecimento. No fim tudo é estímulo para levantar no próximo dia e correr atrás de mais e mais.

Cloud Computing já está aí há muito tempo, tenho certeza que estou atrasado e prometo recuperar esse tempo perdido. Quem sabe alguns posts?


Link para Curso

Queiroz.js: JavaScript Extension for Dimep Kairos


Se você utiliza o sistema Dimep Kairos assim como eu, então você precisa conhecer o Projeto Queiroz.js.

Nesse post quero explicar o que é o Projeto Queiroz.js, motivação, finalidade, tecnologias envolvidas, enfim, quero apresentá-lo de uma maneira melhor da que foi feita no README do GitHub, e demonstrar como uma simples extensão para navegador pode facilitar a vida de muita gente.

O que é o Queiroz.js?


Queiroz.js é uma extensão JavaScript para o serviço online de ponto chamado Dimep Kairos. Basicamente, a extensão fornece cálculos para facilitar a tomada de decisão no dia-a-dia. Se você tem curiosidade em conhecer o código, ou mesmo participar da criação do mesmo, pode conferí-lo aqui.

Como muitas coisas na área da programação, Queiroz.js nasceu da união de dois ingredientes: problema e conhecimento. Eu me perguntava "por que esse programa não mostra cálculo algum?", depois pensei "vou criar algo em JavaScript para rodar nesse console e logar o que preciso".

Evolução da Extensão


Como já foi dito, tudo começou no console de um navegador. Entre um console.log e outro, fui debbugando a interface de ponto, catando elementos, classes, tentando observar os padrões existentes até conseguir trazer para variáveis JavaScript os checkpoints contidos na tela.

Depois do console, foi para um arquivo onde cresceu até atingir a marca de quase 900 linhas de código. Eis a versão 1.0. O formato userscript ganhou lugar e as extensões Tampermonkey no Chrome e Grancemonkey no Firefox começaram a serem utilizadas como gerenciadores para guardar e executar o script. Nesse ponto o arquivo já estava rolando em conversas de Skype e e-mails.

A versão 2.0 foi marcada pela completa reescrita da estrutura do código. Com Gulp, consegui modularizar. Já estava sendo utilizado o Revealing Module Pattern, então não foi tão difícil separar em arquivos, e colocar o Gulp para automatizar diversas tarefas como concatenação e minificação.

A versão 3.0 teve uma grande reescrita do módulo core. Esse módulo é responsável por ordenar a chamada de cada um dos outros módulos fazendo com que todas as operações aconteçam em ordem, desde a coleta dos dados até a apresentação dos resultados. Essa versão também disponibilizou a primeira possibilidade de interação com o usuário: a funcionalidade DayOff, que permite "desligar" dias da semana que são feriados, ou coisas do tipo. Alguns commits depois, surgiu o TimeOn que permite informar folgas em horas. Recentemente, foi incluído a integração com Notifications API.

Código: JavaScript


Não existe mistério. O código literalmente lê a view, coleta os valores de ponto, efetua as conversões para objetos Date e String, efetua cálculos em milisegundos e renderiza novos componentes em tela apresentando todos cálculos em formato humano. Tudo com JavaScript.


A montagem dos componentes HTML também é feita em JavaScript. Entretanto, antes da versão 3.0, foi feita uma propaganda sobre a mesma utilizando um modal. Esse modal foi feito em HTML puro, Gulp fazia a minificação, injetava em uma variável String utilizando replacement, depois renderizava em tela utilizando innerHTML.

Tudo na extensão é JavaScript. A estrutura que não é. O projeto hoje possui HTML, CSS, Strings e JavaScript. A união de tudo isso ocorre graças ao Gulp. Se você olhar no GitHub verá que existem arquivos separados para cada coisa, mas o que realmente é utilizado encontra-se na pasta dist, arquivo queiroz.min.js, que é um compilado de todos os módulos/arquivos.

O que consegui com Queiroz.js?


Bom, em primeiro lugar: Conhecimento e Experiência. Queiroz.js me possibilitou conhecer inúmeras tecnologias, tanto novas como aprofundar as já conhecidas. Git e GitHub, por exemplo, mesmo utilizando no trabalho, hoje posso dizer que tenho muito mais conhecimento em Git Distribuído do que tinha antes. Padrões de Projeto com JavaScript também é algo muito bom e fácil de ser utilizado: Revealing Module Pattern, Observer Pattern e Module Pattern dão vida e organização ao Queiroz.js. Gulp.js é algo que realmente me deixou feliz: um automatizador de tarefas extremamente fácil de utilizar e graças ao Gulp.js a versão minificada e distribuída é tão simples de ser criada.

Em segundo lugar: Networking. Você realmente percebe que nada pode ser criado sozinho e considerado algo suficiente a todos. Receber constantemente opiniões de inúmeros colegas de profissão é algo incrível. Por diversas vezes eu respondo: "Como eu não pensei nisso?!". E imediatamente eu anoto para incluir.

Queiroz.js tem muito que evoluir ainda, que tal fazer parte disso também?

BrasilJS Conf 2017


Nos dias 25 e 26 de agosto de 2017 aconteceu em Porto Alegre o evento BrazilJS Conf 2017, o qual tive o prazer de participar. O evento é focado no universo JavaScript, porém se você acha que verá pessoas mostrando como criar uma variável ou fazer um array na linguagem, você está errado!

Depois desse evento percebi como me acomodei com as situações do cotidiano. Aprendendo um framework aqui, um plugin ali, e pronto, está tudo bem para meu portifólio pessoal de ferramentas de sucesso. Eu estava errado.

É nessas ocasiões, como no BrasilJS, que volto a perceber como a área de TI corre, voa. A quantidade de novas tecnologias que já estão a pleno vapor, prontas para serem exploradas, e eu aqui no meu mundinho perfeito. Não posso diminuir o passo!

Dentre os assuntos do evento, selecionei alguns que me chamaram atenção e que provavelmente serão foco de estudos nos próximos tempos:

      PWA
      Vue.js
      RabbitMQ
      Machine Learning
      Angular 4
      TypeScript

Sem dúvida, BrasilJS colocou um plus na motivação.

Links Úteis


BrasilJS Conf (WebSite)
https://braziljs.org/conf/

Canal BrasilJS (YouTube)
https://www.youtube.com/user/BrazilJS




Chromebook: Como instalar mitmproxy


mitmproxy é uma poderosa ferramenta de interceptação e modificação de requisições HTTP e HTTPS utilizado para testar invasões e desenvolver softwares. Nesse post quero apresentar os comandos para instalar o mitmproxy, bem como a solução de alguns problemas que enfrentei com dependências e locales. Minha máquina é um Chromebook Samsung Exynos 5 Dual Core.

Developer Mode


Você vai precisar habilitar o Modo de Desenvolvedor para prosseguir, sendo assim, veja meu post Chromebook: Modo de Desenvolvedor.

Linux Kali


Vamos utilizar o Linux Kali para rodar o mitmproxy, para isso, siga o passo-a-passo descrito no post Chromebook: Como instalar Kali (-t core).

mitmproxy


Os comandos abaixo devem ser executados dentro do Kali, então vamos acessá-lo com sudo enter-chroot e prosseguir:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python-virtualenv
sudo apt-get install python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev
sudo apt-get install git
sudo git clone https://github.com/mitmproxy/mitmproxy.git
sudo git clone https://github.com/mitmproxy/netlib.git
sudo git clone https://github.com/mitmproxy/pathod.git
mkdir $HOME/virtualenvs
cd $HOME/virtualenvs
virtualenv --python=/usr/bin/python2.7 --no-site-packages mitmproxy_sandbox

Criamos um ambiente virtual Python, uma sandbox. Os comandos abaixo serão executados dentro desse ambiente, então vamos ativá-lo e instalar o mitmproxy:

source mitmproxy_sandbox/bin/activate
pip install mitmproxy
sudo apt-get install locales
sudo dpkg-reconfigure locales
which mitmproxy

Com isso o mitmproxy está instalado. Para ativá-lo use o comando mitmproxy.
Para sair do ambiente virtual utilize deactivate.
Para acessar o ambiente virtual novamente utilize source mitmproxy_sandbox/bin/activate, na pasta $HOME/virtualenvs.
Para limpar o ambiente virtual utilize virtualenv --clear mitmproxy_sandbox, na pasta $HOME/virtualenvs.

Locales


Enfrentei um problema com os locales, onde que após instalar e reconfigurar, o sistema não aceitava a nova configuração e o mitmproxy reclama quando eu tentava iniciá-lo. O que eu fiz então foi utilizar outro comando para isso: export LC_ALL=en_US.UTF-8, e tudo correu bem.

Pillow/setup.py


Outro problema que enfrentei passou a ocorrer todas as vezes depois da segunda instalação do mitmproxy em minha máquina. Em resumo, resolvi instalando uma dependência que na primeira vez não foi solicitada. Os detalhes podem ser conferidos no post ValueError: --enable-jpeg requested but jpeg not found, aborting.

Fonte


mitmproxy - Installation [https://mitmproxy.org/doc/install.html]

ValueError: --enable-jpeg requested but jpeg not found, aborting.


Erro apresentado na instalação do mitmproxy sobre um ambiente virtual Python, rodando Linux Kali em um Chromebook Samsung Exynos 5 Dual Core. Ao executar o comando pip install mitmproxy é apresentado o seguinte erro:


Especificamente, o erro ocorre na execução do arquivo setup.py da library Pillow.

Para resolvê-lo, execute sudo apt-get install libjpeg-dev. Isso irá instalar a dependência necessária. O interessante disso é que na primeira vez que instalei o mitmproxy nenhum erro foi observado e tudo ocorreu perfeitamente, porém na segunda em diante o mesmo erro persistiu.

Se você já havia iniciado a instalação do mitmproxy é interessante, mas não necessário, efetuar a reinstalação, então desinstale com pip uninstall mitmproxy e depois reinstale com pip install mitmproxy.

Fonte


Chromebook: Como instalar Kali (-t core)




Para prosseguir é necessário habilitar o Modo de Desenvolvedor, como descrito em Chromebook: Modo de Desenvolvedor. Se isso não for feito, é impossível acessar o chronos shell via crosh, passo fundamental para instalar a distro Kali.

Crouton


Crouton é um conjunto de scripts que ajudam a instalar novas distros/chroots para Chromium OS. Para conhecer mais, leia em https://github.com/dnschneid/crouton.

Baixe o crouton para sua máquina: https://goo.gl/fd3zc.

Kali


Abra o crosh shell (Ctrl + Alt + T), digite shell e pressione Enter.
Todo o processo deve levar cerca de 7 minutos. Basta digitar o seguinte comando:



Após a instalação, digite sudo enter-chroot e pronto, você estará utilizando o shell prompt do Kali Linux.

Para sair digite exit.

Fonte


Chromium OS Universal Chroot Environment [https://github.com/dnschneid/crouton]

Chromebook: Modo de Desenvolvedor


Em um Chromebook, o Modo de Desenvolvedor permite que você interaja com um shell de comandos (como root se quiser), ou mesmo instale outros sistemas operacionais. Quando ativado, alguns recursos de segurança são desativados como a inicialização verificada (verified boot).

Habilitando


Todo o processo começa pressionando as teclas ESC + Atualizar + Power, elas irão iniciar uma série de passos para entrar no Modo de Desenvolvedor, os quais serão demonstrados nas telas "fotográficas" abaixo.
Tela 1 - Pressione Ctrl + D
Tela 2 - Pressione Enter
Tela 3 - Pressione Ctrl + D

Tela 4 - Transição para o modo de desenvolvedor
Tela 5 - Preparando o sistema
Tela 6 - Configuração do sistema
Pronto. Agora você está em modo de desenvolvedor.

Desabilitando


Para desabilitar o modo de desenvolvedor basta reiniciar o sistema. Ele apresentará as seguintes telas:
Tela 1 - Pressione Space
Tela 2 - Pressione Enter
Tela 3 - Apenas uma confirmação
Basta aguardar, o sistema vai efetuar o reboot e você apenas precisará configurá-lo novamente.

Fonte


Samsumg ARM Chromebook [https://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices/samsung-arm-chromebook]

Markdown: Linguagem e Software



Em projetos criados com GitHub, um popular sistema de controle de versão on-line, muitas vezes usei um arquivo chamado README.md que contém o readme para o projeto. Esses arquivos podem ser comparados com revisões históricas em texto legível (enquanto que um formato binário não pode ser facilmente comparado). A extensão .md era uma incógnita para mim que estava acostumado com XMLs e TXTs, foi assim que conheci Markdown.

Markdown


A definição para Markdown se dá em dois ambitos: como linguagem e como software. Ambos foram desenvolvidos por John Gruber, com importantes créditos para Aaron Swartz. Sua filosofia baseia-se na facilidade de leitura e escrita, pois isso é viável, valorizando a legibilidade acima de tudo. Markdown é destinada a uma finalidade: um formato de escrita para web, mas sem substituir HTML, pois apenas abrange um pequeno subconjunto de tags.

A linguagem


A linguagem Markdown é de marcação leve e permite que as pessoas escrevam em um formato de texto simples. A idéia principal é que seres humanos possam ler sem perceber as marcações no meio do texto e facilmente converter isso para HTML. Uma das extensões de arquivo para linguagem Markdown é a .md, mas também é reconhecida a extensão .markdown. Um arquivo Markdown deve ser publicado como está, como texto simples.

A sintaxe recebeu influências de vários filtros de texto para HTML, como Setext, atx, Textile, reStructuredText, Grutatext e EtText, porém a maior inspiração para a sintaxe de Markdown é o formato de texto para e-mail. Markdown é composta inteiramente por caracteres de pontuação que definem como formatar o texto sem perceber que foi formatado. Abaixo pode-se conferir alguns exemplos:


Cabeçalhos
Título h1    # Título h1
=======
Título h2    ## Título h2
-----------


Ênfase
*itálico*   **negrito**
_itálico_   __negrito__


Link em linha
Um [exemplo](http://url.com/ "Título")


Listas
* Primeiro item
* Segundo item
* Terceiro item
ou
+ Primeiro item
+ Segundo item
+ Terceiro item

O software


John Gruber criou um programa Perl para converter do formato de texto simples para HTML. O programa, chamado "Markdown", exige Perl 5.6.0 ou posterior e também o módulo Digest::MD5. Markdown está disponível no site Daring Fireball e possui uma versão online chamado de Digus [http://daringfireball.net/projects/markdown/dingus].

Fonte



Dev In PF: Gramática, Vocabulário e Pragmática


No dia 06 de abril de 2013 ocorreu o 1º encontro do Dev In PF. Pessoas da área de desenvolvimento de software da cidade de Passo Fundo agora contam com esse grupo que vem com a intenção de proporcionar eventos relacionados a área, fortalecer os laços de amizades e parcerias e estimular o conhecimento. Ver esse grupo nascendo e crescendo é algo espetacular para Passo Fundo e eu estive no 1º encontro apresentando um material "light" sobre boas práticas.



A experiência adquirida no mini-curso da SenaInfo me possibilitou apresentar com calma e confiança. Essa foi a segunda apresentação para um público da área que eu atuo. O público ajudou bastante, pois haviam pessoas que já trabalham na área, o que facilitou a apresentação de boas práticas de programação. Mas de novo esse assunto Vinícius? Novamente eu volto a apresentar boas práticas de programação, pois acredito ser um assunto fundamental e eternamente presente na vida de um profissional do desenvolvimento de software.

Nessa apresentação minimizei a teoria e apliquei mais exemplos, pois isso possibilita prender a atenção do público ao mesmo tempo que facilita o entendimento da mensagem final da apresentação: boas práticas não são uma tarefa de desenvolvimento, mas uma cultura, uma atitude profissional.



Acredito ter acertado no título, no assunto e no público. Dev In PF já possuía outras boas apresentações como TDD, HTML5 e Linguagens e seus ecossistemas, sendo que boas práticas são atitudes empregadas em todas as práticas e linguagens. A apresentação pode ser conferida logo abaixo.


Dummy SMTP: JavaMail API + DevNullSMTP

O que quero mostrar é uma forma de testar o envio de e-mails em ambiente de desenvolvimento simulando um falso servidor SMTP com o framework DevNull SMTP. Pretendo mostrar duas formas de fazer isso: uma utiliza JndiHelper (para usuário de WebLogic Server) e outra utiliza Session (classe integrante da API JavaMail).

Com JndiHelper


1. Crie uma pasta no sistema (exemplo: \devNullSMTP)

2. Baixar o jar do framework DevNull SMTP e coloque nessa pasta.
Link: http://www.aboutmyip.com/AboutMyXApp/DevNullSmtp.jar

3. No console do WebLogin Server vá em Mail Session e crie um novo com os seguintes atributos:

Name: SMTPMailSession
JNDI Name: SMTPMailSession
JavaMail Properties:
mail.debug=True
mail.smtp.port=25
mail.transport.protocol=smtp
mail.smtp.host=127.0.0.1

4. Crie dentro da pasta \devNullSMTP a pasta para receber os emails (exemplo: \emails)

5. Executar em linha de comando o seguinte:

java -jar DevNullSmtp.jar -s emails

Para mais comandos ver http://www.aboutmyip.com/AboutMyXApp/DevNullSmtpUsage.jsp

6. No console que irá abrir, clique em Start Server.

7. Pronto. Observações: Todo e-mail enviado vai para pasta \emails com a terminação EML e pode ser visualizada no Notepad++. O console do DevNull SMTP loga todo o processo em 4 abas disponíveis, incluindo o próprio EML. O console do Eclipse efetua logs também, mas nada além do console do DevNull SMTP.

Com Session


O mesmo processo para usuário de JndiHelper com excessão do passo 3, pois as propriedades são setadas em código da seguinte maneira:

Properties props = new Properties();
props.put("mail.debug", "true");
props.put("mail.smtp.port", "25");
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", "127.0.0.1");
Session session = Session.getInstance(props, null);

O restante do processo segue da mesma forma.

Conclusão


Esse conhecimento permitiu a independência de desenvolvimento de funcionalidades que utilizam envio de e-mail. Tive muitos problemas no passado com funcionalidades desse tipo e sempre dependia de ambiente configurado para os testes, quando encontrava um erro não era apenas uma correção, mas também tinha que esperar por um deploy em ambiente de homologação. Espero que isso também seja útil para você.

Fonte




Um conselho...


Codifique pensando que outras pessoas devem entender seu código, 
por mais que ninguém vá ver, 
pois isso aumenta consideravelmente as chances de no futuro 
você mesmo entender esse código.


Refatoração na prática


"Pensar é o trabalho mais pesado que existe. 
Talvez seja por isso que tão poucos o exercitem."
(Henry Ford)

Ser pragmático tem seu valor. Sempre me importei com o código, gosto de ser assim, pois conheço o real valor dessa ação, ou melhor, dessa conduta. Ser pragmático envolve muitos conceitos e acima de tudo, prática. Nesse post quero demonstrar uma prática muito conhecida de quem é pragmático que é a refatoração de código.

Refatorar código não é tão simples assim, exige conhecimento da lógica de negócio e pode ter resultados catastróficos se for feito por alguém que não possua uma visão macro do projeto e possa visualizar todas as dependências possíveis (lembrando que mesmo com os mecanismos de busca das IDEs muita informação está em locais inacessíveis para estas).



O código a seguir envolve um caso onde um controller é responsável por receber dados para o envio de um e-mail. Esse controller deve validar os dados de entrada, montar o e-mail e enviá-lo. Vou mostrar o código “cru” e então começarei a refatorá-lo. Passo a passo pretendo descrever conceitos que estão sendo atendidos e outros que possivelmente estão sendo violados. Será utilizada a linguagem Java para isso.

Case 1
import java.util.Date;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case1 {

    public void abstractController() throws Exception {
        
        try {
            // dados recebidos de uma fonte qualquer
            String de = "eu@email.com";
            String para = "qualquer@email.com";
            String assunto = "Refactoring na Prática";
            String corpo = "Corpo da Mensagem";
            
            // validacao de email
            Pattern p = Pattern.compile(".+@.+\\.[a-z]+");  
            
            Matcher m1 = p.matcher(de);  
            if (!m1.matches())  
                throw new IllegalArgumentException("Email address is invalid: \"" + de + "\"");
            
            Matcher m2 = p.matcher(para);  
            if (!m2.matches())  
                throw new IllegalArgumentException("Email address is invalid: \"" + para + "\"");
            
            Properties props = new Properties();
            props.put("mail.host", "127.0.0.1");
            Session session = Session.getInstance(props);
            MimeMessage msg = new MimeMessage(session);
            try {
                msg.setFrom(new InternetAddress(de));
                InternetAddress[] to = {new InternetAddress(para)};
                msg.setRecipients(Message.RecipientType.TO, to);
                msg.setSentDate(new Date());
                msg.setSubject(assunto);
                msg.setText(corpo);
                Transport.send(msg);
            } catch (MessagingException e) {
                // omitido
            }
        } catch (Exception e) {
            // omitido
        }
        
    }

}
A classe Case1 é o primeiro caso que quero mostrar. Em seu método abstractController ela executa a lógica de receber os atributos que identificam um e-mail, validar alguns deles, montar o e-mail utilizando a biblioteca javax.mail, então por fim, enviar o e-mail. Um método com muitas responsabilidades.

Isso funciona?

Sim (se você tiver o SMTP configurado em sua máquina como um servidor local). Acredito que nesse ponto se você não for alguém pragmático esse código é algo normal, mas para mim é um monstro. Quando estudei software a primeira coisa que aprendi lá em Algoritmos é que devo pegar um problema e fragmentá-lo, então resolver os problemas menores sendo que ao final terei resolvido o problema todo. O código acima para mim é um problema e que necessita de fragmentação.

Case 2

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case2 {
    
    /**
     * Refactoring:
     * - Criacao da classe EmailAddress
     */
    public void abstractController() {
        
        EmailAddress de = new EmailAddress("eu@email.com");
        EmailAddress para = new EmailAddress("qualquer@email.com");
        
        String assunto = "Refactoring na Prática";
        String corpo = "Corpo da Mensagem";
        
        Properties props = new Properties();
        props.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(props);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(de.toInternetAddress());
            InternetAddress[] to = {para.toInternetAddress()};
            msg.setRecipients(Message.RecipientType.TO, to);
            msg.setSentDate(new Date());
            msg.setSubject(assunto);
            msg.setText(corpo);
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}
Nesse ponto do código, uma classe extra foi criada com a intenção de centralizar o e-mail bem como sua validação. A classe EmailAddress tem a responsabilidade de fazer apenas isso, representar um e-mail. Vale lembrar que em um refactoring não se pode alterar o fluxo que a aplicação já possui, pois isso seria tarefa em uma correção ou manutenção. A classe EmailAddress segue o padrão Tiny Type, que são pequenos tipos de objeto que encapsulam pouca lógica.

EmailAddress

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

/**
 * Tiny Type Pattern
 *
 * @author Vinicius Martins Knob
 */
public final class EmailAddress {

    private final InternetAddress _emailAddress;

    /**
     * Cria um EmailAddress a partir de um email
     * @param emailAddress
     * @throws AddressException
     */
    public EmailAddress(String emailAddress) throws AddressException {
        validar(emailAddress);
        _emailAddress = new InternetAddress(emailAddress);
    }

    private void validar(String emailAddress) {
        Pattern p = Pattern.compile(".+@.+\\.[a-z]+");  
        Matcher m = p.matcher(emailAddress);  
        if (!m.matches())  
            throw new IllegalArgumentException("Email address is invalid: \"" + emailAddress + "\"");
    }
    
    @Override
    public String toString() {
        return _emailAddress.getAddress();
    }
    
    public InternetAddress toInternetAddress(){
        return _emailAddress;
    }
    
    /**
     * Retorna uma lista imutavel de EmailAddress a partir de um array de emails.
     * @param emails
     * @return
     * @throws AddressException
     */
    public static List<EmailAddress> asList(String... emails) throws AddressException {
        if (emails.length == 0)
            return Collections.emptyList();
        
        List<EmailAddress> foo = new ArrayList<EmailAddress>();
        for (String bar : emails) {
            foo.add(new EmailAddress(bar.trim()));
        }
        return Collections.unmodifiableList(foo);
    }
}
O que ganho com isso?

Em um projeto grande, ter aquele trecho de código responsável por validar o e-mail espalhado por toda parte seria inadmissível. Uma alternativa encontrada muitas vezes por mim em outros projetos é uma classe com métodos estáticos onde exista um método validarEmail(), não que seja errado, mas quando criei a classe EmailAddress estive pensando em conceitos muito discutidos pela comunidade de software como coesão, responsabilidade única, legibilidade e baixo acoplamento. EmailAddress é uma classe que quando você vê tem plena noção de seus limites e responsabilidades, pois ela é simples e clara.

Case 3
import java.util.Date;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Case3 {

    /**
     * Refactoring:
     * - Criacao da classe Email
     */
    public void abstractController() {

        EmailAddress de = new EmailAddress("eu@email.com");
        EmailAddress para = new EmailAddress("qualquer@email.com");

        Email email = Email.create()
                .from(de)
                .to(para)
                .subject("Refactoring na Prática")
                .body("Corpo da Mensagem")
            .build();

        Properties props = new Properties();
        props.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(props);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(de.toInternetAddress());
            InternetAddress[] to = {para.toInternetAddress()};
            msg.setRecipients(Message.RecipientType.TO, to);
            msg.setSentDate(new Date());
            msg.setSubject(email.getSubject());
            msg.setText(email.getBody());
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}
No caso 3 externalizei uma segunda parte da lógica, a montagem do e-mail. A montagem consiste na coleta dos dados e suas respectivas validações. Para isso criei a classe Email. Ela segue o padrão Builder.

Email

import java.text.MessageFormat;
import java.util.List;

/**
 * 
 * Esta classe segue o Builder Pattern
 *
 * @author Vinicius Martins Knob
 */
public class Email {
    
    private EmailAddress _from;
    private List<EmailAddress> _to;
    private String _subject;
    private String _body;
    
    private Email() {}
    
    public EmailAddress getFrom() {
        return _from;
    }
    public List<EmailAddress> getTo() {
        return _to;
    }
    public String getSubject() {
        return _subject;
    }
    public String getBody() {
        return _body;
    }
    
    @Override
    public String toString() {
        String pattern = "[from: {0}, to: {1}, subject: {2}, body: {3}]";
        Object[] data = {_from, _to.toString(), _subject, _body};
        return MessageFormat.format(pattern, data);
    }
    
    @Override
    public boolean equals(Object obj) {
        return this.toString().equals(obj.toString());
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result * (_from==null?0:_from.hashCode());
        result = 31 * result * (_to==null?0:_to.hashCode());
        result = 31 * result * (_subject==null?0:_subject.hashCode());
        result = 31 * result * (_body==null?0:_body.hashCode());
        return result;
    }
    
    /**
     * 
     * Builder Method - Builder Pattern
     * 
     */
    public static EmailBuilder create() {
         return new EmailBuilder();
    }
    
    /**
     * 
     * Builder Class - Builder Pattern
     * <br>Inner Class
     *
     */
    public static class EmailBuilder{
        private final Email _email = new Email();
        
        private EmailBuilder(){}
        
        public EmailBuilder from(EmailAddress from){
            _email._from = from;
            return this;
        }
        public EmailBuilder to(List<EmailAddress> to){
            _email._to = to;
            return this;
        }
        public EmailBuilder subject(String subject){
            _email._subject = subject;
            return this;
        }
        public EmailBuilder body(String body){
            _email._body = body;
            return this;
        }
        public Email build(){
            return _email;
        }
        
    }
    
}
A classe Email contempla o padrão Builder, padrão aplicado em virtude da quantidade de parâmetros utilizados, e a tendência dessa classe, dependendo do sistema, é aumentar a quantidade desses parâmetros. Cada método poderia conter a validação respectiva ao dado inserido, centralizando toda a lógica apenas em uma classe, para todo sistema. A classe Email faz a coleta e validação de dados de um e-mail, e só isso. Os atributos atendidos pela classe EmailAddress são também atendidos aqui: coesão, responsabilidade única, legibilidade e baixo acoplamento.

Case 4

import java.util.Arrays;

public class Case4 {
    
    /**
     * Refactoring: 
     * - Criacao da classe Sender
     */
    public void abstractController() {
        try {
            EmailAddress de = new EmailAddress("eu@email.com");
            EmailAddress para = new EmailAddress("qualquer@email.com");

            Email email = Email.create()
                    .from(de)
                    .to(Arrays.asList(para))
                    .subject("Refactoring na Prática")
                    .body("Corpo da mensagem")
                .build();

            Sender.send(email);
        } catch (Exception e) {
            // log
        }

    }
}

No caso 4, uma terceira lógica foi movida do controller para uma classe específica, a classe Sender. Essa classe passa a ser responsável pelo envio do e-mail, e somente isso. Ela encapsula boa parte da API de envio de e-mail do java (javax.mail) e possui apenas um método estático, send(email). O parâmetro de send é um objeto do tipo Email, a classe criada no caso 3. O contrutor de Sender lança uma UnsupportedOperationException, pois não existe motivos para instanciar essa classe, ela foi projetada para apenas enviar um e-mail, não armazenando dado algum. O seu método private getArrayAddress efetua uma conversão da lista de EmailAddress para um array de InternetAddress, exigência esta imposta pela API javax.mail.

Sender

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.NamingException;

/**
 * <p>
 * Classe responsavel por enviar um email.
 * </p>
 * <p>
 * Classe nao-instanciavel, seu unico proposito eh enviar um email,
 * funcionalidade essa fornecidade pelo metodo {@link #send(Email)}.
 * </p>
 * <p>
 * <b>OBS:</b> Classe nao projetada para heranca.
 * </p>
 * 
 * @author Vinicius Martins Knob
 * 
 */
public final class Sender {
    
    private Sender() {
        throw new UnsupportedOperationException("classe nao-instanciavel");
    }
    
    public static void send(Email email) throws NamingException {
        Properties p = new Properties();
        p.put("mail.host", "127.0.0.1");
        Session session = Session.getInstance(p);
        MimeMessage msg = new MimeMessage(session);
        try {
            msg.setFrom(email.getFrom().toInternetAddress());
            msg.setRecipients(Message.RecipientType.TO, getArrayAddress(email.getTo()));
            msg.setSentDate(new Date());
            msg.setSubject(email.getSubject());
            msg.setText(email.getBody());
            Transport.send(msg);
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
    
    private static InternetAddress[] getArrayAddress(List<EmailAddress> emails){
        List<InternetAddress> internetAddresses = new ArrayList<InternetAddress>();
        for (EmailAddress address : emails) {
            internetAddresses.add(address.toInternetAddress());         
        }
        return internetAddresses.toArray(new InternetAddress[internetAddresses.size()]);
    }
}

Conclusão

Acredito que com essa refatoração o controller ficou muitas vezes mais legível, fácil de efetuar uma alteração já que temos as partes do processo bem definidas. As classes geradas por esta refatoração são totalmente reutilizáveis, flexíveis e coesas. Nem sempre é possível efetuar uma refatoração de código, pois esta ação exige conhecimento amplo. Um ambiente preparado para testes seria ideal para manter uma rotina de refatoração diminuindo significamente partes repetidas de um sistema que utiliza uma linguagem orientada a objetos. Conceitos aplicados aqui, e muitos deles posso ter passado por cima, deveria sempre ser aplicados, desde o inicio.