Tipo de dado Abstrato (TDA)

Conhecer para que serve e saber implementar utilizando o tipo de dado abstrato permite a construção de programas modulares e manuteníveis.

Recordando o que é tipo de dados (primitivos)

Se você curtir esse artigo, não esqueça de dar uns cliques nos anúncios para ver o que os anunciantes tem para te oferecer. Vou ficar muito contente.

Quando a gente vai falar de tipo de dados, a gente tem que lembrar duas coisas importantes:

  1. O tipo de dado define um certo domínio de valores.
  2. Define as operações permitidas com esses valores.

Isso pode parecer um pouco confuso, mas fica muito mais fácil se explicar um pouquinho mais.
Tomemos por exemplo, a declaração de um inteiro. Em relação ao tipo de dado inteiro, podemos dizer que ele:

  • Aceita somente valores inteiros
  • As operações permitidas com o tipo de dado inteiro são: adição, soma, multiplicação, subtração, operações de bitwise e outras.

Agora se a gente pegar a declaração de um tipo de dado float, podemos dizer que:

  • Esse tipo de dado aceita apenas valores de ponto flutuante.
  • As operações permitidas são: : adição, soma, multiplicação, subtração e outras, porém operações de bitwise não são permitidas.

Dito isso, a gente confirma que a definição de um tipo de dado (data type) serve para definir o domínio de valor de um dado e as operações que são permitidas com esses valores.

Tipos de dados definidos por usuários:

Uma vez conversado brevemente sobre o que é um tipo de dado primitivo (int, float, double, char…), vamos pular para os tipos de dados definidos por usuários.

Quando a gente está falando de tipos de dados definidos por usuários, as coisas mudam um pouco e as operações e valores do tipo de dado definido pelo usuário não é mais especificado pela linguagem em si. Na verdade os valores definidos pelo usuário e operações permitidas nesses valores que foram definidas pelo usuário, são especificadas pelo usuário.

Isso é feito utilizando estruturas (struct), que permite que diferentes dados sejam combinados, conforme exemplo abaixo:

#include <stdio.h>
#include <stdlib.h>

int main()
{

    struct caixa{
        char tipo[10];
        float tamanho;
        float largura;
        float altura;
    }caixa;
    strcpy (caixa.tipo, "madeira");
    caixa.tamanho = 10.0;
    caixa.largura = 12.5;
    caixa.altura = 7;

    printf(
        "Tipo de caixa: %s.\n"
        "Tamanho: %.2f\n"
        "Largura: %.2f\n"
        "Altura: %.2f\n",
        caixa.tipo,
        caixa.tamanho,
        caixa.largura,
        caixa.altura
    );
}

No programa acima, “caixa” é um tipo de dado definido pelo usuário e a saída desse programa, pode ser vista abaixo:

Tela do programa listando o tipo, tamanho e largura da caixa.

Tudo bem até aí. Mas e onde é que entra o tipo de dado abstrato?

Tipo de dado abstrato é como o tipo de dado definido pelo usuário, o qual utilizando funções, define as operações que podem ser realizadas, sem especificar o que há dentro da função e como a operação é executada.

É como se fosse uma caixa preta. O usuário envia os parâmetros, chama as funções e a estrutura que usa o tipo de dado abstrato processa esses parâmetros, devolvendo o dado processado.

Exemplo de tipo de dado abstrato:

Se pegarmos o exemplo do programa que cria uma caixa, seria possível acrescentar algumas operações como:

OperaçãoFunção
cria_caixa()Cria uma caixa
abre()Abre a caixa
fecha()Fecha a caixa
status()Informações sobre a caixa
Lista de operações possíveis com a caixa

Essas seriam os elementos possíveis e as operações possíveis de se realizar na caixa, mas no caso do tipo de dado abstrato, o usuário apenas define quais são as funções possíveis, sem realizar de fato a implementação e não há a necessidade de conhecer como essas funções são implementadas.

