SkillsTecnológicas
Menu
Conteúdo da trilha

Por que dividir um algoritmo em partes

Organize algoritmos extensos em partes com responsabilidades, entradas e resultados claros, preservando o comportamento e facilitando mudanças.

Conteúdo 34 de 39

Um mecanismo emaranhado desenhado a lápis atravessa um divisor e reaparece organizado em quatro módulos de madeira conectados

Dividir reduz o que precisa ser entendido de uma vez

Dividir um algoritmo em partes significa agrupar passos relacionados sob responsabilidades claras. Em vez de acompanhar simultaneamente entrada, validação, cálculo, regras especiais e apresentação, o leitor pode compreender um bloco de cada vez e depois observar como eles colaboram.

Essa organização é diferente de decompor um problema. A decomposição identifica os subproblemas necessários para resolver uma necessidade. Aqui partimos de uma solução já compreendida — possivelmente até funcionando — e melhoramos suas fronteiras internas.

Uma boa divisão procura responder:

  • qual resultado esta parte produz;
  • quais dados ela precisa conhecer;
  • quais decisões podem permanecer escondidas dentro dela;
  • o que deve continuar igual se sua implementação mudar.

O objetivo não é produzir o maior número de partes. É diminuir a quantidade de contexto necessária para explicar, testar e alterar cada responsabilidade.

Observe quando um algoritmo começa a misturar motivos de mudança

Considere um fechamento de pedido com itens já carregados:

subtotal <- 0

PARA CADA item DO pedido FAÇA
  SE item.quantidade <= 0 OU item.preco < 0 ENTÃO
    EXIBIR "Pedido inválido"
    ENCERRAR
  FIM SE

  subtotal <- subtotal + item.preco * item.quantidade
FIM PARA

SE cupom = "CLIENTE10" ENTÃO
  desconto <- subtotal * 0,10
SENÃO
  desconto <- 0
FIM SE

total <- subtotal - desconto
EXIBIR "Subtotal:", subtotal
EXIBIR "Desconto:", desconto
EXIBIR "Total:", total

O algoritmo é curto o bastante para executar, mas reúne pelo menos quatro responsabilidades:

  1. validar itens;
  2. calcular o subtotal;
  3. decidir o desconto;
  4. apresentar o resumo.

Cada responsabilidade pode mudar por um motivo diferente. A validação pode passar a rejeitar limite de quantidade; o cálculo pode incorporar taxas; a campanha pode trocar cupons; e a saída pode ganhar outro formato.

Comprimento é apenas um indício. O sinal mais útil é a presença de motivos de mudança independentes dentro do mesmo trecho.

Um único bloco mistura validação, subtotal, desconto e apresentação; ao lado, as quatro responsabilidades aparecem separadas e conectadas
A divisão não remove etapas: ela torna visível qual parte responde por cada decisão.

Dê a cada parte um verbo e um resultado

Antes de pensar em sintaxe, escreva o algoritmo em blocos nomeados:

itensValidos <- VALIDAR ITENS(pedido.itens)
subtotal <- CALCULAR SUBTOTAL(itensValidos)
desconto <- DETERMINAR DESCONTO(subtotal, cupom)
total <- subtotal - desconto
APRESENTAR RESUMO(subtotal, desconto, total)

Esses nomes ainda não declaram funções ou procedimentos de uma linguagem. Eles formam um mapa de responsabilidades. A próxima aula mostrará como transformar esse mapa em construções executáveis.

Use o teste da frase: descreva cada bloco com um verbo, um objeto e um resultado observável.

BlocoFrase de responsabilidadeResultado
validar itensverificar se os itens podem participar do pedidoitens válidos ou erro
calcular subtotalcombinar preço e quantidade dos itens aceitosvalor antes do desconto
determinar descontoaplicar a política comercial ao subtotalvalor do desconto
apresentar resumoorganizar os valores para o destinatáriosaída compreensível

Se a frase precisa de vários “e também”, provavelmente há responsabilidades misturadas. Se duas frases produzem exatamente o mesmo resultado, talvez a divisão esteja duplicando uma fronteira.

