Abordarei o tema do título reunindo pontos importantes e históricos, de forma rápida e concisa.
Foi em um ambiente primitivo que Edsger Wybe Dijkstra fez suas grandes descobertas para a programação estruturada. Dijkstra é muito conhecido também por seu algoritmo que soluciona o problema do caminho mais curto, o algoritmo de Dijkstra, mas esse não será o tema deste artigo.
Logo no início de sua carreira na programação, Dijkstra reconheceu que a programação em si era difícil e os programadores da época não eram muito competentes. Um pequeno detalhe despercebido resultava em programas que pareciam funcionar, mas falhavam das maneiras mais surpreendentes.
A solução de Dijkstra foi aplicar a disciplina matemática da prova. Sua proposta era construir uma hierarquia Euclidiana de postulados, teoremas, corolários e lemas. Dijkstra acreditava que os programadores poderiam usar essa hierarquia como os matemáticos, ou seja, os programadores usariam estruturas comprovadas e as integrariam com código, que então eles provaram estar correto.
Para que isso funcionasse, evidentemente, Dijkstra percebeu que precisaria demonstrar uma técnica voltada para a escrita de provas básicas de algoritmos simples. Como logo descobriu, isso era bastante desafiador.
Durante sua investigação, Dijkstra constatou que certos usos de declarações goto evitavam que os módulos fossem decompostos recursivamente em unidades cada vez menores, o que afastava o uso da abordagem dividir-e-conquistar, necessária para a obtenção de provas razoáveis. No entanto, outros usos de goto não apresentavam esse problema. Dijkstra percebeu que esses “bons” usos de goto correspondiam a estruturas de controle simples de seleção e iteração, como if/then/else e do/while. Os módulos que usavam apenas esses tipos de estruturas de controle poderiam ser recursivamente subdivididos em unidades comprováveis.
Dijkstra sabia que essas estruturas de controle, quando combinadas com uma execução sequencial, eram especiais. Elas haviam sido identificadas dois anos antes por Bohm e Jacopini, responsáveis por provar que todos os programas podem ser construídos a partir de apenas três estruturas: sequência, seleção e iteração. Essa descoberta foi extraordinária: as estruturas de controle que tornavam um módulo comprovável correspondiam ao mesmo conjunto mínimo de estruturas de controle a partir das quais todos os programas podem ser construídos. Assim nasceu a programação estruturada.
Dijkstra demonstrou que as declarações sequenciais podiam ser comprovadas por meio de enumeração simples. A técnica traçava matematicamente as entradas da sequência até as saídas da sequência. Essa abordagem não era diferente de qualquer prova matemática normal.
Para lidar com a seleção, Dijkstra utilizou a reaplicação da enumeração. Cada caminho na seleção era enumerado. Se ambos os caminhos produzissem resultados matemáticos adequados ao final, a prova era sólida.
A iteração era um pouco diferente. Para comprovar uma iteração, Dijkstra teve que usar a indução. Ele provou o caso para 1 por enumeração. Em seguida, provou o caso de que, se N era presumido como correto, N + 1 estava correto, novamente, por enumeração. Dijkstra também comprovou os critérios de início e fim da iteração por enumeração.
Essas provas eram trabalhosas e complexas, mas eram provas. Com o desenvolvimento delas, a ideia de que uma hierarquia euclidiana de teoremas poderia ser construída parecia possível.
A programação estruturada permite que os módulos sejam decompostos recursivamente em unidades comprováveis, o que, por sua vez, significa que os módulos podem ser decompostos funcionalmente. Ou seja, você pode pegar uma declaração de um problema de larga escala e decompor em funções de alto nível. Cada uma dessas funções pode então ser decomposta em funções de níveis mais baixos até o infinito. Além disso, cada função decomposta pode ser representada por meio de estruturas de controle restritas da programação estruturada.
Mas as provas nunca vieram. A hierarquia euclidiana dos teoremas nunca foi criada; no fim, o sonho de Dijkstra definhou e morreu. Hoje em dia, poucos programadores acreditam que as provas formais sejam uma maneira adequada de produzir software de alta qualidade.
A programação estruturada nos força a decompor um programa recursivamente em um conjunto de pequenas funções comprováveis. A partir daí, podemos realizar testes para tentar provar que essas pequenas funções comprováveis estão incorretas. Se esses testes falharem em comprovar a inexatidão, consideramos as funções como suficientemente corretas para nossos propósitos.
Por fim, a programação estruturada é essa habilidade de criar unidades de programação menores que podem ser testadas; ela em si já não é mais tão utilizada, mas ainda consideramos a decomposição funcional como uma de nossas melhores práticas. Os arquitetos de software sempre tendem a desenvolver módulos, componentes e serviços que sejam facilmente testáveis. Para isso, fazem uso de disciplinas restritivas similares à programação estruturada, embora em um nível muito mais alto.
Um exemplo de programação estruturada com orientação a objetos, ambas abordando o tema de decomposição funcional.
Trecho, ideias e conceitos retirados do livro “Clean Architecture: A Craftsman’s Guide to Software Structure and Design (Robert C. Martin Series)”