Repetindo, o tipo de dado abstrato deve ser entendido como uma caixa preta que esconde do usuário os detalhes de sua estrutura interior e esconde do usuário como o tipo de dado foi projetado.

Dito isso, temos que entender que há várias maneiras de se implementar um tipo de dado abstrato. Nesse exemplo, o tipo de objeto abstrato caixa. Pode utilizar struct, pode usar listas ligadas ou até um array contendo as informações sobre a caixa e seu status.

Porque precisamos do tipo de dado abstrato?

O tipo de dado abstrato é uma ferramenta importante para a construção de um código modular e de fácil manutenção. Ao separar do programa, a implementação de um tipo de dado, fica muito mais fácil implementar novas funcionalidades, sem interferir no resto do código legado.

São 2 programas e 3 nomes:

  • Programa cliente – que é o programa que utiliza a estrutura de dados (p. ex main.c).
  • Implementação – que é o programa que implementa a estrutura de dados (p. ex tda.c).
  • Interface – Lista as funções que a estrutura de dados pode realizar (p.ex tda.h).

Podemos dizer que o programa que utiliza uma estrutura de dados abstrata, chama-se de programa “cliente” e ele não conhece como a implementação do programa é realizada.
O programa que implementa a estrutura de dado abstrata é chamado de “implementação”.
A “implementação” possui uma parte chamada “interface”, que é a porta de comunicação entre o programa “cliente” e a “implementação”.

Tipo de dado abstrato fornece abstração, ocultando detalhes da implementação do código, simplificando o código e facilitando sua manutenção.

Indo por partes. Exemplo de implementação de um tipo de dado.

Segue um dos exemplos possíveis de como transformar a estrutura “caixa” abordada acima, em um tipo de dado que vou chamar de “tipo_caixa” que vai definir uma variável “caixa” do “tipo_caixa”. Conforme dito, são 3 arquivos: Programa cliente, interface e implementação.

Programa Cliente (arquivo main.c):

#include <stdio.h>
#include "tda.h"

int main() {
    int tamanho_obj,id_caixa;
//Declarando uma caixa do "tipo_caixa"
    tipo_caixa caixa;

//Chamando o conjunto de operacoes permitidas para o tipo_caixa.
    configura_material_caixa(&caixa,"madeira");
    configura_tamanho_caixa(&caixa,12);
    configura_largura_caixa(&caixa,17.4);
    configura_altura_caixa(&caixa,7.2);
    configura_id_caixa(&caixa, 32);
    tamanho_obj = obj_size(&caixa);
    id_caixa = obj_getid(&caixa);

//Imprimindo individualmente cada um dos elementos da caixa
//Nesta implementacao é possivel acessar diretamente
//cada um dos elementos da estrutura
    printf("Material da caixa: %s.\n"
        "Tamanho: %.1fcm\n"
        "Largura: %.1fcm\n"
        "Altura: %.1fcm\n"
        "id da caixa: %d\n"
        "Tamanho do objeto: %d\n\n",
        caixa.material,
        caixa.tamanho,
        caixa.largura,
        caixa.altura,
        id_caixa,
        tamanho_obj
    );

//Funcao para imprimir todos elementos da caixa
    imprime_caixa(&caixa);
   return 0;
}

Interface (arquivo tda.h):

#ifndef TDA_H_INCLUDED
#define TDA_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>

//Interface.
//Aqui lista a estrutura e as operacoes que podem ser
//realizadas na estrutura, bem como os parametros necessários.

 typedef struct caixa{
     char material[20];
     float tamanho;
     float largura;
     float altura;
     int id;
    }tipo_caixa;

//Conjunto de operacoes permitidas para o tipo_caixa.
size_t obj_size(struct caixa *);
void configura_material_caixa(struct caixa *,char[]);
void configura_id_caixa(struct caixa *, int);
void configura_tamanho_caixa(struct caixa *,float);
void configura_largura_caixa(struct caixa *,float);
void configura_altura_caixa(struct caixa *,float);
int obj_getid(struct caixa *);
void imprime_caixa(struct caixa *);