Nomes como processarDados, executarCoisas ou auxiliar não revelam intenção. Prefira ações do domínio que permitam prever o resultado sem ler todos os passos internos.

Faça os dados atravessarem fronteiras explícitas

O algoritmo organizado pode ser lido como uma transformação:

pedido bruto
  → itens válidos
  → subtotal
  → desconto e total
  → resumo apresentado

Esse fluxo retoma o modelo de entrada, processamento e saída, agora aplicado dentro de uma solução maior. Cada parte recebe uma entrada adequada ao seu trabalho e entrega um resultado que a próxima consegue usar.

O pedido fornece itens para validação, os itens válidos alimentam o subtotal, subtotal e cupom alimentam o desconto e os valores calculados seguem para a apresentação
Fronteiras explícitas mostram quem produz e quem consome cada informação.

Evite permitir que todas as partes leiam e alterem o pedido inteiro. Quando um bloco recebe somente os dados necessários, ficam mais claros:

  • suas dependências;
  • os casos que precisam ser testados;
  • o resultado que ele promete;
  • as mudanças que não deveriam afetá-lo.

Por exemplo, “determinar desconto” precisa conhecer subtotal e cupom. Não precisa decidir se um item possui quantidade válida nem como o resumo será exibido.

Esconda a decisão que tem maior chance de mudar

Dividir apenas pela ordem das etapas nem sempre produz a melhor fronteira. Uma orientação clássica de modularidade é concentrar decisões de projeto que podem mudar e expor uma interface estável para o restante da solução.

Imagine que a política comercial deixe de aceitar um código fixo e passe a considerar categoria do cliente e data da campanha. Se essa regra está concentrada em “determinar desconto”, as outras responsabilidades continuam recebendo e produzindo os mesmos conceitos:

entrada: subtotal e contexto comercial
resultado: valor de desconto válido

O algoritmo coordenador não precisa conhecer a tabela, a ordem das verificações ou a fórmula interna. Ele precisa apenas da promessa do bloco.

Isso é ocultação de informação: esconder uma decisão interna atrás de uma fronteira conhecida. Não significa esconder dados por esconder; significa impedir que detalhes mutáveis se espalhem por partes que não precisam deles.

Uma alteração na regra de desconto atinge vários pontos de um algoritmo monolítico, mas fica concentrada em um único bloco quando as responsabilidades estão separadas
Uma fronteira útil limita o conhecimento da regra e reduz a área que precisa ser revista quando ela muda.

Modularidade não garante impacto zero. Alterar o significado do desconto pode exigir novos dados de entrada ou uma nova apresentação. O ganho está em tornar essas consequências visíveis e evitar dependências acidentais.

Valide a reorganização sem mudar o comportamento

Separar partes de um algoritmo existente é uma refatoração quando melhora sua estrutura interna sem alterar o comportamento observável esperado.

Antes de reorganizar, registre casos que caracterizem a versão atual:

CenárioSubtotalCupomDesconto esperadoTotal esperado
sem cupom200ausente0200
cupom válido200CLIENTE1020180
pedido gratuito0CLIENTE1000
quantidade inválidaqualquererronenhum total

Execute os mesmos casos antes e depois da divisão. Se entradas equivalentes deixam de produzir os mesmos resultados, a mudança não foi apenas estrutural.

Confira também a ordem dos efeitos. Calcular o subtotal antes de validar todos os itens pode deixar estado parcial; apresentar o resumo antes de concluir o desconto gera saída inconsistente. Blocos melhor nomeados não corrigem automaticamente uma sequência errada.

Um teste de mesa ajuda a comparar valores e decisões nas duas versões.

Reutilização é possível, mas não obrigatória

Uma parte nomeada pode ser usada em mais de um fluxo. “Calcular subtotal” talvez sirva ao fechamento do pedido e à prévia do carrinho. Centralizar uma regra realmente compartilhada evita cópias que evoluem de maneiras diferentes.

