Porquê aprender Maven?
4
Entry posted on Jan 04
by João Saleiro
Comecei recentemente a dar os primeiros passos na aprendizagem de Maven, e apesar de ainda ser um novato achei por bem partilhar as razões pelas quais esta tecnologia é tão interessante.
Apesar de utilizarmos Maven há já algum tempo nos nossos projectos na Webfuel e de conhecer os seus benefícios, nunca tinha investido tempo na sua aprendizagem visto que a configuração dos projectos com Maven estava a cargo de uma empresa nossa parceira (Web-Responsive). Sou, portanto, tenrinho no assunto, mas sinto-me compelido a partilhar as razões pelas quais vale a pena aprender Maven.
Este post não é um tutorial a explicar como se usa, mas sim uma descrição de alguns dos problemas (use cases) que o Maven resolve em projectos complexos. De notar ainda que o Maven não é específico ao desenvolvimento de RIAs, mas sim aplicável ao desenvolvimento em qualquer tecnologia que tenha um plugin para Maven. Para Flex existe o flex-mojos que cobre grande parte dos tópicos referidos neste post, mas não todos (ainda). Diria que no mundo J2EE, aí sim, o Maven se porta brilhantemente - por exemplo, na configuração de um JAVA + Spring + Hibernate + BlazeDS + Springflex + montes.de.outras.libs.
O Maven é uma ferramenta poderosa para gestão de projectos, gestão de dependências, gestão de versões, automatização do processo de build (compilação, unit tests, reporting, etc), configuração de ambientes de desenvolvimento, entre outras. É uma ferramenta com cerca de 3 megas que se utiliza através da linha de comandos (ou através de alguns plugins para Eclipse, como o m2eclipse), para executar diversas tarefas sobre um (ou vários) projecto(s), regra geral associadas à sua compilação, mas sobretudo manutenção. Bem, o ideal é mesmo ver os exemplos mais abaixo...
É importante compreender que existe uma maneira "à-la-Maven" para a estruturação de um projecto. Um "projecto físico" deve ser separado em vários sub-projectos/componentes/bibliotecas (artefactos), com dependências entre eles. A noção de um projecto, com Maven, pode resultar em 10 ou mesmo 100 "projectos Eclipse". Por outras palavras, o projecto "my-huge-crm-application" quando criado "à-la-Maven" deve ser dividido em "my-crm-users", "my-crm-reports", "my-crm-costumers", "my-crm-commons", "my-crm-config", "my-company-lib", "my-3rdparty-lib", etc - podendo-se atingir com facilidade 10 ou 20 projectos diferentes, com dependências uns dos outros (isto é, o my-crm-commons.jar é usado pelo my-crm-costumers, etc).
À primeira vista, pode parecer mais confuso, mas na realidade resulta numa maior simplicidade - graças à elegância do Maven, como veremos adiante.
O Maven lida com uma simplicidade extrema com as dependências entre projectos e com o processo de build - por exemplo, ao compilar um projecto que depende de outro, o Maven irá descarregar o outro projecto de um repositório onde possa estar instalado, ou se não o encontrar, mas tiver "acesso" ao código fonte, compilar o outro projecto -, e graças a esta arquitectura de gestão de dependências, torna-se relativamente fácil acoplar, desacoplar e substituir projectos/módulos/dependências num projecto.
O Maven resolve essa tarefa em dois segundos com um único comando, recorrendo a algo conhecido por "archetypes". Como? Escrevendo:
mvn archetype:generate
O Maven irá fazer algumas perguntas (tipo de projecto, package, etc) e irá então criar uma estrutura de directorias base com alguns ficheiros necessários (ou dummy) tipicamente necessários nesse tipo de projectos, utilizando uma estrutura de pacotes pré-definida, e criará um ficheiro pom.xml de base. A estrutura de directorias usada pelo Maven obedece a uma *convenção*, normalmente muito bem definida, que torna permite que developers que estejam a analisar um projecto pela primeira vez entendam rapidamente a sua estrutura, e onde encontrar todos os recursos. Além disso, a convenção usada pelo Maven reduz em *muito* a complexidade dos arquivos pom.xml do Maven (especialmente quando comparando ao Ant), pois não é necessário adicionar configurações extra ao pom.xml - O Maven irá "automaticamente" encontrar os recursos necessários nos locais correctos, se as convenções forem seguidas - mas, claro, os developers podem fazer override às convenções, embora não seja recomendável.
Outro dos desconfortos na criação de um projecto a partir do zero, é a configuração do projecto no Eclipse. Descarregar as dependências, adicioná-las à classpath no Eclipse, etc..., e depois fazer isto novamente em cada um dos postos de trabalho de uma equipa. Porém, graças ao maven-eclipse-plugin, basta um comando para gerar estas configurações. Fazendo:
maven eclipse:clean eclipse:eclipse
serão criados automaticamente os ficheiros de configuração do Eclipse (.project, e restantes), de acordo com as dependências e tipo de projecto definidos no pom.xml. A partir daí, basta que em cada posto de trabalho se faça um "Import Eclipse Project" no Eclipse, onde regra geral fica tudo maravilhosamente configurado. Graças ao pom.xml o Maven saberá o suficiente para criar correctamente os ficheiros de configuração do Eclipse. Claro que existem centenas de diferentes tipos de projectos no eclipse com configurações diferentes, e o maven-eclipse-plugin não conhece todas. Mas para JAVA, tem-se portado suficientemente bem para 90% dos meus casos. Os outros 10% são resolvidos facilmente adicionando algumas configurações adicionais no pom.xml.
Em resumo, a utilização de archetypes permite criar a estrutura e configuração de projectos a partir do zero, e o maven-eclipse-plugin permite criar e *actualizar* os ficheiros de configuração do Eclipse, sem ser necessário configurar projecto a projecto cada um dos projectos em cada um dos postos de trabalho à mão (isto, para JAVA, regra geral).
Com o maven, é bastante simples. No pom.xml basta adicionar o seguinte XML para especificar que o projecto tem uma dependência do spring-flex, por exemplo:
org.springframework.flex
spring-flex-core
1.5.0.BUILD-SNAPSHOT
E aí basta correr:
mvn install
O Maven irá automaticamente encontrar a dependência (chamada de "artifact") - pesquisando num conjunto de repositórios de dependências -, fazendo o download da mesma e "instalando-a" no seu repositório local. Irá fazer o mesmo para todas as dependências de cada dependência recursivamente, pelo que não temos que perder tempo com isso. E a partir daí, sempre que compilar com Maven, este irá adicionar cada uma das dependências ao classpath do compilador automaticamente.
A integração com o Eclipse também é incrível - ao executar:
mvn eclipse:clean eclipse:eclipse
o maven irá actualizar (na verdade, irá recriar) os ficheiros de configuração do Eclipse e adicionar as dependências correctamente ao classpath do projecto. Por outras palavras, algo que regra geral pode ser um autêntico pesadelo (definir as dezenas de dependências de um projecto, descarregá-las da internet, configurá-las, e por sua vez fazer o mesmo para as dependências de cada dependência...), é resolvido elegantemente em poucos segundos!
Esta simplicidade na gestão das dependências não só facilita a configuração das bibliotecas externas necessárias aos nossos projectos, como fomenta a separação dos nossos projectos em dezenas de módulos e bibliotecas, visto que com Maven se torna relativamente simples organizar todas as relações entre projectos.
Com Maven, iremos utilizá-lo inevitavelmente para compilar. A compilação é feita através de plagins, que invocam os compiladores com as directrizes de compilação apropriadas, de acordo com o pom.xml. Isto é, não definimos os argumentos dos compiladores; definimos sim a configuração do projecto, e o maven trata de definir os argumentos, regra geral automaticamente! Além da compilação, o Maven irá utilizar o resultado desta para executar tarefas adicionais, como a execução de unit-testing, geração de relatórios, envio de emails quando ocorrem erros de compilação, etc.
Algo extremamente elegante no Maven, é a forma como ele compila vários projectos em "simultâneo", recursivamente de acordo com a ordem das dependências. Algo que iremos ver posteriormente
Artefactos (módulos, bibliotecas, etc) possuem sempre um número de versão que lhes é inerente. Isto é, a biblioteca log4j poderá existir na versão 1.1, 1.2, etc. Isto significa que se o nosso projecto precisar do log4j 1.3, basta abrir o pom.xml, localizar a zona onde a dependência é definida (ver o caso 2), e mudar a versão para 1.3. O Maven irá descarregar automaticamente a versão 1.3 e actualizar as definições do projecto. Posteriormente, ao correr mvn eclipse:clean eclipse:eclipse, o Maven vai ainda reconfigurar o Eclipse com a nova versão da dependência. Agora imagine-se que por alguma razão é preciso voltar à versão 1.1 do log4j. Simples. Basta mudar no pom.xml a versão da dependência para 1.1, e o Maven trata do resto.
Mas o que acontece com suas próprias bibliotecas (aliás artefactos) - como são geridas as versões? Sempre que é executado o "mvn install", o maven faz o build ao projecto, vai criar o package correspondente (i.e. Jar, War) e vai usar o número da versão definido no pom.xml para instalar o artefacto compilado no repositório local (ou online). Um repositório não é mais que um género de "base de dados" de bibliotecas gerida automaticamente pelo Maven.
Imagine-se que estamos a implementar o projecto A v1.0, que depende do projecto B v0.5. Temos ainda o projecto C v0.2 que também depende do Projecto B v0.5. Quando se faz o build install do projecto A v1.0, como este depende de B v0.5, o Maven vai primeiro fazer build, (package), e instalar o projecto B v0.5 e só depois o mesmo ao projecto A. Por sua vez, ao fazer build install ao projecto C v0.2, como o artefacto B v0.5 já existe e está instalado, o Maven não recompila o projecto B (excepto se houverem mudanças no código), usando a versão do projecto B v0.5 instalada no repositório.
Agora imaginemos que evoluimos o projecto B, actualizamos a versão para v0.6 no pom.xml. Estas alterações são necessárias ao projecto A, pelo que mudamos também o versão na dependência definida no pom.xml do projecto A. Porém, o projecto C ainda depende da versão v0.5 do projecto B, e não quer as novas alterações. O maven gere isto confortavelmente, pois irá simplesmente usar a versão do artefacto necessária, já previamente instalada no repositório.
Ou seja, elegantemente conseguimos ir evoluindo as versões das nossas bibliotecas "sem termos que nos preocupar" com quebrar a compatibilidade com projectos que dependem das mesmas, visto que o número de versão toma aqui um papel muito importante, facilmente gerido pelo Maven. Na realidade, é tão fácil, que quase nos esquecemos de porque é que era tão complicado lidar com diferentes versões das bibliotecas...!
Esta é uma tarefa aborrecida no Eclipse, para a qual normalmente usamos o Ant. Mas que o Maven gere de uma forma muito simples, e confortável. Relembrando, cada projecto tem um ficheiro pom.xml. Os projectos podem ser organizados em níveis. Por exemplo:
Cada um dos (artefactos) acima possui um pom.xml. Nos pom.xml "parent", adiciona-se:
ProjectA-server
ProjectA-flex-client
Quando o maven é executado no projecto base, irá analisar a configuração dos módulos, e tentar executar primeiro o build em cada um dos módulos definidos, e por sua vez, em cada um dos sub-módulos, recursivamente na ordem correcta de dependência, "instalando-os" no repositório local. Mas claro, é sempre possível indicar ao Maven que queremos compilar só um projecto.
Os ficheiros de projecto (.project) do Eclipse são específicos da máquina em que estão (i.e. um .project configurado no meu computador provavelmente não funcionará no computador do lado), e portanto não é boa prática colocá-los no SCM para partilhar com a equipa. Isto quer dizer que normalmente todos os elementos da equipa têm que criar à mão um novo projecto no Eclipse, configurar as dependências, definir a build path, etc, etc. Por vezes, estas configurações são até um pouco complicadas e demoradas, e algo corre mal, pelo que alguém tem que andar a saltar de estação em estação a ajudar a configurar o ambiente de desenvolvimento. E o pesadelo repete-se sempre que há uma alteração à configuração (novas dependências, novas versões, etc).
Com o Maven, como já foi referido anteriormente, queremos evitar de mexer com nas configurações de projecto do Eclipse. Por outras palavras, o Maven irá, regra geral, tratar desta tarefa automaticamente por nós. Quando escrevemos:
mvn eclipse:eclipse
O Maven irá ler o pom.xml, e usá-lo para criar os ficheiros de configuração do Eclipse para esse projecto, e para todos os sub-módulos desse projecto (ver Caso 5). Depois basta fazer um "Import > Eclipse project" no Eclipse, e já está, pronto a trabalhar. Naturalmente, já tinha referido isto no caso 1, mas voltei a repeti-lo aqui para reforçar o quão útil é este comportamento no desenvolvimento em equipa, especialmente na configuração inicial do ambiente de desenvolvimento de um novo projecto.
Este post podia alongar-se em muitas mais páginas. Como ainda estou a estudar maven, este é o ponto a que já cheguei:
Mas ainda me falta:
Resumindo, qualquer pessoa numa equipa pode executar um único comando que irá:
Interessa também realçar que os ficheiros pom.xml acabam por se tornar em manuais/guias valiosos para novos developers entenderem muito rapidamente a organização de um sistema complexo. Outro facto de realce, é que o plugin m2Eclipse possui um interface gráfico para editar e gerir os ficheiros pom.xml, simplificando em muito a vida na fase de configuração do projecto.
João Saleiro (Webfuel)
"Casting" e verificação de tipos em AS3.0
3
Entry posted on Jan 25
by Rui Silva
Casting e verificação de tipos foram, desde sempre, conceitos centrais a qualquer linguagem de programação, especialmente nas linguagens orientadas a objectos como é o caso do Actionscript.
De facto, conhecer que tipo de objecto estamos a receber como parâmetro ou a capacidade de o "tranformar" noutro tipo de forma a poder passá-lo para chamadas a métodos e atribuí-lo a propriedades de objectos são a chave para um dos conceitos mais importantes da programação orientada a objectos: O Polimorfismo
De acordo com este princípio, qualquer classe que estenda de uma determinada linha de herança (herança de classes ou implementação de interfaces) pode ser utilizada em substituição de qualquer um das suas classes ascendentes. Isto resulta do facto das classes descendentes possuirem precisamente o mesmo interface de programação que as super-classes (implicitamente através dos mecanismos de herança de classes ou através da implementação obrigatória de métodos definidos em interfaces).
O Casting automático
Por vez o casting de tipos ocorre automaticamente. Por exemplo, se atribuirmos a uma propriedade definida como sendo do tipo Object, um objecto do tipo DisplayObject (que é um descendente de Object), este vai ser automaticamente convertido para Object. Por outras palavras, sempre que se utilizar um descendente em vez de um dos seus ascendentes, o objecto é automaticamente convertido para o tipo do ascendente em questão.
Isto, no entanto, não quer dizer que o objecto deixa de ser do tipo original descendente. Apenas que, naquela referência específica como propriedade de uma classe ou argumento de um método, é identificada como sendo do tipo do seu ascendente. E mantém esse tipo sempre que for referenciada através dessa propriedade ou parâmetro.
Isto equivale a dizer que o que determina o tipo de um objecto a ser acedido não é o que foi utilizado na criação dessa instância, mas o tipo com que foi identificada a propriedade ou variável usada para o aceder.
O Casting explícito
Uma vez que a instância do objecto não perde o tipo com que foi criada quando é feito o cast para uma classe ascendente, ela pode ser utilizada sempre que uma classe desse tipo é necessária. É aqui que as coisas começam a ficar interessantes: Se atribuirmos a uma propriedade de um determinado tipo usando uma instância de uma classe desse mesmo tipo mas guardada numa propriedade definida com um tipo ascendente, o compilador do Flash vai queixar-se de incompatibilidades de tipos.
Para se poder fazer isto é necessário fazer o cast da instância para o tipo descendente. Fazer este cast de volta para um tipo que nós já sabemos ser o de uma determinada instância parece ser redundante e pouco lógico, mas se nos lembrarmos que o que determina o tipo não é a instância em si, mas a variável através da qual estamos a aceder, começa a fazer sentido.
Em Actionscript podem ser utilizados duas técnicas distintas para se fazer casting e uma para se fazer verificação de tipos. Para o casting pode-se utilizar a operação de casting ou o operador as.
A operação de cast
Realizar um cast recorrendo a esta técnica é bastante simples. Basta envolver expressão cujo resultado é um determinado objecto com parêntesis e precedê-la com o nome da classe para a qual se quer fazer o cast.
var variavelDestino:DisplayObject = DisplayObject(expressao);
O operador as
Usar o operador as é igualmente simples. Basta colocar o operador entre a expressão e o nome da classe:
var variavelDestino:DisplayObject = expressao as DisplayObject;
Se o cast está apenas a ser feito para aceder a uma determinada propriedade ou método do objecto, pode-se, mesmo assim, utilizar o operador as:
(expressao as DisplayObject).alpha = 0;
No entanto, neste caso particular, recomendo a utilização da operação de cast em vez do operador por razões que explicarei mais adiante.
DisplayObject(expressao).alpha = 0;
Quando se está a fazer o casting é preciso ter em mente que o mesmo pode falhar devido a uma série de razões que estão fora do âmbito deste artigo. É, no entanto, importante não partir do princípio que um cast é sempre bem sucedido e preparar o código para o caso do mesmo falhar.
Diferenças entre a operação de cast e o operador as
Tanto a operação de cast como o operador as, quando executados com sucesso, têm o mesmo resultado, ou seja, devolvem uma referência para a instância alvo como sendo do tipo da classe para a qual está a ser feito o cast.
De facto, os dois apenas divergem no modo como tratam os erros de casting. Assim, a operação de cast lança um erro em tempo de execução do tipo TypeError quando falha o cast, enquanto que o operador as retorna o valor null na mesma situação.
Quando usar um ou o outro?
Se existe certeza plena de que o cast vai ter sucesso, ou se está disposto a envolver cada cast com expressões try catch, ou então não se importa que a aplicação lance erros de tempo de execução a torto e a direito, então pode-se utilizar a operação de cast sem problemas de maior.
Se, por outro lado, prefere poder lidar com os erros de casting de forma elegante sem ter que recorrer ao uso de expressões try catch, deve utilizar o operador as.
Pessoalmente, tendo a favorecer o operador as, na medida em que torna o código mais legível (a operação de cast pode ser confundida com a utilização de construtores de classes e com operações de conversão de tipos) e está mais de acordo com a sintaxe do operador is utilizado para a verificação de tipos que apresentarei de seguida.
A única altura em que prefiro utilizar a operação de cast é quando estou a fazê-lo apenas para aceder a métodos ou propriedades de uma instância que, de outro modo, estariam inacessíveis. Isto deve-se ao facto do operador as devolver um valor null quando o cast falha e a tentativa de aceder a um método ou propriedade de um valor deste tipo resulta num erro de tempo de execução do tipo de referência para null em vez de um erro de tipo. Como boa prática é preferível fazer com que os erros lançados pela aplicação sejam o mais próximos do erro real que ocorreu, sob pena de se perder tempo nas tentativas de o encontrar em sítios errados.
A verificação de tipos
A verificação de tipos pode muito simplesmente ser realizada utilizando o operador is da mesma maneira que o operador as
var teste:Boolean = expressão is DisplayObject;
O operador is devolve um Booleano com a indicação se a instância é uma extensão da classe ou de uma das suas ascendentes ou uma implementação de um determinado interface.
Próximos passos
Seria interessante verificar se existem impactos na performance e consumo de memória quando se utiliza qualquer uma destas técnicas e qual seria a melhor forma de conseguir extrair toda a performance do código escrito.
43 Hot Flex and ActionScript 3.0 APIs, tips and tools for Autumn 2008
Bookmark posted on Nov 13
by PauloMoreira7
Adobe Feeds
Bookmark posted on Nov 12
by PauloMoreira7
www.thefwa.com
Bookmark posted on Nov 12
by PauloMoreira7
Mailing List
Bookmark posted on Nov 11
by Joao Fernandes
AUG Meeting 2 - Foto 5
Photo posted on Nov 11
by Joao Fernandes
RiaPT - Aniversário - Integração de Flash Cs3 com Flex 2 Parte I
Video posted on Nov 07
by Joao Fernandes
FlashCamp 2009
Photo posted on Jul 10
by Joao Fernandes
| Email: |
Wrox
Sponsor posted on Nov 13
by Joao Fernandes
O'Reilly
Sponsor posted on Nov 13
by Joao Fernandes
Peachpit
Sponsor posted on Nov 13
by Joao Fernandes
Flag
Sponsor posted on Nov 03
by Joao Fernandes
Galileu
Sponsor posted on Dec 03
by Joao Fernandes