.NET - Princípios básicos de um projeto de software


A manutenção de uma aplicação é provavelmente mais difícil e trabalhosa e definitivamente menos empolgante do que criar uma aplicação desde o início.

In the beginner’s mind there are many possibilities. In the expert’s mind there are few. (Shunryu Suzuki)

Uma grande parte do tempo de um desenvolvedor é gasta com tarefas relacionadas com a manutenção de software ao invés de tarefas como planejar e codificar uma nova aplicação.

Então seria lógico que ao construir uma aplicação deveríamos ter em mente um projeto robusto que demandasse pouca manutenção.

A seguir vou relacionar alguns dos maiores problemas encontrados em um sistema de software e que poderiam ser evitados :

  1. Rigidez - O sistema foi feito de forma a ser duro de mudar, qualquer alteração provoca uma cascata de operações por todo o sistema;
  2. Fragilidade - O sistema foi feito de forma que qualquer mudança feita o desestabiliza;
  3. Imobilidade - O código foi feito de forma a ser difícil de ser reusado; nenhuma parte do sistema pode ser aproveitada em outro sistema;
  4. Complexidade - O código foi feito com a aplicação de diversos padrões de projeto a um problemas simples;
  5. Repetição de código desnecessária - O mesmo trecho de código esta esparramado por todo o sistema;

Diante desse cenário seria razoável então dedicar uma atenção especial a um atributo do projeto de software durante a sua criação: A manutenibilidade.

Manutenibilidade é uma característica inerente a um projeto de sistema ou produto, e se refere à facilidade, precisão, segurança e economia na  execução de açãoes de manutenção nesse sistema ou produto.
(BLANCHARD, Benjamin. Logistics engineering and management. 4th ed. Englewwod Cliffs: Prentice Hall, 1992. p. 15).

Em engenharia de software,
manutenibilidade é um aspecto da qualidade de software que se refere à facilidade de um software de ser modificado a fim de corrigir defeitos, adequar-se a novos requisitos, aumentar a suportabilidade ou se adequar a um ambiente novo. Tais atividades são conhecidas como a  manutenção de software, assim como definido pela ISO/IEC 9126.
http://pt.wikipedia.org/wiki/Manutenibilidade

Talvez o maior desafio que os arquitetos de software enfrentam hoje em dia é como projetar e implementar uma aplicação que atenda todos os requisitos em sua primeira versão e que depois de sua implementação possa ser estendida para atender outros requisitos de forma simples e a um baixo custo.

A manutenibilidade é um dos atributos fundamentais do projeto de software tratado desde a primeira versão no artigo ISO/IEC 9126 que fornece uma descrição formal da qualidade de software descrevendo um conjunto de características sobre este atributo. Uma versão PDF pode ser obtida em http://www.iso.org.

O grande desafio dos arquitetos é estar focado nos requisitos atuais do projeto durante a fase de desenvolvimento de forma que o mesmo seja flexível o suficiente para suportar futuras alterações e novas implementações sem quebrar o projeto inicial. Neste escopo a manutenibilidade representa o melhor compromisso pois com um alto grau de manutenibilidade no seu código é muito mais fácil obter desempenho, segurança e escalabilidade.

Colocando as coisas dessa forma parece ser simples mas como escrever software que seja fácil de manter ?

Existem alguns princípios básicos de um projeto de software que se forem propriamente aplicados podem tornar o código mais maleável e flexível. Tenha em mente que não estamos pregando que o software será infalível mas que os erros devem ser mais facilmente encontrados e resolvidos.

Big Ball of Mud (BBM) (A grande bola de lama)

