De que se trata o artigo

Este artigo aborda o que são e de que maneira classes abstratas podem ser utilizadas na codificação de uma aplicação, implementado na linguagem C#.

Em que situação o tema é útil

O tema é útil para quem quer reaproveitar as estruturas de técnicas de OO, aumentando a produtividade no desenvolvimento de software, evitando a duplicação de código ao longo de um sistema.

Reaproveitamento de código com classes abstratas O .NET Framework está fortemente embasado em técnicas de Orientação a Objetos (OO). Classes e objetos são os pilares para a implementação de sistemas nesta plataforma. Diante disto, o conceito de classes abstratas merece ser destacado, já que o mesmo possui estreita ligação com uma das principais metas da OO: o reuso de código.

A Orientação a Objetos (OO), como metodologia de desenvolvimento de software, é fruto de um longo processo de evolução, originado de uma série de práticas e conceitos que demonstraram alta eficácia em numerosos projetos. Uma das grandes motivações por trás do surgimento da OO foi à necessidade de transpor para o mundo de softwares elementos do mundo real, com os mesmos sendo representados por objetos com atributos (características que correspondem a dados) e comportamentos (operações acionadas com a finalidade de processar informações).

O conjunto de conceitos que constitui a OO foi elaborado com o intuito de fornecer um amplo leque de alternativas prestando-se à resolução de problemas dos mais variados tipos dentro do desenvolvimento de software. Tratam-se de técnicas que não estão restritas a uma tecnologia, podendo ser implementadas sem maiores contratempos em plataformas e linguagens bem heterogêneas como C#, VB.NET, Java, C++, Delphi, dentre outras.

As práticas propostas pela Orientação a Objetos dão uma forte ênfase à flexibilidade diante de eventuais mudanças em uma aplicação, além de priorizar a reutilização de elementos que já foram criados anteriormente (através de uma modelagem que procura motivar isso). Considerando especificamente a noção de reuso, bibliotecas de componentes são talvez um dos exemplos mais notórios de aplicação desta ideia: funções voltadas ao atendimento de certas finalidades e que foram criadas anteriormente podem ser reaproveitadas, desde que por sistemas (ou bibliotecas derivadas) criados a partir da mesma plataforma ou linguagem em que tais elementos foram projetados.

Este artigo tem como foco discutir um dos principais meios empregados para se promover o reaproveitamento de código dentro da OO: as classes abstratas. Será apresentado de que modo uma classe abstrata é codificada em C#, além de como subclasses concretas que derivem deste tipo básico são implementadas.

Implementação de Classes Abstratas em .NET

Numa classe abstrata não podem ser gerados objetos diretamente. O objetivo desta estrutura é englobar construções (métodos e propriedades) que são comuns a um conjunto de classes com características semelhantes, deixando que essas últimas apenas implementem funcionalidades que variam de acordo com um conjunto de situações específicas.

Já um tipo que herda de uma classe abstrata é classificado como uma classe-filha ou subclasse deste elemento-base; o tipo abstrato é chamado de superclasse. Classes que implementam todos os membros abstratos de uma definição abstrata são ditas como sendo concretas. É possível que um tipo não represente tudo aquilo que é abstrato em sua superclasse: neste caso, esta subclasse também será abstrata.

A herança é uma ideia central da Orientação a Objetos, além de corresponder a uma das bases do processo de reuso de software; dois conceitos estão dentro da OO diretamente relacionados à herança:

• Generalização: acontece ao criar uma superclasse, com diversos comportamentos possíveis;

• Especialização: codificação de subclasses que herdam funcionalidades definidas num tipo principal (superclasse).

Assim como acontece com outras construções de OO, classes abstratas também contam com uma forma de representação própria em digramas de classe UML. A Figura 1 apresenta um exemplo de uma hierarquia de classes:

• A classe abstrata de nome “ClasseAbstrata”, em que foi declarado um método concreto (“MetodoConcreto”, cujo conteúdo está sendo implementado no tipo em questão), além de dois métodos abstratos (“MetodoAbstratoA” e “MetodoAbstratoB”, a serem codificados em subclasses concretas que estão em níveis hierárquicos inferiores);

