Orientação a objeto em C: Polimorfismo

Polimorfismo

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 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 tópico é válido lembrar que polimorfismo é o divisor de águas entre programação procedural e orientada a objetos. É dito que para uma linguagem ser verdadeiramente orientada a objetos é preciso suportar polimorfismo, de outra maneira, trata-se de uma linguagem baseada em objetos.

 

 

Exemplo de Polimorfismo

 

Polimorfismo permite que o comportamento das classes seja modificado através da herança, no artigo usa-se o exemplo hipotético de um aplicativo gráfico.  Uma parte de código qualquer, desse aplicativo hipotético, terá um apontador para um objeto que será responsável por desenhar um retângulo e outro apontador para um objeto capaz de renderizar texto, ambos os objetos renderizadores terão a função draw() que deve se comportar diferente para cada caso, um, desenhando retângulos e outro texto. O polimorfismo entra para resolver o seguinte problema: nem sempre o código terá a posse de um texto e de um retângulo, como no exemplo, durante a execução ele pode ter a posse de qualquer objeto desenhável e em qualquer quantidade, e, portanto, os seus apontadores precisam apontar um tipo genérico de elemento desenhável, ou seja, é usado uma classe comum para representar qualquer elemento desenhável onde o comportamento da sua função draw() é alterada de acordo com quem herda-a.

 

O exemplo ilustrado pode ser implementado em C com a representação UML abaixo:

exemplo-Polimorfismo
Figura 1 - representação UML do exemplo

 

A chave do polimorfismo se dá pela tabela virtual, em C trata-se de uma simples struct ou, neste caso, classe, que contém apontadores para funções que serão substituídas e mais informações que são necessárias para que métodos da classe herdada sejam implementados pela classe herdeira.

 

Para implementar o polimorfismo em C, são necessários alguns atores como identificado no diagrama UML:

 

  1. Classe Abstrata: widget_t é a classe abstrata, que será usada sempre para chamar a função draw();
  2. Classes Herdeiras: text_t e rectangle_t implementam a função draw() de acordo com as suas necessidades;
  3. Tabela Virtual: widget_vtable_t é preenchida por text_t ou por rectangle_t, a tabela contém os métodos substituíveis através da herança, no caso, chamadas para o método draw() de widget_t são redirecionadas para as classes text_t e rectangle_t através da tabela virtual.

 

Em OOP, quando uma classe filha é criada, o construtor da classe pai também é chamado, no nosso caso, temos que também configurar a tabela virtual. Na solução proposta, a tabela virtual é uma classe assim como widget_t e demais, porém possui uma política de uso bem definida onde deve sempre ser criada e configurada por text_t e rectangle_t e transferida para widget_t através do seu construtor. Essa característica é observada no código abaixo.

 

 

Em OOP também ocorre uma conversão implícita de um objeto da classe herdeira para qualquer classe herdada, agora temos que representar esse comportamento:

 

 

A função draw() da classe widget_t tem que chamar a implementação correta de acordo com a tabela virtual:

 

 

E, finalmente, o uso do esquema apresentado se dá da seguinte maneira:

 

 

No exemplo ilustrado não existe destrutor virtual por fins didáticos, ainda assim, é muito importante permitir que a classe herdeira possa ser destruída corretamente a partir da classe herdada, portanto o método de destruição também deve estar presente na tabela virtual.

 

 Os códigos apresentados são encontrados na íntegra em http://github.com/felipe-lavratti/marsh/tree/master/marsh/src. Leves modificações foram efetuadas por fins didáticos.

 

 

Referências

Design Pattern for Embedded Systems in C, Bruce Powel Douglass – Introduction
Marsh, Interface Gráfica em ANSI C de código aberto, Github.

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.

Felipe Lavratti
Engenheiro, desenvolve software para embarcados e Linux, evangelista dos processos de qualidade. Mais informações e redes sociais: http://flp.lv

2
Deixe um comentário

avatar
 
1 Comment threads
1 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Felipe LavrattiRichard Heller Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Richard Heller
Visitante
Richard Heller

Olá Felipe, este é o segundo artigo seu que eu li (o anterior foi sobre o princípio da responsabilidade única) e gostei muito da maneira como você busca difundir o uso de padrões de projeto no mundo dos embarcados. O Polimorfismo é sem dúvida uma característica fundamental da OO. Muitas vezes nos deparamos com a necessidade de criar em C uma mesma função que tenha comportamento diferente dependendo do contexto em que se encontra. E no C, a maneira "padrão" de resolver este problema é utilizando condicionais, como um "switch" ou um "if else"; porém a implementação fica muito mais… Leia mais »

Felipe Lavratti
Visitante
Felipe Lavratti

Obrigado Richard, você está certo, implementar polimorfismo em C não acontece sem alguns overheads.