#endif // TDA_H_INCLUDED

Implementação (arquivo tda.c):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tda.h"

//Implementacao das operacoes permitidas para o tipo_caixa
void configura_material_caixa(struct caixa *o, char i[]){
     strcpy (o->material, i);
}

void configura_id_caixa(struct caixa *o, int i) {
    o->id = i;
}

void configura_tamanho_caixa(struct caixa *o,float i){
    o->tamanho = i;
}

void configura_largura_caixa(struct caixa *o,float i){
    o->largura =i;
}

void configura_altura_caixa(struct caixa *o,float i){
    o->altura = i;
}

int obj_getid(struct caixa *o) {
    return o->id;
}

size_t obj_size(struct caixa *o) {
    return sizeof (*o);
}

void imprime_caixa(struct caixa *o){
    printf("Material de caixa: %s.\n"
        "Tamanho: %.1fcm\n"
        "Largura: %.1fcm\n"
        "Altura: %.1fcm\n"
        "Id da caixa %d\n"
        "Tamanhho do objeto %u\n",
        o->material,
        o->tamanho,
        o->largura,
        o->altura,
        obj_getid(o),
        (unsigned)obj_size(o)
    );
}
Tela mostrando a saída da implementação de um tipo de dado “tipo_caixa”. Note que é possível acessar diretamente a estrutura do dado a partir do programa cliente main.c e isso deve ser evitado.

Implementando um tipo de dado abstrato e ocultando sua estrutura

No caso a seguir, estou implementando novamente o tipo de dado “tipo_caixa” com os três arquivos: Programa Cliente, Interface e a Implementação.

A diferença é que a estrutura do mesmo “typedef struct Caixa” fica oculta dentro da implementação e a interação vai se dar apenas utilizando as funções que estiverem definidas na interface, que no caso é o arquivo “tda.h”. Analisando o código, não é mais possível acessar diretamente os atributos do tipo de dado “tipo_caixa”. Dá erro de compilação se tentar fazer isso.

Programa cliente (arquivo main.c)

#include <stdio.h>
#include "tda.h"

int main() {
    int tamanho_obj,id_caixa;
//Declarando uma caixa do "tipo_caixa"
    tipo_caixa caixa =Caixa_new();

//Chamando o conjunto de operacoes permitidas para o tipo_caixa.
    configura_material_caixa(caixa,"madeira");
    configura_tamanho_caixa(caixa,12);
    configura_largura_caixa(caixa,17.4);
    configura_altura_caixa(caixa,7.2);
    configura_id_caixa(caixa, 32);
    tamanho_obj = obj_size(caixa);
    id_caixa = obj_getid(caixa);

//Funcao para imprimir todos elementos da caixa
    imprime_caixa(caixa);

//Função para imprimir somente o id da caixa
    imprime_id_caixa(caixa);


//Nesta implementacao NÃO É possivel acessar diretamente
//cada um dos elementos da estrutura.
//Se descomentar essa parte, vai dar erro de compilação.
//    printf("Material da caixa: %s.\n"
//        "Tamanho: %.1fcm\n"
//        "Largura: %.1fcm\n"
//        "Altura: %.1fcm\n"
//        "id da caixa: %d\n"
//        "Tamanho do objeto: %d\n\n",
//        caixa.material,
//        caixa.tamanho,
//        caixa.largura,
//        caixa.altura,
//        id_caixa,
//        tamanho_obj
//    );

//Liberando memoria
    destroi_caixa(caixa);

    return 0;
}

Interface (arquivo tda.h)

#ifndef TDA_H_INCLUDED
#define TDA_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>

//Interface.
//Aqui lista a estrutura e as operacoes que podem ser
//realizadas na estrutura, bem como os parametros necessários.

typedef struct Caixa *tipo_caixa;

