Autor: Luiz Ricardo (Página 13 de 16)

Morte por Power Point!

EPSON scanner image

Calma… o título é apenas uma brincadeira. Mas que tem muita gente morrendo de sono por aí, isso sim é verdade!

Bem, é certo que existem muitas pessoas que odeiam PPTs. Algumas até fazem campanha contra eles. Não que apresentações de slides sejam ruins por natureza, mas a culpa recai sobre todos nós, usuários de softwares como o Power Point. Iludidos por pseudo-animações, efeitos de transição e sons (argh!), fomos de alguma forma convencidos de que tais recursos multimídia são um diferencial em nossas apresentações.

death-by-powerpoint

Quem já não tentou fazer caber aquele texto fantástico em um slide, diminuindo o tamanho da fonte? Só para depois dizer: “eu sei que não dá para ler aí do fundo, mas está escrito que…”. Mais interessantes ainda são as transições de texto letra a letra. O indivíduo que incluiu esta funcionalidade no softwares de apresentação é um “trolador” de alta categoria.

A verdade é que, em geral, a audiência se distrai com os efeitos especiais; por amor ou por ódio. Mas como evitar que isso aconteça?

Dicas para criar uma apresentação eficaz

Conteúdo
Prepare o conteúdo antecipadamente. Não comece criando slides a partir de um modelo e depois recheando a apresentação de conteúdo. Organize as ideias e tópicos antes de partir para a parte visual.

Visual
Vá direto ao assunto. Transmitir a mensagem de forma efetiva é o objetivo. Portanto imagens, animações e textos devem colaborar para isso, mas não significa que o design tem que ser simplista. Pergunte-se: isso é apenas um enfeite (distração) ou contribuirá para a apresentação?

Apresentação
Treine e obtenha feedback. Evite falar demais, não seja redundante e mantenha-se no tempo.

Enfim, não tenho a pretensão de ser exaustivo. São apenas alguns pontos gerais que considero importante. Diversas dicas específicas podem ser encontradas por toda a web.

Alternativa Interessante

Ser diferente às vezes é bom. Já pensou em usar uma alternativa ao Power Point?

Existem diversos sites que indicam essas alternativas, mas gostaria de apontar uma. Há alguns dias venho testando uma plataforma de apresentações num estilo bem diferente do sistema de slides comum. É o Prezi.

Basicamente, você tem um espaço onde colocar toda a informação da apresentação. Então, a “câmera” se movimento de um ponto a outro, aproximando-se ou afastando-se (zoom). Apresentações conceituais, onde os slides relacionam-se uns com os outros, podem se beneficiar grandemente deste tipo de visualização.

Para efeito de exemplo, criei uma versão do meu currículo utilizando um template. Para falar a verdade, foi uma sugestão da própria plataforma, então decidi arriscar. Confira o resultado:

Não conseguiu ver? Tente atualizar a página com F5 ou clique aqui.

O Prezi é gratuito e a edição da apresentação é feita online, mas na versão paga há um aplicativo desktop para quem precisa editar offline. De qualquer forma dá para fazer o download da apresentação caso você fique sem conexão à Internet.

Construindo objetos de forma inteligente: Builder Pattern e Fluent Interfaces

building-with-legosA Orientação a Objetos é constantemente mal utilizada. Sem o devido treino, temos a tendência de raciocinar de forma estruturada.

Esse problema está tão enraizado que muitas vezes encontramos dificuldades nos pontos mais fundamentais da programação, tal como a construção de instâncias de objetos um tanto mais complexos que beans simples. Como consequência, nosso código fica difícil de manter e mais propenso a erros.

Portanto, todo desenvolvedor deve estudar Padrões de Projetos (Design Patterns), já que estes ajudam a resolver os problemas mais comumente enfrentados e nos dão o treino necessário para pensarmos sobre os problemas mais complexos. Além disso, existem outras técnicas, como as Interfaces Fluentes (Fluent Interfaces), que ajudam a criarmos código mais legível e, dessa forma, aumentam a produtividade e diminuem a propensão a erros.

Criando um objeto (the dummy way)

Suponha que precisamos criar um objeto com diversos atributos “opcionais”. Vamos usar uma pizza como exemplo (um dos exemplos clássicos para ilustrar padrões de projetos). Já vi muitos construtores de “pizza” por aí da seguinte forma:

public class Pizza {

	private int tamanho;
	private boolean queijo;
	private boolean tomate;
	private boolean bacon;

	Pizza(int tamanho) {
		this.tamanho = tamanho;
	}

	Pizza(int tamanho, boolean queijo) { 
		this(tamanho);
		this.queijo = queijo;
	}

	Pizza(int tamanho, boolean queijo, boolean tomate) {
		this(tamanho, queijo);
		this.tomate = tomate;
	}

	Pizza(int tamanho, boolean queijo, boolean tomate, boolean bacon) {
		this(tamanho, queijo, tomate);
		this.bacon = bacon;
	}

}

Diga a verdade, você já faz isso em algum momento? E pode ficar pior se acrescentarmos construtores com combinações e ordenação diferentes de parâmetros!

A sobrecarga é interessante quando temos algumas poucas variações de parâmetros e há poucas mudanças no conjunto de atributos. Porém, chega uma hora que nem sabemos mais o que está acontecendo. Quanto tempo você já perdeu inspecionando conteúdo de classes de terceiros para entender que valores deveria usar? Exemplo:

new Pizza(10, true, false, true, false, true, false, true, false...)

Que tipo de pizza é essa mesmo? 😯

Criando um objeto com Builder Pattern

Alerta: antes que alguém fique agitado porque este exemplo diverge do Builder Pattern originalmente publicado no GoF, leia o livro com atenção, pois os próprios autores propõem implementações alternativas dos padrões de projeto e afirmam categoricamente que estes devem ser adaptados de acordo com o contexto. É sério, está escrito lá, acredite ou não.

