decomposição hierárquica

Você nunca deve começar a escrever classes para seu aplicativo imediatamente. Primeiro, ele precisa ser projetado. O design deve terminar com uma arquitetura pensada. E para obter essa arquitetura, você precisa decompor consistentemente o sistema.

A decomposição deve ser realizada hierarquicamente - primeiro, o sistema é dividido em grandes módulos / subsistemas funcionais que descrevem sua operação da maneira mais geral. Em seguida, os módulos resultantes são analisados ​​com mais detalhes e divididos em submódulos ou objetos.

Antes de selecionar objetos, divida o sistema em blocos semânticos básicos, pelo menos mentalmente. Em aplicativos pequenos, isso geralmente é muito fácil de fazer: alguns níveis de hierarquia são suficientes, pois o sistema é primeiro dividido em subsistemas / pacotes e os pacotes são divididos em classes.

decomposição hierárquica

Essa ideia não é tão trivial quanto parece. Por exemplo, qual é a essência de um “padrão de arquitetura” tão comum como Model-View-Controller (MVC)?

Trata-se de separar a apresentação da lógica de negócios . Primeiro, qualquer aplicativo de usuário é dividido em dois módulos - um é responsável por implementar a própria lógica de negócios (Model) e o segundo é responsável por interagir com o usuário (User Interface ou View).

Acontece que os módulos devem interagir de alguma forma, para isso eles adicionam um Controller, cuja tarefa é gerenciar a interação dos módulos. Também na versão móvel (clássica) do MVC, o padrão Observer é adicionado a ele para que o View possa receber eventos do modelo e alterar os dados exibidos em tempo real.

Os módulos típicos de nível superior, obtidos como resultado da primeira divisão do sistema nos maiores componentes, são precisamente:

  • Logíca de negócios;
  • Interface de usuário;
  • Base de dados;
  • Sistema de mensagens;
  • Contêiner de objetos.

A primeira divisão geralmente divide todo o aplicativo em 2-7 (máximo de 10 partes). Se dividirmos em mais partes, haverá um desejo de agrupá-las e obteremos novamente de 2 a 7 módulos de nível superior.

decomposição funcional

A divisão em módulos/subsistemas é melhor feita com base nas tarefas que o sistema resolve . A tarefa principal é dividida em suas subtarefas constituintes, que podem ser resolvidas/executadas de forma autônoma, independentemente umas das outras.

Cada módulo deve ser responsável por resolver alguma subtarefa e executar sua função correspondente . Além da finalidade funcional, o módulo também se caracteriza por um conjunto de dados necessários para que ele desempenhe sua função, ou seja:

Módulo = Função + Dados necessários para executá-lo.

Se a decomposição em módulos for feita corretamente, a interação com outros módulos (responsáveis ​​por outras funções) será mínima. Pode ser, mas sua ausência não deve ser crítica para o seu módulo.

Um módulo não é um pedaço de código arbitrário, mas uma unidade de programa separada funcionalmente significativa e completa (subprograma) que fornece uma solução para uma determinada tarefa e, idealmente, pode funcionar de forma independente ou em outro ambiente e ser reutilizado. O módulo deve ser uma espécie de "integridade capaz de relativa independência no comportamento e no desenvolvimento". (Christopher Alexandre)

Assim, a decomposição competente é baseada, antes de tudo, na análise das funções do sistema e dos dados necessários para executar essas funções. Funções neste caso não são funções de classe e módulos, porque não são objetos. Se você tiver apenas algumas aulas em um módulo, exagerou.

Conectividade forte e fraca

É muito importante não exagerar na modularização. Se você der a um iniciante um aplicativo Spring monolítico e pedir a ele para dividi-lo em módulos, ele colocará cada Spring Bean em um módulo separado e considerará que seu trabalho está concluído. Mas isso não.

O principal critério para a qualidade da decomposição é como os módulos estão focados na resolução de suas tarefas e são independentes.

Isso geralmente é formulado da seguinte forma: "Os módulos obtidos como resultado da decomposição devem ser conjugados ao máximo internamente (alta coesão interna) e minimamente interconectados entre si (baixo acoplamento externo)."

