Herança (POO)

Para um melhor entendimento deste Artigo veja o Índice (Programação Orientada a Objetos)

Herança

Iremos entrar agora em um tópico que teremos muito o que discutir. Tentarei ser o mais simples , claro e objetivo.
Mas antes de falarmos sobre herança, iremos falar sobre a hierarquia das classes.

Hierarquia

Quando vamos trabalhar com muitas classes, temos que ter em mente a hierarquia (ordem) que estas classes deverão seguir.

Por exemplo:

Não poderemos ter os filhos antes dos pais e antes dos avós. A hierarquia correta para este tipo de classe seria:

Avô -> Pai -> Filho



(quando ví esta imagem, não resisti. Serve perfeitamente para o exemplo de hierarquia)

Assim temos a hierarquia bem definida dentro de nossas classes.

Transformando isso em programação teríamos generalização e especialização.

Como foi visto, a classe mais ao topo da hierarquia é a generalização, enquanto a última classe é a mais especializada.
No diagrama de seqüencia abaixo, podemos ver que as classes mais a esquerda são as genéricas e as mais a direita são as especializadas.

Agora vamos tratar dos tipos de herança que podemos ter.

Herança de Implementação

A herança de implementação é aquela onde herdamos todo o código da classe Pai, não precisamos reescrever código para utilizá-lo, estamos falando de reutilização de código.

Caso desejamos subsitituir um método da classe pai devemos usar overriding, como foi discutido em Definição (Intermediário).

Creio que a herança de implementação é a mais usada, pois herda a assinatura e os métodos da classe pai.

Herança de Interface

Caso não conheçam Interface e Abstração leiam este tópico.

A herança de interface é útil quando precisamos:

  • agrupar as nossas classes dentro de um mesmo contexto;
  • Na programação genérica (veja: Tipos Genéricos (Generics) );
  • Outros que vocês irão descobrir com o tempo;

Na herança de interface apenas herdamos as assinaturas de métodos e propriedades ao contrário da herança de implementação que além de herdar  as assinaturas herdamos também seu código, logo, no caso da interface temos que escrever o código do método ou propriedade.

Agregação

A agregação define uma dependência fraca entre as classes, isto quer dizer que os outros objetos continuam existindo mesmo que o todo for removido.
Podemos dizer que uma classe agregada (o todo) é montada com um conjunto de componentes (as partes).

Como exemplo vamos usar um carro.
Em nosso carro (classe agregada, o todo) temos os componentes (as partes), rodas, portas, motor.
Se desmontarmos o carro, as partes (rodas, portas, motor) continuaram a existir e poderão ser usadas em outro carro.

Composição

A composição define uma dependência forte entre as classes, isto quer dizer que se o todo deixar de existir as suas partes também deixaram de existir.

Exemplo:
Uma revista em quadrinhos, ela é composta por suas páginas. Se o todo, a revista, deixar de existir, suas páginas não farão sentido.

Como outro exemplo podemos citar um pedido e seus itens. Esta ligação é forte, logo é uma composição. Os itens não fariam sentido sem o Pedido. Se excluirmos o pedido seus itens serão excluídos. (Isto não te lembra um relacionamento feito em definição de tabelas? 1 para n ON DELETE CASCADE).

Para finalizarmos vamos colocar um diagrama de classes e um código para reforçar a idéia.

Diagrama acima convertido em código:

Interface IPessoa

namespace Heranca
{
    /// <summary>
    /// esta é uma interface.
    /// Percebam que declaramos apenas os métodos,
    /// iremos usar neste caso a herança de interface
    /// </summary>
    interface IPessoa
    {
        void Dancar();
        DateTime DataNascimento { get; set; }
        void Envelhecer();
        void Falar();
        int Idade { get; }
        string Nome { get; set; }
        Sexo Sexo { get;  }
    }
}

Classe Pessoa

namespace Heranca
{
    public enum Sexo
    {
        NaoDefinido,
        Masculino,
        Feminino
    }

    /// <summary>
    /// esta classe é uma classe genérica pra todo tipo de pessoa.
    /// vejam que aqui iremos herdar a interface IPessoa,
    /// neste caso será uma herança de interface.
    /// Devemos escrever código para todo método e propriedade
    /// definido na interface.
    /// </summary>
    abstract class Pessoa : IPessoa
    {
        /// <summary>
        /// esta propriedade poderá ser sobreescrita
        /// nas classes filhas. mas não é obrigatória
        /// </summary>
        public virtual int Idade
        {
            get
            {
                int idade = DateTime.Now.Year - DataNascimento.Year;
                if (DateTime.Now.Month < DataNascimento.Month ||
                (DateTime.Now.Month == DataNascimento.Month &&
                DateTime.Now.Day < DataNascimento.Day))
                    idade--;
                return idade;
            }
        }

        public DateTime DataNascimento { get; set; }

        /// <summary>
        /// esta propriedade deverá obrigatoriamente ser declarada
        /// na classe pai
        /// </summary>
        public abstract Sexo Sexo { get; }

