Design Patterns em C? – c design-pattern

Pergunta:


Iniciei o desenvolvimento de um sistema embarcado em C puro. O projeto requer uma documentação detalhada de software e hardware, além de utilizar os padrões de projetos recomendados.
É sabido que existem diversos padrões de projetos voltados para métodos, classes, pacotes e afins da programação orientada a objetos. O que existe a respeito do C? No meu caso irei criar os arquivos de cabeçalho .h e os arquivos de implementação propriamente dita .c.

Li em alguns locais que o que se tem de padrão para o C é a declaração das funções no início do arquivo e a implementação logo abaixo das declarações, ficando:

void funca1(int arg1, int arg2);
int funca2(bool arg1, int arg2);
...
void funca1(int arg1, int arg2){
// code here...
}

int funca2(bool arg1, int arg2){
// code here
}

É só isso mesmo que existe para o C?

Autor da pergunta touchmx

Comunidade

Como eu já comentei, essa é uma ótima pergunta, embora um pouco difícil porque pode ser abordada de algumas formas distintas. Há uma pergunta muito similar (se não exatamente igual) no SOEN. Eu não sei se você já a conhecia ou se inspirou nela, mas seguramente o conteúdo de lá também irá te ajudar.

Sobre o uso de Padrões para Documentação

Como o próprio nome diz, os Padrões de Projeto (Design Patterns) são perfis de problemas recorrentes acompanhados de abordagens de solução já bem utilizadas, testadas e avaliadas para os problemas que propõem resolver. Naturalmente o seu uso primordial (isto é, originalmente intencionado) é facilitar a solução de problemas recorrentes. Um projetista que estudou Padrões de Projeto e reconhece um problema já bem descrito pode rapidamente utilizar a melhor abordagem, e assim diminuir o acoplamento, facilitar a manutenção, etc, etc, etc.

Mas naturalmente os Padrões de Projeto também servem para auxiliar na documentação de sistemas. Afinal, se as soluções empregadas se tratam de abordagens padronizadas e amplamente reconhecidas pela comunidade, a mera menção delas já ajudará um bocado. Por exemplo:

O jogo XXXXXX usa o padrão Bridge na renderização dos obstáculos,
de forma a separar o desenho do formato (circular, triangular,
quadrilátero, etc) do traçado utilizado no desenho (contínuo, tracejado, pontuado,
piscante, etc).

Nesse cenário de exemplo, qualquer novo desenvolvedor que chega ao projeto e já conhece o padrão Bridge vai ter uma boa noção de como a implementação foi feita, e saberá que na necessidade de criar um novo formato (hexagonal, por exemplo) bastará herdar a classe base dos formatos para automaticamente ser capaz de reutilizar nele todos os traçados já existentes.

Pensando dessa forma, a dificuldade da documentação não está no padrão de projeto em si, mas na sua descrição formal e em seu reconhecimento por uma comunidade. Os padrões de projeto do GoF, por exemplo, foram criados e documentados em um formato padronizado que inclui as seguintes descrições:

  • Nome. O nome do padrão projeto, que serve como um identificador único para referência facilitada.
  • Intenção. O propósito desse padrão, isto é, por que ele existe, qual é a sua utilidade, que tipo de vantagens ele traz.
  • Problema. O problema que o padrão se propõe a resolver. Essa descrição não inclui apenas texto, mas também diagramas, exemplos e o que mais for de ajuda para tornar o entendimento do problema amplo e generalizado (afinal, os problemas podem ser recorrentes independentemente do domínio do sistema).
  • Solução. Como o padrão fornece a solução ao problema. Novamente, aqui também pode se incluir texto, diagramas e exemplos, com a intenção de descrever em detalhes como essa implementação ocorre.
  • Participantes e Colaboradores. As entidades envolvidas na solução e como elas representam partes do problema. No GoF originamente se mencionavam as classes envolvidas, mas entidades podem ser entidades como quaisquer estruturas computacionalmente implementáveis na linguagem/tecnologia que se utiliza.
  • Consequências. Descrição das possibilidades decorrentes do uso do padrão, incluindo problemas potenciais evitados, facilidades de evolução, interação com outros padrões, etc.