• Duas subclasses concretas (“SubClasseConcretaA” e “SubClasseConcretaB”), em que todos os elementos abstratos de “ClasseAbstrata” (“MetodoAbstratoA” e “MetodoAbstratoB”) foram devidamente implementados;

• Uma subclasse (“SubClasseAbstrataA”) que não implementa uma das operações (“MetodoAbstratoB”) do tipo principal do qual herda (“ClasseAbstrata”), sendo por essa razão também (e obrigatoriamente) abstrata.

Figura 1. Diagrama de classes UML demonstrando como representar classes abstratas

Nota: O diagrama de classe detalhado anterior foi criado a partir da ferramenta Astah UML. Esta aplicação possui tanto versões gratuitas quanto proprietárias, constando na seção de Links o endereço do qual pode ser efetuado o download.

Na notação seguida pelos diagramas de classe UML assume que elementos são abstratos (classes e declarações de métodos) quando seus identificadores estiverem em “itálico”. Fonte normal indica que o item em questão é concreto (ou seja, não abstrato e, portanto, implementado naquele ponto).

Nota do DevMan

Um diagrama de classes é um modelo UML empregado para demonstrar as diferentes características e relacionamentos existentes entre classes concretas, abstratas e outras construções que participam de um contexto.

UML (“Unified Modeling Language”) é uma linguagem para modelagem de estruturas e que se baseia em conceitos de orientação a objetos. Este padrão contempla uma série de diagramas, sendo o de classes é um dos mais conhecidos.

A definição de elementos abstratos em C# é feita por meio da palavra-chave “abstract”. Através desta instrução é possível marcar que classes, métodos e propriedades não serão implementados na superclasse, cabendo a alguma das subclasses baseadas no mesmo a codificação da estrutura correspondente. A simples existência de um método e/ou propriedade abstrata força que a classe em questão precise também ser marcada com a palavra-chave “abstract”. Considerando o suporte que a plataforma .NET oferece para a criação de classes abstratas, são possíveis as seguintes construções:

• Tipos sem nenhuma propriedade ou método abstrato, tendo somente a definição de classe marcada como abstrata. Subclasses neste caso precisarão apenas herdar da superclasse, não havendo a necessidade de preocupar com membros (propriedades e métodos) abstratos;

• Classes que contem com métodos e/ou propriedades abstratas. Classes concretas derivadas precisarão implementar diretamente ou indiretamente (herdando de outras subclasses que estão acima delas na hierarquia) os elementos marcados como abstratos;

• Subclasses que não implementam todos os itens abstratos da superclasse, delegando tal tarefa às classes concretas que estão numa hierarquia mais abaixo.

A Listagem 1 apresenta um exemplo de código contendo uma classe abstrata.

Listagem 1. Exemplo de classe abstrata

  ...
   
  namespace TesteClassesAbstratas
  {
      public abstract class ClasseAbstrata
      {
          public abstract int PropriedadeAbstrata { get; set; }
   
          public int PropriedadeConcreta
          {
              get
              {
                  // Código que retorna o valor da propriedade
                  ...
   
              }
              set
              {
                  // Código que recebe um novo valor para a propriedade
                  ...
   
              }
          }
   
          public abstract void MetodoAbstrato();
   
          public void MetodoConcreto()
          {
              // Código implementando o método
              ...
   
          }
      }
  }

Em conformidade com o que já foi explicado anteriormente, métodos e propriedades abstratos são marcados com a palavra reservada “abstract”. Em termos práticos, isso implica que os mesmos serão implementados somente em classes que derivam do tipo principal.

Já na Listagem 2 é apresentado um exemplo de instrução inválida.

Listagem 2. Exemplo de instrução inválida envolvendo uma classe abstrata


  ...
   
  ClasseAbstrata objeto = new ClasseAbstrata(); // Instrução inválida
   
  ... 

Instanciar uma classe abstrata resulta em um erro de compilação. Uma premissa básica dentro da Orientação a Objetos é o fato de que não se podem criar instâncias de uma classe abstrata. Objetos são gerados somente a partir da instanciação de uma classe concreta.

