Desenvolvimento - Sistemas

Introdução a Orientação a Objetos - Parte 3

Finalizando nossos artigos sobre Orientação a Objetos, falo dessa vez de Herança, uma das funcionalidades mais poderosas que a OOP podes nos dar.

por Marden Menezes



Olá pessoal! Finalizando nossos artigos sobre Orientação a Objetos, falo dessa vez de Herança, uma das funcionalidades mais poderosas que a OOP podes nos dar.

Herança em C#

Como já aprendemos a criação de classes em C#, podemos agora passar a um conceito mais poderoso de reaproveitamento de código, o conceito de Herança.

Vamos imaginar que temos que criar um sistema que trabalha em uma loja de animais de estimação. Como temos vários tipos de animais diferentes e com características diferentes, começamos a criar classes que representam os animais: Cachorro, Gato, Coelho, etc... Percebemos então que temos um problema. Boa parte do código de Cachorro, Gato, Coelho e das outras classes representantes de animais é totalmente igual! Todos os animais têm o atributo e propriedade Raça e possuem o método respirar(). Estamos então repetindo código, e poderíamos utilizar o mecanismo de herança para evitar isso.

Herança é uma funcionalidade de C# que permite criar uma classe "mãe" que define características que todas as classes "filhas" também terão. Poderíamos então criar a classe Animal, que teria a propriedade Raça e o método respirar() e depois dizer que cada classe criada herdava de (ou extendia) Animal. Todas as propriedades, métodos e eventos da classe mãe serão herdadas pelas classes filhas, reaproveitando o código.

Para dizermos que uma classe herda de outra, utilizamos ":" (dois pontos) na definição da classe como descrito abaixo:

class <nomeDaClasseFilha> : <nomeDaClassePai>

É importante lembrar que em C# não existe herança múltipla para classes, o que significa que cada classe só pode herdar de apenas uma outra classe e não mais de uma.

Veja o exemplo no código abaixo sobre herança:

using System;

public class Animal 
{
	private string raca;
	

	public Animal(string raca) 
	{
		Raca = raca;
	}

	public string Raca 
	{
		get { return raca; }
		set { raca = value;}
	}

	public void Respirar() 
	{
		//algum código
	}

}

public class Cachorro : Animal 
{
	//chamando o construtor da classe pai a partir da palavra chave base
	public Cachorro(string raca) : base (raca) 
	{}
}

public class App 
{
	public static void Main() 
	{
		Cachorro c = new Cachorro("raca");
		//possui o método respirar pois herdou da classe Animal
		c.Respirar();
	}
}

Herança com interfaces

Até agora utilizamos uma classe para nos permitir evitar repetição de código no mecanismo de herança. Utilizando a idéia de classe, sempre devemos ter alguma forma de implementação na classe mãe para que possamos reutilizar esse código nas classes filhas ou entendê-los de alguma forma. O mecanismo de herança com uso de interfaces em .NET nos permite definir um tipo de "contrato".

Uma interface nada mais é do que um bloco de código definindo um tipo e os métodos e propriedades que esse tipo deve possuir. Na prática o que acontece é que qualquer classe que quiser ser do tipo definido pela interface deve implementar os métodos dessa interface.

A interface não contém nenhum código de implementação, apenas assinaturas de métodos e/ou propriedades que devem ter seu código implementado nas classes que implementarem essa interface.

Veja no código abaixo um exemplo de uma interface que possui duas assinaturas de métodos e uma de propriedade que devem ser implementadas por uma classe:

	public interface IPessoa 
	{
		//assinaturas dos métodos a serem implementados
		void Trabalhar(string tipo);
		string RetornaLocalTrabalho(int anoInicio, int anoFim);
	
		//assinatura da propriedade a ser implementada
		string  Nome { get; set; }

}

Percebemos então que, para implementarmos essa interface, nossa classe deve possuir implementação para todos os membros da interface, sejam eles propriedades ou métodos. Um outro ponto importante é o fato de não podermos definir na interface os modificadores de acesso de cada membro, deixando isso para as devidas implementações. Colocar um modificador de acesso em uma assinatura de método ou propriedade dentro do bloco da interface causa um erro de compilação.

Classes abstratas

Classes abstratas são aquelas que estão incompletas, ou seja, pode conter alguns métodos ou propriedades que ainda não estão implementados, além de outros que já estão. A idéia é então uma mistura de classes com interfaces.

Classes abstratas devem ser usadas como classes bases (ou classes "mães") e não podem ser instanciadas, devendo então ser herdadas por alguma outra classe para podermos ter a implementação completa das mesmas.