É minha opinião (e gostaria de deixar bem claro que essa parte é mesmo uma opinião, embora fundamentada em experiência) de que qualquer abordagem de projeto que sirva como solução e foi criada por você mesmo pode ser documentada de uma forma similar. É por isso que eu te sugeri em comentários para procurar por “melhores práticas”. Melhores práticas não deixam de ser soluções padronizadas para problemas reconhecidos, comumente descritas textualmente e com exemplos de código.

De fato, o próprio GoF indica que elementos da abordagem orientada a objetos poderiam ser considerados padrões de projeto na linguagem C (texto em tradução livre da citação nesta resposta do SOEN):

“A escolha da linguagem de programação é importante porque ela
influencia o ponto de vista do programador. Nossos padrões assumem
linguagens com características do nível das em Smalltalk/C++, e essa
escolha determina o que pode e não pode ser implementado facilmente.
Se assumirmos linguagens procedurais, poderíamos incluir como padrões
de projeto a “Herança”, o “Encapsulamento” e o “Polimorfismo”.
De
forma similar, alguns dos nossos padrões são suportados diretamente
por linguagens menos orientadas a objeto. CLOS tem multi-métodos, por
exemplo, que diminuem a necessidade de padrões como o Visitor.”

Os itálicos são meus.

Padrões Existentes em C

Certamente há padrões de projeto existentes para a linguagem C, bem documentados e amplamente conhecidos. Os mais populares são os do livro “Patterns in C” de Adam Tornhill (que se chamava Adam Petersen antes de se casar e mudar de sobrenome). A resposta aceita da pergunta do SOEN que eu referenciei no começo tem links para capítulos do livro dele, mas eu sugeriria comprá-lo porque não é caro ($ 4,99 é o valor mínimo para a versão digital no site dele) e estimula o bom trabalho de pessoas como ele.

Nesse livro você encontra, por exemplo, o padrão First-Class ADT, que separa a “interface” (o que é publicamente acessível) da implementação de “objetos” (ponteiros para estruturas). A ideia é bem simples: você usa a características de poder declarar tipos incompletos na linguagem C para definir um ponteiro para a estrutura e fornece funções para alocar e desalocar a estrutura real, escondendo assim o acesso direto aos elementos da estrutura. Há também, por exemplo, o padrão States, que utiliza uma tabela de transição de estados para evitar a repetição de (potencialmente longas) estruturas de decisão todas as vezes que a variável de estado precisa ser verificada. Este padrão, apesar de ser documentado para utilização em C, é muito relacionado com o mesmo problema que pode ocorrer em C++, como já bem documentado aqui no SOPT.

Como eu já opinei antes, eu acho que padrões de projeto não precisam incluir somente soluções a problemas de mais alto nível, mas também soluções reconhecidas que intencionam evitar problemas de implementação de baixo nível (erros de compilação, falhas em acesso a memória, dificuldades com tipos de dados, etc). Nesse caso, eu repito a sugestão que ofereci em comentários: melhores práticas são muito úteis quando documentadas, porque não apenas descrevem a abordagem idealizada como padrão para o time de desenvolvedores como também os motiva a pensar a respeito dos problemas potenciais em não se utilizá-las.

Exemplos notáveis (embora muitas vezes ainda discutidos) são o uso de definições (#define) para evitar inclusão circular de cabeçalhos, o uso de const ao invés de #define para definir valores constantes, e também evitar o uso de scanf para diminuir erros de invasão de memória.

P.S.: Note como na resposta do @pmg há o uso de um desses padrões
citados. O #ifndef HEADER_H_INCLUDED serve justamente para evitar a
inclusão circular do cabeçalho. 🙂

No que respeita a ficheiros de cabeçalho e ficheiros de código e localização dos protótipos, o mais cómodo é meter os protótipos no .h e incluir esse .h no .c. Assim só escreves cada protótipo apenas 1 vez … e o compilador verifica se o protótipo bate certo com a definição quando compilar.

Por exemplo, o ficheiro de cabecalho “foobar.h”

#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
void foo(int);
int bar(void);
#endif

e o ficheiro de código “foobar.c”

#include "foobar.h"

int foo(int n) {
    return n + 42;
}
int bar(int n) {
    return n + 42;
}

Quando compilares “foobar.c” o compilador vai verificar as funções e dar erro (ou warning) porque o protótipo não bate certo com a definição.

Nota: configura o teu compilador para o máximo de warnings e tenta fazer código que compile com 0 warnings.

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *