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:

#include "usuario.h"
#include 

#define TAM_MAX_SENHA 10

struct usuario{
   char* nome;
   char* login;
   char* senha;
   };

struct usuario* usuario_new(void)
{
   struct usuario* usr;
   usr = malloc(sizeof(struct usuario));
   return usr;
}

int usuario_del(struct usuario* usr)
{
   free(usr);
   return 1;
}

int usuario_autentica(struct usuario* usr, char* senha)
{
  if (strcmp(usr->senha, senha, TAM_MAX_SENHA))
    return 1;
  else 
    return 0;
}

char* usuario_getSenha(struct usuario* usr)
{
   return usr->senha;
}

char* usuario_getLogin(struct usuario* usr)
{
   return usr->login;
}

char* usuario_getNome(struct usuario* usr)
{
   return usr->nome;
}

int usuario_setSenha(struct usuario* usr, char* senha)
{
   usr->senha = senha;
   return 1;
}

int usuario_setLogin(struct usuario* usr,char* login)
{
   usr->login = login;
   return 1;
}

int usuario_setNome(struct usuario* usr, char* nome)
{
   usr->nome = nome;
   return 1;
}

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:

struct usuario;

/* retorna um objeto do tipo usuario */
struct usuario* usuario_new(void);

/* deleta o objeto do tipo usuario */
int usuario_del(struct usuario* usr);

/* verifica se a senha eh igual */
int usuario_autentica(struct usuario* usr,char* senha);

/* retorna a senha do usuario */
char* usuario_getSenha(struct usuario* usr);

/* retorna o login do usuario */
char* usuario_getLogin(struct usuario* usr);

/* retorna o nome do usuario */
char* usuario_getNome(struct usuario* usr);

/* seta a senha do usuario */
int usuario_setSenha(struct usuario* usr, char* senha);

/* seta o login do usuario */
int usuario_setLogin(struct usuario* usr,char* login);

/* seta o nome do usuario */
int usuario_setNome(struct usuario* usr, char* nome);

Para testar vamos utilizar este main.c:

#include 
#include 
#include "usuario.h"

int main()
{
 struct usuario* usr;
 usr = usuario_new();
 usuario_setSenha(usr, "testando");
 printf("Senha: %s\n",usuario_getSenha(usr));
 return 0;
}

É 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:

 /* A linha abaixo ocasiona em erro de compilacao, o main nao conhece os atributos de usuario */
 printf("Senha: %s\n",usr->senha);

 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:

struct usuario{
   char nome[TAM_MAX_NOME];
   char login[TAM_MAX_LOGIN];
   char senha[TAM_MAX_SENHA];
   };

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