OOP - Herança x Composição


O paradigma da Programação Orientação a objetos - OOP - traz muitos conceitos novos e, para quem vem do paradigma procedural usando as linguagens estruturadas e do desenvolvimento orientado ao banco de dados, às vezes muitos desses conceitos podem não ser bem compreendidos.

Vamos iniciar recordando a definição de programação procedural:

O termo Programação procedural (ou programação procedimental) é as vezes utilizado como sinônimo de Programação imperativa (Paradigma de programação que especifica os passos que um programa deve seguir para alcançar um estado desejado), mas o termo pode se referir a um paradigma de programação baseado no conceito de chamadas a procedimento.

Procedimentos, também conhecidos como rotinas, sub-rotinas, métodos, ou funções (que não devem ser confundidas com funções matemáticas, mas são similares àquelas usadas na programação funcional) simplesmente contém um conjunto de passos computacionais a serem executados. Um dado procedimento pode ser chamado a qualquer hora durante a execução de um programa, inclusive por outros procedimentos ou por si mesmo.

(fonte : http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_procedural)

Apenas para ilustrar, abaixo temos uma tabela comparando as características básicas do paradigma OOP e do procedural:

Programação Orientada a Objetos Programação Estruturada
Métodos Procedimentos e funções
Instâncias de variáveis Variáveis
Mensagens Chamadas a procedimentos e funções
Classes Tipos de dados definidos pelo usuário
Herança -
Polimorfismo

Se você quer construir um código limpo , legível , escalável e reutilizável usando os conceitos da OOP terá que ter uma compreensão sólida dos principais conceitos deste paradigma.

Como o C# e VB .NET são linguagens Orientada a Objetos(OO), podemos usá-las para criar projetos dentro do paradigma OOP.

Este artigo procurar focar dois mecanismos básicos usados para reutilizar código muito utilizados na programação orientada a objetos (POO) : a herança e a composição.

Qual seria melhor ?   Usar composição ou usar herança ?

Vou responder a pergunta e depois explicar...

Resposta:

Em geral usar composição traz mais vantagens do que utilizar herança.

Os projetos tendem a ser mais simples e reutilizáveis em se favorecendo a composição ao invés da herança.

Na verdade a herança deve ser usada com cuidado e apenas em algumas situações.

Estranhou a resposta ???

Você achava que herança era o supra-sumo dos recursos do arsenal das linguagens OO ??

Se você pensava assim estava enganado...

Então senta que o leão é manso...

Obs: Lembrando que nem a linguagem C# nem a VB .NET suportam herança múltipla; assim uma classe pode herdar de apenas uma única classe.

Ah!! Outro detalhe, eu estou considerando apenas herança de implementação e não estou entrando em detalhes em herança de Interface. Ok ?

Uma rápida revisão de conceitos

Herança

Exemplo:

Pessoa - classe Pai, classe Base ou Super Classe
  • PessoaFisica - classe Filha ou sub-classe
  • PessoaJuridica - classe Filha ou sub-classe

- A classe Pessoa é a classe genérica;
- As classes PessoaJuridica e Pessoa Fisica são especializações;

- PessoaFisica É uma pessoa;
- PessoaJuridica É uma pessoa;

Implementação:

Namespace Herança

Public Class Pessoa
     Private nome As String
     Private Endereco As String      
End Class

Public Class PessoaFisica
    Inherits Pessoa

    Private CPF As String
    Public Sub New()
    End Sub
End Class

Public Class PessoaJuridica
      Inherits Pessoa

     Private CNPJ As String
     Public Sub New()
     End Sub
End Class

End Namespace

namespace Herança
{
        public class Pessoa
        {
            private String nome;
            private String Endereco;
        }
        public class PessoaFisica : Pessoa
        {
            private String CPF;
            public PessoaFisica()
            {}
        }
        public class PessoaJuridica : Pessoa
        {
            private String CNPJ;
            public PessoaJuridica()
            { }
        }
}
VB .NET C#

Composição

Exemplo

Pedido - Classe que contém uma instância da classe Itens;

- Um pedido TEM UM Item;

Implementação

Public Class Pedido

	Private i As Itens
	Public Sub New()          
    		i = New Itens()
	End Sub
End Class

Public Class Itens
	Public Sub New()
	End Sub
End Class
public class Pedido
{
       private Itens i;
       public Pedido( )
       {
               i = new Itens();         
       }
}

public class Itens {
     public Itens( ){}
}
VB .NET C#

Mas porque devemos mesmos dar preferência ao usa da composição a usar herança ?

Usando herança:

1- Ao usar herança estamos violando um dos pilares da orientação a objetos: o encapsulamento, visto que os detalhes da implementação da classe Pai são expostos nas classes Filhas;
2- Ao usar herança estamos violando um dos princípios básicos das boas práticas de programação : manter o acoplamento entre as classe fraco, visto que as classes filhas estão fortemente acopladas à classe Pai e alterar uma classe Pai pode afetar todas as classes Filhas;
3- As implementações herdadas da classe Pai pelas classes Filhas não pode ser alteradas em tempo de execução;

Usando composição:

1- Os objetos que foram instanciados e estão contidos na classe que os instanciou são acessados somente através de sua interface;
2- A composição pode ser definida dinamicamente em tempo de execução pela obtenção de referência de objetos a objetos de do mesmo tipo;
3- A composição apresenta uma menor dependência de implementações;
4- Na composição temos cada classe focada em apenas uma tarefa (princípio SRP);
5- Na composição temos um bom encapsulamento visto que os detalhes internos dos objetos instanciados não são visíveis;

Percebemos que a herança viola dois conceitos básicos que sempre devemos aplicar em nosso código enquanto que a composição naturalmente nos leva a usar tais conceitos.

Mas então eu nunca devo usar herança ??? (Nunca é uma palavra que nós mortais deveríamos pronunciar com muito cuidado....)

Não existe um mandamento para nunca usar herança, mas podemos definir algumas regras para identificar quando podemos usá-la de forma não ter os problemas que ela acarreta:

Então , considere a utilização da herança se...:

- A classe Filha expressar "um tipo especial de" e não "ser um papel desempenhado por";
- Uma instância de uma classe Filha NUNCA precisar tornar-se um objeto de outra classe;
- A classe filha estender ao invés de substituir total ou parcialmente as responsabilidades da classe Pai;
- A sua hierarquia de herança representar um relacionamento "É um" e não um relacionamento "Tem um";
- Você desejar ou precisar realizar alterações globais para as suas classes filhas alterando uma classe Pai;
- Você precisar aplicar a mesma classe e métodos a diferente tipos de dados;

Obs: Cuidado com a aplicação do princípio "É Um" às vezes ele nos leva a cometer erros que percebemos só depois de olharmos o código. (Vide a violação do princípio LSP)

Eu sei é apenas OOP , mas eu gosto...

"E Jesus lhes disse: Eu sou o pão da vida: aquele que vem a mim não terá fome; e quem crê em mim nunca terá sede." (João 6:35)

Referências:

José Carlos Macoratti