        public virtual String Nome { get; set; }

        public virtual void Envelhecer()
        {
            DataNascimento = DataNascimento.AddYears(1);
            Console.WriteLine("A pessoa envelheceu um ano.");
        }

        public virtual void Dancar()
        {
            Console.WriteLine("A pessoa está dançando");
        }

        public virtual void Falar()
        {
            Console.WriteLine(@"A pessoa disse: visitem
            http://desenvolvedores.net");
        }
    }
}

Classe Pai

namespace Heranca
{
    /// <summary>
    /// Aqui iremos herdar a classe pessoa.
    /// vejam que apenas escrevi a propriedade Sexo.
    /// Pois na classe pai (abstrata) estava definido
    /// que as filhas deveria escrever o código para sexo.
    /// Mas as outras propriedades e métodos serão herdados de pessoa
    /// </summary>
    class Pai : Pessoa
    {
        public override Sexo Sexo { get { return Sexo.Masculino; } }

        /// <summary>
        /// este método poderá ser sobreescrito nas classes filhas
        /// neste caso o pai está apenas brincando.
        /// Veremos ele sobreescrito na classe filha
        /// </summary>
        public virtual void Brincar()
        {
            Console.WriteLine("O pai está brincando");
        }
    }
}

Classe Filho

namespace Heranca
{
    /// <summary>
    /// aqui o filho herdou todas as características do pai
    /// </summary>
    class Filho : Pai
    {

        /// <summary>
        /// aqui iremos substituir o método Brincar
        /// </summary>
        public override void Brincar()
        {
            Console.WriteLine("O filho está brincando com seu pai.");
        }
    }
}

Classe Aluno

namespace Heranca
{
    /// <summary>
    /// mais um classe que herda de pessoa
    /// </summary>
    class Aluno : Pessoa
    {
        public override Sexo Sexo
        {
            get { return Sexo.Feminino; }
        }

        public int VerNota()
        {
            return new Random().Next(0, 10);
        }
    }
}

E finalmente o código escrito em uma aplicação do tipo console.
Atenção, prestem bastante atenção aos comentários, eles têm dicas importantes.

namespace Heranca
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
             * aqui vai um dica útil.
             * Cuidado ao declarar o seu tipo de objeto
             * pois as propriedades e métodos que você
             * irá visualizar depende do tipo declarado
             *
             * Vejam aqui, vou declarar como um objeto Pessoa
             * mas vou instanciar como um Aluno
             */

            Pessoa pessoaAluno = new Aluno();

            pessoaAluno.Nome = "Marcelo";

            /*
             * reparem que o método VerNota() só existe
             * na classe Aluno, logo na linha 17,
             * mesmo o objeto sendo instanciado como
             * new Aluno() seus métodos só existirão
             * através do seu tipo definido Pessoa
             */

            Console.WriteLine(pessoaAluno.Nome);

            /*
             * Mas, e se precisarmos acessar
             * os dados de Aluno?
             * Simplesmente faça um cast (conversão)
             * de um objeto para outro
             * Vejam:
             */

            Aluno aluno = pessoaAluno as Aluno;

            Console.WriteLine(aluno.VerNota());

            /*
             * A dica acima é válida tambem para os tipos
             * interfaces
             */

            IPessoa pessoaFilho = new Filho();
            pessoaFilho.Falar();

            Filho filho = pessoaFilho as Filho;
            filho.Brincar();

            /*
             * Abaixo um exemplo do override em filho
             */

            Pai pai = new Pai();

            //aqui chamamos o método brincar de Pai
            pai.Brincar();

            //aqui chamamos o método brincar que foi substituido
            Filho meuFilho = new Filho();
            meuFilho.Brincar();

            /*
             * Legal. Mas posso chamar o método
             * Brincar da classe base?
             */

            pai = filho as Pai;
            pai.Brincar();

            /*
             * Não. Não podemos.
             * Apesar da instrução acima não dar erro
             * o método que será chamado é sempre
             * o método substituído.
             *
             * Mas a afirmação acima não é 100% correta.
             * Se em nossa classe Filho o método tivesse
             * sido declarado assim:
             * public new void Brincar()
             * O método chamado seria o da classe Pai,
             * pois neste caso não substituimos o método
             * e sim criamos um novo.
             *
             * Vejam as duas declarações juntas para comparar
             *
             * public new void Brincar()
             * public override void Brincar()
             */

            Console.ReadKey();

        }
    }
}

Ver Índice

É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil.
Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software.
Para saber mais ... http://desenvolvedores.net/marcelo
[]'s

Você vai gostar de...

Postagens populares.

6 Comments

  1. Muito esclarecedor sobre herança e programação orientada a objetos. Obrigado por compartilhar

  2. […] Herança (POO) Tipos Genéricos (Generics) (POO) Abstração e Interface (POO) […]

  3. […] Uma classe ao herdar uma interface deverá escrever todos os seus métodos (iremos ver isso em Herança de Interface nos próximos artigos), ao contrário da classe abstrata que só iremos escrever os […]

Deixe um comentário

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.