Classes – a
Programação
Orientada a Objetos
Quem tem culpa disto?
• A programação orientada a objetos tem
as suas raízes nas pesquisas e
experiências de cientistas da computação
nas décadas de 1960 e 1970.
• Um dos pioneiros da OOP foi Ole-Johan
Dahl, que juntamente com Kristen
Nygaard, desenvolveu a primeira
linguagem orientada a objetos, Simula,
em 1967.
• Simula introduziu os conceitos de classes,
objetos, herança e polimorfismo, que são
as principais características da OOP.
• Outra figura influente foi Alan Kay,
que junto com os seus colegas da
Xerox PARC, desenvolveu a
linguagem Smalltalk em 1972.
Smalltalk foi a primeira linguagem
pura orientada a objetos, onde tudo
era um objeto, até mesmo números
e funções. A Smalltalk também
introduziu os conceitos de digitação
dinâmica, garbage collection e Olhem o Alan, a rir-se de
interface gráfica do utilizador (GUI). nós.
E o que são classes e objetos?
• Nesta página da Wikipedia encontramos a classificação
dos animais. Às tantas, encontramos a Filo Chordata
(Cordados), onde está a classe dos vertebrados e,
por sua vez, a subclasse Mammalia, dos mamíferos.
• Este tipo de classificação em classes e subclasses – com
as características de cada uma – ajuda muito na
programação.
Uma classe: automóvel
• Imaginemos que queríamos criar um jogo de corridas de
carros. Nele haveria vários, cada um com a sua marca,
modelo, cor, potência, número de portas, tempo
de aceleração dos 0 aos 100Km, etc.
• Mas também ações que poderíamos efetuar com um
carro: abrir porta, fechar porta, acelerar, travar,
etc.
• Marca, modelo, cor, etc são exemplos de atributos.
Abrir porta ou acelerar são exemplos de métodos.
E os objetos?
• Um objeto é uma instância de uma classe, ou seja,
uma concretização de uma classe.
• Mas isso é simples de entender: uma classe é apenas
uma definição de atributos e métodos. Depois, há que
criar objetos concretos dessa classe para os podermos
usar.
Agora em Ruby
Classes, objetos, atributos e métodos
Classes – um exemplo
• Como programadores, construímos essas classes para
representar entidades do mundo real (ex. classe Livro
representando todos os livros) e a classe é usada como
um modelo na criação de uma instância dessa classe
(ex. um Livro singular ).
• A classe define quais os atributos que uma instância
da classe terá (por exemplo, um livro terá atributos
como título, autor, descrição e classificação), mas não
determina quais serão os valores desses atributos.
Uma classe Livros
class Livros
end
Atributos
• Para definir quais os atributos que todos os Livros
devem ter, é necessário fornecê-los dentro da classe.
• Para definir os atributos de todos os Livros, usaremos
attr_accessor. attr_accessor cria dois métodos (em
vez de ter que escrevê-los manualmente na classe) - o
método getter e o método setter.
• O método getter é usado para obter o valor de um
atributo, enquanto que o método setter é usado para
atribuir um valor a um atributo. Novamente, ambos são
criados nos bastidores quando usamos attr_accessor:
class Livro
attr_accessor :titulo, :autor, :descricao, :classificacao
end
Objetos
• Conforme mencionado acima, as classes (por exemplo,
a classe Livro) são usadas como modelo para a criação
de instâncias da classe.
• Um objeto é uma instância individual de uma classe
(por exemplo, um livro singular).
• Na linguagem Ruby, quase tudo são objetos.
Objetos
• Tirando o exemplo dos livros dos parênteses - em
termos orientados a objetos, o Livro individual é uma
instância da classe de objetos conhecida como Livro.
• No nosso exemplo, um Livro é composto por título,
autor, descrição e classificação - esses são os
atributos da classe Livro e quando uma instância da
classe Livro é criada, os valores (dados)
correspondentes a esses atributos serão atribuídos.
Objetos da classe livro
• Para criar um novo objeto Livros, usamos o método new da
Ruby na classe que fornece os valores para os atributos
definidos da classe:
primeiro_livro = Livro.new()
primeiro_livro.titulo = "Ganda Livro Este"
primeiro_livro.autor = "Paulo Dias"
primeiro_livro.descricao = "O melhor livro que já
leste!"
primeiro_livro.classificacao = 5
Outra forma de definir a classe e um
objeto
class Livro
def initialize(titulo,autor,descricao,
classificacao)
@titulo = titulo
@autor = autor
@descricao = descricao
@classificacao = classificacao
end
end @atributo
Criar um objeto
primeiro_livro = Livro.new ("Ganda Livro
Este","Paulo Dias", "O melhor livro que já
leste!", 5)
• Acima, criámos uma nova instância da classe Livro,
também conhecida como objeto e a atribuímos a uma
variável (primeiro_livro) e então usamos a notação de
ponto do Ruby para atribuir valores aos atributos da
instância.
• Para aceder aos atributos deste objeto utilizamos a
seguinte sintaxe:
puts primeiro_livro.titulo
Métodos
• Um Livro também pode ter outras funções além de
apenas criar uma instância dele mesmo.
• É aqui que os métodos entram em jogo. Os métodos
Ruby agrupam uma ou mais instruções repetíveis numa
única unidade. Os métodos são definidos num objeto —
uma classe pode ser composta por uma combinação de
características (atributos) e funcionalidades
(métodos).
• Seguindo a convenção Ruby, os nomes dos métodos
devem começar com uma letra minúscula; se
começarem com uma letra maiúscula, podem ser
interpretados como uma constante e a chamada pode
ser analisada incorretamente.
• Além disso, os métodos Ruby devem ser definidos antes
de serem chamados, caso contrário, uma exceção para
invocação de método indefinido seria gerada.
• Por exemplo, a classe Livro pode ter um método
chamado mudar_autor que altera o atributo autor de
uma instância Livro. Este método seria definido dentro
da classe Livro, pois é específico da classe. Sintaxe para
criar um método:
def mudar_autor
#instruções do método
end
Argumentos para os métodos
• Os métodos também podem aceitar argumentos; com o nosso método
de exemplo, podemos fornecer o nome do novo autor pelo qual
gostaríamos de substituir o existente. Para fazer isso é preciso escrever o
método com um parâmetro:
class Livros
attr_accessor :titulo, :autor, :descricao, :classificacao
#definir método mudar_autor para aceitar argumentos
def mudar_autor(obj, novo_autor)
obj.autor = novo_autor
end
end
#chamar o método mudar_autor no objeto
# primeiro_livro com argumentos obj e novo_autor:
primeiro_livro.mudar_autor(primeiro_livro, "J. R. R.
Tolkien")
#mostrar no ecrã o autor do objeto primeiro_livro:
puts primeiro_livro.autor
De volta ao attr_accessor
Fonte:
https://stackoverflow.com/questions/4370960/what-is-attr-accessor-
in-ruby
Imaginemos uma classe Pessoa:
class Pessoa
end
pessoa = Pessoa.new
pessoa.nome
# Erro por 'nome' não ter sido definido
class Pessoa
def nome
@nome # Apenas o atributo 'nome'
end
end
pessoa = Pessoa.new
pessoa.nome
# retorna um nil porque não foi dado um nome à
pessoa
pessoa.nome = "Paulo"
# Erro: no method error
Erro porquê?
• Podemos ler o nome, mas isso não significa que lhe
possamos atribuir um conteúdo. São métodos
diferentes. O primeiro é o reader e o segundo é o
writer. Mas ainda não criámos este segundo. Vamos a
isso?
class Pessoa
def nome
@nome
end
def nome=(str)
@nome = str
end
end
pessoa = Pessoa.new
pessoa.nome = "Paulo"
pessoa.nome
#Saída:
Paulo
• Fixe. Agora podemos escrever e ler o @nome da pessoa
usando os métodos reader e writer.
• Agora: isto é feito tantas vezes, que para quê perder
este tempo com este modo de usá-los? Solução:
class Pessoa
attr_reader :nome
attr_writer :nome
end
Mas mesmo assim…
• Mesmo este modo pode tornar-se repetitivo, se quisermos ler e
escrever. Porque não usar apenas o accessor?
class Pessoa
attr_accessor :nome
end
pessoa = Pessoa.new
pessoa.nome = "Paulo"
pessoa.nome
# Saída: Paulo
• Também funciona e é bem mais simples! E adivinhem: a variável de
instância @nome no nosso objeto pessoa será definida exatamente
como quando fizemos isso manualmente, então podemos usá-la noutros
métodos.
class Pessoa
attr_accessor :nome
def saudacao
print "Olá ", @nome
end
end
pessoa = Pessoa.new
pessoa.nome = "Paulo"
pessoa.saudacao
# Saída: "Olá Paulo"
Exemplo final
Fonte:
https://launchschool.com/books/oo_ruby/read/classes_and_objects_
part1#exercises
• Crie uma classe chamada MeuCarro. Ao inicializar uma
nova instância ou objeto da classe, permita ao utilizador
definir algumas variáveis de instância (atributos) que
informe sobre o ano de fabrico, a cor e o modelo do
carro.
• Crie uma variável de instância definida como 0 durante
a instanciação do objeto para rastrear também a
velocidade atual do carro. Crie métodos de instância
que permitem que o carro acelere, trave e desligue.
class MeuCarro
def initialize(ano, modelo, cor)
@ano = ano
@modelo = modelo
@cor = cor
@velocidade_atual = 0
end
def acelerar(numero)
@velocidade_atual += numero
puts "Aceleraste ",numero," Km/h"."
end
def travar(numero)
@velocidade_atual -= numero
puts "Desaceleraste ",numero," Km/h"
end
def velocidade_atual
puts "Estás agora a andar a ", velocidadde_atual, " Km/h"
end
def desligar
@velocidade_atual = 0
puts "Acabou o passeio!"
end
end
Instanciar a classe e usar métodos
golf = MeuCarro.new(1976, 'VW Golf GTI MK1", 'preto')
golf.acelerar(20)
golf.velocidade_atual
golf.acelerar(30)
golf.velocidade_atual
golf.travar(20)
golf.velocidade_atual
golf.travar(10)
golf.velocidade_atual
golf.desligar
Exercício resolvido
• Adicione um método acessor à sua classe MeuCarro
para alterar e visualizar a cor do seu carro.
• Em seguida, adicione um método de acesso que
permita visualizar, mas não modificar, o ano do seu
carro.
Solução
class MeuCarro
attr_accessor :cor
attr_reader :ano
#...
end
golf.cor = 'Roxo'
puts golf.cor
puts folf.ano
Módulos
Módulos
• Um módulo é uma coleção de métodos, variáveis e
constantes armazenados num contentor (container).
• É semelhante a uma classe, mas não pode ser
instanciada.
• Em Ruby, a palavra-chave module permite definir um
novo módulo:
module saudacao
def ola
puts 'Olá, Rubynista!'
end
end
E para quê módulos?
• Os módulos têm 2 finalidades principais:
• fornecer um nomespace e evitar conflitos de nomes
• usar o recurso mixin para composição
Módulos como nomespaces
• nomespacing é uma forma de agrupar objetos
logicamente relacionados.
• Os módulos servem como uma ferramenta conveniente
para isso. Isso permite que classes ou módulos com
nomes conflitantes coexistam, evitando colisões.
• Um bom exemplo de nomespace é o módulo Rails.
• Este módulo contém um monte de classes e módulos —
Por exemplo, a classe Rails::Application
module Rails
class Application
...
end
end
• Aqui podemos ver que a classe Application está definida dentro do
módulo Rails.
• Observe que para aceder a esta classe de fora do módulo Rails,
usamos a sintaxe ::
• Como a classe Application é um nome de classe bastante comum
que poderíamos encontrar noutra gema - e para evitar conflitos - a
classe Application é encapsulada no módulo Rails.
• Isso significa que a classe Rails::Application nunca entrará em
conflito com uma classe Application ou uma classe
Dummy::Application que pode ser definida em qualquer outro lugar.
Nota sobre herança múltipla
• A herança é uma característica da programação usando classes.
A ideia é termos classes que podem herdar atributos e métodos
de outras classes.
• Herança múltipla ocorre quando uma classe pode herdar de
duas ou mais classes e a Ruby não a suporta…
mixmin
• Ruby não lida então com herança múltipla.
• Então, como organizar uma classe para mantê-la sustentável no longo prazo?
• A resposta é aplicar o princípio da composição sobre herança.
• O princípio da Composição baseia-se no fato de que uma classe deve conter
um conjunto de objetos que forneçam a funcionalidade desejada.
• Portanto, ao invés de passar a funcionalidade usando a cadeia de herança, é
preferível compor uma classe de objetos que seja responsável por fornecer a
funcionalidade desejada.
include
• Na verdade, a Ruby facilita o uso de composição usando
o recurso mixin.
• Um módulo pode ser incluído noutro módulo ou classe
usando as palavras-chave include, prepend e extend.
• Então, vamos detalhar como usar essas palavras-chave
module Comentavel
def comentario
" Adoro comentários!"
end
end
class Post
include Comentavel
end
class Citacao
include Comentavel
end
Post.new.comentario # => " Adoro comentários!"
Quote.new.comentario # => " Adoro comentários!"
Post.ancestors # => [Post, Comentavel, Object, Kernel, BasicObject]
Citacao.ancestors # => [Citacao, Comentavel, Object, Kernel, BasicObject]
• Aqui, o módulo Comentavel é adicionado à cadeia
ancestral da classe Post usando a instrução include
Comentavel.
• Então, quando chamamos o método
Post.new.comentario — e como o Post#comentario não
está definido — então é o método
Comentavel#comentario que é chamado.
• O que acontece se tivermos dois módulos incluídos que
definem um método com o mesmo nome?