Estudantes e praticantes de Engenharia de Software geralmente tem dúvidas sobre como modelar um sistema de forma efetiva.
Atualmente, a abordagem mais recomendada é a Orientada a Objetos, preferencialmente usando UML, já que essas técnicas são amplamente reconhecidas e propagadas.
O problema é que muita confusão ocorre por falta de conhecimento sobre os objetivos da modelagem de sistemas, as fases de um projeto de desenvolvimento de software e os diferentes níveis de modelagem possíveis. Por exemplo: quais classes devem ser incluídas num Diagrama de Classes? Devemos colocar as classes do framework? Como identificar as classes necessárias em um sistema?
O ponto de partida é não misturar a Análise do problema com o Design (projeto) da solução ou com a Implementação tecnológica.
Em resumo, vamos analisar nos tópicos abaixo como aplicar a modelagem orientada a objetos em diferentes fases de um projeto, tendo em mente alcançar objetivos concretos com a modelagem, além das diferentes aplicações dos modelos utilizados.
Análise
O analista é o profissional responsável por identificar um problema a ser resolvido ou necessidade a ser atendida e elicitar os requisitos para a criação de uma solução.
O conjunto de requisitos define o que o sistema deve fazer para atender às necessidades identificadas.
Baseando-se nos requisitos, o analista continua o processo de Análise identificando em alto nível de quais funcionalidades o sistema deverá possuir para atender aos requisitos.
Uma solução comum para mapear cada funcionalidade é através de Casos de Uso (não confundir com o Diagrama da UML). Um Caso de Uso é uma espécie de passo-a-passo da interação entre usuário e sistema, embora esse conceito possa variar um pouco. Além disso, geralmente ele descreve as pré-condições necessárias para a correta execução e as pós-condições, que são os resultados da ação realizada.
Note que ainda estamos em alto nível e nada aqui tem a ver com a solução tecnológica.
Continuando, o analista treinado em Orientação a Objetos e UML irá modelar o conhecimento sobre o domínio e o problema utilizando os diagramas adequados, que geralmente são: Diagrama de Caso de Uso, Diagrama de Atividades, Diagrama de Classes e Diagrama de Estados.
O Diagrama de Caso de Uso é uma representação visual simples das interações do sistema com o mundo externo. Os atores que interagem com o sistema são representações de usuários, outros sistemas ou qualquer entidade externa ao sistema que se comunique com o mesmo. Este diagrama não exclui a necessidade de mapear em detalhes os casos de uso conforme mencionado anteriormente.
O Diagrama de Atividades representa os passos do caso de uso numa espécie de fluxograma, incluindo bifurcações de cenários alternativos, cenários de erro, etc. Nem todos os cenários precisam ser representados no mesmo diagrama.
O Diagrama de Classes, neste estágio de um projeto, deve incluir apenas as classes de domínio, sem qualquer referência à tecnologia que será usada na implementação. Poderíamos chamar este diagrama de Diagrama de Classes de Domínio. A função do diagrama é representar as entidades necessárias e o relacionamento entre elas. Em suma, é a forma moderna e orientada a objetos do Diagrama de Entidade-Relacionamento (DER), embora ambos possam ser usados concomitantemente. No entanto, o DER geralmente é associado à modelagem estruturada.
O Diagrama de Estados é usado para as entidades do sistema que seguem um fluxo de estados. Por exemplo, uma parcela pode estar “em aberto”, “liquidada”, “em atraso”, “em prejuízo”. Este diagrama representa os estados e como ocorrem as transições entre eles.
Com tudo isso, o analista pode validar a solução verificando se as classes e os casos de uso atendem aos requisitos iniciais. Por exemplo, se houver um requisito de que “o gerente poderá extrair um relatório com o total de produtos vendidos no mês”, o analista deve olhar se a classe Produto
possui um relacionamento “navegável” com Venda
e ItemVenda
e se é possível extrair a informação de totalização das vendas de forma lógica. Ele também pode adicionar os métodos e atributos importantes às classes de modo a atender aos requisitos.
Design (Projeto)
Com base em todas essas informações, entram em ação os arquitetos e desenvolvedores para propor uma solução tecnológica para o problema. Isso não necessariamente vem em sequência, muito pode ocorrer em paralelo.
Os projetistas técnicos poderão criar vários outros diagramas para representar o que será implementado. O Design pode ser feito de forma agnóstica, isto é, sem considerar quais tecnologias, frameworks e bibliotecas serão usadas. No entanto, creio que é mais produtivo modelar a divisão de componentes e classes já pensando um pouco na implementação, de forma a não gerar mais um gap de informação.
Os diagramas mais relevantes e geralmente usados são:
- Diagrama de Componentes: representa a divisão em alto nível dos componentes principais do sistema. A divisão não representa a estrutura de pastas dos arquivos do projetos, mas é uma divisão lógica de responsabilidades.
- Diagrama de Deployment: uma representação do ambiente onde o sistema será executado, incluindo servidores, bancos de dados, replicação, proxies, etc.
- Diagrama de Sequência: para uma determinada ação no sistema, este diagrama representa a interação entre diversos objetos através das chamadas executadas e do retorno, permitindo visualizar a sequência de chamadas no decorrer tempo.
Note que cada um dos diagramas citados podem ser produzidos para vários casos diferentes. Quando falamos em Diagrama de Classes ou Diagrama de Componentes, não há necessariamente um único diagrama que representa o sistema como um todo. A representação pode ser feita em níveis diferentes, por exemplo, um mostrando os componentes gerais do sistema e outros diagramas mostrando a estrutura interna de cada componente individualmente. A representação também pode ser feita em contextos diferentes, por exemplo, vários diagramas para representar apenas o necessário para uma funcionalidade importante, ignorando classes e pacotes não relevantes naquele contexto.
Implementação
A implementação deve seguir o que foi definido no Design, porém não significa que cada método, classe, pacote e componente deve ser mapeado um-para-um no projeto “físico”, em seus arquivos e estrutura de diretórios.
O programador deve ter a liberdade de encontrar a melhor solução para atender ao que foi solicitado com a estrutura que ele desejar. Seria péssimo, do ponto de vista de boas práticas, impor cada detalhe do que deve ser implementado. Se isso fosse possível, não precisaríamos de programadores, mas de um gerador de código.
Construindo a ponte
Ao estudar com atenção as “fases” (entre aspas porque não são uma sequência linear) de um projeto de desenvolvimento de software, é possível notar que existe um grande salto (gap) entre cada uma delas. Uma analogia comumente usada nos livros de Engenharia de Software consiste em construir uma ponte entre o que o cliente precisa e a solução tecnológica.
Ainda hoje, a Engenharia de Software é uma disciplina um tanto imatura. Não temos uma forma padronizada de construção como a Civil ou Elétrica para nos apoiar. A validade de um Modelo de Análise, um Modelo de Design ou da solução implementada depende quase exclusivamente de fatores humanos, como a capacidade de comunicação e entendimento dos analistas, além da capacidade técnica dos desenvolvedores.
Não existem regras definitivas sobre como e em qual nível modelar, assim como não há regras sobre como traduzir uma necessidade em um requisito, um requisito em um modelo e um modelo numa implementação.
A UML foi um grande avanço, mas os diversos diagramas sempre variam em nível de detalhe, abrangência e muitos outros fatores de projeto para projeto, de equipe para equipe e de indivíduo para indivíduo.
Princípios de modelagem
Mesmo com as últimas afirmações acima, não quero ser pessimista. Embora não haja uma resposta definitiva para a modelagem de sistemas, existem alguns princípios que podem nos guiar:
- Coloque a comunicação em primeiro lugar. O objetivo de um diagrama é comunicar informação e não simplesmente ser um espelho do código. Se um diagrama não comunica algo útil, não perca tempo com ele. Considere sua equipe e o seu projeto e faça os diagramas que forem relevantes com os detalhes relevantes para que as pessoas saibam o que estão fazendo. O seu time consegue se reunir numa mesa e discutir um diagrama, rabiscando-o e usando-o como base para a conversa?
- Não faça diagramas de tecnologias específicas. Se alguém quiser saber como Servlets, Rails ou Django funcionam, é melhor comprar um livro. Você só vai confundir as pessoas. Já vi muitos diagramas por aí que nada mais são do que o modelo MVC com nomes diferentes.
- Verifique se o diagrama atende os requisitos. O seu diagrama deve ser útil não só para entender o que deve ser feito, mas também para validar se a sua solução atende ao que o cliente precisa. Faça testes mentais lógicos, olhando para as classes, métodos e relacionamentos, verificando se elas tem motivo de estarem ali, se para um certo cenários você consegue extrair os dados necessários, etc.
Como mencionei algumas vezes, a Análise, o Design e a implementação provavelmente serão feitas muitas vezes durante o ciclo de desenvolvimento. Não espere ter tudo certo no começo. Investir tempo demais em detalhamento é ruim, vários autores já alertaram.
Identificando as classes necessárias
Existe um método para identificação de classes de domínio em potencial que consiste na análise de um texto à procura de substantivos. Esta técnica pode ser útil quando você não tem ideia do que está fazendo e quer algumas ideias iniciais. No entanto, é péssima porque em muitos lugares é ensinada como uma forma “burra” de extrair informação.
Volta e meia ouço alguém com a velha ideia de criar um interpretador mágico, um tipo de Inteligência Artificial, capaz de gerar um sistema com base num texto descrevendo as necessidades do usuário. Devaneios à parte, é melhor nos concentrarmos no que é real hoje.
Aliás, é comum usarmos a palavra “extrair” ou “levantar” indevidamente. Quando criamos um sistema, não extraímos ou levantamos os requisitos e as classes necessárias como se elas já existissem ali, ocultas de alguma forma.
Quanto aos requisitos, o termo “elicitar” é mais adequado, com o sentido de descobrir e descrever. Quanto às classes, nós simplesmente decidimos de forma espúria quais delas o sistema deverá conter de modo a atender às necessidades. Se verificarmos que elas não atendem aos requisitos, nós as modificamos para que o façam.
Tudo isso trata-se de um processo criativo e não de um processo de extração como se faz com matéria prima. Por “criativo”, não pense em arte pós-moderna, mas num processo criativo metódico e até científico.
Minha recomendação é identificar, através dos requisitos, quais dados são necessários para que o sistema funcione, assim como o relacionamento entre elos. Já dizem os DBAs: os dados são o coração do sistema. Assim conseguimos as classes que representam as entidades necessárias.
Depois, com base nas funcionalidades que o sistema deve ter, pode-se definir classes que serão responsáveis por tratar essas funcionalidades.
Considerações finais
A Engenharia de Software ainda possui muitos desafios pela frente para se tornar realmente uma engenharia no sentido completo da palavra.
Muitos hoje (inclusive eu) consideram-se mais artesãos tecnológicos do que engenheiros propriamente ditos. Isso é bom em certo sentido, mas também abre muitas brechas para as “artes abstratas”. 😉
Enfim, não há uma resposta definitiva para o sucesso na modelagem de um sistema, mas espero ter exemplificado bem um caminho que aprendi ao longo de alguns anos estudando e refletindo sobre como desenvolver software adequadamente, não apenas do ponto de vista técnico.
Este artigo foi baseado na minha resposta no StackOverflow em Português!