A expressão "big ball of mud" (BBM-http://www.laputan.org/mud), a grande bola de lama (em uma tradução livre), indica um anti-padrão para arquitetos e desenvolvedores e refere-se a um software que possui todas as qualidades que resultam em um código difícil de entender, manter e estender.

Um sistema BBM é o resultado de uma combinação de efeitos oriundos das seguintes causas:

A melhor coisa a fazer diante de um situação que nos leva a tratar um sistema BBM talvez seja reescrever a aplicação novamente baseada nos novos conjuntos de requisitos. Mas na prática isso quase nem sempre é viável em termos do tempo e do custo envolvido. Então temos um elefante branco que provavelmente terá uma longa vida.

Quais os sintomas para identificar um sistema BBM ?

Você pode dobrar um pedaço de madeira ?

E o qual o risco que você corre se você insistir em tentar fazer isso ?

Um pedaço de madeira geralmente é dura e rígida e é caracterizada por alguma resistência à deformação. (resistência a mudança)

Quando uma força suficiente é aplicada pode-se causar uma deformação e a deformação torna-se permanente.

Um sistema inflexível tem um o mesmo comportamento.

Infelizmente o BBM ainda parece ser o projeto de software mais produzido nos dias atuais.

O resultado ??

Um código mal definido e mal estruturado produzindo sistemas rígidos que são difíceis de testar , manter , entender, usar , estender, enfim uma grande bola de lama.

Como identificar um projeto BBM ?

Faça uma alteração aqui e o código quebra ali

E o que você acha que acontece com um software "rígido" ?

Um Software 'rígido' caracteriza-se por ser resistente às mudanças. A resistência é medida em termos de regressão. Você faz uma alteração em um módulo, mas os efeitos de sua alteração se propagam em cascata para baixo na lista de módulos dependentes. Como resultado, fica muito difícil prever o quão grande será o impacto feito por qualquer mudança, mesmo a mais simples, e isso fragiliza o software.

Como fragilidade e rigidez andam de mãos dados quando uma alteração feita ocasionar uma quebra de outro módulo por causa de dependências ocultas fica claro que você tem um sistema ruim e que precisa corrigir esta situação o mais rápido possível.

Mais fácil de usar do que reutilizar

Você tem um código que funciona muito bem em um projeto e deseja usá-lo em seu projeto atual mas apenas copiar o código no novo projeto simplesmente não funciona.

Se o código não funciona quando é movido para outro projeto, é por causa da dependências. No entanto, o verdadeiro problema não é apenas dependências, é o número e profundidade de dependências. O risco é que para obter a reutilização de um pedaço de funcionalidade em outro projeto, você terá que importar um conjunto muito maior de funções. Nestes casos geralmente procura se reescrever a funcionalidade novamente do início causando a duplicação de código.

Isso agrega um atributo negativo ao seu projeto de software chamado : imobilidade.

Mais fácil de remendar do que corrigir

Ao aplicar uma mudança de um módulo de software, não é incomum você encontrar mais de uma maneira de fazer isso. Na maioria das vezes, uma forma de fazer as coisas é elegante e coerente com o projeto, mas terrivelmente trabalhosa para implementar por causa de certas restrições. A outra forma é, ao contrário, muito mais fácil e mais rápida de codificar, mas ela é uma espécie de remendo que "cheira mal"(“bad-smells”).

Definido por Martin Fowler e Kent Beck, o termo “bad-smell” (mal cheiro) se refere às características
encontradas nas estruturas de códigos que devem ser refatorados.

Alguns dos “bad-smells” que podem ser encontrado no livro do Martin Fowler (2004) são:

* Código Duplicado;
* Método Longo;
* Classes Grandes;
* Lista de Parâmetros Longa;
* Alteração Divergente;
* Grupos de Dados;
* Hierarquias Paralelas de Herança;
* Classe Ociosa;
* Campo Temporário;
* Cadeias de Mensagens;
* Biblioteca de Classes Incompleta

Aplicar um remendo não é uma situação ideal, porque uma remendo (solução alternativa) pode ser mais fácil de aplicar que a solução certa, e se for assim isso significa que seu código possui muitas dependências desnecessárias entre as classes e que suas classes não são coesas.

Isto agrega um atributo negativo ao seu projeto chamado: viscosidade.

E como evitar um projeto BBM ?

Podemos evitar um projeto BBM aplicando os seguintes princípios:(pelo menos é o que se recomenda fazer)

Diminua o acoplamento

As expressões "acoplamento fraco" ou "acoplamento forte" são comuns em quase todas as discussões sobre projeto de software. O acoplamento entre classes ou subsistemas é uma medida da interconexão entre essas classes ou subsistemas. O acoplamento forte significa que as classes relacionadas precisam conhecer detalhes internos umas das outras, as alterações se propagam pelo sistema, e o sistema é potencialmente mais difícil de entender e manter.

- Acoplamento é o nível de dependência/conhecimento que pode existir entre as classes;
- Uma classe com acoplamento fraco não é dependente de muitas classes para fazer o que ele tem que fazer;
- Uma classe com acoplamento forte depende de muitas outras classes para fazer o seu serviço;
- Uma classe com
acoplamento forte é mais difícil de manter, de entender e de ser reusada;

Resumindo, os objetivos por trás da obtenção de um acoplamento fraco entre classes e módulos são:

Aumente a coesão

Coesão é o nível de integralidade interna de uma classe; (Veja o principio da responsabilidade única - SRP)

- A coesão mede o grau que um classe ou seus métodos fazem sentido, ou seja, quão claro é o entendimento do que a classe ou método possui
- Uma classe com alta coesão possui responsabilidades bem definidas e são difíceis de serem desmembradas em outras classes;
- Uma classe com baixa coesão possui muitas responsabilidades, geralmente que pertencem a outras classes, e podem ser facilmente desmembradas em outras classes;
- Uma classe com
baixa coesão é difícil de entender, manter e reusar;

 Portanto quanto mais forte o acoplamento e mais baixa a coesão de um código mais difícil ele será de entender, manter e ser reusada.

Separe responsabilidades (Separation of Concerns)

O princípio da separação de responsabilidades (ou conceitos ou interesses) (SoC) foi introduzido por Edsger W. Dijkstra em seu artigo "Sobre o papel do pensamento científico" de 1974. Se você está interessado, pode baixar o documento completo em http://www.cs.utexas.edu/users/EWD/ewd04xx/EWD447.PDF.

A separação de interesses é objetivo essencial do processo de decomposição de um problema onde :

A separação de interesses eficiente promove um código de melhor qualidade pois possui:

Sistemas de software consistem de um conjunto de "áreas de interesse" ou responsabilidades distintas como, por exemplo, responsabilidades funcionais (lógica de negócio) e não-funcionais (performance, persistência de dados, logging, autenticação de usuários, segurança, verificação de erros, etc.).

Existem também as responsabilidades relacionadas com o processo de desenvolvimento de software, como clareza de entendimento, facilidade de manutenção, rastreabilidade, simplicidade de evolução do software, etc. (http://www.dextra.com.br/empresa/artigos/aspectprog.htm)

O princípio é simples: fazer com que duas funcionalidades tenham o mínimo de sobreposição.

O princípio trata em como dividir o sistema em responsabilidades distintas e não sobrepostas onde cada recurso que você quer no sistema representa uma funcionalidade e um aspecto do sistema.

O principio sugere que você se concentre em uma responsabilidade especial em um dado momento sem ignorar as outras funcionalidades do sistema. Depois que você atribuir uma funcionalidade a um módulo de software você se concentra na construção desse módulo a partir da perspectiva do módulo.

Separar responsabilidades, implica fazer com que uma aplicação seja modularizada, com o objetivo de resolver um único problema. A separação de responsabilidades, pode também ser aplicada a classes, métodos e componentes. O objetivo e fazer cada parte resolver um único problema.

A eficiência do desenvolvimento aumenta na medida em que conseguimos separar as suas diferentes responsabilidades em módulos estanques. Este princípio é razoavelmente antigo, e a OOP nos trouxe uma importante resposta a ele: a classe como uma dimensão para a decomposição de responsabilidades.
(
http://www.dextra.com.br/empresa/artigos/aspectprog.htm)

Na prática podemos aplicar o princípio em dois níveis:

- nivel conceitual :

- nivel de código :

A separação de conceitos é concretamente alcançada por meio de um código modular e fazendo amplo uso do ocultamento de informações (encapsulamento).Programação modular incentiva o uso de módulos separados para cada característica significativa. Aos Módulos são dados a sua própria interface pública para se comunicar com outros módulos e podem conter pedaços internos de informação para uso privado.

Apenas os membros na interface pública são visíveis para outros módulos. Dados internos ou não são expostos ou são encapsulados e expostos de forma filtrada.

A ocultação de informações é um princípio de projeto em geral que se refere a esconder atrás de uma interface alguns detalhes de implementação de um módulo de software que estão sujeitos a alterações. Desta forma, módulos conectados continuar a ver a mesma interface fixa e não são afetados por alterações.

Separação de interesses é o pilar teórico usado em sistemas com várias camadas (ou apenas de múltiplas camadas)

Você essencialmente consegue a separação de responsabilidades ao isolar dependências e abstraindo-as em interfaces. Isso é chamado baixo acoplamento, ou programação baseada em interface, ou também, o principio da inversão da a dependência. Nomes diferentes, cada um adequado ao seu própria contexto.

Aplique os princípios SOLID

Os princípios SOLID para programação e design orientados a objeto são de autoria de Robert C. Martin (mais conhecido como Uncle Bob) e datam do início de 2000.

A palavra SOLID é um acróstico onde cada letra significa a sigla de um princípio: SRP, OCP, LSP, ISP e DIP.

Os princípios SOLID devem ser aplicados no desenvolvimento de software de forma que o software produzido tenha as seguintes características:

Cada um dos princípios pode ser consultado nos links a seguir:

- OOP - Herança x Composição
-
OOP - O princípio da substituição de Liskov (LSP)
-
OOP - O princípio Open-Closed (OCP)
-
Padrões de Projeto - Os 7 princípios básicos do desenvolvimento de software

Conclusão:

Assim como um arquiteto que projeta uma casa não iria ignorar os códigos de construção que se aplicam ao contexto, um arquiteto de software que trabalha em um contexto orientado a objetos não deve ignorar os princípios de padrão de projetos tais como os princípios SOLID que envolvem o baixo acoplamento, a alta coesão e o princípio das separação de responsabilidades ao projetar um software.

"Quem é o mentiroso, senão aquele que nega que Jesus é o Cristo? Esse mesmo é o anticristo, esse que nega o Pai e o Filho." 1 João 2:22

Referências:


José Carlos Macoratti