Archive for the ‘java’ Category

Nem só de troca de mensagens vivem os objetos

Sunday, May 25th, 2008

Percebi que boa parte das dúvidas quanto ao meu post sobre como objetos não possuem atributos se deve ao fato das pessoas não terem geralmente um conhecimento real sobre o que é troca de mensagens.

Perfeitamente compreensível. Na maior parte dos livros e faculdades as pessoas aprendem que Orientação a Objetos é sobre como utilizar classes e sobre como as funções são chamadas de métodos. Por algum motivo esquecido nas areias do tempo decidiu-se que chamar o método em uma classe era passar uma mensagem e por isso algumas pessoas notoriamente pedantes usam este termo ao invés de dizer apenas “chama a função”.

Bem, os conceitos no parágrafo acima estão errados. Orientação a Objetos não é sobre classes e sim sobre…er… objetos. Você pode ter OO sem ter classes, como JavaScript e Io e pode ter também OO sem mensagens.

Troca de mensagens é um conceito utilizado em diversas áreas, não apenas Orientação a Objetos. Você pode ter um Sistema Operacional baseado neste conceito -como o MINIX por exemplo- ou criar uma arquitetura de computação distribuída como SOAP.

O que distingue a passagem de mensagens é o fato de que o recipiente da mensagem, seja um objeto, um serviço ou um processo, é quem decide o que é feito em decorrência de sua invocação.

Para tentar içar um pouco mais claro eu criei um meta-modelo bem bobinho em Java. Este representa um sistema Orientado a Objetos com classes e passagem de mensagens. O código abaixo mostra como declarar uma Classe calculadora e enviar uma mensagem dizendo para que esta multiplique números.

BlocoDeCodigo bloco = new BlocoDeCodigoImpl<Integer, Integer, Integer>(){
			public Integer executar(Integer a, Integer b){
				return a * b;
			}
		};

		Classe classeCalculadora = novaClasse("Calculadora");
		classeCalculadora.declaraMensagem("multiplique", bloco);

		Instancia calculadora = instanciar("Calculadora");

		assertEquals(8, calculadora.enviaMensagem("multiplique", 2, 4));

Note os passos realizados. Primeiro criamos um bloco de código, uma função. Depois dizemos ao sistema que existe uma classe chamada calculadora. Logo apos registramos o fato de que calculadora responde a uma mensagem executando o bloco que havíamos declarado.

Em termos de semântica, este código é mais ou menos equivalente a este:

public class Calculadora{
 public Integer multiplicar(Integer a, Integer b){
  return a * b;
}
}

Depois nós instanciamos a classe e passamos uma mensagem para ela, o que seria equivalente a:

Calculadora calc = new Calculadora();
calc.multiplicar(2,4);

As classes relevantes:

public class Ambiente {
	static Map<String, Classe> classesDeclaradas = new HashMap<String, Classe>();

	static Classe novaClasse(String nomeDaClasse) {
		ClasseImpl classe = new ClasseImpl(nomeDaClasse);
		classesDeclaradas.put(nomeDaClasse, classe);
		return classe;
	}

	static Instancia instanciar(String nomeDaClasse) {
		return new Instancia(classesDeclaradas.get(nomeDaClasse));
	}
}

public class ClasseImpl implements Classe {

	private final String nome;

	private Map<String, BlocoDeCodigo> mensagens = new HashMap<String, BlocoDeCodigo>();

	public ClasseImpl(String nome) {
		this.nome = nome;
	}

	public void declaraMensagem(String nomeDaMensagem, BlocoDeCodigo blocoASerExecutado) {
		mensagens.put(nomeDaMensagem, blocoASerExecutado);
	}

	public String nome() {
		return nome;
	}

	public boolean respondeA(String nomeDaMensagem) {
		return mensagens.containsKey(nomeDaMensagem);
	}

	public BlocoDeCodigo codigoParaMensagem(String nomeDaMensagem) {
		return mensagens.get(nomeDaMensagem);
	}

}

public class Instancia {
	private final Classe minhaClasse;

	public Instancia(Classe classe) {
		this.minhaClasse = classe;
	}

	public Object enviaMensagem(String mensagem, Object... args) {
		Object primeiro = args.length > 0 ? args[0] : null;
		Object segundo = args.length > 1 ? args[1] : null;

		return minhaClasse.codigoParaMensagem(mensagem).executar(primeiro,
				segundo);
	}

	public Classe classe() {
		return minhaClasse;
	}

}

Esse meta-modelo é baseado em troca de mensagens. A classe Calculadora não recebe código a ser executado ela apenas recebe o nome de uma mensagem e parâmetros. Imagine que eu registre o mesmo bloco de código para várias mensagens, ou que eu use recursos de AOP e intercepte a execução do bloco. Nada disso é relevante para quem invoca a mensagem, ele apenas a envia e o que acontece em decorrência disso é responsabilidade do receptor.

Como quase todas as linguagens atuam desta forma pode ser difícil entender o conceito já que nunca se viu nada diferente. Vamos então implementar outro meta-modelo que não usa troca de mensagens mas sim uma outra forma chamada Data-Directed.

Nesta forma de invocar operações em objetos –que, como a anterior não é específica de OO- quem decide qual função será aplicada é o ambiente de execução, o runtime. Quando você invoca uma operação o ambiente vai procurar dentre os métodos registrados qual é o aplicável para aquele objeto e vai executar o método nele. Common Lisp utiliza este recurso de maneira tão poderosa em suas Generic Functions que praticamente elimina a necessidade de coisas como proxies e AOP.

Nosso meta-modelo para Data-Directed é executado dessa forma:

BlocoDeCodigo bloco = new BlocoDeCodigoImpl<Integer, Integer, Integer>(){
			public Integer executar(Instancia instancia, Integer a, Integer b) {
				return a * b;
			}
		};

		Classe classeCalculadora = novaClasse("Calculadora");
		registrarMetodo("multiplique", classeCalculadora, bloco);

		Instancia calculadora = instanciar("Calculadora");

		assertEquals(8, executarMetodo("multiplique", calculadora, 2, 4));

