A relevância de se projetar – ou fazer design de – software pode ser explicada pela complexidade crescente dos sistemas de software. Devido a essa complexidade, o risco de se construir um sistema que não alcance seus objetivos é eminente.
Para evitar tal risco, a prática comum de qualquer engenharia para se construir um artefato complexo, um sistema de software complexo em nosso caso, é construí-lo de acordo com um plano. Em outras palavras, projetar o sistema antes de construí-lo. O resultado dessa atividade, também conhecida como de atividade de design, é também chamado de design. O design facilita duas atividades que são essenciais no ciclo de vida de um sistema de software. Primeiro, ele possibilita a avaliação do sistema contra seus objetivos antes mesmo dele ser construído. Dessa maneira, ele aumenta a confiança de que o sistema construído, se de acordo com o design, alcançará seus objetivos. Obviamente, uma vez que nesse ponto há apenas o modelo do sistema – o design –, a avaliação não será completa, mas isso também não quer dizer que ela não ofereça resultados importantes que levem ao sucesso do sistema. Já a outra atividade beneficiada pelo design é a própria construção do sistema, dado que ele também serve como guia para a implementação do software.
A seguir, mostramos um exemplo de quando o design permite a avaliação do software. O Exemplo 1 mostra parte da primeira versão do design de um sistema distribuído de armazenamento, o HBase1 e, através de uma breve avaliação desse design, observamos uma grave limitação do software.
Exemplo 1
O HBase é um sistema de armazenamento distribuído. Isso quer dizer que os dados submetidos a ele não serão guardados em um único servidor, mas em vários. De forma simplificada, o design do HBase define dois tipos de entidades no sistema: o data node, que é o subsistema que armazena os dados, e o master node, que é o subsistema que sabe em quais data nodes os dados foram escritos e podem ser recuperados. Na primeira versão do HBase, só existia um master node que coordenava todos os data nodes. Assim, para recuperar ou escrever dados no HBase, um cliente realizava os seguintes passos: primeiro, o cliente se comunicava com o master node a fim de conseguir, de acordo com uma chave2, o endereço do data node em que ele pode realizar a operação desejada (leitura ou escrita). Em seguida, o master node, que coordena onde os dados devem ficar, retorna o endereço do data node que deveria possuir dados para referida chave. A partir daí, o cliente, já com o endereço, se comunicava diretamente com o data node e realizava a operação desejada (escrita ou leitura).
Se avaliarmos este design, podemos perceber duas características do HBase. A primeira, é que ele não adota o uso de um cliente magro (thin client). Com isso, a implementação e configuração do cliente se torna mais complexa, uma vez que o cliente precisa conhecer o protocolo de escrita e leitura do HBase, além de precisar acessar tanto o master node quanto os data nodes. Isto dificulta o desenvolvimento, a operabilidade e a eventual evolução do software, uma vez que mudanças no protocolo afetam clientes e servidores. Além disso, por possuir apenas um master node, a funcionalidade do HBase fica condicionada à sua disponibilidade. Afinal, se o master node estiver inacessível, nenhum cliente poderá ler ou escrever no sistema, o que o torna um ponto único de falhas.
O que é Design de Software
Para definir design de software, alguns autores o fazem em dois sentidos distintos: quando design de software é usado como produto e quando é usado como processo. Quando usado no primeiro sentido, o termo design de software indica o produto que emerge do ato (ou processo) de projetar um sistema de software e sendo assim algum documento ou outro tipo de representação do desejo do projetista (ou designer). Esse produto é o resultado das decisões do designer para formar uma abstração do sistema que é desejado no mundo real. Existem diversas formas de como representar essa abstração do sistema. Podemos citar, por exemplo, desenhos usando caixas e setas, textos descritivo, ou ainda uso de linguagens ou ferramentas criadas para este propósito, como linguagens de modelagem de software, redes de petri, pseudocódigo, etc. Já quando o termo é usado no segundo sentido, fazer design indica o processo seguido para se obter um projeto. Esse é um processo que faz parte do processo de desenvolvimento e que é orientado aos objetivos do software. Ele deve ser realizado tendo em mente os diversos stakeholders do sistema e deve ser fundamentado no conhecimento do designer sobre o domínio do problema.
A partir da visão de design como artefato, podemos observar que ele deve descrever diversos aspectos do software para que, assim, possibilite sua construção. Entre estes aspectos, estão:
- a estrutura estática do sistema, incluindo a hierarquia de seus módulos;
- a descrição dos dados a serem usados;
- os algoritmos a serem usados;
- o empacotamento do sistema, em termos de como os módulos estão agrupados em unidades de compilação; e
- as interações entre módulos, incluindo as regras de como elas devem acontecer e porque elas acontecem.
Podemos perceber que, apesar dos exemplos anteriores descreverem apenas parte do design de dois sistemas, eles mostram boa parte dos aspectos que esperamos no design de um software.
Por fim, citamos uma definição de design que engloba todos estes aspectos:
- Definição 1: design de software
"É tanto o processo de definição da arquitetura, módulos, interfaces e outras características de um sistema quanto o resultado desse processo.”3
Características de Design de Software
Projetar os diversos aspectos de um sistema de software é um processo trabalhoso. No entanto, pode proporcionar diversos benefícios.
Design de software permite avaliação prévia. Como desenvolver software custa tempo e dinheiro, não parece sensato alguém investir seus recursos no desenvolvimento de um sistema que não soluciona os problemas propostos pelos interessados. Dessa maneira, a avaliação prévia do sistema se torna imprescindível para garantir que ele alcance os objetivos desses interessados. Como o design descreve diversos aspectos que estarão presentes no sistema quando construído, ele permite esse tipo de avaliação. Além disso, fazer o design de um sistema é, geralmente, mais barato que construí-lo.
Exemplo 2
Considerando o sistema do Exemplo 1 e que um de seus objetivos fosse a alta disponibilidade, podemos avaliar que design apresentado não seria a melhor solução para o objetivo proposto. Isso ocorre porque seu design possui um ponto único de falhas, que é uma característica indesejável para sistemas que buscam alta disponibilidade. Note ainda que não foi necessário ter o HBase desenvolvido para percebermos esse problema (na época em que implementava tal design, ele possuía cerca de cem mil linhas de código e alguns anos de desenvolvimento e, portanto, não sendo um software de desenvolvimento trivial), bastou apenas estudarmos seu design.
Design de software estimula modelagem. Ao modelar um sistema, o designer se concentra no domínio do problema, ignorando temporariamente detalhes menos significativos para se alcançar a solução. Isso facilita na separação da complexidade essencial da complexidade acidental do problema. E, como já dito por Fred Brooks em The Mythical Man-Month, essa separação é benéfica para a qualidade final do sistema projetado.
Design de software envolve planejamento. Uma vez que o design serve de guia para a construção do sistema, o designer deve então antecipar o que será necessário para tanto. Esse planejamento ajuda na estimativa dos diversos custos envolvidos no desenvolvimento do sistema. Entre esses custos, podemos citar:
- Quanto tempo durará todo o desenvolvimento,
- Quantos desenvolvedores serão necessários para o módulo A,
- Se comprado, quanto custará o módulo B, e se for implementado,
- Ou qual será o custo total do desenvolvimento do sistema.
Design de software facilita a comunicação, pois contém conhecimento sobre o sistema que pode ser gravado, transmitido e discutido entre os interessados. Um caso bem comum é o de apresentar um sistema a novos membros de um time de desenvolvimento. Informações valiosas, como por exemplo, quais os principais módulos e seus diversos comportamentos, lhes podem ser passadas através do design do sistema antes de mostrá-los o código-fonte. Dessa maneira, essas informações de alto nível de abstração ajudarão a situá-los no código posteriormente. No entanto, o design não serve apenas para os desenvolvedores. Um usuário do sistema pode procurar no design informações de um nível ainda maior de abstração, como quais funções o sistema é capaz de realizar, ou qual o desempenho delas.
Por outro lado, design de software também demanda algumas observações importantes.
O problema a ser resolvido pode não permanecer o mesmo durante todo o processo de design. Ao passo que o design é implementado, o cliente, que é o stakeholder interessado em que o software construído solucione um problema em particular, (1) pode mudar de ideia quanto à natureza do problema; (2) pode ter descrito o problema incorretamente; ou ainda (3) pode decidir que o problema mudou ou mesmo que já fora resolvido enquanto o design é feito. Essas possibilidades não devem ser ignoradas durante o desenvolvimento, uma vez que elas podem ocasionar em perda de tempo e dinheiro durante a fase de design ou ainda ocasionar o fracasso no atendimento das necessidades do cliente.
Há diferenças entre o design e o sistema construído a partir dele. O design de um software é apenas um modelo, do qual o nível de detalhes pode não ser adequado para certos tipos de avaliação. Por sinal, avaliar um design insuficientemente detalhado pode levar a resultados errôneos e, consequentemente, há sistemas que não resolvem os problemas da forma esperada. Isso é comum acontecer, por exemplo, quando por erro do projetista, detalhes importantes para a avaliação não são incluídos no design. O exemplo a seguir ilustra um caso em que a avaliação inadequada resultou em um produto com problemas.
Exemplo 3
Um caso conhecido de produto com falhas por avaliação inadequada é o caso de um sistema de controle de armamento para cruzadores da marinha norte-americana que foi desenvolvido pela empresa Aegis. Depois de desenvolvido, o sistema de armamento foi instalado no cruzador U.S.S. Ticonderoga para o primeiro teste operacional. No entanto, os resultados do teste demonstraram que o sistema errava 63% dos alvos escolhidos devido a falhas no software. Posteriormente, foi descoberto que a avaliação e os testes do software de controle foram realizados numa escala menor do que as condições reais e que, além disso, os casos de teste incluíam uma quantidade de alvos menor que a esperada em campo de batalha.4
Por mais eficaz que um design seja, sua implementação pode não ser. O fato de haver um design bem elaborado para um determinado software não garante que na fase de implementação os desenvolvedores sigam as regras previamente especificadas e que o código produzido reflita fielmente o que foi especificado. Isto é certamente um grande problema na construção de sistemas de software, pois pode acarretar a construção de um produto que não era o esperado, e até mesmo levar ao insucesso em sua construção. Felizmente, na Engenharia de Software existem dois mecanismos que visam diminuir as divergências entre design e implementação. O primeiro mecanismo diz respeito à verificação de software, isto é, verificar se o software foi construído corretamente, se atendeu às especificações do design. Por outro lado, a validação de software está ligada à satisfação do cliente diante do produto, isto é, se o software construído é o desejado, se atende aos requisitos do cliente.