Veja no código abaixo um exemplo de utilização de classes abstratas. Perceba que na classe abstrata Pessoa temos o método abstrato Trabalhar e a propriedade abstrata Nome. Ambos precisam ser implementados por uma classe que herde de Pessoa. Caso a classe que herde não implemente todos os membros abstratos (implementar apenas um ou nenhum), essa classe também deve ser caracterizada como abstrata e assim por diante com todas as suas classes filhas que não implementarem todos os membros abstratos, até que todos sejam implementados.

using System;

namespace ClassesAbstratas
{

	public abstract class Pessoa 
	{
		protected string nome;

		//assinaturas dos métodos a serem implementados
		public abstract void Trabalhar(string tipo);
		public string RetornaLocalTrabalho(int anoInicio, int anoFim) 
		{
			return "Seattle";
		}

		//assinatura da propriedade a ser implementada
		public abstract string  Nome { get; set; }

	}

	class Funcionario : Pessoa
	{

		public override void Trabalhar(string tipo)
		{
			Console.WriteLine("o funcionario está trabalhando...");
		}

		public override string Nome
		{
			get
			{
				return nome;
			}
			set
			{
				if (value != "") 
				{
					nome = value;
				}
			}
		}
	}
}

Polimorfismo

Polimorfismo se refere à capacidade de criação de múltiplas classes com funcionalidades diferentes mas que possuem os mesmos métodos e propriedades, só que implementados de maneiras diferentes. Polimorfismo é essencial para programação orientada a objetos pelo fato de permitir o uso de itens com mesmo nome, não importando o tipo do objeto que está em uso. Por exemplo, se tivermos uma classe base Animal e cada classe filha possuir o método Respirar(). Polimorfismo permite que tenhamos diferentes implementações do método Respirar() em cada classe filha mas que sejam utilizados por outros objetos ou métodos exatamente da mesma forma, independente de qual tipo o objeto é. Por exemplo, polimorfismo nos permite usar o método Respirar() da classe base Animal da mesma forma que chamamos o método Respirar da classe filha Cachorro.

Métodos virtuais e sobrescrita de métodos

A orientação a objetos nos permite sobrescrever métodos. Diferentemente da sobrecarga (overload) , quando podemos criar várias versões diferentes do mesmo método, a sobrescrita (override) permite criarmos uma nova implementação para um método existente em uma classe mãe, substituindo-o. As classes derivadas irão herdar essa nova implementação ao invés da antiga. Não podemos modificar a assinatura do método, tendo que manter a quantidade e os tipos dos parâmetros como também o tipo de retorno do método.

Para podermos sobrescrever um método, ele deve permitir a sobrescrita tendo sido declarado como "virtual" na sua declaração. O método que vai sobrescrever deve ter a palavra chave "override" na sua declaração.

Uma dúvida que pode surgir é : "Se eu estou substituindo a implementação da classe base e ela será então apagada, não há nenhuma forma de extender a implementação ao invés de substitui-la? Ou seja, utilizar a implementação da classe base e adicionar algum código a mais no novo método?". A palavra chave "base" é a forma de chamar a classe base. A partir dela nós podemos chamar o método da classe base e adicionarmos mais código para extender suas funcionalidades no método que sobrescreverá. Veja no exemplo abaixo a utilização de polimorfismo e de sobrescrita de métodos:

using System;

namespace SobrecargaPolimorfismo
{
	public class Pessoa 
	{
		public virtual void Respirar() 
		{
			Console.WriteLine("Método respirar da classe Pessoa");
		}
	}

	public class Funcionario : Pessoa 
	{
		public override void Respirar() 
		{
			Console.WriteLine("Método respirar da classe Funcionario");
		}
	}

	public class Entregador : Funcionario 
	{
		public override void Respirar() 
		{
			//utilizando a implementação da classe Funcionario
			base.Respirar();
			Console.WriteLine("Método respirar da classe Pessoa");
		}
	}
	public class App 
	{
		public static void Main() 
		{
			Pessoa p = new Pessoa();
			p.Respirar();
			p = new Funcionario();
			p.Respirar();
			p = new Entregador();
			p.Respirar();
		}
	}
}

É só, pessoal! Obrigado pela atenção e se sintam livres para enviar e-mails para mim com dúvidas, sugestões, opiniões e reclamações!

Grande abraço!

Marden Menezes

Marden Menezes - Líder do maior grupo de usuários do Brasil, o Sharp Shooters (www.sharpshooters.org.br). Marden é Microsoft Certified Professional e Microsoft Student Ambassador, ministrando palestras e cursos em todo o Brasil e difundindo a plataforma .NET nas universidades.
Como representante do comitê de relacionamento com grupos de usuários da INETA (www.ineta.org), Marden vem trabalhando para a difusão dos grupos de usuários .NET no país.