Entretanto, reutilização não deve ser a única justificativa para separar. Um bloco usado uma única vez ainda pode:

  • reduzir a quantidade de detalhes no fluxo principal;
  • permitir teste isolado de uma regra delicada;
  • dar nome a uma decisão importante;
  • limitar o impacto de mudanças futuras.

O inverso também é verdadeiro: dois trechos apenas parecidos não deveriam compartilhar uma parte se representam regras que podem evoluir separadamente. Reutilize uma responsabilidade comum, não somente linhas com aparência semelhante.

Encontre o nível de divisão pelos custos de compreensão

Divisão insuficiente

Há poucos blocos quando cada um ainda exige acompanhar muitas regras, estados e efeitos ao mesmo tempo. Sintomas comuns:

  • nome amplo que não antecipa o resultado;
  • várias validações e cálculos independentes misturados;
  • alteração simples exige reler o algoritmo inteiro;
  • testes só conseguem verificar o fluxo completo.

Fragmentação excessiva

Há partes demais quando a leitura exige saltar continuamente entre nomes que pouco acrescentam. Sintomas:

  • cada operação elementar recebe um bloco próprio;
  • a coordenação fica maior que o trabalho realizado;
  • entradas e resultados são repassados por muitas camadas sem transformação relevante;
  • as partes nunca são compreendidas ou testadas isoladamente.

Fronteira inadequada

Mesmo uma quantidade razoável pode separar pelos critérios errados. Se validação de cupom aparece em três blocos, a decisão continua espalhada. Se “calcular valores” reúne subtotal, impostos, descontos e apresentação, o nome esconde múltiplos motivos de mudança.

Não existe número universal de linhas ou partes. Avalie se cada fronteira reduz o contexto necessário mais do que aumenta o custo de coordenação.

Use uma revisão em cinco perguntas

Para cada parte proposta, pergunte:

  1. Responsabilidade: consigo descrevê-la em uma frase sem vários “e também”?
  2. Fronteira: entradas e resultados estão claros?
  3. Conhecimento: ela conhece somente os dados e decisões necessários?
  4. Mudança: uma regra interna pode mudar sem obrigar a reescrever partes alheias?
  5. Verificação: consigo testar seu resultado com poucos casos focados?

Depois analise o conjunto:

  • os blocos estão no mesmo nível de abstração?
  • existe uma sequência compreensível entre eles?
  • algum resultado não possui produtor ou consumidor claro?
  • alguma regra aparece em mais de um lugar?
  • a nova organização preservou o comportamento?

Essas perguntas são critérios de projeto, não uma pontuação automática. Duas divisões diferentes podem ser adequadas para mudanças esperadas diferentes.

Pratique reorganizando um boletim

Considere um algoritmo que:

  • recebe quatro notas;
  • rejeita valores fora de 0 a 10;
  • calcula a média;
  • classifica o estudante como aprovado, recuperação ou reprovado;
  • apresenta notas, média e situação.

Faça o exercício em cinco etapas:

  1. escreva uma versão linear ou identifique as responsabilidades no fluxo existente;
  2. proponha blocos usando verbo e resultado;
  3. liste somente os dados necessários para cada bloco;
  4. crie casos que preservem limites como 0, 5, 7 e 10;
  5. simule a mudança da regra de aprovação de média 7 para média 6 e desenhe o mapa de impacto.

Revise se a regra de classificação ficou concentrada, se a apresentação conhece detalhes desnecessários e se algum bloco foi criado apenas para executar uma operação trivial.

O que você deve guardar

Dividir um algoritmo em partes reduz a quantidade de decisões que precisam ser compreendidas simultaneamente. A fronteira é útil quando reúne uma responsabilidade coesa, declara os dados que recebe, entrega um resultado previsível e protege detalhes que podem mudar.

Não divida por uma meta de linhas nem suponha que toda parte precisa ser reutilizada. Compare benefícios de compreensão e teste com o custo de coordenação, e preserve o comportamento com casos executados antes e depois. A seguir, aprenda como transformar esses blocos conceituais em funções e procedimentos.

Referências