Desenvolvimento de Aplicações J2EE com UML e Rational Rose À medida que as empresas procuram desenvolver software mais rapidamente, com maior previsibilidade e com maior qualidade, a plataforma Java 2 Enterprise Edition (J2EE) emerge como um standard para o desenvolvimento de aplicações empresariais. Além de fornecer um paradigma completo para o desenvolvimento de aplicações empresariais, o J2EE também ajuda a unir várias tecnologias herdadas do passado. Os aspectos chave para o sucesso com o J2EE são os mesmos que para qualquer outra plataforma de software complexa: comunicação eficiente dos requisitos, decisões correctas em termos de análise e desenho, identificação das melhores opções de implementação. As organizações que seguem as melhores práticas da modelação visual aceites pela indústria são capazes de desenvolver software de forma mais rápida e de construir sistemas com maior qualidade. O que é a Unified Modeling Language (UML)? A UML é um standard do Object Management Group (OMG) desde finais de 1997 e uma linguagem gráfica para a modelação e desenvolvimento de sistemas de software. Fornece suporte de modelação e visualização para todas as fases do desenvolvimento de software, desde a análise dos requisitos até à especificação, passando ainda pela construção e pela implementação. A ideia central que está por detrás da utilização da UML para a modelação visual consiste na identificação dos detalhes significativos sobre um sistema – por exemplo, se os requisitos para o projecto são compreendidos de forma clara, se é desenvolvida a arquitectura da solução, ou se uma dada implementação é identificada e construída de forma clara. Isto requer uma notação rica para a modelação visual de sistemas de software. A UML fornece essa notação para os blocos básicos em construção e disponibiliza formas de expressar relações complexas entre esses blocos. Tais relações são expressas sob a forma de diagramas UML. Compreender os requisitos Os projectos fracassam frequentemente porque os requisitos não foram bem compreendidos ou comunicados. Na realidade, isto não é assim tão surpreendente, sobretudo se considerarmos que qualquer linguagem – escrita ou oral – é ambígua e imprecisa por natureza. Desta forma, podemos aplicar a modelação de casos de utilização (use cases) UML para desenvolver um modelo preciso daquilo que é requerido ao sistema e, seguidamente, utilizar os casos de utilização como base orientadora para outros aspectos do desenvolvimento do sistema empresarial. Na prática, um caso de utilização age como o fio que liga as pérolas de um colar. Os casos de utilização estabelecem a ponte entre o utilizador final e os requisitos de um sistema. Podem, portanto, ser utilizados para estabelecer o acompanhamento (traceability) entre os requisitos funcionais e a própria implementação do sistema. Os casos de utilização servem ainda como pontos de ligação para o documento de casos de utilização onde estão os detalhes dos requisitos. A Figura 1 mostra um diagrama parcial de casos de utilização para uma loja de CDs online, resultante da transformação dos requisitos escritos e verbais para a funcionalidade esperada em casos de utilização. Neste caso, é imediatamente óbvio que o comprador (representado pela figura de um indivíduo, designado por actor na UML) pode utilizar o sistema de quatro formas (representadas através de elipses, designadas como Casos de Utilização na UML). Figura 1. Um diagrama simples de casos de utilização. Por sua vez, cada caso de utilização é elaborado por meio de um ou mais cenários, normalmente através de diagramas de sequência. Evidentemente, nesta fase inicial da recolha e análise dos requisitos, os diagramas de sequência são relativamente simples e poderão estar incompletos. Um exemplo deste tipo de diagramas de sequência é apresentado na Figura 2. Figura 2. Um diagrama de sequência a ilustrar o caso de utilização checkout. Desenhar uma solução O passo seguinte – Análise do Caso de Utilização – fornece uma definição inicial e de alto nível sobre a forma como é que os elementos internos interagem para satisfazer os requisitos funcionais do sistema e como estão relacionados estaticamente entre si. Esta actividade pode envolver muito trabalho de teste e erro até serem criadas soluções satisfatórias. As “analysis classes” (classes de análise) – para as quais os comportamentos são frequentemente descritos de forma abstracta utilizando linguagem natural – constituem uma ferramenta útil para ser utilizada durante este tipo de análise. As classes de análise não estão normalmente implementadas no software, embora possam estar. Em vez disso, são refinadas mais tarde (durante o processo de desenho global) em classes de desenho e subsistemas definidos de forma precisa. Isto começa com a elaboração dos diagramas de sequência, de forma a revelarem os mecanismos internos do sistema. Em vez de mostrar a interacção entre os actores e um sistema monolítico, o sistema é dividido em objectos de nível de análise (analysis level objects). As responsabilidades do sistema são divididas pelos objectos de nível de análise para se alcançar um diagrama de sequência mais pormenorizado. São utilizados três tipos de objectos de análise. Objectos Boundary. Os objectos boundary representam todas as interacções entre os mecanismos internos do sistema e aquilo que os rodeia. Estão aqui incluídas a interacção com os utilizadores através de uma interface gráfica, as interacções com outros actores (nomeadamente aqueles que representam outros sistemas), as comunicações com dispositivos, etc. Os objectos boundary servem para isolar e proteger o resto do sistema de preocupações externas. De uma forma geral, cada interacção entre um caso de utilização e um actor é representada num objecto boundary. Objectos Entity. Os objectos entity representam informação com significância para o sistema. São normalmente persistentes e têm uma duração alargada. O seu propósito principal consiste em representar e gerir informação dentro do sistema. Os conceitos chave dentro de um sistema manifestam-se como objectos entidade no modelo. Objectos Control. Os objectos control são utilizados para modelar comportamento dentro do sistema. Estes objectos não implementam necessariamente o comportamento. Em vez disso, podem funcionar com outros objectos para realizarem o comportamento do caso de utilização. A ideia é separar o comportamento da informação subjacente associada ao modelo, fazendo com que seja mais fácil lidar posteriormente com alterações em ambas as partes. A UML disponibiliza a noção de estereótipo, representada sob a forma de texto entre aspas duplas, para distinguir entre diferentes tipos de classes. No Rational Rose podem-se criar facilmente classes de análise através da alteração do campo de estereótipo de classe para <<boundary>>, <<entity>>e <<control>>, respectivamente. Isto pode ser depois utilizado como base para a criação de diagramas de nível de análise. A Figura 3 mostra uma versão actualizada do diagrama de sequência para o caso de utilização checkout – desta vez com o sistema decomposto em objectos de análise. A figura utiliza a representação icónica para os objectos fronteira, controlo e entidade (círculo com um T, círculo com uma seta e círculo com uma linha tangencial, respectivamente). Figura 3. Diagrama de sequência refinado com objectos de análise. As classes participam frequentemente de vários casos de utilização e é importante compreender as suas relações estáticas para assegurar consistência ao longo do sistema. O diagrama de classes UML é útil para identificar as relações estáticas entre diferentes elementos estruturais. O primeiro passo consiste em identificar e colocar num diagrama de classes todas as classes que participam num caso de utilização. Já se distribuiu o comportamento do caso de utilização aos objectos, pelo que é um exercício relativamente simples criar operações de análise para as responsabilidades atribuídas a cada uma. Convém sublinhar que se trata de operações de análise, o que significa que estas operações precisarão provavelmente de evoluir à medida que continuamos com o nosso trabalho de análise e desenho. O Rational Rose permite-nos definir facilmente novas operações na classe de análise a partir de diagramas de sequência. Para isso, basta seleccionar a mensagem existente e escolher <new operation> no menu de contexto (como mostra a Figura 3). Se já tivermos definido operações numa classe, podemos simplesmente seleccionar a operação existente a partir da lista. Isto é típico da abordagem utilizada no Rational Rose para melhorar a produtividade dos utilizadores e assegurar a consistência – e consequentemente a qualidade – ao longo de todo o modelo. Outras capacidades igualmente úteis incluem a possibilidade de interrogar o modelo onde as classes e as mensagens estejam por resolver (por exemplo, não representação para as classes reais ou operações no modelo). Outro aspecto de análise de cada classe individual consiste em identificar atributos para a classe. Os atributos representam informação que pode ser pedida à classe por outras, ou que pode ser pedida pela própria classe cumprir as suas responsabilidades. Nesta fase da análise, é apropriado identificar atributos como tipos genéricos – por exemplo, número, sequência (string), etc. A identificação das relações entre as classes completa o diagrama de classes para o caso de utilização. As relações em que estamos especialmente interessados nesta fase são as de associação, dependência e herança. Depois da análise de todos os casos de utilização e da criação dos diagramas de classes para cada caso de utilização, é altura de combinar as várias classes de análise para chegar a um modelo de análise unificado. Esta é uma actividade importante, na medida em que se pretende chegar a um conjunto mínimo de classes e evitar redundâncias desnecessárias no modelo de análise final. A tarefa chave nesta fase anda em torno da identificação de classes que possam estar duplicadas ao longo dos casos de utilização ou camufladas por ligeiras variações. Por exemplo, as classes de controlo que tiverem um comportamento similar ou representarem o mesmo conceito ao longo de casos de utilização deverão ser fundidas. As classes de entidade que tiverem os mesmos atributos também deverão ser fundidas e o seu comportamento combinado numa única classe. A Figura 4 mostra um diagrama de classes de nível de análise preliminar para os casos de utilização identificados na Figura 1. Como estamos interessados principalmente nas relações entre as classes, utilizámos as capacidades de filtragem do Rational Rose para filtrar os detalhes das classes individuais através da não verificação do Format>Show todos os atributos e do Format>Show todas as operações. Figura 4. Diagrama de classes de nível de análise preliminar. Implementação do software Apesar do modelo de análise nos poder ajudar a desenvolver uma base sólida para resolver um problema, continua a estar muito longe de uma implementação. Durante o desenho, temos que ter em conta restrições e requisitos adicionais impostos à nossa aplicação pela tecnologia subjacente. Como tal, precisamos de tentar desenhar a solução para a implementação óptima. Por exemplo, se estivermos a construir uma aplicação online que requer uma aplicação cliente, a tecnologia a utilizar poderá ser diferente das escolhas que seriam feitas para uma aplicação baseada na Web com uma funcionalidade idêntica. Ainda para efeitos de exemplo, vamos partir do pressuposto que estamos a construir uma aplicação baseada na Web. A implementação de uma aplicação deste tipo exige uma abordagem bem pensada. O modelo de análise ajuda neste caso, na medida em que fornece o ponto de partida para determinar a forma como as diferentes tecnologias J2EE se adequam à solução. As classes <<control>> adequam-se bem às Servlets Java ou aos Enterprise JavaBean (EJB) Session Beans. Esta abordagem adequa-se bem ao modelo de implementação J2EE (por níveis, ou tiered) e à “Model 2” Reference Architecture da Sun. O Rational Rose fornece uma interface simplificada para o desenvolvimento de servlets e de EJB Session Beans. A Figura 5 mostra a caixa de diálogo para a criação de uma servlet. Figura 5. Caixa de diálogo para a definição de uma servlet. O Rational Rose também fornece uma interface conveniente para a criação de EJBs, apesar de um EJB envolver múltiplas interfaces e classes. A caixa de diálogo para a criação de EJBs é apresentada na Figura 6. Neste caso específico, a caixa de diálogo mostra as especificações necessárias para criar uma Session Bean sem estado (stateless). Figura 6. Criação de uma EJB Session Bean. A Session EJB resultante é apresentada na Figura 7. Esta baseia-se na modelação UML para o perfil EJB que está a ser desenvolvido no JSR-26 sob os auspícios do Sun Java Community Process. Mostra relações entre os vários elementos que constituem um EJB, nomeadamente as interfaces home e remote e a classe de implementação EJB. Uma vez que o EJB não implementa realmente as interfaces home e remote (estas são implementadas por objectos gerados automaticamente pelo utilitário de implementação), a relação não é um reconhecimento, mas antes <<EJBRealizeHome>> e <<EJBRealizeRemote>>, respectivamente. A dependência entre as interfaces home e remote mostra que a interface home instancia a interface remote. Figura 7. Uma Session Bean sem estado. As classes <<boundary>> adequam-se mais ou menos às JSP, às páginas HTML e aos formulários. Se estivermos a desenvolver uma aplicação tradicional baseada em cliente, também se adequa mais ou menos a uma caixa de diálogo de aplicação cliente. Utilizamos as JSP como as construtoras de tudo aquilo que é apresentado às entidades que interagem com o sistema. Uma vez que uma JSP tem realmente dois aspectos – nomeadamente apresentação cliente e comportamento do lado do servidor, é modelada como consistindo numa Client Page e numa Server Page, com um estereótipo especial <<build>> na relação. A Figura 8 mostra um exemplo. Figura 8. JSP como uma página servidor e cliente. A criação de uma JSP é ainda mais simples do que a de um EJB e é efectuada através do menu Web Modeler>New>Server Page do menu de contexto do browser, como mostra a Figura 9. Figura 9. Criação de uma JavaServer Page no Rational Rose. As classes <<entity>> – como Catalog, Order e Customer – são boas candidatas para Entity Beans. Estes últimos são criados utilizando a caixa de diálogo apresentada na Figura 6, destinada à criação de Session Beans. Uma técnica comum utilizada nas aplicações J2EE consiste na utilização de JavaBeans para passar informação entre as servlets e as JSP. Isto é realizado facilmente no Rational Rose através da criação de atributos numa classe Java e da especificação dos atributos para propriedades (utilizando a caixa de diálogo de especificação de atributos apresentada na Figura 10. Convém sublinhar que isto também pode ser efectuado numa base global, através de uma especificação de utilizador. Figura 10. Especificação de um atributo como proprietário. A criação de JSPs, servlets, JavaBeans e EJBs é útil sobretudo no contexto de um modelo de implementação global. O Rational Rose permite-nos modelar facilmente as relações e realizar os detalhes básicos, não só das JSPs, servlets, EJBs e JavaBeans, mas também das páginas HTML e dos Formulários envolvidos na implementação. Esses detalhes podem depois ser remetidos para quem desenvolve a apresentação a fim de se conseguir um maior refinamento, mantendo a compatibilidade com a lógica da aplicação. A Figura 11 mostra um diagrama de classe parcial que apresenta a implementação com as diferentes tecnologias envolvidas na loja de CDs online. O diagrama foi pensado para mostrar classes e a forma como se adequam à apresentação, lógica de negócio e níveis de dados. Desta forma, as páginas clientes estão à esquerda, a servlet controladora está no meio e os Entity Beans estão à direita. Figura 11. Modelo de implementação parcial para a loja de CDs online. Este diagrama mostra apenas algumas das classes necessárias para os casos de utilização “checkout” e “ver detalhes de CD”. Para ler o diagrama seguindo um cenário simples de checkout, vamos partir do princípio que acabámos a nossa navegação e seleccionámos alguns CDs que queremos comprar. Seleccionamos então a opção “checkout” na página principal (topo à esquerda). Esta acção invoca o controlador MainServlet, como é mostrado através da associação estereotipada <<link>>. A MainServlet obtém os detalhes da encomenda do Order EJB, constrói um Order JavaBean, especifica-o como um atributo para a sessão e encaminha o pedido (como identificado através do estereótipo <<forward>>) para a Checkout JSP. Esta última utiliza o Order JavaBean – como se vê pelo estereótipo <<Use Bean>> na associação entre a JSP e a Order JavaBean – para criar a página Checkout_Client e a apresentar ao utilizador/comprador. Obviamente, descorámos alguns detalhes. Por exemplo, num projecto real, utilizaríamos provavelmente um carrinho de compras para efectuar o acompanhamento dos itens. Por outro lado, as responsabilidades de controlo poderiam ser mais distribuídas, em vez de residirem numa única MainServlet monolítica. O aspecto principal a reter neste caso tem a ver com o facto da UML ser uma poderosa ferramenta para desenhar e desenvolver aplicações J2EE complexas. Trabalhar com a implementação O Rational Rose dá uma ajuda em tudo isto, permitindo a criação de JSPs, JavaBeans, HTML, servlets e código EJB directamente a partir deste diagrama. Por exemplo, uma associação <<include>> entre duas JSPs (não apresentada no diagrama), resulta automaticamente em: <%@include file=“header.jsp”%> na JSP apropriada. De forma análoga, o estereótipo <<Use Bean>> resulta em: <jsp:useBean id=“cd” class=“com.rational.cdshop.util.CD” scope=“session”> na JSP que utilize o JavaBean CD. Do lado do EJB, além de gerar código para todos os três tipos de EJBs identificados no EJB 2.0 (bem como EJBs compatíveis com EJB 1.1), o Rational Rose disponibiliza várias funcionalidades para simplificar o desenvolvimento de EJBs. Por exemplo, um dos aspectos entediantes do desenvolvimento de EJBs é a necessidade de codificar os métodos na interface e na classe de implementação. O Rational Rose fornece assim uma opção de menu que trata desses detalhes, bastando para tal o clique num botão. Também fornece uma opção de menu “check and repair” para verificar se o EJB que definimos é de facto legal (por exemplo, se os métodos remotos na interface remota correspondem aos seus correspondentes na classe de implementação EJB). Se não corresponderem, a ferramenta oferece-se para resolver os problemas por nós. Outra poderosa capacidade relacionada com os EJB proposta pelo Rational Rose é a Rational Quality Architect (RQA), para o desenho dos testes dos EJBs. Por exemplo, podemos utilizar a RQA para o teste unitário dos EJBs. Também podemos utilizar os diagramas de sequência que definimos no Rose para o teste de múltiplos EJBs. De igual modo, podemos utilizar a RQA para gerar chamadas de atenção (stubs) para aquelas alturas em que temos uma dependência num componente de software que ainda não está pronto. Para assegurar que podemos trabalhar com a implementação de acordo com as nossas condições, sem nos preocuparmos com alterações ao modelo UML e vice-versa, o Rational Rose fornece um editor de código (incluído) com opções de sincronização configuráveis pelo utilizador. Por exemplo, podemos optar por manter a sincronização activa permanentemente. Neste caso, o modelo UML no Rose será actualizado automaticamente sempre que actualizarmos o nosso código fonte e guardarmos essa alteração. Algumas vezes, poderemos querer apenas experimentar para ver como é que as coisas funcionam, sem causar qualquer impacto no nosso modelo. Nestes casos, podemos desactivar a sincronização de forma global ou por classe. Não existe contestação ao facto de que quando se trata de implementação e de código, precisamos de trabalhar com um IDE robusto, como o Forte da Sun para Java ou o Borland JBuilder. O Rational Rose fornece uma estreita integração e autosincronização com os principais IDEs, pelo que será possível continuar com o IDE que preferirmos e tirar partido ao mesmo tempo do Rational Rose para a modelação UML e para o desenvolvimento de aplicações J2EE. Mais sobre UML e J2EE Neste artigo abordámos apenas de forma superficial a utilização da UML para a modelação e desenvolvimento de aplicações J2EE. Por exemplo, podemos utilizar os diagramas de actividade UML para modelar a gestão das sessões pelas várias entidades envolvidas na sessão. Outro desafio consiste na comunicação da sequência adequada das chamadas de operação esperadas por uma sessão EJB. Um diagrama de sequência pode identificar um único cenário, pelo que precisamos de vários diagramas de sequência para expressar toda a gama de cenários suportados pelo componente. Há quem tente utilizar um diagrama de sequência com controlo e declarações. No entanto, isto só conduz a diagramas de sequência complexos e difíceis. Por outro lado, os diagramas de estado UML (statechart diagrams) fornecem uma poderosa capacidade para a modelação e comunicação deste aspecto. Podemos depois validar um diagrama de sequência relativamente ao diagrama de estado, verificando se o componente suporta a forma como estamos a tentar utilizá-lo. Existe uma linha muito ténue entre o desenvolvimento de software que desempenha o seu trabalho e o desenvolvimento de software que não desempenha o seu trabalho. Podemos melhorar significativamente as nossas hipóteses de desenvolver software escalável, fácil de manter e durável se utilizarmos a UML para compreender os requisitos e efectuar análises e desenho de forma adequada, bem como desenvolver uma solução baseada em princípios com provas dadas e na implementação das melhores práticas da indústria. Baseado no artigo “Developing J2EE Applications with the UML and Rational Rose”, de Khawar Ahmed, membro da Rational Rose eBusiness Products Team (Rational/IBM). Tel: (+351) 239 497 230 / 2 • Fax: (+351) 239 497 231 E-mail: [email protected] • Internet: www.engenharia-software.com