Fork me on GitHub

Screencast de Introdução a Objective-C – Parte 2

E agora temos a segunda parte do screencast de introdução a Objective-C no ar, nessa segunda parte finalmente chegamos aos componentes do Cocoa e implementamos uma interface básica, usando labels, botões e campos de texto, além de conhecer alguns detalhes importantes como lidar com os teclados e um pouco mais sobre gerenciamento de memória. Mais uma vez, pra acompanhar esse screencast você precisa ter seguido o screencast anterior e ter pelo menos o XCode 4.0.1 instalado no seu Mac ( o código fonte está disponível no GitHub ) :

2 – Introdução a Objective-C – Parte 2 from Maurício Linhares on Vimeo.

Se você não viu o screencast anterior, pode encontrar ele aqui e se você prefere baixar esse screencast e assistir ele quando quiser, é só fazer o download do vídeo aqui.

Os próximos screencasts já estão sendo preparados, então fique de olho aqui no blog, no meu twitter e no vimeo.

Se você quer ir mais fundo em desenvolvimento iOS, alguns livros pra seguir em frente:

Tags: , , ,

Screencast – Introdução a Objective-C – Parte 1

E eis que finalmente surge o primeiro screencast! Demorou mas agora saiu, revitalizando o material de Objective-C e iOS que eu comecei ano passado, resolvi gravar screencasts pra essa atualização em vez de escrever pra ver se funciona melhor e o primeiro vídeo, de introducão a linguagem Objective-C, vocês encontram aí embaixo ( o código fonte está disponível no GitHub ) :

Introdução a Objective-C from Maurício Linhares on Vimeo.

Lembre-se que pra serguir esse tutorial você já deve ter o XCode, pelo menos na versão 4.0.1, instalado na sua máquina.

Comentários, dicas, whatever são todos bem vindos, especialmente porque esse é somente o primeiro vídeo de muitos outros que ainda estão por vir. Os screencasts não vão ser somente sobre Objective-C, já existem alguns sobre Ruby, Scala e JavaScript, mas a primeira leva de vídeos é um material de introdução ao desenvolvimento de aplicações pra iOS, então fiquem de olho aqui no blog, no twitter e na minha conta do Vimeo pra estar sempre sabendo quando atualizações aparecerem.

Se você prefere assistir no conforto do seu computador sem fazer streaming, o arquivo pode ser baixado direto do Vimeo clicando aqui.

A parte 2 desse tutorial está disponível aqui.

Se você quer ir mais fundo em desenvolvimento iOS, alguns livros pra seguir em frente:

Tags: , , , , , ,

Criando um programador

De tempos em tempos surge nas listas de discussão de tecnologia a conversa sobre estágio, como aprender e como se “formar” um programador. Eu tive uma experiência interessante sobre isso e acho que finalmente é a hora de escrever sobre o assunto.

Continue reading “Criando um programador” »

Como ser chutado de uma avaliação pra uma vaga na fase de currículos

Aviso aos navegantes: esse texto foi publicado originalmente em 27/01/2011, a OfficeDrop não está mais recebendo currículos pra essas vagas.

Essa semana anunciamos vagas pra trabalho Home Office pra OfficeDrop:

Recebemos vários currículos, alguns bons e outros inacreditavelmente ruins. Erros crassos na construção do currículo ou completo desleixo com o mesmo fizeram com que vários candidatos fossem desconsiderados simplesmente porque era impossível retirar alguma informação útil do documento digital. O currículo ou continha lixo demais ou era tão direto que era impossível de se adivinhar quais eram as tarefas que a pessoa fazia no trabalho.

Resolvi então que seria uma boa hora pra fazer uma listinha dos erros que nós encontramos pra ficar pra posteridade.

Continue reading “Como ser chutado de uma avaliação pra uma vaga na fase de currículos” »

Tags: ,

Agilidade, snake oil, power balances e a falácia do “nós somos diferentes”

Um companheiro de uma das listas que participo recebeu um email bem interessante, e eu preciso fazer alguns comentários sobre isso.

Continue reading “Agilidade, snake oil, power balances e a falácia do “nós somos diferentes”” »

Meu ambiente de desenvolvimento em 7 itens

Recebi o convite do @cmilfont e agora estou entrando na brincadeira também, vamos lá.

Continue reading “Meu ambiente de desenvolvimento em 7 itens” »

Produzindo um avião com métodos ágeis