Repare que agora o bloco de código recebe como seu primeiro argumento uma referência para a instancia a qual se aplica (se você já usou java.lang.Method sabe que isso não é incomum quando se desce ao nível de implementação de linguagem). Caso nosso exemplo fosse minimamente usável seria desta forma que o bloco obteria acesso ao objeto em si.

Logo depois criamos a classe como antes mas ao invés de registrar uma mensagem na classe nós registramos um método no ambiente, dizendo que o método se aplica àquela classe. A invocação em si é bem parecida com a anterior.

Na implementação a única classe mais interessante é o Ambiente, que agora é bem mais esperto:

public class Ambiente {
	static Map<String, Classe> classesDeclaradas = new HashMap<String, Classe>();
	static Map<String, Map<Classe, BlocoDeCodigo>> metodos = new HashMap<String, Map<Classe, BlocoDeCodigo>>();

	static Classe novaClasse(String nomeDaClasse) {
		ClasseImpl classe = new ClasseImpl(nomeDaClasse);
		classesDeclaradas.put(nomeDaClasse, classe);
		return classe;
	}

	static Instancia instanciar(String nomeDaClasse) {
		return new Instancia(classesDeclaradas.get(nomeDaClasse));
	}

	static void registrarMetodo(String nomeDoMetodo, Classe tipoEmQueSeAplica,
			BlocoDeCodigo bloco) {
		metodo(nomeDoMetodo).put(tipoEmQueSeAplica, bloco);
	}

	static Object executarMetodo(String nomeDoMetodo, Instancia instancia,
			Object... args) {
		Map<Classe, BlocoDeCodigo> tiposAceitaveis = metodo(nomeDoMetodo);
		if (!tiposAceitaveis.containsKey(instancia.classe()))
			throw new RuntimeException("Metodo inexistente");

		BlocoDeCodigo bloco = tiposAceitaveis.get(instancia.classe());

		Object primeiro = args.length > 0 ? args[0] : null;
		Object segundo = args.length > 1 ? args[1] : null;

		return bloco.executar(instancia, primeiro, segundo);
	}

	private static Map<Classe, BlocoDeCodigo> metodo(String nomeDoMetodo) {
		if (!metodos.containsKey(nomeDoMetodo))
			metodos.put(nomeDoMetodo, new HashMap<Classe, BlocoDeCodigo>());

		return metodos.get(nomeDoMetodo);
	}

}

Agora não apenas registra as classes mas também os métodos que são aplicáveis à cada classe e faz a invocação dos métodos em si.

Estes exemplos são bem educacionais, sem muita aplicação pratica, mas como já vimos em posts passados o fato de se usar message-passing ou Data-Directed ou properties, ou componentes ou qualquer outra coisa interfere no modo como devemos projetar nosso software. Existe uma vasta literatura sobre este tema mas ainda assim é uma das coisas mais desconhecidas pelo programador profissional.

Objetos não são atributos + funções

Sunday, May 18th, 2008

Quando simplificamos demais ao ensinar conceitos acabamos criando problemas que demoram anos para se resolver, se é que são resolvidos. Um exemplo que eu observei hoje foi como algumas pessoas pensam sobre objetos.

É comum que seja ensinado no primeiro contato das pessoas com orientação a Objetos que objetos representam atributos e funções embaladas no mesmo saco. Bom, isso é uma simplificação extrema utilizada para fazer com que quem conheça programação procedural trabalhe com objetos de maneira mais simples, mas é uma simplificação tão grosseira que atrapalha algumas pessoas para o resto da vida.

Objetos não possuem propriedades + funções, eles possuem estado + comportamento. Você provavelmente vai usar atributos (i.e. variáveis de instancia) e funções (de instancia também) para implementar isso mas é um detalhe de implementação, não algo que deva ser exposto.

Um agravante deste problema são propriedades. Sejam propriedades bem resolvidas como as do C# ou meia-boca como do Java, elas representam apenas mais uma mensagem que o objeto recebe (já que ambas as linguagens são baseadas em troca de mensagens) e não “atributos” do objeto.

get/set em Java são herança de um modelo de componentes. Componentes não são necessariamente objetos, o que a (finada?) especificação JavaBeans fez foi utilizar classes Java para implementar componentes gráficos. Como não havia metadados (annotations em Java) naquela época o melhor era adotar uma convenção, daí os setXxx e getXxx da vida.

O problema é que este idioma saiu do controle. Ao invés de utilizarmos um método que faça sentido dentro do domínio preferimos criar um get/set burro, típico de arquiteturas BOLOVO.

Por exemplo, suponha que você possua uma classe Pedido que possui um status. Quando alguém vai atualizar o status do pedido para “finalizado” qual idioma é mais comum?

//1) set/get
pedido.setStatus(Pedido.Status.FINALIZADO);

//2) Mensagem
pedido.finalizar();

Creio que você concorda comigo que o primeiro é de longe mais utilizado. Qual o problema com ele? Como dissemos antes, Java é uma linguagem baseada em troca de mensagens. Uma mensagem é algo que o objeto recebe e decide o que fazer, no caso de Java isso acontece quando chamamos um método. Quem deve decidir o que deve ser feito não é o cliente e sim o destinatário da mensagem. Quando você diz setStatus(Status) você está dizendo para a classe que quer que os status dela seja o especificado. Pensa bem, é isso que você quer?

O que você quer em verdade é que o pedido seja finalizado. Como isso é feito, se muda status ou não, não é papel do código cliente, é papel do objeto (e deve ser documentado, especificado e verificado através de testes unitários).

Da mesma forma, quando você executa um getStatus() no objeto o que você faz com isso? Exceto em alguns casos específicos, um método getXxx() só deveria ser utilizado porque você precisa executar algo com ele que não vai envolver mudança de estado, como por exemplo exibir numa interface gráfica. Se você faz um getXxx() porque baseado no retorno você chama uma lógica ou outra provavelmente você está fazendo algo errado, já que é uma violação gritante da Lei de Deméter.

