Eu sempre vejo que a abstração é um recurso muito útil que o OO fornece para gerenciar a base de código. Mas como são gerenciadas grandes bases de código não OO? Ou eles acabam se tornando uma " grande bola de lama " eventualmente?
Atualização:
parecia que todo mundo estava pensando que 'abstração' é apenas modularização ou ocultação de dados. Mas IMHO, também significa o uso de 'Classes Abstratas' ou 'Interfaces', que são uma obrigação para injeção de dependência e, portanto, testes. Como as bases de código não OO gerenciam isso? Além disso, além da abstração, o encapsulamento também ajuda muito a gerenciar grandes bases de códigos, pois define e restringe a relação entre dados e funções.
Com C, é muito possível escrever código pseudo-OO. Não sei muito sobre outros idiomas que não sejam OO. Então, é a maneira de gerenciar grandes bases de código C?
fonte
Respostas:
Você parece pensar que OOP é o único meio de alcançar a abstração.
Enquanto OOP é certamente muito bom em fazer isso, não é o único caminho. Projetos grandes também podem ser mantidos gerenciáveis por modularização intransigente (basta olhar para Perl ou Python, que se destacaram nisso, e linguagens funcionais como ML e Haskell) e usando mecanismos como modelos (em C ++).
fonte
static
modificador de acesso anexado.Módulos, funções (externas / internas), sub-rotinas ...
como disse Konrad, OOP não é a única maneira de gerenciar grandes bases de código. Por uma questão de fato, um monte de software foi escrito antes dele (antes de C ++ *).
fonte
O princípio da modularidade não se limita às linguagens orientadas a objetos.
fonte
Realisticamente, mudanças pouco frequentes (pense nos cálculos de aposentadoria da Seguridade Social) e / ou conhecimento profundamente arraigado porque as pessoas que mantêm esse sistema o fazem há algum tempo (tomada cínica é segurança no emprego).
As melhores soluções são a validação repetível, com o que quero dizer teste automatizado (por exemplo, teste de unidade) e teste humano que segue as etapas prescritas (por exemplo, teste de regressão) "em vez de clicar e ver o que quebra".
Para começar a avançar para algum tipo de teste automatizado com uma base de código existente, recomendo a leitura de Working Factor with Legacy Code , de Michael Feather , que detalha abordagens para levar as bases de código existentes até algum tipo de estrutura de teste repetível OO ou não. Isso leva ao tipo de idéias que outras pessoas responderam, como a modularização, mas o livro descreve a abordagem correta para fazer isso sem quebrar as coisas.
fonte
Embora a injeção de dependência baseada em interfaces ou classes abstratas seja uma maneira muito agradável de se testar, não é necessário. Não se esqueça que quase qualquer idioma tem um ponteiro de função ou uma avaliação, o que pode fazer qualquer coisa com uma interface ou classe abstrata (o problema é que eles podem fazer mais , incluindo muitas coisas ruins, e que não eles mesmos fornecem metadados). Esse programa pode realmente obter injeção de dependência com esses mecanismos.
Eu descobri que ser rigoroso com os metadados é muito útil. Nas linguagens OO, os relacionamentos entre bits de código são definidos (até certo ponto) pela estrutura da classe, de maneira padronizada o suficiente para ter coisas como uma API de reflexão. Nas linguagens processuais, pode ser útil inventar essas informações você mesmo.
Também descobri que a geração de código é muito mais útil em uma linguagem processual (em comparação com uma linguagem orientada a objetos). Isso garante que os metadados estejam em sincronia com o código (já que são usados para gerá-los) e oferece algo parecido com os pontos de corte da programação orientada a aspectos - um local em que você pode injetar código quando necessário. Às vezes, é a única maneira de fazer a programação DRY em um ambiente que eu possa descobrir.
fonte
Na verdade, como você descobriu recentemente , as funções de primeira ordem são tudo o que você precisa para a inversão de dependência.
C suporta funções de primeira ordem e até fechamentos até certo ponto . As macros C são um recurso poderoso para programação genérica, se manuseadas com os cuidados necessários.
Está tudo lá. SGLIB é um bom exemplo de como C pode ser usado para escrever código altamente reutilizável. E acredito que há muito mais por aí.
fonte
Mesmo sem abstração, a maioria dos programas é dividida em seções de algum tipo. Essas seções geralmente se relacionam a tarefas ou atividades específicas e você trabalha naquelas da mesma maneira que trabalharia nos bits mais específicos dos programas abstraídos.
Em projetos de pequeno a médio porte, é realmente mais fácil fazer isso com uma implementação OO purista às vezes.
fonte
Abstração, classes abstratas, injeção de dependência, encapsulamento, interfaces e assim por diante, não são a única maneira de controlar grandes bases de código; Esta é uma maneira justa e orientada a objetos.
O principal segredo é evitar pensar em POO ao codificar não POO.
Modularidade é a chave em idiomas não OO. Em C, isso é alcançado exatamente como David Thornley mencionou em um comentário:
fonte
Uma maneira de gerenciar o código é decompô-lo nos seguintes tipos de código, seguindo as linhas da arquitetura MVC (model-view-controller).
Esse método de organização de código funciona bem para softwares escritos em qualquer linguagem OO ou não-OO, porque padrões de design comuns geralmente são comuns a cada uma das áreas. Além disso, esses tipos de limites de código geralmente são os mais fracamente acoplados, exceto algoritmos, porque vinculam os formatos de dados das entradas ao modelo e depois às saídas.
As evoluções do sistema geralmente assumem a forma de fazer com que seu software lide com mais tipos de entradas ou mais tipos de saídas, mas os modelos e visualizações são os mesmos e os controladores se comportam de maneira muito semelhante. Ou um sistema pode precisar, com o tempo, suportar mais e mais tipos diferentes de saídas, mesmo que as entradas, modelos, algoritmos sejam os mesmos e os controladores e visualizações sejam semelhantes. Ou um sistema pode ser aumentado para adicionar novos modelos e algoritmos para o mesmo conjunto de entradas, saídas e visualizações semelhantes.
Uma maneira pela qual a programação OO dificulta a organização do código é porque algumas classes estão profundamente ligadas às estruturas de dados persistentes e outras não. Se as estruturas de dados persistentes estão intimamente relacionadas a coisas como relacionamentos 1: N em cascata ou relacionamentos m: n, é muito difícil decidir os limites de classe até que você codifique uma parte significativa e significativa do seu sistema antes de saber que está certo . Qualquer classe vinculada às estruturas de dados persistentes será difícil de evoluir quando o esquema dos dados persistentes mudar. Classes que lidam com algoritmos, formatação e análise têm menos probabilidade de serem vulneráveis a alterações no esquema das estruturas de dados persistentes. O uso de um tipo de organização de código MVC isola melhor as alterações de código mais confusas no código do modelo.
fonte
Ao trabalhar em idiomas que não possuem estrutura interna e recursos de organização (por exemplo, se não houver namespaces, pacotes, assemblies etc ...) ou onde forem insuficientes para manter uma base de código desse tamanho sob controle, a resposta natural será desenvolver nossas próprias estratégias para organizar o código.
Essa estratégia da organização provavelmente inclui padrões relacionados a onde diferentes arquivos devem ser mantidos, coisas que precisam acontecer antes / depois de certos tipos de operações e convenções de nomenclatura e outros padrões de codificação, além de muito "é assim que é configurado" - não mexa com isso! " digite comentários - que são válidos desde que expliquem o porquê!
Como a estratégia provavelmente acabará sendo adaptada às necessidades específicas do projeto (pessoas, tecnologias, ambiente etc ...), é difícil fornecer uma solução única para o gerenciamento de grandes bases de códigos.
Portanto, acredito que o melhor conselho é abraçar a estratégia específica do projeto e tornar o gerenciamento uma prioridade-chave: documentar a estrutura, por que é assim, os processos para fazer alterações, auditar para garantir que está sendo respeitada, e crucialmente: mude quando precisar mudar.
Estamos familiarizados principalmente com classes e métodos de refatoração, mas com uma grande base de código em um idioma como esse, é a própria estratégia de organização (completa com a documentação) que precisa ser refatorada conforme e quando necessário.
O raciocínio é o mesmo da refatoração: você desenvolverá um bloqueio mental para trabalhar em pequenas partes do sistema, se achar que a organização geral está uma bagunça e, eventualmente, permitirá que ele se deteriore (pelo menos é a minha opinião) isto).
As advertências também são as mesmas: use testes de regressão, verifique se você pode reverter facilmente se a refatoração der errado e projete-o para facilitar a refatoração em primeiro lugar (ou você simplesmente não fará isso!).
Concordo que isso é muito mais complicado do que refatorar o código direto, e é mais difícil validar / ocultar o tempo de gerentes / clientes que podem não entender por que isso precisa ser feito, mas esses também são os tipos de projeto mais propensos à podridão de software causada por projetos inflexíveis de nível superior ...
fonte
Se você está perguntando sobre o gerenciamento de uma grande base de código, está perguntando como manter sua base de código bem estruturada em um nível relativamente aproximado (bibliotecas / módulos / construção de subsistemas / uso de espaços para nome / documentação correta nos lugares certos) etc.) Os princípios de OO, especialmente 'classes abstratas' ou 'interfaces', são princípios para manter seu código limpo internamente, em um nível muito detalhado. Portanto, as técnicas para manter uma base de código grande gerenciável não diferem para código OO ou não OO.
fonte
Como é tratado é que você descobre as bordas dos elementos que usa. Por exemplo, os seguintes elementos em C ++ têm uma borda clara e quaisquer dependências fora da borda devem ser cuidadosamente pensadas:
Combinando esses elementos e reconhecendo suas bordas, você pode criar praticamente qualquer estilo de programação que desejar no c ++.
Um exemplo disso é que uma função seria reconhecer que é ruim chamar outras funções de uma função, porque causa dependência; em vez disso, você deve chamar apenas funções membro dos parâmetros da função original.
fonte
O maior desafio técnico é o problema do espaço para nome. A ligação parcial pode ser usada para contornar isso. A melhor abordagem é projetar usando padrões de codificação. Caso contrário, todos os símbolos se tornam uma bagunça.
fonte
O Emacs é um bom exemplo disso:
Os testes do Emacs Lisp usam
skip-unless
elet-bind
executam dispositivos de detecção e teste de recursos:Como é o SQLite. Aqui está o design:
sqlite3_open () → Abra uma conexão com um banco de dados SQLite novo ou existente. O construtor para o sqlite3.
sqlite3 → O objeto de conexão com o banco de dados. Criado por sqlite3_open () e destruído por sqlite3_close ().
sqlite3_stmt → O objeto de instrução preparado. Criado por sqlite3_prepare () e destruído por sqlite3_finalize ().
sqlite3_prepare () → Compile texto SQL em código de bytes que fará o trabalho de consultar ou atualizar o banco de dados. O construtor para sqlite3_stmt.
sqlite3_bind () → Armazena dados do aplicativo em parâmetros do SQL original.
sqlite3_step () → Avança um sqlite3_stmt para a próxima linha de resultados ou para a conclusão.
sqlite3_column () → Valores da coluna na linha de resultados atual para um sqlite3_stmt.
sqlite3_finalize () → Destruidor para sqlite3_stmt.
sqlite3_exec () → Uma função de wrapper que executa sqlite3_prepare (), sqlite3_step (), sqlite3_column () e sqlite3_finalize () para uma cadeia de caracteres de uma ou mais instruções SQL.
sqlite3_close () → Destruidor para sqlite3.
O SQLite usa uma variedade de técnicas de teste, incluindo:
Referências
Vistas conceituais da arquitetura do Emacs (pdf)
A interface do SO SQLite ou "VFS"
O mecanismo de tabela virtual do SQLite
Uma introdução à interface SQLite C / C ++
Programação Emacs-Elisp · GitHub
Testes e seu ambiente - Emacs Lisp Regression Testing
Como o SQLite é testado
fonte