2.1.2 Barreiras de Abstração
Antes de continuar com mais exemplos de dados compostos e abstração de dados, vamos considerar algumas das questões levantadas pelo exemplo de números racionais. Definimos as operações de números racionais em termos de um construtor make_rat e seletores numer e denom. Em geral, a ideia subjacente da abstração de dados é identificar para cada tipo de objeto de dados um conjunto básico de operações em termos das quais todas as manipulações de objetos de dados desse tipo serão expressas, e então usar apenas essas operações na manipulação dos dados.
Podemos visualizar a estrutura do sistema de números racionais como mostrado na figura acima. As linhas horizontais representam barreiras de abstração que isolam diferentes "níveis" do sistema. Em cada nível, a barreira separa os programas (acima) que usam a abstração de dados dos programas (abaixo) que implementam a abstração de dados. Programas que usam números racionais os manipulam exclusivamente em termos das funções fornecidas "para uso público" pelo pacote de números racionais: add_rat, sub_rat, mul_rat, div_rat e equal_rat. Estas, por sua vez, são implementadas exclusivamente em termos do construtor e seletores make_rat, numer e denom, que eles mesmos são implementados em termos de pares. Os detalhes de como os pares são implementados são irrelevantes para o resto do pacote de números racionais, desde que os pares possam ser manipulados pelo uso de pair, head e tail. Na prática, as funções em cada nível são as interfaces que definem as barreiras de abstração e conectam os diferentes níveis.
Esta ideia simples tem muitas vantagens. Uma vantagem é que torna os programas muito mais fáceis de manter e modificar. Qualquer estrutura de dados complexa pode ser representada de várias maneiras com as estruturas de dados primitivas fornecidas por uma linguagem de programação. Claro, a escolha da representação influencia os programas que operam sobre ela; assim, se a representação fosse alterada em algum momento posterior, todos esses programas poderiam ter que ser modificados de acordo. Esta tarefa poderia ser demorada e cara no caso de programas grandes, a menos que a dependência da representação fosse confinada por design a muito poucos módulos do programa.
Por exemplo, uma maneira alternativa de abordar o problema de reduzir números racionais aos menores termos é realizar a redução sempre que acessamos as partes de um número racional, em vez de quando o construímos. Isso leva a diferentes funções construtoras e seletoras:
Carregando playground de código...
A diferença entre esta implementação e a anterior reside em quando computamos o gcd. Se em nosso uso típico de números racionais acessamos os numeradores e denominadores dos mesmos números racionais muitas vezes, seria preferível computar o gcd quando os números racionais são construídos. Caso contrário, podemos ser melhor servidos esperando até o momento do acesso para computar o gcd. Em qualquer caso, quando mudamos de uma representação para a outra, as funções add_rat, sub_rat e assim por diante não precisam ser modificadas de forma alguma.
Restringir a dependência da representação a algumas poucas funções de interface nos ajuda a projetar programas, bem como modificá-los, porque nos permite manter a flexibilidade para considerar implementações alternativas. Para continuar com nosso exemplo simples, suponha que estamos projetando um pacote de números racionais e não podemos decidir inicialmente se devemos realizar o gcd no momento da construção ou no momento da seleção. A metodologia de abstração de dados nos dá uma maneira de adiar essa decisão sem perder a capacidade de progredir no resto do sistema.
Exercício 2.2
Considere o problema de representar segmentos de linha em um plano. Cada segmento é representado como um par de pontos: um ponto inicial e um ponto final. Declare um construtor make_segment e seletores start_segment e end_segment que definem a representação de segmentos em termos de pontos. Além disso, um ponto pode ser representado como um par de números: a coordenada x e a coordenada y. Consequentemente, especifique um construtor make_point e seletores x_point e y_point que definem essa representação. Finalmente, usando seus seletores e construtores, declare uma função midpoint_segment que recebe um segmento de linha como argumento e retorna seu ponto médio (o ponto cujas coordenadas são a média das coordenadas dos pontos extremos). Para testar suas funções, você precisará de uma maneira de imprimir pontos:
Carregando playground de código...
Exercício 2.3
Implemente uma representação para retângulos em um plano. (Dica: Você pode querer fazer uso do exercício 2.2.) Em termos de seus construtores e seletores, crie funções que computem o perímetro e a área de um dado retângulo. Agora implemente uma representação diferente para retângulos. Você pode projetar seu sistema com barreiras de abstração adequadas, de modo que as mesmas funções de perímetro e área funcionem usando qualquer uma das representações?
Carregando playground de código...
📝 Encontrou algo errado nesta página?
Sua ajuda é muito importante para melhorar a qualidade da tradução!
🐛 Encontrou um erro?
Se você encontrou:
- Erro de tradução (palavra incorreta, termo técnico errado)
- Erro de ortografia ou gramática
- Link quebrado
- Código de exemplo que não funciona
- Problema de formatação
❓ Tem uma dúvida?
Se você tem:
- Dúvida sobre o conteúdo desta seção
- Pergunta sobre um conceito do SICP
- Dificuldade em entender algum exemplo
- Questão sobre a tradução de algum termo
💡 Tem uma sugestão de melhoria?
Se você quer sugerir:
- Melhoria na explicação
- Exemplo adicional
- Recurso visual (diagrama, ilustração)
- Qualquer outra ideia
🌍 Quer discutir a tradução?
Se você quer debater:
- Escolha de tradução de algum termo
- Consistência de terminologia
- Nuances do português
Obrigado por ajudar a melhorar o SICP.js PT-BR! ✨