Produto final produzido no curso de CSM

Produto final produzido no curso de CSM

Este último sábado foi também o último dia da minha disciplina de Introdução aos Métodos Ágeis e Scrum da especialização em Metodologia para Engenharia de Software na Faculdade iDez. Como projetinho de última aula resolvi roubar a idéia que eu havia visto no curso de Certified Scrum Master que o Michel Goldenberg ministrou em Recife no início do ano.

A idéia básica é que os alunos fariam um produto (que no caso do curso foi um folder com de propaganda) e se utilizariam do Scrum pra produção do mesmo. O trabalho foi simples e exigiu muito mais organização e foco por parte da equipe pra entregar o folder pronto do que trabalho duro propriamente dito.

Muitas idéias diferentes surgiam enquanto estávamos planejando e começando o trabalho, se tivéssemos seguido todas, o produto não teria sido entregue, mas o tempo finito e a comunicação constante da equipe nos mantiveram na linha pra finalizar a tarefa. No fim foi uma ótima forma de cristalizar o conhecimento básico de Scrum que foi passado durante os dois dias de curso e eu sabia que eu não podia terminar essa disciplina de Scrum sem fazer o mesmo com o pessoal.

Será a Embraer?

Coloquei na cabeça que faria a dinâmica, mas faltava a idéia de “o que” produzir. Não queria repetir a idéia dos folders de propaganda, então o que seria um trabalho manual interessante pra colocar o pessoal pra produzir? Aviões, claro!

Na brincadeira, cada equipe ficaria responsável por produzir um avião com o material disponível dentro do tempo estimado de uma hora e meia. Essa hora e meia consistiria tanto no tempo pra planejar a construção do avião (Release e Sprint Planning Meetings) como efetuar a construção em si. Dividi então o tempo nos timeboxes:

  • 20 minutos iniciais – Release Planning – cada equipe tem que fazer o planejamento do release, que vai conter dois sprints e também fazer o planejamento dos dois sprints;
  • Dois sprints de 30 minutos – aqui é onde eles finalmente executam o trabalho, esses 30 minutos são divididos então em 3 blocos de 10 minutos, com um Daily Scrum entre cada um deles.
  • Ao fim de cada sprint a equipe deve fazer uma breve apresentação sobre o que eles conseguiram produzir e depois avaliar o que deu certo ou não durante o trabalho (a retrospectiva do sprint);
  • Terminados os sprints as equipes demonstram o seu produto final pra todos na sala;

A idéia é que o evento todo seja simples, nós fazemos priorização e planning poker durante os 20 minutos iniciais, mas não consideramos velocidade ou fazemos o burndown do projeto. O foco maior é nos timeboxes e no objetivo final que é entregar o avião pronto. Se a equipe percebe que tudo o que eles planejaram não vai ser entregue até o fim do tempo, eles precisam se reorganizar e diminuir o escopo pra poder ter um produto completo ao fim das duas sprints.

Equipe Boeing planejando o trabalho

Equipe Boeing planejando o trabalho

Montando as equipes, ambiente e materiais

O meu caso eram duas equipes com 8 pessoas cada, das 8 pessoas, 1 era o Scrum Master, 1 o Product Owner e os outros eram a equipe de desenvolvimento em si. O Product Owner não trabalha efetivamente, mas ele pode direcionar a equipe e aceitar ou não o trabalho sendo feito.

O Scrum Master também não deve fazer trabalho diretamente, seu serviço é apenas resolver impedimentos na equipe, como falta de material. Eu também andei atrapalhando as equipes conversando besteira, removendo material, ocupando os desenvolvedores com outros trabalhos, pra manter os Scrum Masters ocupados.