//Conjunto de operacoes permitidas para o tipo_caixa.
extern size_t obj_size(struct Caixa *);
extern void configura_material_caixa(struct Caixa *,char[]);
extern void configura_id_caixa(struct Caixa *, int);
extern void configura_tamanho_caixa(struct Caixa *,float);
extern void configura_largura_caixa(struct Caixa *,float);
extern void configura_altura_caixa(struct Caixa *,float);
extern int obj_getid(struct Caixa *);
extern void imprime_caixa(struct Caixa *);
extern void imprime_id_caixa(struct Caixa *);
extern void destroi_caixa(struct Caixa *);

#endif // TDA_H_INCLUDED

Implementação (arquivo tda.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tda.h"

typedef struct Caixa{
     char material[20];
     float tamanho;
     float largura;
     float altura;
     int id;
};

tipo_caixa Caixa_new(void){
    tipo_caixa caixa = malloc(sizeof (struct Caixa));
    caixa->id = 1;
    strcpy (caixa->material, "teste");
    caixa->altura = 10.2;
    caixa->largura = 2.3;
    caixa->tamanho = 8.4;
    return caixa;
}

void destroi_caixa(struct Caixa *o){
    free(o);
}

//Implementacao das operacoes permitidas para o tipo_caixa
void configura_material_caixa(struct Caixa *o, char i[]){
     strcpy (o->material, i);
}

void configura_id_caixa(struct Caixa *o, int i) {
    o->id = i;
}

void configura_tamanho_caixa(struct Caixa *o,float i){
    o->tamanho = i;
}

void configura_largura_caixa(struct Caixa *o,float i){
    o->largura =i;
}

void configura_altura_caixa(struct Caixa *o,float i){
    o->altura = i;
}

int obj_getid(struct Caixa *o) {
    return o->id;
}

size_t obj_size(struct Caixa *o) {
    return sizeof (*o);
}

void imprime_caixa(struct Caixa *o){
    printf("Material de caixa: %s.\n"
        "Tamanho: %.1fcm\n"
        "Largura: %.1fcm\n"
        "Altura: %.1fcm\n"
        "Id da caixa %d\n"
        "Tamanhho do objeto %u\n\n",
        o->material,
        o->tamanho,
        o->largura,
        o->altura,
        obj_getid(o),
        (unsigned)obj_size(o)
    );
}

void imprime_id_caixa(struct Caixa *o){
    printf("Imprimindo apenas o Id da caixa\n"
           "Id da caixa %d\n", o->id);
}
Tipo de dado abstrato, ocultando sua estrutura, a implementando no arquivo tda.c. Nesse caso não dá para acessar a estrutura de dados diretamente a partir do programa cliente main.c

Implementando conforme indicado acima, é possível criar bibliotecas e as vender. Fornece-se apenas o arquivo .h (header) com as funções permitidas e a implementação já compilada, ficando a cargo do usuário apenas a construção do programa cliente.

GitHub

https://github.com/renatopierri/algoritmos/tree/main/TipoDado
https://github.com/renatopierri/algoritmos/tree/main/TipoDadoUsuario
https://github.com/renatopierri/algoritmos/tree/main/TipoDadoAbstrato

Bibliografia:

TENENBAUM, Aaron M.; LANGSAM, Yedidyah; AUGESNSTEIN, Moshe J.. Estruturas de Dados Usando C. São Paulo: Pearson, 2013. 884 p. ISBN 13: 978-85-346-0348-5
UNIVERSITY, Princeton. Abstract Data Types. 2004. Disponível em: https://www.cs.princeton.edu/courses/archive/spring04/cos217/lectures/Adts.pdf. Acesso em: 19 dez. 2022.
DATA Types vs. Abstract Data Types. Música: Axol X Alex Skrindo – You [Ncs Release]. [S.I.]: Neso Academy, 2020. (8 min.), Youtube, son., color. Disponível em: https://youtu.be/ZniDyolzrBw. Acesso em: 18 dez. 2022.

Curtiu?

Vou ficar muito contente se você clicar nos anúncios do meu site e dar uma olhadinha no que os anunciantes tem de bom para te oferecer.

Last updated by at .