O que é Design Modular em programação ?
Existem diversas formas pelas quais podemos construir uma aplicação e uma delas é subdividir um sistema em pequenas partes. Um design que segue esses princípios é chamado design modular.
Em design modular esses pequenos componentes são chamadas módulos. Podemos entendê-los de várias formas: classes, subsistemas, serviços, APIs, microsserviços.
No mundo ideal (irreal) os módulos não devem possuir qualquer dependência com outros. Infelizmente o mundo ideal é inalcançável. Módulos chamam outros módulos e de uma forma ou de outra são construídos para trabalharem juntos.
Por exemplo, os parâmetros de uma função criam uma dependência desta para o código que a chama. Se em algum momento os parâmetros mudarem, todas as chamadas a essa assinatura precisarão ser modificadas.
Em design modular o objetivo é minimizar a dependência entre módulos. E para gerenciar dependências podemos dizer que módulos contém duas partes: interface e implementação.
Basicamente a interface apresenta o que módulo pode fazer sem dizer como, escondendo a complexidade existente. E a implementação é a concretização das promessas feitas pela interface.
Um módulo bem escrito e pensado possui interface simples mesmo que sua funcionalidade seja complexa, ou seja, mantém a simplicidade para seus usuários.
As medidas de tamanho da interface e suas respectivas funcionalidades codificadas na implementação juntas poderiam descrever a “profundidade de um módulo”.
Na imagem acima, pode ser visto dois módulos diferentes. O primeiro módulo(a esquerda) é considerado profundo (deep module) e o segundo definido como “raso” (shallow module).
Módulos profundos são aqueles que proveem muitas funcionalidades através de interfaces simples. Em termos de complexidade de sistemas o custo de um módulo é a sua interface. Assim, quanto mais grande ela é mais complexidade impõe a um sistema.
Um bom exemplo de módulo profundo é o mecanismo de I/O provido pelo sistema operacional Linux. As cinco operações abaixo são as únicas necessárias para fazer chamadas de sistema para I/O do SO.
Módulos rasos são aqueles que possuem interfaces relativamente complexas em comparação com a funcionalidade que fornecem. Com isso, ao invés de trazer benefícios em seu uso trazem complexidade adicional ao sistema.
Um bom (mau) exemplo de módulo raso é a API de IO da linguagem Java. Veja o código abaixo:
Os objetos fileStream
e bufferedStream
nunca são usados pois FileInputStream
não possibilita escrita de dados. BufferedInputStream
só tem a função de adicionar buffering ao fileStream
. Por fim, só a partir de ObjectInputStream
é possível trabalhar lendo e escrevendo dados.
Este cenário mostra uma certa complexidade descenecessária na configuração do uso das interfaces da biblioteca. Se por acaso no desenvolvimento de uma funcionalidade BufferedInputStream
não for criado o processo de buffering não será habilitado e o I/O ficará lento.
Conclusão
Utilizar design modular possibilita construir partes de um sistema de forma mais simples e independente.
Módulos são compostos por interface e implementação.
Um bom módulo é aquele que é profundo, isto é, possui uma interface simples mas relevantes funcionalidades. Essa abordagem possibilita esconder complexidade do resto do sistema e faz com que usuários precisem entender apenas a abstração da interface e nenhum outro detalhe de sua implementação.
“O Código é o projeto.”
Este texto foi inspirado no quarto capítulo do livro “A Philosophy of Software Design”.´
Referências