Orientação a objeto em C: Encapsulamento com estruturas opacas

Encapsulamento

Três são os conceitos básicos de programação a orientação a objeto: Herança, polimorfismo e encapsulamento. Neste artigo vamos ver como aplicar o encapsulamento no seu código em C!

 

A programação orientada a objeto nos concede muitas vantagens. O encapsulamento nos permite que só exista uma forma de acessar nossos dados, os famosos "getters", e uma forma de alterá-los, os famosos "setters". Não existe acesso direto ao atributo! Isto nos dá segurança pois podemos fazer validações antes de alguém acessar ou alterar o nosso dado.

 

Outra vantagem do encapsulamento é que os detalhes internos de implementação são escondidos do resto do código. Desta forma eles podem ser alterados dependendo de plataforma ou situação sem que o resto do software sofra alterações.

 

 

Exemplo de Encapsulamento

 

Vamos imaginar a classe Usuario, o Usuario possui um nome, um login e uma senha.  Além destes atributos ele também possui o método "autenticar" que retorna verdadeiro se a combinação de login e senha for verdadeira. Os getters e setters não são colocados no diagrama de classe para fins de visibilidade. Mas também considere a existência dos métodos getNome, getLogin, getSenha, setNome, setLogin e setSenha.

 

Encapsulamento-classe
Figura 1 - Classe usuário

 

No nosso caso a classe Usuario em C consiste de dois arquivos: um .h e um .c. No caso, usuario.c e usuario.h.

 

Segundo as boas praticas da orientação a objeto os atributos de uma classe são internos a ela e só ela os conhece. Ou seja, a estrutura de dados com os atributos Nome, Login e Senha só pode ser conhecida por quem a utiliza, no nosso caso o arquivo usuario.c:

 

Porém os objetos do tipo "Usuario" devem poder ser utilizados por todos que o desejarem. Por isso uma declaração da nossa estrutura de dados é feita no nosso arquivo usuario.h, porém é declarado como uma estrutura opaca, ninguém sabe o que realmente tem dentro dela a não ser nosso .c.

 

Arquivo usuario.h:

 

 

Para testar vamos utilizar este main.c:

 

É impossível um arquivo que não o usuario.c acesse os atributos e métodos desta classe e esses acessos são controlados pelos nossos getters e setters via usuario.c. Se adicionarmos esta linha ao arquivo main.c veremos um erro:

 

 Se formos desenvolver para um sistema embarcado Bare metal e não quisermos utilizar alocação dinâmica de memória podemos simplesmente mudar a definição da nossa struct para algo do tipo:

 

 

e reescrever nossos métodos no arquivo usuario.c, eliminando os métodos de alocação e liberação de memória.

 

Observação: Diversas verificações no arquivo usuario.c foram deixadas de lado para fins didáticos. Verificações como se o objeto é nulo antes de dar um get ou set, ou ainda se o malloc/free foram realizados com sucesso.

 

 

Aprenda mais

 

Orientação a objeto em C: Polimorfismo

 

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

10
Deixe um comentário

avatar
 
5 Comment threads
5 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
5 Comment authors
Henrique RossiFelipe LavrattiErisson SiqueiraAndré CastelanMarcelo Barros de almeida Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
trackback

[…] deve ser usado para definição de objetos opacos [5], embora isso não seja encorajado no Kernel, tipos de inteiros limpos (u32, u16 ..)  e alguns […]

trackback

[...] Orientação a objeto em C – Encapsulamento [...]

Erisson Siqueira
Visitante
Erisson Siqueira

Obrigado pelo POST André, excelente! Sempre procuro artigos relacionados a essas conversões para poder trabalhar a partir de diagramas UML e desenvolvimento em camadas (pensando em desenvolvimento de firmware) Uma dúvida que eu tenho seria a seguinte: Suponha que eu crie a estrutura em um arquivo (.h) typedef struct { int inicio; int fim; } timeTypedef; Teoricamente todos que tiverem essa referência podem então acessá-la. Mas daí eu crio a variável em um Handler, muito comum em microcontroladores (considerando que seja uma camada superior, por exemplo o handler de um timer) timeTypedef tempo[TAM_MAX_TIME]; Teria o mesmo efeito do exemplo que… Leia mais »

Felipe Lavratti
Visitante
Felipe Lavratti

Regra do encapsulamento: Evite globais, passe por parâmetros.

André Castelan
Visitante

Olá Erisson, agradeço.

Primeiramente não teria o mesmo efeito pois todos que incluírem este .h poderão mexer diretamente nos atributos do teu timer. O Lavratti escreveu algo que você pode gostar, não sei se já leu: http://www.embarcados.com.br/principio-da-unica-responsabilidade-em-firmwares/

O dano a um sistema embarcado com vazamento de memória ou falta de memória pode ser catastrófico. Não esqueça que muitos rodam por anos sem reinicializar.

Além disso alocar memória é uma tarefa "blockante", o fluxo do seu sistema não fica mais tão determinístico. O que é crucial para sistemas de tempo real.

Abraço

Henrique Rossi
Visitante

Erisson, Caso você tenha a seguinte declaração: timeTypedef tempo[TAM_MAX_TIME]; Não vai ser utilizado um tipo opaco, e, portanto, vai ser possível acessar todos os membros da estrutura em cada instância criada no array. Caso você queira referenciar (consumir) essas instâncias criadas em outros módulos, eu criaria funções globais que o fizessem, evitando criar variáveis globais e encapsulando as suas implementações. Na minha opinião, não é utilizada com frequência alocação dinâmica de memória em sistemas bare-metal pois na grande maioria dos casos existe pouca memória disponível no sistema (mem < 100kB). Por consequência, os algoritmos de alocação (allocators) acabam comprometendo o… Leia mais »

Marcelo Barros de almeida
Visitante
Marcelo Barros

Oi André, como vai ?

Parabéns pela iniciativa em divulgar a técnica.

Um typedef para "struct usuario*" deixaria mais limpo o código, não ?

No mais, a técnica de ponteiros opacos é a única coisa que consegue resolver vários problemas de programação em C. Realmente recomendada.

Marcelo Barros

André Castelan
Visitante

Olá Marcelo, vou bem! Muito obrigado. Também gosto muito desta técnica.

Sim um typedef deixaria o código bem mais limpo! Mas um typedef de um ponteiro esconde algumas informações importantes.

Abraço

Marcelo Barros de almeida
Visitante
Marcelo Barros

Sim, eu concordo, numa situação usual. É que aqui o objetivo é mesmo esconder. Nesse caso, o typedef geraria uma espécie de handle para aquilo que você decidiu encapsular. As chamadas sucessivas apenas levam um handle para o recurso, o usuário da API não precisa saber do que se trata. Bom, mas é um detalhe. Até !

trackback

[...] e encapsulamento. Neste artigo vamos ver como aplicar o polimorfismo no seu código em C. No artigo Orientação a objeto em C: Encapsulamento com estruturas opacas é apresentado como criar uma classe em C e sua leitura é sugerida. Antes de entrarmos nos bits do [...]