Na Listagem 3 é demonstrado um exemplo de classe concreta que implementa o tipo abstrato ClasseAbstrata.

Listagem 3. Implementando uma classe concreta a partir de um tipo abstrato


  ...
   
  namespace TesteClassesAbstratas
  {
      public class ClasseConcreta : ClasseAbstrata
      {
          public override int PropriedadeAbstrata
          {
              get
              {
                  // Código que retorna o valor da propriedade
                  ...
   
              }
              set
              {
                  // Código que recebe um novo valor para a propriedade
                  ...
   
              }
          }
   
          public override void MetodoAbstrato()
          {
              // Código implementando o método que
              // era abstrato
          }
      }
  }

O fato de um tipo ser concreto força o mesmo a implementar todos os elementos abstratos das estruturas básicas das quais este deriva. A classe resultante poderá então ser instanciada, conforme exemplificado na Listagem 4.

Listagem 4. Instanciando uma classe concreta


  ...
   
  ClasseConcreta objeto = new ClasseConcreta(); // Instrução válida
   
  ... 

Uma técnica bastante comum é declarar parâmetros de métodos especificando uma classe abstrata. Em termos práticos, apenas instâncias de tipos concretos poderão ser repassadas, com isto representando uma forma flexível de se implementar uma solução: a operação de destino não precisa conhecer detalhes de como as classes concretas encontram-se estruturadas, esperando assim, apenas um objeto compatível com o superclasse/tipo abstrato utilizado.

Importante destacar que a utilização de classes abstratas é o pré-requisito na implementação de vários padrões de projeto (design patterns), fornecendo assim os meios para a resolução de problemas específicos dentro do desenvolvimento de software. Esse é o caso de muitos dos patterns conhecidos como GoF.

Nota do DevMan

Design patterns ou padrões de projetos são técnicas de eficiência já comprovada na resolução de situações comuns na codificação de sistemas. Um padrão representa, em termos gerais, um conjunto de orientações possíveis de voltadas ao atendimento de um tipo de problema específico.

GoF é a sigla em inglês para “Gang of Four”, sendo um apelido que se refere ao trabalho conjunto de quatro autores (Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides). Estes especialistas foram os responsáveis por documentar 23 padrões no famoso livro “Design Patterns: Elements of Reusable Object-Oriented Software”. Tais patterns foram especificados na época tomando-se como base exemplos desenvolvidos em C++ e Smalltalk; contudo, a ampla abrangência destes padrões permitiu que os mesmos tenham validade ainda hoje, contribuindo em atividades cotidianas de desenvolvedores de plataformas como .NET e Java.

Conclusão

As linguagens de maior popularidade na atualidade têm na Orientação a Objetos uma das suas principais bases. As diversas ideias propostas pela OO procuram fornecer meios para um desenvolvimento bem estruturado, simplificando o dia a dia dos desenvolvedores, além de priorizar ideias simples, mas de grande valia em projetos de software: um bom exemplo disto é a possibilidade de reaproveitamento de código.

Considerando os diversos conceitos englobados pela Orientação a Objetos, classes abstratas são umas das principais maneiras de se cumprir com a meta de reuso de estruturas dentro de uma aplicação. Estes elementos permitem que comportamentos genéricos fiquem centralizados dentro de uma classe, com os tipos que herdam desta última apenas implementando funcionalidades específicas, as quais variam em atendimento a algum requisito da solução.

O próprio .NET Framework faz um uso intensivo de classes abstratas para a definição/implementação de diversos recursos. Componentes pertencentes a bibliotecas comercializadas ou oferecidas gratuitamente por terceiros também aplicam esta técnica, possibilitando inclusive que algumas funcionalidades possam ser customizadas conforme demandas do processo de desenvolvimento.

Links

abstract (C# Reference)
http://msdn.microsoft.com/en-us/library/sf985hc5%28v=vs.100%29.aspx

Abstract and Sealed Classes and Class Members
http://msdn.microsoft.com/en-us/library/ms173150.aspx

Astah UML
http://astah.net/editions/uml

Unified Modeling Language
http://www.uml.org/