O que eu deveria fazer, então? Suponha que eu tenha o seguinte código:

public void notificarMudancaEm(Pedido p){
 if(p.getStatus().equals(Pedido.Status.FINALIZADO){
     servicoEmail.enviamensagemConclusao(p);
 }
}

Isso parece bem razoável, não? A lógica de enviar um e-mail de conclusão não está no Pedido e sim em algum serviço próprio para isso. Ele provavelmente é um observador do pedido e cada vez que você chama setStatus() esta é notificado. Num caso real recente eu estava trabalhando numa aplicação legada com o código exatamente como o acima. De repente alguém decidiu que um Pedido também está finalizado se estiver em status CANCELADO.

Essa é a hora em que o programador xinga a mãe do analista de negócios e vai caçar um email de três meses atrás que contem uma documentação, assinada em sangue, que contraria isso. O gerente de projetos vê aquilo, dá um sorriso e manda um email para o cliente daquela consultoria de três letrinhas dizendo que é uma mudança de escopo e que vai precisar de 50 horas a mais para isso. Ele diz ao programador que na verdade só tem 5 horas porque as outras 45 ele vai usar como desculpa quando o projeto atrasar. De quem é a culpa?

Bom, de quem é a culpa é assunto para outro post mas eu te garanto que não é da Orientação a Objetos. O problema no código acima (e certamente este código está repetido em milhões de outros lugares) é que ele não cumpre o princípio básico de deixar o objeto ser responsável pela sua própria vida. Você precisa saber se o pedido está finalizado mas ao invés de perguntar isso ao objeto você pergunta qual seu status.

Imagine que ao invés do código acima você possuísse algo mais… OO:

public void notificarMudancaEm(Pedido p){
 if(p.isFinalizado()){
     servicoEmail.enviamensagemConclusao(p);
 }
}

E suponha que você tenha que alterar o que FINALIZADO significa. Qual é mais fácil?

É este tipo de coisa que você perde ao transformar objetos em atributos+funções. Ao invés de pensar “quais são os atributos deste objeto?” pense “quais mensagens este objeto responde?”.

Qi4j @ ThoughtWorks Community College

Thursday, April 3rd, 2008

Ontem tivemos mais um Community College na ThoughtWorks Melbourne, desta vez focamos no Qi4j.

É uma idéia interessante. Basicamente o qi4j (”quiforjêi”) usa micro-aspectos para modelar qualquer coisa. O problema é que a sintaxe atrapalha. Eles usam a Linguagem Java, com Language Adaptations e uma Factory mágica, provavelmente uma linguagem própria teria mais efeito.

J2EE Spider no InfoQ

Thursday, March 13th, 2008

Saiu no InfoQ uma matéria sobre o J2EE Spider, do Bruno Braga. Eu recebi um email do Bruno há algum tempo me convidando para fazer um review da nova versão da ferramenta, e dando ênfase no fato que não é um gerador abrace-o-mundo como vemos por aí.

Ainda não tive tempo de fazer o teste da ferramenta mas já deixo aqui os parabéns pela notoriedade.

Gerenciando Débitos

Sunday, February 17th, 2008

Todo projeto que já participei, dos meus pet-projects até os com equipes imensas, possuem algum nível de tech debt. Sempre a mesma história: não temos tempo para isso agora, na próxima oportunidade corrigimos.

O problema é que em muitos casos o acúmulo de coisas que deixamos pelo meio do caminho é prejudicial à saúde do projeto. Mais que preciosismo de nerds e perfeccionistas, tech debt pode –e geralmente vai- atrasar o andamento do time.

Nos projetos que eu gerencio eu gosto de alocar um orçamento para resolver estes problemas. Durante o planning game eu deixo claro que precisamos resolver problemas enquanto estamos implementando funcionalidades e geralmente aloco alguns pontos na iteração para eles, normalmente algo perto de 20% do trabalho. Normalmente eu aceito que estas histórias técnicas tenham prioridade baixa e no geral tudo ocorre bem.

Se temos uma emergência então eu costumo não ser muito flexível em relação à solução do problema. As histórias técnicas neste caso ganham prioridade máxima dentro da iteração.

Como geralmente o cliente está satisfeito com a velocidade da equipe num processo ágil (se não está temos outro problema) quando sobra –e quase sempre sobra- tempo extra numa iteração geralmente eu preencho com tech debt, e em especial deixo os desenvolvedores priorizarem o que querem fazer. Muitas vezes não dá tempo para fazer homologação destas mudanças durante a iteração vigente e elas acabam indo para produção apenas na iteração posterior, mas é uma boa estratégia.

O que importa é não deixar o tech debt acumular. Se você tem duvidas dos problemas que o acúmulo de histórias técnicas causam basta lembrar a última vez que você entrou em um projeto para dar manutenção em um sistema pré-existente. Eu nunca vi um caso onde o sistema antigo não tenha toneladas de problemas causados por “deixar para depois” mudanças que não eram urgentes mas foram crescendo em urgência com o tempo.

E claro que meu projeto atual não é diferente. Trata-se da conversão de boa parte de um sistema legado em Java para Ruby (não Rails, Ruby). Como todo projeto deste tipo o orçamento não contempla uma reescrita do sistema, apenas uma conversão. Isso quer dizer que se um módulo assovia e chupa cana em Java ele, teoricamente, vai assoviar e chupar cana em Ruby.

O bom de trabalhar em um time ágil é que não é porque no início do projeto não se pensou em melhorar as coisas que isso precisa ser verdade até o fim dele. Após a equipe (desenvolvedores, analistas de negócios e gerente de projetos) percebemos que alguns itens realmente estavam atrapalhando o andamento do projeto. Nossos dias estão cercados de tarefas repetitivas que existem apenas para contornar alguma “gambiarra” que o sistema original tinha e nós estamos reproduzindo de maneira burra. Levamos a questão aos clientes e fizemos entender que se gastarmos alguns pontos nestas tarefas em algumas iterações nossa velocidade irá aumentar, e muito.

Apos conseguirmos 25% dos pontos de uma iteração para histórias técnicas veio a questão: temos dezenas de problemas, o que faremos primeiro? Como é um time grande cada um tem seu ponto de vista sobre o que está errado e o que precisa melhorar, então fizemos da maneira ágil: disciplina e flexibilidade.

Marcamos uma reunião com o time e coletamos em cartões todos os problemas que conseguíssemos pensar. Os cartões foram pregados na parede, divididos entre coisas do dia-a-dia e coisas que realmente indicam a visão que o sistema deve tomar, aspectos arquiteturais.

14022008446.jpg

Depois cada um recebeu 5 votos para distribuir entre os cartões. Quase todas as histórias eram importantes então precisamos limitar o numero de votos para entender o que realmente é critico.

14022008445.jpg

Apos este exercício nós criamos um backlog paralelo para o produto, apenas com histórias técnicas. Este backlog foi estimado e baseado nele o time decide que história técnica entra nos 25% de pontos disponíveis.

13022008442.jpg

Uma das vantagens dessa abordagem já foi percebida. Nosso sistema é um fluxo de operações em sequência. Operações em sequência são uma ótima área para programação procedural e isso fez com que os desenvolvedores originais do sistema seguissem este paradigma.

Claro que quando se mistura uma linguagem orientada a Objetos com código procedural é necessária muita cautela, e a maioria das pessoas acha que para o código ser procedural basta usar atributos públicos nas suas classes. Existem muitas métricas para qualidade de código procedural (a maioria das métricas de código OO são evoluções ou adaptações destas, na verdade) e nosso código não seguia nenhuma.

Aproveitando o nosso orçamento para histórias técnicas nós introduzimos um sistemas de jobs, uma implementação do padrão Chain of Responsibility. Até agora 50% das funções já foram convertidas para o modelo novo e a cada iteração mais são convertidas.

O resultado das ultimas iterações mostra um aumento consistente de 10% na velocidade. Todos os envolvido creditam esta melhoria à mudança e ainda estimamos que quando todo o sistema for convertido para a nova arquitetura teremos por volta de 25% de aumento total.

Existem coisas simples que podem decidir se um projeto vai ser um sucesso ou fracasso. Não esconder sujeira debaixo do tapete é uma delas.

Você Pergunta: RAD

Thursday, January 24th, 2008

Uma pessoa me escreveu um email sobre o tópico anterior. Novamente não vou citar nomes porque não pedi permissão (e não tenho motivos para expôr esta):

Shoes, beleza cara?

Então, lendo sobre a discussão sobre o Maker eu cheguei no seu blog e li sobre RAD.
Aquela ferramenta no NetBeansLixo pra fazer GUIs é um RAD? Ele desenvolve parte de um software, eu sei, não é um modelo de processo de desenvolvimento, né??

Enfim, acredito que existam mais algumas por aí como Maker e queria saber da sua opinião: todas essas ferramentas são RADs e todas elas cometem gafes? Onde quero chegar é na pergunta: Hoje em dia, SEMPRE teremos um desenvolvedor por trás da solução?

É que eu sou totalmente contra essas ferramentas de gerar códigos automáticos… não confio muito nessas coisas… prefiro eu mesmo fazer os códigos, mas tenho a dúvida: estou muito antiquado (no sentido de muito atrasado e não acompanhando as tecnologias que estão evoluindo hoje) ou estou no caminho correto?

Bom, se você respondeu que sim na primeira pergunta (do NetBeans sobre GUIs), a principal desvantagem das ferramentas RAD (se é isso que aquele Lixo é) é a manutenção e evolução, certo? Uma vez, fazendo um trabalho pra faculdade, tive que usar aquele NetBeans. Quando fui usar Refactoring… nossa cara, que coisa mais chata… tive que refazer praticamente toda a interface gráfica.

Abraço.

Apesar de usar, eu odeio o termo RAD. Ele significa um grupo enorme de coisas e ao mesmo tempo não significa nada. O livro Software Development: Building Reliable Systems define RAD:

For the last ten years, many software projects have incorporated the use of “Rapid Application Development” methodologies in an effort to decrease development times. RAD, as it is generally referred to, incorporates an umbrella of methodologies based on spiral, iterative development technologies. RAD techniques range from the simple use of GUI development tools to quickly build prototypes, to processes incorporating complete, cross-functional business analysis. […]

Então vou considerar que você quer dizer por RAD “ferramentas geradoras de código” do contrario este post nunca vai acabar. Sim, o Matisse do Netbeans é uma ferramenta geradora de código.

Ferramentas geradoras, em geral, são úteis quando não querem dominar o mundo. Eu adoro quando o Hibernate ou o Rails gera meu DDL SQL e eu não tenho que escrever CREATE TABLE. Eu adoro quando o Eclipse gera meus getters e setters e coisa do tipo. O problema é que esses geradores não costumam sobreviver num projeto de verdade.

O grande problema do gerador de código padrão, como o citado, é que ele possui diversos vazamentos de abstração. O Joel Spolsky fala especificamente sobre geradores:

The law of leaky abstractions means that whenever somebody comes up with a wizzy new code-generation tool that is supposed to make us all ever-so-efficient, you hear a lot of people saying “learn how to do it manually first, then use the wizzy tool to save time.” Code generation tools which pretend to abstract out something, like all abstractions, leak, and the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don’t save us time learning.

Como pode-se perceber pelas discussões citadas o mercado destas ferramentas é… complicado. Um desenvolvedor que entende como a plataforma funciona (Java EE, no caso) dificilmente se encantaria pela ferramenta. Ele pode utilizar algo como um Spring IDE para gerar arquivos de configuração mas sabe que isso não é tão simples assim.

Os programadores mais recentes na plataforma Java podem não ter consciência disso mas uma ferramenta chamada xdoclet era muito usada quando sofríamos diariamente com EJBs 2.1. O que o xdoclet faz é gerar código, ele gerava todo o mapeamento do EJB (uns 3 arquivos XML diferentes, pelo menos) a partir de anotações JavaDoc (não existia Java 5 naquela época). O xdoclet era a salvação da lavoura, conseguia abstrair muitos problemas mas.. não era perfeito, nem pretendia.

Eu já trabalhei em projeto onde 50% do código era gerado pelo xdoclet e do restante ele não dava conta. A ferramenta não possui o tal do round-tripping então não dava para misturar código gerado com modificado. A solução foi optar por uma arquitetura que isolava as duas partes em módulos diferentes e só conseguimos chegar até ela porque –como disse o Joel- sabíamos como a ferramenta funcionada. Essa é uma ferramenta de automação, não de “geração automática de sistema”. Ela automatiza o trabalho que você teria, não te livra do fato de conhecer o que… bem… é sua profissão.

Acho que o Matisse se enquadra neste tipo.

O problema é que essas ferramentas –e seus persuasivos vendedores- dizem que irão abstrair todo o desenvolvimento. Para os cenários ideais elas podem fazer todo o prometido mas não houve até hoje uma ferramenta que conseguisse real sucesso fora dos casos mais simples e isso porque não é assim que construímos sistemas hoje. Nossos sistemas são uma salada de linguagens, conceitos, mapeamentos e configurações que uma ferramenta deste tipo não consegue acompanhar.

O que geralmente elas fazem é te prender à uma ou algumas arquiteturas genéricas que servem razoavelmente para alguns casos -geralmente sitezinhos CRUD de intranet- mas não para todos ou sequer para a maioria. Como arquitetura não é commodity não se pode simplesmente aplicar a mesma estrutura em todos os cenários.

Apos algum tempo em TI você começa a entender que o dia-a-dia desse mercado é um grande “mais do mesmo”. Você sempre está criando as mesmas coisas. Da última vez que contei eu já tinha criado uns sete gerenciadores de conteúdo web. Dá para utilizar a mesma arquitetura em todos? Não. Uns eram simples formulários, outros se integravam com back-ends complicados, outros era assíncronos, outros tinham integração com legado… cada um tinha uma história. Se há oito anos atrás eu comprasse uma ferramenta dessas e a tivesse aplicado nestes projetos eu teria que fazer personalização (i.e. sair do maravilhoso mundo do fluxograma) a cada projeto.

Mas eu estou falando de desenvolvimento de software de um tipo, não do mercado todo. Quando estive na faculdade pela última vez (antes da segunda fuga) estudei com uma pessoa de 43 anos que desde os anos 80 vive fazendo software em coisas como CLIPPER para lojinhas e boutiques. Ele não ganha mal. Pelo que entendo dos softwares deste tipo (já trabalhei neste ramo) a arquitetura é quase sempre a mesma. Talvez ele se favorecesse de algo assim nos seus negócios, apesar de que eu ainda acharia um risco desnecessário.

Então sim, hoje precisamos de desenvolvedores para projetar um sistema. Desenvolver sua arquitetura, seu design e verificar para que tudo seja eito da maneira mais adequada para o projeto (que nem sempre é a técnica mais adequada). Mas e o futuro?

O futuro prevê o uso de ferramentas de um nível mais alto, mas de maneira diferente. O ponto crucial é que essas ferramentas não são geradores de código Java (ou C#, ou Ruby ou o que for), elas mudam a visão sobre o código. Ferramentas como Domain-Specific Languages aumentam o nível da linguagem não tornando-a mais abstrata apenas mas sim levando para perto do negócio. Ferramentas de visualização criam pontos de vista diferenciado sobre o mesmo artefato, dependendo de com que olhos se enxerga.

Tudo indica que para desenvolver softwares no futuro o desenvolvedor não terá o mesmo papel que tem hoje. Ao invés de criar o código do sistema em si iremos criar as ferramentas que dão suporte à criação de sistemas pelos usuários.

Seja qual for o futuro ele não é sobre gerar código. Isso nós já fazemos à décadas e serve apenas como quebra-galho para nos livrar da complexidade que nós mesmo criamos.

Expressividade no Código

Friday, December 28th, 2007

Um post no GUJ mais uma vez rende uma resposta maior do que se supunha. A thread em si é bastante útil mas existe muito ruído então vou tentar sumarizar:

Vocês pegam códigos que te faz passar horas e horas tentando entender o que está sendo feito? Valores que você nem imagina de onde estão vindo?

Eu sou muito ruim ou isso é normal?

E logo surge alguém sugerindo que deveria haver documentação. Existem casos onde documentação, seja JavaDoc, especificação funcional ou etc. é fundamental mas neste caso é diferente. JavaDoc, especificação textual e etc. devem ser uma fonte importante quando que está interessado nessa informação não vai lero código, é como reutilizar uma biblioteca ou um framework. Ninguém quer abrir o Spring para entender como utilizá-lo, precisamos de documentação para isso.

Um cenário completamente diferente é quando você recebe de presente um código já existente para dar manutenção. Neste caso o código tem que fazer sentido, tem que dizer algo. Tem que ser expressivo, mostrar suas intenções claramente.

NOTA: O trecho abaixo foi escrito direto no editor de texto, sem ajuda de compilador ou IDE. Por favor me corrijam.

Qualquer programador meia-boca sabe sua linguagem. Sabe o domínio dela. Veja o trecho abaixo:

abstract class A{
 public abstract int d(){
 }
}

class B extends A{

 int z= 0;
 int x=-1;

 public B(int z, int x){
  this.z=z;
  this.x=x;
 }

 public int d(){
   if(z==15) return x + x* 0.15;
   esle return x;
 }

}

Você entendeu muito bem, tenho certeza, que A é uma classe abstrata implementada por B. Que B tem um construtor que recebe dois inteiros, os armazena e usa numa computação simples com uma ramificação abaixo. Ótimo.

Imagine que você recebeu um email do seu usuário dizendo: “Precisamos fazer com que clientes do sexo feminino que comprem mais de R$1000,00 ganhem 10% de desconto.”. Tente implementar isso no código acima enquanto eu vou almoçar.

E aí, terminou? Sim, sim, claro que é impossível sem saber d que se trata. Então depois de procurar bastante você encontra no diretório onde fica a documentação do projeto um arquivo que pode te ajudar a entender. Após umas cinquenta páginas de diagramas de alto nível inúteis explicando tudo que acontece no container web você chega a uma descrição de algo como:

A classe A tem a lógica abstrata de uma venda. As vendas são sempre feitas de acordo com critérios específicos por isso existem classes que implementam vendas específicas. No momento (01/10/2003) só existe uma implementação, na classe B, que é a venda para uma pessoa física.

A primeira coisa que você pensa é: se desde 2003 ninguém precisou de outra implementação para que essa maldita classe abstrata? Mas ants de mexer no código precisamos entendê-lo, então vamos em frente.

O méodo e questão recebe três argumentos: a quantdade em reais vendida, o sexo da pessoa (segundo código vindo do mainframe na tabela ETXS32) e se a compra é casada ou não (um booleano).

De acordo com o caso de uso UC171 o sexo do comprador é utilizado para aplicar descontos.

TABELA ETXS32
15 -> masculino
22 -> feminino

Se a compra for casada o processamento é feito delegando para outra classe, mantendo o padrão Strategy, Composite e Adapter do Decorator. Note que o Chain of Responsibility do Bridge é usado com Visitors para passar as instâncias de Flyweight pelos Interpreters[…]

Após a sequência de buzzwords de padrões é bom parar. Acho que a informação necessária já está aí em cima e olha que não se passaram nem 4 horas de procura! Vamos lá: recebemos o valor, o sexo segundo um código numérico sem lóica que vêm de outro sistema. Também é passado um boolean.. cadê o boolean?

Procurando no histórico deste arquivo no CVS (poxa vida, eles ainda usam CVS!) você vê que no meio de 2005 alguém tirou o boolean de lá com um comentário ‘Removido compra casada. Ninguém usa isso e ninguém entende isso. Ninuém vai sentir falta’. Acho que a pessoa estava correta mas ela esqueceu que existem uns 300 documentos que precisam ser revistos porque todos fazem referência a esta tal venda casada, do caso de uso, diagrama de domínio até diagrama de deployment. Cada mudança simples implica em editar 300 documentos… provavelmente mais tempo atualizando a tal documentação que o código… dá pra culpar o desenvolvedor?

Bom, agora você entende o que o código faz ao menos. Ele está aplicando um desconto de 15% para homens, você não conseguiu achar isso no caso de uso mas se ninguém está reclamando em produção é porque deve ser assim mesmo. Amanhã (afinal, você perdeu o dia inteiro hoje na ‘documentação’ do sistema) você faz a mudança.

Novo dia e você está pronto para alterar este código. Hmm… alterar pode ser muito rápido e sujo ou devagar e bem feito. Como disse o Uncle Bob recentemente sujo nunca é rápido então você opta pelo caminho com mais qualidade (e mais ético).

Como desenvolvedores profissionais escrevem testes (e gerar você deve começar por aí. Você sabe muito pouco sobre este sistema e o teste vai te dar alguma garantia que a menos esta pequena parta que está mexendo vai continuar funcionando após suas modificações. Vamos lá:

class TestVenda extends TestCase{
 public void testDeveAplicarDescontoSeSexoDoCompradorForMasculino(){
  B vendaParaUmHomem = new B(15, 100);
  assertEquals("Valor final sofreu 15% de desconto", 85, vndaParaUmHomem.d());
 }

 public void testDeveNaoAplicarQualquerDescontoSeVendaNaoCaiEmNenhumaPromocao(){
  B vendaParaUmHomem = new B(9999, 1);
  assertEquals("Valor final intacto", 1, vndaParaUmHomem.d());
 }
}

Os testes executam. Agora vamos alterar um pouco esta classe, pensando no pobre coitado que for mexer nela após você. Vamos começar agregando nomes mais expressivos:

abstract class VendaAbstrata{
 public abstract int vender(){
 }
}

class Venda extends VendaAbstrata{
 static final int NAO_INFORMADO = -1;
 static final int MASCULINO = 15;
 static final int FEMININO = 22;

 int sexoDoComprador= NAO_INFORMADO;
 int valorDaCompra=-1;

 public B(int sexoDoComprador, int valorDaCompra){
  this.sexoDoComprador=sexoDoComprador;
  this.valorDaCompra=valorDaCompra;
 }

 public int vender(){
   if(sexoDoComprador==MASCULINO) return valorDaCompra - valorDaCompra* 0.15;
   esle return valorDaCompra;
 }

}

Já está bem melhor, não? Compare com a primeira versão do código Os testes passam? Então é hora de commitar (eu acho esse neologismo horrível mas alguém sugere algo melhor?).

Vamos para o segundo round: pequeno refactoring. Já é possível fazer a alteração neste código mas anda temos tempo para deixá-lo um pouquinho mais legível, mais claro, mais expressivo. Vamos alterar:

abstract class VendaAbstrata{
 public abstract int vender(){
 }
}

class Venda extends VendaAbstrata{
 static final int NAO_INFORMADO = -1;
 static final int MASCULINO = 15;
 static final int FEMININO = 22;

 int sexoDoComprador= NAO_INFORMADO;
 int valorDaCompra=-1;

 public B(int sexoDoComprador, int valorDaCompra){
  this.sexoDoComprador=sexoDoComprador;
  this.valorDaCompra=valorDaCompra;
 }

 public int vender(){
   int valorFinalDaCompra = aplicarDescontosSobreValorDaCompra();

   return valorFinalDaCompra;

 public int aplicarDescontosSobreValorDaCompra(){

  int valorComDesconto= valorDaCompra;

  if(sexoDoComprador==MASCULINO)
   valorComDesconto = descontarPorcentagem(15, valorDaCompra);

  return valorComDesconto;
 }

 public int descontarPorcentagem(int porcentagem, int valorOriginal){
  return valorOriginal * (porcentagem / 100.0);
 }

}

Agora que tal esta versão do código + teste unitário contra a versão antiga + trezentos documentos e especificações? A implementação da regra nova ficou fácil? Acho que sim, tanto que deixo como exercício ao leitor, bem como algumas dezenas de refactorings que vão deixar o código acima decente.

A resposta curta para a thread do GUJ é: geralmente o problema não é seu mas de quem escreveu o código.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

-Martin Fowler, “Refactoring: Improving the Design of Existing Code “

Usando Netbeans Para Editar Código Ruby? Ainda não…

Sunday, December 9th, 2007

Essa semana no trabalho resolvemos (meu par e eu) ceder aos outros 3 pares que trabalham conosco (a rotação de pares infelizmente é baixa por alguns critérios internos) e tentar usar o Netbeans para editar Ruby (ninguém na equipe ou na empresa sonha em usar esta ferramenta para Java). Eu já havia conversado com um pessoal no escritório da TW sobre isso e me pareceu bom dar uma chance.

O Eclipse RDT era nossa ferramenta mas desde que a Aptana “comprou” (usando o antigo vocabulário do Marc Fleury, que Zahl o tenha) o projeto usá-lo é um problema só. Ninguém morre de usar mas dá raiva.

Então vamos lá. Um download gigantesco (normal para as IDEs inchadas de hoje em dia…) e temos o bicho rodando. A versão mais recente, claro. Ops, JVM capotou na primeira tentativa, vamos tentar de novo. Ok, funcionou.

Quase uma semana usando a ferramenta eu posso dizer que o editor para Ruby está muito melhor que qualquer versão do RDT. O code complete, syntax highlight, tudo muito bem. Aí você começa a ter os problemas quando sai do editor (afinal, uma IDE é um ambiente integrado, certo?). A integração com SVN é uma piada, é m parto fazer checkouts e commitar coisas. Os diffs e logs são bem burrinhos. A navegação entre abas e entre arquivos é custosa e muitas vezes não funciona (me parece que você só consegue transitar entre os X últimos arquivos que mexeu). Criar uma source folder para Ruby? Isso não faz sentido. Em Ruby geralmente tudo é código-fonte, da sua task rake até a configuração do banco de dados. Os atalhos de teclado são razoáveis, você só precisa se dar conta que está numa espécie de mundo bizarro do Eclipse, os atalhos são a mesma coisa só que com ALT em vez de CTRL ou coisa assim (para quem fez a migração pra Mac isso não é novidade).

Minha conclusão final é que o editor do Netbeans para Ruby (ao contrário de para Java) é infinitamente superior ao do Eclipse RDT. O problema é que não só o editor faz a IDE, se for para não er o ambiente integrado eu prefiro usar emacs. Acho que dados os pontos fracos de cada um tanto faz ficar com Eclipse ou Netbeans. Eu não ficaria com nenhum.

No Mac eu uso o maravilhoso Textmate. No Windows e Linux eu passei algumas semanas brincando com o OpenKomodo e ele é muito bom, traz os benefícios do Textmate para o ‘PC’ (hoje em dia tudo é PC mas vocês entenderam…)

Modelo de Negócios e Tecnologia

Sunday, December 2nd, 2007

Há alguns anos eu trabalhava para uma grande empresa que cria produtos em um nicho muito específico. Dá última vez que eu vi alguém fazer uma estatística 90% dos produtos eram em C, 5% em C++ (”muito lenta”) e o restante em PERL, Python e Java.

A coisa que eu mais gostava sobre essa empresa era como o modelo de negócios deles era movido por inovação. Quando alguém tinha uma boa idéia ele era convidado a montar um protótipo em laboratório que seria oferecido à clientes. Haviam muitos ganhos para os bem-sucedidos, meu chefe, diretor regional de tecnologia, subiu ao seu cargo após duas idéias convertidas em produto (além de ganhar uma boa grana).

No entanto, apesar da inovação presente nos novos produtos lançados simplesmente não havia inovação técnica. Não importa o software, tudo era feito em C. Eu fui contratado para um novo time que cuidava das alicações Java. Estas aplicações só foram criadas porque os clientes exigiam interfaces web e RMI para os produtos da empresa e porque contratar programadores C sempre foi um problema. Mesmo trabalhando basicamente com Java (e naquela época o sonho de todo mundo era arrumar um emprego “100% Java, nada de PHP ou VB”) boa parte do meu trabalho era escrever e testar interfaces em C que se comunicavam com o sistema “de verdade” (o feito em C).

Certa vez me mandaram por 10 dias, que viraram 20, numa viagem de trabalho. O lugar não era muito amigável com turistas então passei boa parte do tempo no quarto de hotel tentando entender a televisão. Como não tinha Internet liberada eu passava no dia baixando PDs e páginas completas para ler à noite e nos fins-de-semana. Com o tempo pensei: e se eu reconstruisse o sistema em Java? Obviamente que não consegui reproduzir o sistema todo dado o tempo mas se o sistema antigo tinha 5% em Java eu consegui que fosse unas bons 30%. Chegue no escritório doido para mostrar ao meu chefe.

Obviamente foi um banho de água fria. A pessoa ficou feliz por eu me interessar pelos projetos internos e disse que iria ver o sistema, um dia. Esse dia nunca chegou. Após alguns meses eu pedi demissão da empresa.

Dia desses estava falando com um amigo que ainda trabalha lá e soube que 50% dos clientes foram embora. Na época que eu trabalhava nessa empresa ela tinha quase que o monopólio de um dado tipo de sistema, com tempo concorrentes foram surgindo. O sistema da empresa era claramente superior aos outros mas era muito menos flexível. Ele era muito bom no que fazia mas quando precisávamos que ele fizesse a coisa um pouquinho diferente o projeto durava meses. Os concorrentes chegaram com linguagens como C++ e Java e apesar de não terem um produto com 10 anos de bons serviços prestados eles conseguiam mudar rapidamente.

Há dez anos a escolha por utilizar C e IPC de UNIX na mãozona era certa. Nenhuma plataforma da época oferecia o desempenho aceitável. Infelizmente as pessoas acabam construindo as piores coisas do mundo com desculpa de performance e o sistema era completamente monolítico e depende tanto do SO que mudar de uma versão minor para a outra leva um ano com um time de 3 pessoas completamente dedicados.

Se esta empresa acompanhasse os movimentos da indústria veria que existiam plataformas que já ofereceriam performance aceitável (eu já trabalhei em sistemas Java mais eficientes que aquele em ouros lugares) e que iriam oferecer a flexibilidade necessária. Infelizmente só repararam isso quando a concorrência invadiu o mercado.

A empresa continua com produtos ótimos em suas idéias mas ninguém consegue esperar 2 anos para colocar algo no ar. As coisas mudaram e escolher a tecnologia certa para cada caso é cada vez mais o qe define sucesso ou fracasso de um projeto. Ou de uma empresa.

Programadores Profissionais Escrevem Testes, Ponto Final.

Wednesday, October 31st, 2007

O tópico já tem oito páginas. Acho que chega à 10. Por mais que minha mão coce para comentar lá eu não vou, simplesmente porque já tive problemas demais com o pessoal do Mentawai.

De qualquer forma sempre me preocupa a possibilidade de algum desenvolvedor ler o tópico e pensar “Poxa, se esses caras que fazem todos estes frameworks não usam testes por que eu vou usar?”.

Desenvolvedores profissionais escrevem testes. Simples assim.

Uma pessoa que não ganha milhões de dólares mas escreveu uma das obras mais clássicas deste ramo deixa bem claro em sua primeira aula que programar é gerenciar complexidade. Nós precisamos gerenciar complexidade o tempo todo, por isso criamos funções, objetos e tudo mais. Não adianta, mesmo Einstein teve que provar que suas fórmulas e execuções estavam corretas, que poderiam ser verificadas. Na faculdade aprende-se isso desde as cadeiras básicas (e o fato de ser esquecido como “coisa teórica inútil” me faz novamente perguntar sobre o valor do ensino formal).

Existe uma grande diferença entre fazer Test-Driven Development e testar. TDD é sobre modelagem de objetos e especificações, não sobre testes (tanto que Behaviour Driven Development está se consolidando como algo mais eficiente que TDD) apesar de que no final você acaba ganhando uma suíte de testes de graça.

É muito difícil achar um projeto open-source relevante que não tenha testes. Na verdade os projetos decentes só aceitam um relatório de bug ou um patch se vier acompanhando por um caso de testes. Imagine uma aplicação feita colaborativamente por diversas pessoas, como saber que o que eu acabei de fazer commit não vai quebrar o que você modificou ontem? Boas práticas de orientação a Objetos? Não se iludam, OO não foi feita para este tipo de verificação! Com boas práticas você consegue minimizar o impacto de mudanças diminuindo dependências mas você não vai ter certeza disso.

Eu sinceramente não sei que técnica é essa que faz programação defensiva evitar testes. Eu já li bastante sobre Orientação a Objetos e programação defensiva e não vi nada deste tipo, pelo menos não vindo de uma fonte com um mínimo de credibilidade. Um exemplo simples, imagine que o framework web imaginário Pagai possui um código parecido com este:


Acao acaoSendoExecutada = controladorPrincipal.acao(requisicao.acaoDesejada());

Simples, não? Agora imaginemos que o código do método acao(String) seja algo assim:


public Acao acao(String pathInvocado){
//verifica se acao possui o formato desejado, deve ter uma barra e deve ter dois itens separados por barra apenas
if((pathInvocado.indexOf("/") == -1) || (pathInvocado.split("/").size < 2)) throw new IllegalArgumentException("Path invocado ["+pathInvocado+"] nao possui o formato adequado (consulte a documentacao XYZ)");
//lógica...
}

Isso é programação defensiva: eu não estou aceitando o que me passam, eu verifico se é o que deveria e se for eu continuo, se não eu paro ali mesmo e deixo alguém tomar conta do problema, seja a classe em questão ou alguma outra mais acima.

Imagine que eu por engano commitei um código que utilize “” em vez de “/” nesta requisição. Se for numa parte central do código é bem possível que uns testezinhos peguem mas imagine que é utilizado apenas em um caso específico e que, por um acaso, eu baseei meu sistema de controle de jatinhos particulares 9meu chefe tem muitos jatinhos) nele. Quando eu fiz o comit não alertou. Quando eu fiz o build não alertou. Quando eu fiz meus testezinhos não alertou. Quando foi para a produção eu tive erro.

Ok, acontece. Programadores de qualquer tipo cometem erros. Eu vejo o problema muito rapidamente e o corrijo, temos uma versão beta em 15 minutos no ar, fantástico. Aí daqui há um mês outro programador comete o mesmo erro. Quando fizer o build não vai alertar. Quando fizer seus testezinhos não vai alertar. Quando for para a produção… Isso não é profissionalismo.

O que eu preciso é de uma suite de testes, unitários e de integração, que me digam que o sistema está incorreto já no processo de build, sem lançar jars beta, alfa ou gama.

Mas se tem um argumento nessa história toda que realmente me irrita é quando as pessoas dizem que “num mundo capitalizado não há tempo para testes” ou que “o cliente não quer saber como é feito, ele quer que funcione”. O cliente realmente não quer saber como funciona, ele quer que funcione. Mas ele também não vai querer saber que você alterou uma classe que usava barra para barra invertida e tudo parou de funcionar, ele quer que o problema não aconteça e se acontecer que seja corrigido rapidamente. Se seu sistema não tem qualidade -e testes fazem parte de qualidade- você não consegue isso. TI gasta fortuna das empresas reescrevendo sistemas simplesmente porque não foram feitos por profissionais, e profissionais se preocupam com a qualidade do que fazem. E isso inclui testes.


Não seja um amador.