Alta Coesão, alta coesão ou "coesão" dentro do módulo, indica que o módulo está focado na resolução de um problema restrito e não está envolvido na execução de funções heterogêneas ou responsabilidades não relacionadas.

A coesão caracteriza o grau em que as tarefas executadas pelo módulo estão relacionadas entre si.

Uma consequência da Alta Coesão é o Princípio da Responsabilidade Única - o primeiro dos cinco princípios SOLID , segundo o qual qualquer objeto/módulo deve ter apenas uma responsabilidade e não deve haver mais de um motivo para alterá-lo.

Low Coupling , acoplamento solto, significa que os módulos em que o sistema está dividido devem ser, se possível, independentes ou fracamente acoplados entre si. Eles devem ser capazes de interagir, mas ao mesmo tempo saber o mínimo possível um do outro.

Cada módulo não precisa saber como o outro módulo funciona, em que linguagem está escrito e como funciona. Freqüentemente, para organizar a interação de tais módulos, é utilizado um determinado contêiner, no qual esses módulos são carregados.

Com um design adequado, se você alterar um módulo, não precisará editar outros ou essas alterações serão mínimas. Quanto mais solto o acoplamento, mais fácil é escrever/entender/estender/reparar o programa.

Acredita-se que módulos bem projetados devem ter as seguintes propriedades:

  • Integridade e integridade funcional - cada módulo implementa uma função, mas a implementa bem e completamente, o módulo executa independentemente um conjunto completo de operações para implementar sua função.
  • Uma entrada e uma saída - na entrada, o módulo do programa recebe um determinado conjunto de dados iniciais, realiza um processamento significativo e retorna um conjunto de dados de resultado, ou seja, o princípio IPO padrão é implementado - entrada -\u003e processo -\u003e saída.
  • Independência lógica - o resultado do trabalho do módulo do programa depende apenas dos dados iniciais, mas não depende do trabalho de outros módulos.
  • Links fracos de informações com outros módulos - a troca de informações entre os módulos deve ser minimizada, se possível.

É muito difícil para um iniciante entender como reduzir ainda mais a conectividade dos módulos. Em parte, esse conhecimento vem com a experiência, em parte - depois de ler livros inteligentes. Mas é melhor analisar as arquiteturas dos aplicativos existentes.

Composição em vez de herança

A decomposição competente é uma espécie de arte e uma tarefa difícil para a maioria dos programadores. A simplicidade engana aqui, e os erros custam caro.

Acontece que os módulos dedicados são fortemente acoplados entre si e não podem ser desenvolvidos de forma independente. Ou não está claro qual função cada um deles é responsável. Se você encontrar um problema semelhante, provavelmente o particionamento em módulos foi feito incorretamente.

Deve estar sempre claro o papel que cada módulo desempenha . O critério mais confiável de que a decomposição é feita corretamente é se os módulos são sub-rotinas independentes e valiosas que podem ser usadas isoladamente do restante do aplicativo (e, portanto, podem ser reutilizadas).

Ao decompor um sistema, é desejável verificar sua qualidade fazendo a si mesmo as perguntas: "Qual tarefa cada módulo executa?", "Quão fáceis são os módulos para testar?", "É possível usar os módulos sozinhos ou em outro ambiente?" afeta os outros?"

Você precisa tentar manter os módulos o mais autônomos possível . Como mencionado anteriormente, este é um parâmetro chave para a decomposição adequada . Portanto, deve ser realizado de forma que os módulos sejam inicialmente pouco dependentes um do outro. Se você conseguiu, então você é ótimo.

Se não, nem tudo está perdido aqui também. Existem várias técnicas e padrões especiais que permitem minimizar e enfraquecer ainda mais os links entre os subsistemas. Por exemplo, no caso do MVC, o padrão Observer foi utilizado para esse fim, mas outras soluções são possíveis.

Pode-se dizer que as técnicas de desacoplamento constituem o principal “kit de ferramentas do arquiteto”. Basta entender que estamos falando de todos os subsistemas e é preciso enfraquecer a conexão em todos os níveis da hierarquia , ou seja, não só entre as classes, mas também entre os módulos de cada nível hierárquico.