public class Pizza {

	private int tamanho;
	private boolean queijo;
	private boolean tomate;
	private boolean bacon;

	public static class Builder {

		// requerido
		private final int tamanho;

		// opcional
		private boolean queijo = false;
		private boolean tomate = false;
		private boolean bacon = false;

		public Builder(int tamanho) {
			this.tamanho = tamanho;
		}

		public Builder queijo() {
			queijo = true;
			return this;
		}

		public Builder tomate() {
			tomate = true;
			return this;
		}

		public Builder bacon() {
			bacon = true;
			return this;
		}

		public Pizza build() {
			return new Pizza(this);
		}

	}

	private Pizza(Builder builder) {
		tamanho = builder.tamanho;
		queijo = builder.queijo;
		tomate = builder.tomate;
		bacon = builder.bacon;
	}

}

Aplicando o padrão de projeto Builder, temos agora um objeto “construtor” para o objeto Pizza. A classe Pizza está um pouco mais complexa, mas confira como ficou elegante a forma de temperarmos:

Pizza pizza = new Pizza.Builder(10)
                       .queijo()
                       .tomate()
                       .bacon()
                       .build();

É muito mais fácil de codificar com essa API e entender o que está acontecendo.

O Builder Pattern é muito utilizado em boas bibliotecas que disponibilizam APIs intuitivas e fáceis de aprender, como construtores de XML e o Response do JAX-RS, por exemplo.

Composição de Objetos

Outro ponto onde temos dificuldades encontra-se na criação de objetos que são compostos por vários objetos. Como exemplo, vamos montar um pedido com alguns itens para um cliente:

public class Pedido {

	List<Item> itens;
	Cliente cliente;

	void adicionarItem(Item item) {
		itens.add(item);
	}

	void setCliente(Cliente cliente) {
		this.cliente = cliente;
	}

	void fechar() {
		// ...
	}

}

O uso fica assim:

Pedido p = new Pedido();
p.setCliente(new Cliente("José"));
p.adicionarItem(new Item("Motocicleta", 1));
p.adicionarItem(new Item("Capacete", 2));
p.fechar();

Embora, neste exemplo, nosso código esteja relativamente simples, um objeto mais complexo dificultaria ao desenvolvedor entender o código. Mas, estou certo, podemos fazer muito melhor que isso!

Interface Fluente

A “mágica” das Interfaces Fluentes está em encadear métodos que retornam a instância do próprio objeto, evitando repetições desnecessárias no código “cliente”. Abaixo, o exemplo anterior foi ligeiramente modificado:

public class Pedido {

	List<Item> itens;
	Cliente cliente;

	Pedido com(int quantidade, String nome) {
		itens.add(new Item(nome, quantidade));
		return this;
	}

	Pedido para(String nome) {
		cliente = new Cliente(nome);
		return this;
	}

	void fechar() {
		// ...
	}

}

O uso fica assim:

new Pedido()
	.para("José")
	.com(1, "Motocicleta")
	.com(2, "Capacete")
	.fechar();

Muito mais limpo, com maior clareza e intuitivo, não é mesmo?

Classes Utilitárias

Temos ainda classes utilitárias com seus métodos estáticos, usadas nos mais diversos pontos da arquitetura de um sistema. Elas causam problemas quando mal planejadas, pois a tendência é acumularmos muitos métodos sobrecarregados com diferentes objetivos que infectam todo o código. Então, sem perceber, perdemos o controle, já que agora todo o sistema está acoplado a tais classes e alterações acabam impactando onde não esperamos.

Vejamos um exemplo de uma típica classe com rotinas de tratamento de datas:

public class Data {

	public static Date converteTextoParaData(String dataStr) {
		try {
			return new SimpleDateFormat("dd/MM/yyyy").parse(dataStr);
		} catch (ParseException e) {
			return null;
		}
	}

	public static String converteDataParaTexto(Date data) {
		return new SimpleDateFormat("dd/MM/yyyy").format(data);
	}

	public static Date avancarDiasCorridos(Date dataInicial, int dias) {
		Calendar c = Calendar.getInstance();
		c.setTime(dataInicial);
		c.add(Calendar.DATE, dias);
		return c.getTime();
	}

}

Eis como ficaria uma manipulação simples de data usando essa classe:

String inputDateStr = "28/02/2013";
Date inputDate = Data.converteTextoParaData(inputDateStr);
Date resultDate = Data.avancarDiasCorridos(inputDate, 30);
String resultDateStr = Data.converteDataParaTexto(resultDate);

Entediante. Vamos refatorar a classe com o conceito de Interface Fluente:

public class Data {

