Herança

Herança é uma relação entre duas classes. Sabemos que todos gatos são mamíferos e que todos mamíferos são animais. O benefício da herança é que classes em níveis mais baixos da hierarquia obtêm as características daquelas em um nível mais alto, mas podem também adicionar características específicas a si mesmas. Se todos mamíferos respiram, então todos gatos respiram. No Ruby,
uma classe pode apenas herdar de uma única outra classe. Outras linguagens suportam herança múltipla, uma funcionalidade que permite que classes herdem características de múltiplas classes, mas Ruby não suporta isso.

Podemos expressar esse conceito no Ruby como mostrado no programa p033mammal.rb abaixo:

 1   class Mamifero
 2     def respira
 3       puts "inspira e expira"
 4     end
 5   end
 6 
 7   class Gato < Mamifero
 8     def fala
 9       puts "Miau"
10     end
11   end
12 
13   rani = Gato.new
14   rani.respira
15   rani.fala

Embora não tenhamos especificado como um Gato deve respirar, cada gato herdará este comportamento da classe Mamífero visto que Gato foi definido como uma subclasse de Mamífero (na terminologia de orientação a objetos, a classe menor é a subclasse e a classe maior é a superclasse. A subclasse é às vezes conhecida como classe derivada ou filha e a superclasse como classe pai). Portanto, do ponto de vista do programador, gatos obtêm a a habilidade de respirar de graça; após adicionarmos o método fala, nossos gatos podem respirar e falar.

Existirão situações em que certas propriedades da superclasse não devem ser herdadas por uma subclasse particular. Embora aves em geral saibam como voar, pinguins são uma subclasse de aves que não voam. No exemplo p034bird.rb abaixo, nós sobrescrevemos (override) o método voa na classe Pinguim:

 1   class Ave
 2     def limpase
 3       puts "Estou limpando minhas penas."
 4     end
 5     def voa
 6       puts "Estou voando."
 7     end
 8   end
 9 
10   class Pinguim < Ave
11     def voa
12       puts "Desculpe. Prefiro nadar."
13     end
14   end
15 
16   p = Pinguim.new
17   p.limpase
18   p.voa

Ao invés de definir exaustivamente cada característica de cada nova classe, precisamos apenas adicionar ou redefinir as diferenças entre cada subclasse e de sua superclasse. Esse uso da herança é algumas vezes chamado de programação diferencial. É um dos benfícios da programação orientada a objetos.

Os dois programas abaixo foram tirados do Guia do Usuário Ruby

Então, a herança permite que você crie uma classe que é uma versão refinada ou uma especialização de outra classe. A herança é indicada por <.

Aqui está outro exemplo, p035inherit.rb:

 1   class GF
 2     @m =10
 3     puts @m.object_id
 4     def initialize
 5       puts 'Na classe GF'
 6     end
 7     def gfmethod
 8       puts 'chamada de metodo de GF'
 9     end
10   end
11 
12   # classe F, subclasse de GF
13   class F < GF
14     def initialize
15       super
16       puts 'Na classe F'
17     end
18   end
19 
20   # classe S, subclasse de F
21   class S < F
22     def initialize
23       super
24       puts @m
25       puts @m.object_id
26       puts 'Na classe S'
27     end
28   end
29 
30   filho = S.new
31   filho.gfmethod

Uma classe pode apenas herdar der uma classe de cada vez (isto é, uma classe pode herdar uma classe que herda de outra classe e que, por sua vez, herda de outra classe, mas uma única classe não pode herdar de várias classes ao mesmo tempo).

Existem muitas classes e módulos (mais sobre isso será explicado adiante) que vem com Ruby por padrão. Elas estão disponíveis para todos programas Ruby automaticamente; não é necessário usar o require. Algumas classes inclusas no Ruby são Array, Bignum, Class, Dir, Exception, File, Fixnum, Float, Integer, IO, Module, Numeric, Object, Range, String, Thread, Time. Alguns módulos inclusos são Comparable, Enumerable, GC, Kernel, Math.

A classe Objeto é a classe pai de todas as classes em Ruby. Seus métodos estão portanto disponíveis para todos os objetos ao menos que sejam explicitamente sobrescritos. No Ruby 1.9, Object não é mais a raiz da hierarquia de classes. Uma nova classe chamada BasicObject tem esse objetivo, e Objeto passa a ser uma subclasse de BasicObject. Esta é uma classe bem simples, com quase nenhum método próprio. Quando você cria uma classe no Ruby 1.9, você ainda extende Object ao menos que você especifique a superclasse, e, a maioria dos programadores, nunca precisará usar ou extender BasicObject.

No Ruby, initialize (inicializar) é um método ordinário e é herdado como qualquer outro.

NO RAILS: A Herança é uma das técnicas chave usadas na organização do projeto da programação e do framework.

Herança e Variáveis de Instância

Considere o código:

 1   class Cachorro
 2     def initialize(raca)
 3       @raca = raca
 4     end
 5   end
 6 
 7   class Lab < Cachorro
 8     def initialize(raca, nome)
 9       super(raca)
10       @nome = nome
11     end
12 
13     def to_s
14       "(#@raca, #@nome)"
15     end
16   end
17 
18   puts Lab.new("Labrador", "Benzy").to_s

O método to_s na classe Lab referencia a variável @raca da superclasse Cachorro. Esse código funciona como você esperaria:

1    puts Lab.new("Labrador", "Benzy").to_s ==> (Labrador, Benzy)

Visto que esse código se comporta como esperado, você pode ousar dizer que todas as variáveis foram herdadas. Mas não é assim que Ruby funciona.

Todos objetos Ruby tem um conjunto de variáveis de instância. Essas não são definidas pelos objetos da classe – elas são simplesmente criadas quando um valor é associado as mesmas. Por variáveis de instância não serem definidas por uma classe, elas não tem relação com o mecanismo de herança.

No código acima, Lab define um método initialize que encadeia o método initialize de sua superclasse. Isto é, o método super passa o argumento raca para o método initialize da classe pai (Cachorro). O método encadeado atribui um valor para a variável @raca, o que faz com que essa passe a existir para uma instância particular de Lab.

super é um método especial que invoca um método com mesmo nome do método atual na superclasse da classe em questão.

A razão para que às vezes pareça que essas variáveis de instância são herdadas está no fato das variáveis de instância serem criadas pelos primeiros métodos que atribuem valores a elas, e estes métodos são geralmente herdados ou encadeados.

Visto que as variáveis de instância não tem nada a ver com a herança, segue que uma variável de instância usada por uma subclasse não pode “ocultar” uma variável de instância na superclasse. Se uma subclasse usa uma variável de instância com o mesmo nome de uma variável usada em um de seus ancestrais, ela irá sobrescrever o valor da variável de seus ancestrais.

Logo do Guru-SP

Este material tem como base o tutorial do RubyLearning.com de Satish Talim e foi traduzido por membros do GURU-SP com a permissão do autor.

Ajude o RubyLearning participando em algum dos cursos pagos ou fazendo uma doação para o projeto

Voltar para o índice