O material necessário pra cada equipe é:

  • 2 garrafas de refrigerante de 2 litros (preferenciamente garrafas que não tenham detalhes no formato, como as de Guaraná Antartica);
  • 2 tesouras;
  • 1 estilete;
  • 2 folhas de cartolina guache/grossa;
  • 2 folhas de cartolina fina;
  • 1 conjunto de canetas hidrocor;
  • Baralhos pra planning poker;
  • Post-its e index cards/folhas de resumo;
  • 1 pacote de palitos de churrasco, preferencialmente de bambu;
  • 1 rolo de fita adesiva transparente;
  • 1 régua grande (ao menos 50cm);
  • Os dois produtos finalizados após os 2 sprints

    Os dois produtos finalizados após os 2 sprints

    Lições da primeira vez

    Não levei estiletes, foi bem difícil pro pessoal cortar as garrafas, também levei apenas uma tesoura pra cada grupo e isso foi um problema, já que muita coisa precisava ser cortada, ter duas tesouras teria agilizado muita coisa. Levei apenas um rolo de fita adesiva e ela rodou demais entre as duas equipes, ter um rolo pra cada equipe também teria complicado menos as coisas.

    No fim, acho que o saldo final da brincadeira foi bem positivo, os dois grupos produziram os seus aviões e cada um completamente diferente do outro (e eles estavam lado a lado dentro de sala). As equipes mudaram o escopo e a idéia do que iam produzir baseados no caminho que a “construção” estava tomando (a equipe Boeing só resolveu realmente construir um avião com as duas garrafas no segundo sprint, quando a equipe Teco-Teco já estava com o esqueleto do avião quase todo pronto).

    Dinâmicas, jogos e atividades interativas no geral deveriam ser mais comuns no ensino em tecnologia, ainda está pra surgir uma forma de ensinar que seja mais completa do que envolver os alunos na atividade de verdade. Clique aqui pra ver a galeria de fotos completa com todas as imagens da aula.

    Tags: , ,

    Porque é importante saber como o protocolo HTTP funciona

    É interessante perceber que há tantas pessoas trabalhando escrevendo aplicações web que não entendem o básico sobre a internet e o protocolo HTTP. Você pode encontrar aplicações que demonstram comportamentos bizarros em qualquer lugar, as pessoas simplesmente esquecem de ler as especificações ou dormiram durante as aulas sobre o protocolo HTTP na universidade.

    Um dos casos mais infelizes dessa falta de conhecimento é a “febre do POST”. Todos so formulários na aplicação usam o método POST para se comunicar com o servidor, não importa o que ele está fazendo ou se existem ou não “efeitos colaterais” envolvidos no caso. Simplesmente funciona dessa forma e as pessoas aparentemente não tem nenhum motivo pra não fazer dessa forma. Se você pergunta a alguém, eles vão provavelmente soltar a pérola, “ah, me disseram que o GET tem o limite no tamanho dos parâmetros que você pode mandar, então é melhor prevenir do que remediar”.

    Mas o que é que há de tão mal nisso?

    Se você der uma olhada no RFC do HTTP, vai descrobrir que o método GET é descrito como “seguro” (‘safe’). Seguro, no contexto do HTTP, significa que você deveria ser capaz de fazer vários GETs para uma aplicação web e isso não deveria causar efeitos colaterais, como apagar um cliente ou coisas do gênero, ele não deve causar alterações no recurso que está sendo requisitado, porque toda a idéia do método GET é de que você deveria simplesmente receber uma cópia (bem no estilo copiar-colar) do recurso que está naquela URL específica. Você não está fazendo nada de especial com o recurso, você deveria ser capaz de recebê-lo em qualqure lugar e a qualquer momento que desejasse.

    Mas se você olhar a descrição do método POST vai descobrir que ele é um método “inseguro” (‘unsafe’). Se você manda um POST pra uma URL, você pode estar definitivamente alterando alguma coisa no servidor e causando efeitos colaterais malignos, como libertar a Skynet e os Exterminadores que vão trazer o armagedom para a Terra. Ou você pode simplesmente estar criando um novo recurso naquela aplicação, como esse post de blog que você está lendo.

    A diferença óbvia é que POSTs podem (e normalmente devem) alterar o estado de alguma coisa do lado do servidor, enquanto um GET nunca deveria ser capaz de fazer uma coisa dessas. Comparando com bancos de dados que usam SQL, GETs seriam como “selects” e POSTs como “inserts”. Você já viu um “insert” que retornava uma tabela de resultados ou um “select” que inseria dados no banco? Nem eu :)

    Mas é claro que tudo ainda pode ficar pior. Imagine que você é o dono daquele site maligno que usa apenas POSTs em todos os seus formulários e um desses é um formulário de pesquisa. Usuários vão usá-lo para buscar produtos e adicioná-los as seus carrinhos de compra. Um usuário está interessado em comprar o novo disco do AC/DC, mas ele não tem certeza do nome, então ele simplesmente digita AC/DC e aperta “enter” no teclado.

    Voila!

    Lá, no topo da página, está “Black Ice”, o novo album deles (Já comprou o seu? Deveria!). Ele clica no link e enquanto ele está vendo a página, se lembra que não comprou o album antes desse, “Stiff Upper Lip”. “Vou apertar no botão voltar e procurar por ele na lista de discos do AC/DC”, pensa o incauto usuário e quando ele clica no botão, o navegador mostra uma mensagem interessante:

    “O navegador precisa enviar dados para o servidor para executar esta ação. Você tem certeza que deseja fazer isso?”

    O usuário olha aterrorizado para a mensagem. “O que foi que eu fiz? Será que eles vão me cobrar por isso? Vão me mandar o novo disco da Britney Spears porque eu estou tentando apertar no botão de voltar?”.

    Como protocolo HTTP define, POSTs não são métodos “seguros” e as ferramentas (normalmente os navegadores) devem avisar o usuário de que alguma coisa ruim pode acontecer se eles tentarem dar um POST por acidente em uma página e é exatamente isso que acontece se você tenta clicar no botão voltar após um POST. Nesse exemplo, o usuário não estaria fazendo nada de errado, mas imagine se em vez de estar voltando pra uma página de busca, ele poderia estar voltando para o formulário de “adicionar cliente” e um “voltar” poderia muito bem fazer com que ele re-criasse o cliente no banco de dados, o que não é exatamente a idéia.

    Pior, se você está usando POST em um formulário de busca, os usuários nunca vão poder usar o botão voltar (os mestres da usabilidade dizem que ele é a coisa mais usada nos navegadores) e eles também não vão poder colocar aquela página de resultado nos seus favoritos! Você consegue imaginar algo pior do que isso? Você está evitando que as pessoas possam expressar todo o seu amor pelo seu site postando links pra eles no del.icio.us!

    A idéia é bem simples, se você não está alterando nada no servidor, você deveria sempre usar GETs, seja lá o que for. Eles não quebram o botão voltar ou atualizar, deixam os usuários colocar as páginas requisitadas nos favoritos e não vão fazer com que os navegadores mostrem mensagens assustadoras para os usuários. Se você estiver alterando o estado de alguma coisa no servidor, você deve usar POST (e os outros métodos do HTTP que são definidos como “inseguros”, como PUT e DELETE), requisições que usam GET NUNCA deveriam alterar coisas no servidor (sabe aquele link que você fez que apaga um registro no banco de dados? Foi uma péssima idéia!).

    E antes que eu me esqueça, depois de um POST com sucesso você deve sempre REDIRECIONAR o usuário para a página de sucesso, nunca, por motivo nenhum do universo, mostre a página de sucesso como resultado do POST. Ao redirecionar o usuário após um POST com sucesso você evita que ele reenvie os dados usando um “atualizar” ou clicando no botão voltar do navegador.

    PS: Tradução de “Why learning HTTP does matter”, prometo que eu vou traduzir mais posts de lá, é só ter tempo o suficiente, de qualquer forma, se você sabe inglês, pode ler lá antes :)

    Tags: ,

    Como tratar os seus testes?

    Já percebeu o quanto se fala em qualidade e testes de software nos últimos tempos? Testar software hoje não é mais apenas sentar o seu usuário (vulgo “cliente”) na frente de uma tela rodando o seu sistema pra que ele possa validar se as funcionalidades estão implementadas a contento. A algum o uso de testes unitários, de integração e funcionais se tornaram um fato corriqueiro para equipes que trabalham com software e prezam pela mínima qualidade (até porque qualidade não é apenas falta de bugs) e facilidade de se fazer alterações sem quebrar o sistema todo.

    No caminhar da escrita de testes, os desenvolvedores aprenderam muitas coisas, sendo uma delas que o teste que você escreve já é o primeiro cliente do seu código. Quando você está escrevendo um teste pra um método ou função do seu programa, você está escrevendo também o primeiro código que vai fazer o uso dessa funcionalidade e vai poder perceber se é fácil ou difícil fazer uso desse método, se existem muitas dependências e se é necessário carregar estado demais para fazer com que o resultado final aconteça.

    Você poderia, inclusive, escrever o teste antes do código que vai ser testado para guiar a sua implementação através do teste, assim você já escreve a sua funcionalidade partindo de um cliente real, que é o próprio teste, e não da sua imaginação, que acha que uma coisa deve ser de um jeito ou de outro. Com desenvolvimento orientado a testes você consegue caminhar mais rápido e escrever código que vai realmente ser utilizado, não apenas o que você “acha” que deveria escrever porque alguém pode vir algum dia a utilizar.

    Com esse primeiro “cliente” do seu código você já pode perceber falhas na sua abstração e até mesmo na forma que você pensava que a funcionalidade deveria ser implementada. Você consegue descobrir falhas no seu design, no modo que você pensou que a funcionalidade deveria ser implementada e essa é uma característica que vai levar os nossos testes a um novo patamar, onde eles não vão mais ser testes.

    Ora, mas se os meus testes não são testes, eles são o que?

    Quando nós pensamos em um “teste”, o que vem a mente é alguma coisa que você faz a um objeto pra descobrir se ele “funciona” ou não. Normalmente você faz testes em um objeto que já está ou acredita-se que esteja “pronto”, você normalmente não testa objetos incompletos ou que já sejam conhecidamente falhos, porque você sabe que eles vão falhar de uma forma ou outra. E, principalmente, você não testa para melhorar, apenas para verificar.

    No mundo do software você não precisa ter acesso ao que vai ser testado antes de escrever o teste, você pode já escrever o código do teste antes mesmo de ter a funcionalidade implementada, no seu teste você especificaria o que você espera passar como entrada e o que você espera receber na saída. O seu teste, neste momento, deixa de ser apenas um teste e se transforma em uma especificação, ele descreve como alguma coisa no sistema deve se comportar através de um conjunto finito de entradas e saídas (porque você não tem tempo infinito pra testar).

    Ao deixar de encarar os testes como simples validadores de que o seu código funciona e assumir que eles definem as características do programa, você dá um passo além não apenas na questão de garantir a qualidade como também na própria documentação do sistema e gerência do projeto. Se você tem diversas “estórias” (ou “casos de uso”) pra serem implementados no sistema, eles se transformariam em diversas especificações do que o sistema faz e você teria, a cada especificação que passasse, a indicação de que alguma coisa está sendo produzida, você ganha de brinde, além das vantagens de ter software testado, um medidor de funcionalidades embutido.

    A questão de deixar de tratar testes apenas como validadores do código chegou ao ponto de que até mesmo as ferramentas de testes unitários estão começando a ser suplantadas por ferramentas de desenvolvimento direcionado a comportamentos (ou Behaviour Driven Development – BDD).

    É possível encontrar uma pequena definição sobre BDD na Wikipedia:

    “Behavior Driven Development (or BDD) is a software development technique that questions the behavior of an application before and during the development process. Created in response to the failings the founders perceived in Test Driven Development, Behavior Driven Development addresses requirements and specification in a way that is more textual than its predecessor. By asking questions such as “What should this application do?” or “What should this part do?” developers can identify gaps in their understanding of the problem domain and talk to their peers or domain experts to find the answers. By focusing on the behavior of applications, developers try to create a common language that’s shared by all stakeholders: management, users, developers, project management and domain experts.”

    A idéia é, além de tornar os testes, que agora são especificações, a documentação natural e a fonte de informações sobre o que o sistema faz e como faz. Cada especificação diz que “o sistema deveria fazer X” e se a especificação passa, é porque o sistema realmente faz “X”. Isso facilita a comunicação dentro da equipe, pois agora eles não se perguntam se o teste “Z” passou, mas se a funcionalidade “Z” foi implementada e também vai facilitar ainda mais para os especialistas de domínio que podem simplesmente chegar pra equipe e explicar o que o sistema deve fazer e as especificações garantindo que ele faz vão ser escritas.

    Deixar de pensar em testes como validadores e passar a vê-los como definições facilita a comunicação entre todos os envolvidos no projeto, pois agora eles tendem a falar uma mesma língua (a “linguagem ubíqua” tanto pregada pelos defensores do Domain Driven Design), já que as funcionalidades do sistema vão ser expressas através de especificações definidas pelo especialista do domínio e a equipe de desenvolvimento.

    Vejamos um simples exemplo de especificação utilizando o framework RSpec, que é uma ferramenta de BDD para Ruby:

    it 'Should not allow a not logged in user send an upload' do
        post :prepare
        response.should redirect_to( :action => 'login' )
    end

    A nossa especificação diz que “Usuários não logados não devem poder fazer um upload”, então o código dela deve comprovar que é isso que acontece e é exatamente isso que ele faz, ele primeiro faz uma requisição usando o método HTTP “POST” (na primeira linha da especificação) e depois garante que a resposta seja um “redirect” para a “ação” login. Mesmo que você não entenda Ruby ou RSpec, é fácil entender o que esse código quer dizer, as afirmações são claras e é essa facilidade de entendimento que faz com que BDD seja realmente uma ótima maneira de se expressar as funcionalidades que um software deve ter.

    Então, se você continua vendo testes apenas como um modo de validar o seu sistema, está na hora de começar a mudar o pensamento e aproveitar melhor esses testes tranformando-os nas especificações de o que o seu sistema faz. Você iria escrever os testes de qualquer maneira, não vai custar nem um pouco a mais, vai?

    Referências:

    Tags: , , , , ,

    De many-to-many para many-to-one com JPA

    Introdução

    Relacionamentos em bancos de dados dificilmente são tão simples quanto parecem, especialmente quando você começa a utilizar relacionamentos “N:N” (muitos-para-muitos), esse tipo de relacionamento, um extremamente comum no mundo real, normalmente é um pouco mais complicado quando é abstraído para um banco de dados relacional. Neste material você vai entender como transformar um relacionamento N:N em um N:1 utilizando JPA.

    Para continuar você deve ter conhecimentos da biblioteca de persistência do Java, a JPA, e do framwework Hibernate. Os exemplos mostrados são, na verdade, testes do JUnit, então ter conhecimento conhecimento básico de o que ele é e para que serve vão lhe ajudar a entender melhor os exemplos. Você pode fazer o download do projeto de exemplo aqui, o projeto é um projeto comum do Eclipse mas também é um projeto do Maven.

    Relacionamento “muitos-para-muitos”

    Quando estamos iniciando a análise dos nossos sistemas orientados a objetos e começando a montar o banco de dados que vai dar suporte e persistência a esse modelo, é comum que encontremos relacionamentos do tipo “muitos-pra-muitos” (many-to-many – N:N). Nesse tipo de relacionameto entre duas tabelas, nós criamos uma tabela de ligação, que contém sempre um par de colunas, onde cada uma aponta para uma chave primária de uma das tabelas que fazem parte do relacionamento, como no diagrama do nosso exemplo abaixo:

    Imagem 1 – Diagrama de exemplo com many-to-many
    many-to-many-example.jpeg

    O nosso relacionamento demonstra que um cliente pode ter vários produtos, algo que poderia ser utilizado em um sistema de inventário.

    Vejamos o código em Java utilizando JPA que exemplifica esse diagrama:

    Listagem 1 – Persistivel.java

    package alinhavado;
    
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.MappedSuperclass;
    
    @MappedSuperclass
    public class Persistivel {
    	@Id
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	private Long id;
    	// métodos get/set
    }

    A nossa primeira classe não é exatamente uma classe do sistema, mas uma classe básica para evitar a repetição de código desnecessária, nela nós declaramos o código que define a propriedade “id” que é o identificador de cada linha das tabelas no banco de dados e também definimos o tipo de gerador para a coluna como sendo “identity”, que auto-incrementa automaticamente o valor da coluna.

    Como essa classe não representa uma tabela ou uma entidade no sistema mas nós queremos que as suas propriedades existam para as suas subclasses, nós definimos ela com a annotation “@MappedSuperclass”, assim, qualquer objeto que herdar dela vai automaticamente herdar os campos que foram definidos com as anotações do JPA, portanto nenhum dos objetos do nosso exemplo precisa definir uma propriedade “id”, ela já foi definida na superclasse. Usando @MappedSuperclass você evita repetição de código desnecessária par seus objetos e ainda garante que todos vão ter as mesmas propriedades e comportamentos, graças a herança.

    Listagem 2 – Cliente.java

    package alinhavado;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.OneToMany;
    
    @Entity
    @Table(name=”clientes”)
    public class Cliente extends Persistivel {
    	private String nome;
    	@ManyToMany(cascade=CascadeType.ALL)
    	@JoinTable(name="clientes_produtos",
    			   joinColumns=  @JoinColumn( name = "cliente_id"),
    			   inverseJoinColumns= @JoinColumn(name = "produto_id") )
    	private Set produtos = new HashSet();
    	// métodos get/set
    }

    A classe Cliente herda de Persistivel (e consequentemente tem como @Id a propriedade “id” de Persistivel) além de ter uma coleção de produtos relacionados a ela na forma de um relacionamento N:N.

    Listagem 3 – Produto.java

    package alinhavado;
    
    import javax.persistence.Entity;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="produtos")
    public class Produto extends Persistivel {
    	private String nome;
    	//métodos get/set
    }

    O nosso objeto Produto também herda de Persistivel e não tem nada além de uma única propriedade, ele não precisa estar relacionado diretamente nem a Cliente nem a Item. Quando estiver fazendo o mapeamento de relacionamentos, evite utilizar relacionamentos bidirecionais, só faça com que os dois lados de um relacionamento se conheçam se isso for estritamente necessário para que o código esteja correto, no nosso exemplo não interessa a um produto saber quais clientes o tem, interessa apenas ao cliente saber quais produtos ele possui, então o melhor a se fazer é não colocar o relacionamento também em Produto.

    Transformando um “muitos-para-muitos” em um “muitos-para-um”

    Continuando com o nosso exemplo, também é comum que conforme o nosso conhecimento sobre o problema em si aumente e a modelagem evolua, esses relacionamentos N:N comecem a tomar corpo de forma que eles deixam de ser apenas um simples relacionamento e se transformam em uma entidade própria, com suas próprias informações e ciclo de vida. Relacionamentos N:N são, no fim, incomuns em sistemas reais, porque na maior parte das vezes nós temos que guardar informações sobre o relacionamento em si e simplesmente colocar novos atributos na tabela de qualquer forma torna o modelo do banco de dados difícil de se lidar e complica o modelo de objetos que vai agir sobre ele. Vejamos um teste de exemplo do código que faria uso dessa modelagem:

    Listagem 4 – Teste do relacionamento many-to-many

    Cliente cliente = new Cliente();
    cliente.setNome( "José" );
    
    Produto produto = new Produto();
    produto.setNome("Camisa de banda");
    
    cliente.getProdutos().add(produto);
    
    EntityManager manager = HibernateLoader.createEntityManager();
    
    manager.getTransaction().begin();
    manager.persist( cliente );
    manager.getTransaction().commit();
    
    Assert.assertTrue(
    	 mensagem( "clientes" ) ,
    	 contarClientes(manager) > quantidadeDeClientes );
    
    Assert.assertTrue(
    	mensagem( "produtos" ) ,
    	contarProdutos(manager) > quantidadeDeProdutos );
    manager.close();
    
    manager = HibernateLoader.createEntityManager();
    Cliente clienteDoBanco = manager.find( Cliente.class , cliente.getId());
    Assert.assertTrue(
    	"A quantidade de produtos do cliente deve ser maior que zero",
    clienteDoBanco.getProdutos().size() > 0 );
    manager.close();

    Testar o código que se escreve é não apenas normal, como também obrigatório pra que se consiga software de qualidade nos dias de hoje, por isso o nosso exemplo é um teste escrito utilizando a biblioteca de testes JUnit. O código cria um Cliente, um Produto e relaciona o produto ao cliente, após isso nós começamos a testar as funcionalidades implementadas, primeiro nós testamos se a quantidade de clientes e produtos no banco de dados se alterou (os métodos “contarProdutos()“ e “contarClientes()”, “mensagem()” são métodos utilitários da nossa classe de testes que você pode conferir nos arquivos desse tutorial, a classe HibernateLoader é apenas uma classe utilitária criada no exemplo para criar os EntityManagers), após garantir que as quantidades foram alteradas, nós vemos se o produto realmente foi relacionado ao cliente. Para fazer esse último teste, nós criamos um novo EntityManager, isso foi necessário porque algumas implementações da JPA (como o Hibernate) mantém os objetos em um cache no próprio EntityManager, portanto se eu tentasse carregar o Cliente com o mesmo EntityManager que o salvou ele simplesmente me retornaria o objeto “cliente” que estava no seu cache em vez de fazer uma nova consulta no banco de dados.

    Tomando como base o nosso exemplo anterior, digamos que agora nós precisemos saber exatamente qual a quantidade de um produto específico um cliente tem, com o nosso diagrama anterior nós precisaríamos fazer uma contagem dos produtos relacionados ao cliente específico, o que é possível mas pouco prático, o melhor seria se o próprio relacionamento entre produtos e clientes já trouxesse esse relacionamento, dessa forma nós não precisaríamos ter produtos repetidos no relacionamento como também não seria necessário fazer contagens manuais, no próprio relacionamento a contagem já estaria feita. Vejamos como esse diagrama ficaria agora:

    Imagem 2 – Diagrama de exemplo com many-to-one
    many-to-one-example.jpeg

    Agora nós não temos apenas uma tabela que liga os dois objetos, mas uma entidade própria, que tem seus próprios atributos e representação dentro do sistema. O nosso item representa o relacionamento entre as tabelas clientes e produtos, além de conter informações que caracterizam o relacionamento, que no nosso caso é a quantidade de produtos que o cliente tem. A tabela de relacionamento “clientes_produtos” não precisa mais existir, pois a nova tabela “itens” já faz o trabalho dela. Vejamos agora como ficariam os códigos para esse nosso novo modelo:

    Listagem 5 – Novo Cliente.java

    package alinhavado;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.JoinColumn;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="clientes")
    public class Cliente extends Persistivel {
    	private String nome;
    	@OneToMany(mappedBy="cliente", cascade=CascadeType.ALL)
    	private Set items = new HashSet();
    	// métodos get/set
    }

    O nosso cliente agora não mais se relaciona diretamente com os produtos, agora ele se relaciona com os itens, que por fim vão ser o relacionamento com os produtos. E já que falamos neles, vejamos a nossa classe Item:

    Listagem 6 – Item.java

    package alinhavado;
    
    import javax.persistence.CascadeType;
    import javax.persistence.Entity;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    @Entity
    @Table(name="itens")
    public class Item extends Persistivel {
    	private Integer quantidade;
    	@ManyToOne(cascade=CascadeType.ALL)
    	@JoinColumn(name="produto_id")
    	private Produto produto;
    	@ManyToOne(cascade=CascadeType.ALL)
    	@JoinColumn(name="cliente_id")
    	private Cliente cliente;
    	// métodos get/set
    }

    É na classe Item que reside agora o nosso relacionamento, ela contém uma referência para um Cliente e também para um Produto, além de guardar a quantidade de produtos que esse Item representa. Vejamos o exemplo de código que mostra esse relacionamento sendo utilizado:

    Listagem 7 – Exemplo do relacionamento many-to-one

    Cliente cliente = new Cliente();
    cliente.setNome( "José" );
    
    Produto produto = new Produto();
    produto.setNome("Camisa de banda");
    
    Item item = new Item();
    item.setQuantidade( 10 );
    
    item.setCliente(cliente);
    item.setProduto(produto);
    
    EntityManager manager = HibernateLoader.createEntityManager();
    manager.getTransaction().begin();
    manager.persist( item );
    manager.getTransaction().commit();
    
    Assert.assertTrue(
    	 mensagem( "clientes" ) ,
    	 contarClientes(manager) > quantidadeDeClientes );
    Assert.assertTrue(
    	mensagem( "produtos" ) ,
    	contarProdutos(manager) > quantidadeDeProdutos );
    Assert.assertTrue(
    	mensagem( "itens" ) ,
    	contarProdutos(manager) > quantidadeDeItens );
    
    manager.close();
    
    manager = HibernateLoader.createEntityManager();
    Cliente clienteDoBanco = manager.find( Cliente.class, cliente.getId());
    Assert.assertTrue(
    	"A quantidade de itens deve ser maior do que zero",
    	clienteDoBanco.getItems().size() > 0);
    manager.close();

    Como você pode perceber, as diferenças do código são pequenas, nós criamos um Cliente, um Produto e em vez de simplesmente relacionar os dois, nós criamos um novo objeto, o Item, que guarda uma referência para o Cliente e outra para o Produto, além disso ele também conta com uma propriedade, a quantidade. Seguindo no teste nós validamos que agora existem mais clientes e produtos que antes, além de ver se o item foi realmente relacionado ao cliente em questão no último teste.

    Conclusão

    Relacionamentos N:N podem ser transformados de forma simples em relacionamentos N:1 quando você precisa guardar informações sobre a relação em si, você não deve, em momento algum, criar uma nova coluna em uma tabela de ligação e continuar tratando ela como sendo apenas uma tabela de ligação, se o relacionamento começar a ter propriedades próprias, é porque ele não é mais apenas um relacionamento, mas uma entidade real do seu sistema e deve começar a ser tratado como tal.

    Referencias

    Documentação oficial do Hibernate. Disponível em: http://hibernate.org/, acesso em 30/12/2007.

    Tags: , ,