	private Date data;
	public Data(String dataStr) {
		try {
			data = new SimpleDateFormat("dd/MM/yyyy").parse(dataStr);
		} catch (ParseException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public String toString() {
		return new SimpleDateFormat("dd/MM/yyyy").format(data);
	}

	public Data avancarDiasCorridos(int dias) {
		Calendar c = Calendar.getInstance();
		c.setTime(data);
		c.add(Calendar.DATE, dias);
		data = c.getTime();
		return this;
	}

}

E o uso fica assim:

String resultDateStr = new Data("28/02/2013").avancarDiasCorridos(30).toString();

Simples, não?

Nota: considere este como um exemplo de estudo. O uso do new para instanciar objetos é discutível, assim como o uso da classe java.util.Date.

Conclusões

Simplificar o código não é luxo. Trata-se de uma necessidade na luta contra a crescente complexidade dos sistemas de software.

A utilização de padrões de projetos como o Builder Pattern e técnicas como a Fluent Language nos auxiliam nessa empreitada, além de tornar a codificação mais simples e agradável.

Outro ponto positivo é que, ao utilizar objetos específicos para tratar problemas comuns, como no exemplo de Fluent Language, fica mais fácil manter o foco da responsabilidade das classes. Com métodos estáticos, a tendência é acrescentar novos métodos indiscriminadamente, enquanto com um objeto “inteligente” temos uma melhor percepção dos limites.

Leitura Obrigatória

Para ler mais sobre Fluent Interfaces, acesse http://martinfowler.com/bliki/FluentInterface.html.

Além disso, todo desenvolvedor deve ler o livro Design Patterns: Elements of Reusable Object-Oriented Software. Infelizmente, o livro é um tanto antigo e os exemplos podem ser um pouco “chatos” para desenvolvedores web, pois muitos aplicam-se à construção de GUI de aplicativos desktop.

Especificamente para quem está iniciando em Java, uma leitura mais leve é o Head First Design Patterns. É mais fácil de entender, porém incompleto. Não espere encontrar todos os padrões em detalhes. Muitos deles só constam no final como uma espécie de resumo.

Você é um desenvolvedor consciente?

Nossa jornada como profissionais de TI é uma grande curva de aprendizado. Entretanto, desenvolvedores recém-formados raramente compreendem isto. Alguns acham que já sabem tudo o que precisam porque conhecem uma ou duas linguagens de programação. Falo com conhecimento de causa, pois alguns anos atrás eu mesmo acreditava ser suficiente aprender tudo sobre uma linguagem para tornar-se um desenvolvedor experiente.

Mas não é apenas importante estudar constantemente, como todos já afirmam, mas também desenvolver o caráter e personalidade como um todo. Alguns de nós têm um certo prazer nerd em estudar apenas tecnologia. Gastamos nossos dias “escovando bits” no porão da empresa, deixando a barba crescer e ficando sem tomar banho.

É claro que estou exagerando, mas infelizmente isso não está tão longe da realidade. Tenho amigos que são excelentes técnicos, mas por falta de saber trabalhar em equipe e comunicar seu conhecimento, acabam isolados e não se desenvolvem como profissionais. Vencer a timidez, o comodismo, a procrastinação não é algo que ocorre da noite para o dia, mas é possível.

Pessoas são complexas. Entretanto, para fins de ilustração, classifiquemos três níveis de maturidade que caracterizam nossa jornada profissional:

1. O Lobo Solitário

lobo

Também conhecido como prima donna (ver Modern usage outside opera) por ter o ego inflado e se achar a estrela principal e indispensável do espetáculo. Em geral possui a síndrome chamada not invented here, isto é, prefere “reinventar a roda” ao reaproveitar o trabalho de alguém. Ele despreza o processo e segue apenas suas próprias regras.

A vantagem de um profissional desse nível é que ele não necessita de muito treinamento e trabalha bem em ambientes com poucos desenvolvedores que conduzem pequenos projetos sozinhos. Porém, ele causa muitos problemas quando uma equipe de verdade é necessária.

Não importa quantos anos de trabalho ou quanto conhecimento um desenvolvedor de software tenha. Enquanto orgulho, ego inflado e superioridade forem suas características, ele nunca será um profissional maduro.

O principal passo para vencer essa fase é reconhecer a importância do trabalho dos outros, de cooperar com os colegas e das regras estabelecidas na organização.

2. O Senhor dos Processos

agent-smithO desenvolvedor aprende sobre alguns processos, metodologias, normas ou padrões. Ele adota uma metodologia, um par de padrões e tenta seguir tudo à risca, acredita que toda regra estabelecida é importante e todos os que não acompanham nesse pensamento estão errados, não entendendo como essas pessoas podem ser tão tolas.

Em geral, o desenvolvedor adota uma abordagem única, a qual ele acredita ser a melhor para todos os cenários. Ele pode ter sucesso em alguns empreendimentos semelhantes, mas quando surgir um projeto diferente ele estará em maus lençóis.

Para vencer esse nível,  é preciso aumentar o leque de conhecimentos, estudar outras abordagens e compreender os diferentes contextos de cada projeto de software.

3. O Desenvolvedor Consciente

desenvolvedor-experienteO desenvolvedor consciente trabalha com base em princípios. Ele utiliza esses princípios para selecionar a melhor abordagem para cada situação, extraindo regras de metodologias existentes, mas aplicando-as com sabedoria quando elas devem ser aplicadas.

A maior dificuldade desse nível é a necessidade de grande investimento em educação para que o desenvolvedor entenda os princípios do desenvolvimento de software efetivo. Porém, uma vez que o desenvolvedor está treinado conforme esses princípios, ele estará equipado com todas as ferramentas necessárias para enfrentar uma grande variedade de projetos.

Nesse nível, o profissional não é apenas um programador, mas um Engenheiro de Software. Deste ponto em diante, sua própria experiência e sabedoria o guiarão no seu aperfeiçoamento contínuo.

* Este artigo foi parcialmente baseado em Raising Your Software Consciousness, de Steve McConnell.

9 pecados capitais do planejamento de projetos

MorteEm seu artigo Nine Deadly Sins of Project Planning, Steve McConnell nos apresenta uma forma bem interessante de reconhecermos um projeto mal planejado. O autor baseou-se em sua experiência para identificar os “pecados capitais” que frequentemente levam os projetos ao fracasso. Francamente, suspeito que ele andou visitando algumas empresas que eu conheço!

O que se segue abaixo é uma tradução um tanto informal do artigo, isto é, não muito ao pé da letra. Eis os nove pecados capitais do planejamento de projetos:

#1 → Não planejar nada

Um dos erros mais comuns. Não é necessário ser um especialista para planejar. Pessoas pouco experientes já o fizeram com sucesso simplesmente porque tiveram o cuidado de atentar para os requisitos do projeto.

Se tiver de escolher entre um especialista em planejamento que não pensa cuidadosamente sobre o plano e um iniciante na carreira, mas que analisa criteriosamente as necessidades do projeto, é melhor ficar com o último.

#2 → Não planejar todas as atividades necessárias

O pecado número 1 é não planejar, o número 2 é não planejar o suficiente.

Alguns planos de projeto simplesmente assumem que ninguém ficará doente, participará de treinamentos, sairá de férias ou deixará a empresa. Tais planos preparam o projeto para um desastre. Há inúmeras variações neste linha. Por exemplo, não incluir atividades auxiliares tais como criar instalação do software, conversão de dados de versões anteriores e outros tipos de tarefas por vezes irritantes que levam mais tempo do que gostamos de admitir.

Em alguns projetos que estão com o cronograma atrasado, tenta-se recuperar o prazo diminuindo o tempo que foi originalmente planejado para os testes. A justificativa é que provavelmente não haverá tantos erros como a princípio se pensava. Fica como exercício para o leitor imaginar por que então o tempo de testes já não foi menor desde o início.  😉

#3 → Não planejar considerando os riscos

Henry Petroski argumenta que os maiores desastres na construção de pontes ocorrem após períodos de sucesso que dão margem à autoconfiança. Os projetistas tornam-se complacentes e simplesmente copiam os atributos de uma ponte para outra sem atentar-se para os riscos em potencial da nova construção.

Em muitos projetos, a palavra “risco” não é mencionada a menos que estejamos com sérios problemas. Em projetos de software, se não estamos usando essa palavra cotidianamente e incorporando-a em nossos planos, não estamos fazendo bem nosso trabalho. Como disse Tom Gilb: “se você não atacar ativamente os riscos, eles irão atacar ativamente você”.

#4 → Usar o mesmo planejamento em todos os projetos

Algumas organizações familiarizam-se com uma abordagem para executar projetos. Ela é mais conhecida como “o jeito que nós fazemos as coisas por aqui”. As coisas costumam ir bem quando os novos projetos são bem parecidos com os anteriores. Mas, se aparecer algo diferente, essa abordagem vai causar mais problemas do que ajudar.

Um bom planejamento visa necessidades específicas do projeto para o qual ele foi criado. Muitos elementos podem ser reusados, mas deve-se considerar cuidadosamente se os elementos antigos se aplicam ao novo contexto.

#5 → Aplicar modelos de planejamento indiscriminadamente

Um planejamento que alguém realizou chega na forma de um livro ou metodologia e parece funcionar muito bem do jeito que está. Então usa-se este plano genérico indiscriminadamente sem pensamento crítico ou consideração pelas particularidades do projeto.

Exemplos conhecidos são o RUP (Rational Unified Process), o XP (Extreme Programming) e, até certo ponto, meu próprio “Guia de Sobrevivência a Projetos de Software” (Software Project Survival Guide). Esses planos “pré-fabricados” podem ajudar a evitar os pecados #1 e #2, mas não substituem nossa análise das demandas únicas do projeto.

Nenhum especialista externo pode entender as necessidades específicos de um projeto como as pessoas envolvidas diretamente nele. O plano de um especialista deve ser adaptado para as circunstâncias específicas. Felizmente, existem gerentes que estão atentos para esses problemas e usam o senso comum para selecionar as partes dos livros de Engenharia de Software que atendem suas demandas.

#6 → Permitir que o planejamento esteja divergente da realidade

Uma abordagem comum ao planejamento é criar o plano no início do projeto e depois abandoná-lo numa gaveta, juntando poeira até o fim projeto. Quando as condições mudam, o planejamento torna-se irrelevante e, no meio do caminho, o projeto anda sem rumo. Não há mais relação do planejamento com a realidade do projeto.

Isso pode estar relacionado ao pecado #5, pois quando alguém abraça uma metodologia pré-fabricadas algumas vezes torna-se relutante em mudar quando ela não funciona. Eles pensam que o problema está na aplicação, quando, na verdade, está no planejamento.

Um bom planejamento deve ocorrer e ser revisado incrementalmente em todo o projeto.

#7 → Planejar muito detalhadamente logo de início

Alguns gerentes bem intencionados tentam mapear todas as atividades do projeto logo no início. Mas um software consiste num constante desdobramento de decisões, cada fase gera novas pendências para decisões futuras. Como não temos uma bola de cristal, tentar planejar atividades distantes em detalhes é um exercício burocrático tão ruim quanto não planejar nada.

Quanto mais trabalho é despendido em criar planos prematuros, maiores são as chances do plano ser engavetado (pecado número #6). Mas ninguém gosta de jogar seu precioso trabalho fora, então o gerente algumas vezes tentará forçar a realidade do projeto para se encaixar em seus detalhados planos, ao invés de revisar o plano prematuramente detalhado.

Eu penso num bom planejamento de projeto como dirigir a noite com os faróis do carro ligados. Eu posso ter um mapa que me diga como ir da cidade A para a cidade B, mas a distância que posso ver com os faróis é limitada. Em um projeto médio ou grande, um macro planejamento pode mapear todo o projeto. Então micro planejamentos detalhados conduzem o projeto apenas algumas semanas por vez.

#8 → Planejar para correr atrás do tempo perdido

Em projetos que começam a atrasar, um erro comum é planejar para se recuperar depois, correndo para “alcançar” o cronograma. A racionalização típica é: “a equipe passou pela curva de aprendizado no início do projeto, nós aprendemos várias lições duras, mas agora que entendemos o que estamos fazendo devemos conseguir concluir o projeto rapidamente”. Resposta errada!

Uma pesquisa de 1991 em mais de 300 projetos descobriu que dificilmente projetos recuperam-se, pelo contrário, a tendências é atrasar ainda mais. O erro desse raciocínio é que a equipe de desenvolvimento toma decisões importantes no início do projeto, enquanto ainda está aprendendo sobre as aspectos tecnológicos, de negócio, e sobre as metodologias. Nas fases posteriores do projeto, o passo não irá acelerar, mas diminuir na medida em que são colhidas consequências de decisões iniciais erradas e então é necessário investir tempo corrigindo esses erros.

#9 → Não aprender com os “pecados” cometidos anteriormente

O pecado mais mortal de todos é não aprender com os pecados já cometidos. Projetos de software podem levar muito tempo e a memória das pessoas pode ficar enevoada pelo ego e pelo tempo. No fim de um projeto longo, pode ser difícil lembrar todas as decisões iniciais que afetaram sua conclusão.

Uma forma fácil de contornar essa tendência e prevenir-se de pecados futuros é conduzir uma autópsia estruturada do projeto. Uma revisão não vai apagar os pecados passados, mas pode certamente evitar mais erros futuros.

Integração com Web Sevices usando Axis e inspecionando o conteúdo da mensagem SOAP

É comum realizarmos integrações entre sistemas através de Web Services tradicionais. Na minha empresa, em particular, usamos o Apache Axis, uma implementação Java do protocolo SOAP (Simple Access Object Protocol).

Observação: tratando-se de prover serviços de integração, eu, particularmente, escolheria o protocolo REST, mas quando acessamos serviços de terceiros não temos muita escolha, não é?

Irei descrever, em alto nível, os passos que geralmente realizamos para implementar a integração.

Requisitos

Antes de mais nada precisamos do arquivo WSDL (Web Servcie Definition Language), o qual define todos os serviços disponíveis, parâmetros e tipos de dados utilizados.

Algumas vezes recebemos a URL do end point, isto é, o endereço do Web Service. Por exemplo:

http://servidor:8080/app/webservices

Neste caso, obtemos o arquivo WSDL acresentando um parâmetro ao endereço, assim:

http://servidor:8080/app/webservices?wsdl

Gerando o client

O primeiro passo de implementação é gerar as classes de domínio e as interfaces stub usando o plugin do Eclipse WTP. (Siga os links para maiores detalhes)

Os objetos client são gerados com base no WSDL e assim o acesso ao Web Service se dá como uma chamada de método local, usando objetos locais, via RPC (Remote Procedure Call). A conversão dos objetos de e para XML no formato SOAP, além do transporte na rede é feito de forma transparente para o desenvolvedor.

Testando

O testes de integração com serviços de terceiros, pelo menos no nosso caso, não é algo viável de automatizar, pois não termos controle sobre o ambiente, nem acesso direto.

Para verificarmos se o serviços estão sendo acessados corretamente, criamos uma classe de teste com valores fictícios. O modelo do código ficou mais ou menos assim:

ServiceFactory serviceFactory = ServiceFactory.newInstance();
Service service = serviceFactory.createService(
        new ServicoServiceLocator().getServiceName()
    );
ServicoSoapStub stub = new ServicoSoapStub(
        new java.net.URL("http://site.com.br/app/ws"), 
        service
    );
ClasseRetorno retorno = stub.executarServico(
        new ParametroServico(...)
    );

Este link contém mais alguns que você pode usar como modelo para o seu Web Service.

Analisando o conteúdo da mensagem SOAP

Num caso específico, o cliente enviou exemplos de mensagens XML no protocolo SOAP e quisemos verificar se as mensagens geradas pelo Axis a partir do nosso código eram equivalentes em conteúdo.

Para inspecionar o Axis e ver o conteúdo da mensagem, adicionei os jars do log4j, commons-logging e commons-discovery. Também acrescentei na configuração do log (log4j.properties) a seguinte linha:

log4j.logger.httpclient.wire.content=DEBUG

Dessa forma, foram adicionadas mensagens no LOG com todo o conteúdo enviado pelo Axis.

Processos feitos para o fracasso

imagesO universo da tecnologia da informação é repleto de processos. Temos processos para desenvolvimento de software, prestação de serviços, gerenciamento de projetos (específicos ou não de software), garantia de qualidade e até para preparar o cafezinho da cozinha.

Note que não estou me referindo exatamente a modelos de processos como waterfall (cascata), iterativo, incremental, evolucionário, espiral e de prototipação, nem de metodologias ou frameworks de processos como Scrum, XP, RUP, ITIL, COBIT, CMMI ou MPS.BR. Todos estes possuem seu valor e nos dão uma base para definir o processo que adotamos no dia-a-dia nos mais diversos aspectos, incluindo artefatos, atividades e boas práticas fundamentais.

O foco deste artigo é no processo implementado em nosso ambiente de trabalho, o qual somos (algumas vezes) obrigados a seguir. 

Em conversas com colegas de trabalho, ao pronunciar a palavra mágica “processo”, geralmente ocorrem duas reações: uma gargalhada ou uma cara feia. No primeiro caso, o riso é acompanhando de desdém, tipo: ” se pelo menos tivéssemos um processo de verdade…”. Do outro lado, estão os que ficam quase irados: “não entendo porque nos fazem perder tempo com esses processos que não servem pra nada!”

Mas e aí? Você se encaixa em um desses grupos? O processo é um obstáculo no seu trabalho? Ou talvez você esteja do outro lado, criando processos para sua equipe ou empresa e acredita que certas atividades são de grande importância, mas não consegue se fazer entender. Vamos analisar alguns desses aspectos nos tópicos a seguir.

Quando realmente sofremos com o absurdo

angry-computer-large

Para ilustrar o sofrimento que alguns desenvolvedores tem de aturar, irei descrever exemplos de alguns dos piores casos de processos, ou de motivos para a definição deles, que já testemunhei:

Ditadura
Alguém da diretoria quer “controlar melhor” as atividades, culminando em algo excessivo e desnecessário. Houve um caso onde o chefe queria relatórios de atividades dos funcionários com detalhes em minutos! Por quê? Vai saber…

Labirinto
Processo confuso e redundante. Além de mandar e-mail, preencher documentos e incluir informações no sistema de controle, ainda é necessário imprimir e assinar um documento. Nesse tipo de processo, o desenvolvedor sempre é “culpado” por esquecer algum dos passos “essenciais” do processo.

Surpresa
Tudo está normal, de repente, você dá de cara com o processo.

Certa vez, ao  solicitar o envio de um script de correção urgente ao cliente, responderam-me:
– “Onde está o documento de envio?”
– “Que documento?”, repliquei.
E então, a surpresa:
– “Houve uma reunião com a diretoria hoje e recebemos ordens de, a partir de agora, não enviar nada ao cliente que não esteja acompanhados do respectivo documento de envio.”
Bem, restou-me correr atrás de alguém que pudesse me dizer do que isso se tratava… ordens são ordens, não é?

Morto-vivo
Às vezes o processo é essencial, outras nem tanto. Você leu o tópico anterior? Algum tempo depois disseram que o tal documento não era mais necessário. Isso mudou intermitentemente algumas vezes. Já aconteceu de você ficar perdido em como sua empresa funciona?

“Garantia” de Qualidade
Neste ponto alguém pode até argumentar em contrário, devido às boas práticas que alguns processos agregam. Porém, já presenciei casos onde um determinado processo foi adotado para tentar diminuir o número de defeitos num sistema! Primeiro, de forma geral, a burocracia apenas deixa o programador mais nervoso. Além disso, os desenvolvedores eram pouco experientes, então seria melhor investir em treinamento ou incluir pessoas mais maduras no projeto.
Em outra situação, um funcionário que recebeu a incumbência de uma atividade simples estava errando repetidamente, semana após semana. Ele simplesmente devia copiar alguns valores de uma planilha e fazer a inclusão no banco de dados, mas frequentemente os valores do sistema ficavam errados. Seu superior não sabia o que fazer. Seria de propósito? Seria uma total falta de inabilidade? Seria falta de comprometimento? O gerente tentou resolver a situação com um “processo” onde o funcionário problemático teria que “prestar contas” de suas ações. Obviamente isso não resolveu.

Marketing
Determinados certificados relacionados a processos (como CMMI e MPS.BR) colaboram para o status das empresas. Logo algumas investem tempo e dinheiro em práticas e documentações inúteis a fim de aumentar sua credibilidade para com clientes em potencial, sem se preocupar efetivamente com a qualidade de seus produtos ou com a queda de produtividade. Resta aos funcionários ficar até mais tarde para cumprir as exigências adicionais.

Processos servem para alguma coisa, afinal?

O processo é uma ferramenta. Como tal, ele deve ser usado para que os envolvidos e a organização como um todo alcancem seus objetivos. Estes objetivos devem estar alinhados às metas da organização, aos requisitos dos clientes e com as exigências dos órgãos regulatórios.

Um grande problema é que os requisitos e exigências são por vezes conflitantes. Por exemplo, para o cliente, as entregas de correções devem ser imediatas. Entretanto, do ponto de vista de qualidade, é necessário haver revisão e testes, que oneram razoavelmente o tempo necessário para entregar algo. Nos ambientes mais desorganizados, o desenvolvedor pode executar um script diretamente em produção. Por outro lado, em algumas empresas os usuários estão acostumados a ficar dias parados porque as correções não podem ser aplicadas sem uma série de aprovações de diferentes setores.

O processo pelo processo

Um dos maiores erros na engenharia de software é adotar um processo sem um motivo concreto, porque é “legal”, a última moda. Pior, alguns acreditam que determinado processo é a solução para as dificuldades do dia-a-dia. Ainda há aqueles que ficam tão impressionados com um determinado processo que tentam adotá-lo integralmente, com toda sua carga de artefatos e atividades.

Muitos gerentes e diretores acreditam que podem resolver os problemas de gerenciamento simplesmente adotando uma nova abordagem de processos. Eles não conseguem entender porque seus subordinados são tão avessos e resistem tão intensamento a mudanças. Por que será?

É uma questão de mentalidade

Por que resistimos aos processos e reclamamos deles? Por que alguns relatam um “renascimento” ao adotar processos ágeis? Por que os gerentes estão sempre impondo novos processos?

Existem conflitos de expectativas e entendimento. Considerando que não haja absurdos no processo, os desenvolvedores reclamam porque não entendem a necessidade da diretoria. Por que parece que quando um programador é promovido a gerente ele se “converte ao lado negro da força”? Na verdade ele simplesmente amadureceu e percebeu a importância de um gerenciamento adequado. Nós, desenvolvedores, devemos procurar amadurecer e compreender os desafios de nossos superiores, pois assim poderemos contribuir de forma mais efetiva em nosso trabalho e, quem sabe, os gerentes não sentirão a necessidade de criar processos rígidos para resolver seus problemas.

Por outro lado, os gerentes devem entender que processos não resolvem os problemas por si mesmos. Analogamente ao nosso governo, criar leis duras e colocar o exército nas ruas não irá mudar a mentalidade do povo, embora a opressão possa dar a impressão de maior organização e controle. Fazer “a coisa certa” é um desafio gigante, mas conseguir a sincera colaboração e motivação dos envolvidos é algo que não tem preço.

E os processo ágeis? Os “agilistas” relatam que métodos ágeis podem aumentar em diversas vezes a produtividade da equipe, a qualidade, deixar o cliente mais satisfeito e os desenvolvedores mais felizes e realizados. Será que realmente não seria o caso de existir um processo “bala de prata”, isto é, que garantisse o sucesso?

Se você observar a literatura dos “agilistas”, perceberá que antes de alcançar qualquer nível de sucesso, houve uma mudança de mentalidade geral dos envolvidos. Mas, quando o processo ágil falha, logo dizem que ele não foi seguido corretamente. Há uma certa verdade nisso, pois sempre que falhamos é porque não fizemos as coisas como deveriam ser feitas. Porém isto também se aplica a todos os processos.

Conclusões

Enquanto, em nosso trabalho, não pararmos de correr desenfreadamente em qualquer direção, pensando apenas em entregar código e se livrar do fardo que os superiores nos colocam, nunca iremos amadurecer, crescer profissionalmente e ter o prazer de entregar software bem feito.

Devemos rever nossos conceitos e despertar para a realidade do que estamos fazendo, não apenas continuar a repetir indefinidamente e irrefletidamente o que já praticamos vez após vez.

Devemos usar o processo como um aliado para nossos objetivos, que deve intervir em caso de necessidade e não como lei primordial. A chave para o sucesso do projeto (e do processo também), consiste em:

  • Transparência: o desenvolvedor merece saber e deve entender a razão do processo;
  • Confiança: o processo não deve ser feito baseado na desconfiança entre as partes ou resultará em um mal-estar geral;
  • Equilíbrio: embora virtualmente fosse bom controlar tudo o que ocorre na empresa, o processo deve ser apenas bom o suficiente para cumprir seus objetivos.

Lembre-se: o processo pode tanto contribuir como obstruir o desenvolvimento de software.

Para refletir: como saber se seu processo foi feito para o fracasso?

Pense nas atividades que devem ser executadas. Se o sentimento que lhe vier à mente for equivalente a quando você precisa fazer uma ligação de cancelamento de serviços (TV a cabo, celular, …), então provavelmente as pessoas farão tudo ao seu alcance para evitá-lo!

Se você está nessa situação (e é fã de Star Trek) vai entender que “resistir é inútil!”

Does good developers write complex code?

Did you ever see a developer boasting of himself because he created a hard-to-understand code? Perhaps you ever fancied yourself doing something like this:

void Run( const std::string& v, int tgt, int start ) { for( ; tgt >= 2 * start + 1; ++start ) Run( v + ' ' + boost::lexical_cast( start ), tgt - start, start + 1 ); std::cout << v << ' ' << tgt << std::endl;} int main() { Run( std::string(), 10, 1 ); getchar();}

(Source: http://www.cplusplus.com/forum/lounge/13042/)

Complex code is bad code

spaghetti-code A good developer knows complexity is his enemy. When we work with real software we have to fight it. Complexity hides errors, makes software obscure, and is a hindrance to maintenance. If all that doesn’t bother you, think in your own wallet, mainly if you’re a consultant, a freelancer, or own a company.

Did you ever work with code written by yourself a long time ago and had a hard time? How could you write such a hideous thing? If you were more careful, perhaps it would keep you or a colleague from future nightmares, isn’t? Of course it would! And hope shines if you are now able to see that!

Complex code is bad code. Maintenance will require too much refactoring and testing. Did you ever see a messed program you prefer rewrite it thoroughly instead of just make the changes required?

“Oh, but we have comments, don’t we?”, you can argue. Not so fast! Many will oppose to you. To begin with, processes like XP (eXtreme Programming) states that the code should be clear and self-explaining. Comments often are our enemies too. They can indeed fool us stating something the code doesn’t actually does. In excess, they pollute the code visually and make our sources longer.

Be simple

One approach to escape complexity is the principle known as KISS: “Keep It Simple, Stupid!“. Let’s outline a few steps from this “rehabilitation program for bad developers”:

  • Be humble, don’t think of yourself as a genius.
  • Break down your tasks in sub tasks: don’t work more than a few hours in the same code.
  • Study seriously about cohesion, coupling, class and method responsibility, SOLID principles.
  • Solve the problem first, then code the solution.
  • Don’t be afraid of throw code away.

An experienced software engineer should know how to use the available tools to produce the desired result with the minimum complexity and effort.

On the other hand, developers love to reinvent the wheel. In my first years as a developer, I thought to my myself: “I have no reason to learn framework XYZ if I can write my own framework!”. Yes, I’m guilty. I wrote a lot of small frameworks. Did it help me? Of course! I tested my own limits, learned the internals of these little monsters and then realized the importance of the work of other developers. I threw much code away and now I’m happy using frameworks and tools knowing exactly how they work and why they do what they do.

Simplicity example

Watch this video as if the guy at right was you in college and the guy at left you with years of experience.

Conclusion

Who cares if the program takes a bit less time to run or how much “pretty” the code is? Are we talking about the developer’s ego?

A mature IT professional should strive to improve his communication abilities. Whatever is your specialization, all artifacts in the development process should fulfill its purpose in communicating the information as effectively as possible.

Furthermore, it’s better keep our artistic skills for things like the International Obfuscated C Code Contest:

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

Bons programadores escrevem código complexo?

Você já viu algum desenvolvedor se gabar de ter criado código difícil de ler? Talvez você já tenha se sentido o máximo ao fazer algo assim:

void Run( const std::string& v, int tgt, int start ) { for( ; tgt >= 2 * start + 1; ++start ) Run( v + ' ' + boost::lexical_cast( start ), tgt - start, start + 1 ); std::cout << v << ' ' << tgt << std::endl;} int main() { Run( std::string(), 10, 1 ); getchar();}

(Extraído de http://www.cplusplus.com/forum/lounge/13042/)

Código complexo é código ruim

spaghetti-code Bons programadores sabem que a complexidade é sua inimiga. Quando trabalhamos com software de verdade temos de lutar contra ela. A complexidade esconde erros, dificulta a leitura do código e inviabiliza a manutenção. Se nada disso lhe diz muita coisa, pense que o impacto vai diretamente para o seu bolso, principalmente para consultores e freelancers.

Você já precisou dar manutenção em código escrito por você mesmo há muito tempo e teve dificuldades em entender, além de ficar pasmo por ter escrito algo tão hediondo? Será que o cuidado ao produzir código no passado poderia ter lhe poupado de dificuldades e perda de tempo no presente? É claro que sim! E grande esperança há para você se for capaz, hoje, de fazer melhor que antes!

Código complexo é código ruim. Sua manutenção exigirá refatoração abrangente. Você já olhou um programa tão confuso que, antes de modificá-lo, preferia reescrevê-lo completamente?

“Ah, mas pra isso existem os comentários”, você pode argumentar. Não se precipite! Muitos irão lhe contradizer. Processos como o XP enfatizam a necessidade do código ser claro e auto-explicativo. Comentários são por vezes nossos inimigos. Eles podem nos enganar ao declarar algo que o código efetivamente não faz. Em excesso, poluem visualmente o código e tornam nossos fontes mais extensos.

Seja simples

Uma das abordagens para fugir da complexidade é o princípio conhecido como KISS: “Keep It Simple, Stupid!“. Em português poderia ser: “faça isto simples, seu estúpido!”

Resumirei alguns passos deste “programa de reabilitação para programadores”:

  • Seja humilde, não pense em si mesmo como um gênio.
  • Divida suas tarefas em sub-tarefas: não leve mais do que algumas horas para trabalhar em um código.
  • Aprenda muito bem sobre coesão, acoplamento e responsabilidades de classes e métodos.
  • Resolva o problema primeiro, codifique a solução em seguida.
  • Não tenha medo de descartar código.

Um engenheiro de software experiente deve saber usar as ferramentas à disposição para produzir o resultado desejado com o mínimo de complexidade e esforço. Por outro lado, programadores são apaixonados por “reinventar a roda”. Nos meus primeiros anos, pensava comigo: “para que aprender o framework X  se posso escrever o meu?!”. Já reescrevi muitos frameworks. Serviu-me de alguma coisa? Claro! Principalmente em ver minhas limitações e a importância do trabalho alheio!

Exemplo de simplicidade

Pense no cara da direita como você na faculdade e no da esquerda já com alguns anos de experiência.

Conclusão

Quem se importa se o programa leva alguns milissegundos a menos para executar ou se o código é mais “bonito”? O ego do programador?

Um profissional de TI maduro deve se esforçar para aperfeiçoar suas habilidades de comunicação. Isto independe de sua área de atuação, pois todos os artefatos do processo de desenvolvimento, incluindo o código, devem ter qualidade suficiente para cumprir o seu papel e transmitir a informação o mais efetivamente possível.

Além disso, é melhor guardarmos nossas habilidades artísticas para um concurso como o International Obfuscated C Code Contest:

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

Comportamento recursivo inesperado no SQL Server

Como todo desenvolvedor, provavelmente você já perdeu horas com um problema que beira o bizarro até perceber que era algo muito simples.

Uma colega estava testando um sistema onde uma procedure era acionada e, após algum tempo, ocorria um erro “Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)”.

O erro refere-se ao limite de chamadas empilhadas ou recursivas, por exemplo, uma função que chama outra, que chama outra e assim por diante. Entretanto, a procedure não tinha nenhuma chamada desse tipo.

Analisei por alguns segundos os script de criação da procedure, algo como:

CREATE PROCEDURE MINHA_PROC (...) AS
BEGIN
... conteúdo ...
END
EXEC PROCEDURE MINHA_PROC [valores de teste]

Então lembrei de uma “pegadinha” de sintaxe. Para o SQL Server, o conteúdo de uma procedure é tudo o que está entre o CREATE e o “GO”. É o equivalente à barra invertida no Oracle.

Repare que no código de exemplo não existe um GO, pois o desenvolvedor assumiu que o bloco BEGIN/END demarcaria a procedure, mas não é o caso. O comando “EXEC” usado para testar a procedure acabou incluído como parte dela e, quando executada, criou chamadas recursivas até estourar o limite.

Solução? Tão simples quando um “GO”:

CREATE PROCEDURE MINHA_PROC (...) AS
BEGIN
... conteúdo ...
END
GO
EXEC PROCEDURE MINHA_PROC [valores de teste]

Uma forma simples de contar registros e limpar tabelas de uma base de dados

Uma colega estava com alguns backups de uma base de dados SQL Server para realizar testes e queria diminuir o tamanho das bases limpando tabelas de log e operacionais, mantendo apenas as configurações do sistema. O problema é que as bases eram tão grandes que ultrapassavam o limite de 10 GB estabelecido na versão SQL Express 2008.

Ele não precisava e nem queria entender a estrutura de tabelas, então sugeri uma maneira rápida e simples de identificar tabelas com muitos registros e uma forma de excluí-las rapidamente.

A query abaixo retorna vários selects que exibem o total de registros de cada tabela de um banco de dados:

use NOME_BASE
go
select 'select ''' + name + ''', count(*) from ' + name from sys.tables

Basta executar esse código, substituindo o nome da base a ser usada, copiar o resultado gerado e executá-lo.

Esta query gera deletes para cada tabela, no caso de ser necessário limpar a base toda:

select 'delete from ' + name from sys.tables

A versão usando truncate:

select 'truncate table ' + name from sys.tables

Página 13 de 16

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.