1 of 40

Herança e�classes abstratas

Programação

Orientada a Objetos

(com Python)

Prof. Rodrigo Rocha <rodrigorgs@ufba.br>

Instituto de Computação

Universidade Federal da Bahia

2 of 40

Herança

3 of 40

Herança

Herança é um mecanismo de linguagens OO na qual uma classe (classe derivada) adquire membros de outra classe (classe base)

​

​

Na notação UML, representamos a herança com uma seta vazada

3

4 of 40

Nomenclatura

Dizemos que:

  • Gato herda de Animal
  • Gato deriva de Animal
  • Gato estende Animal

​

4

Animal

Gato

classe base

classe derivada

classe mãe

classe filha

superclasse

subclasse

5 of 40

Herança em Python

Ao declarar uma classe, escreva o nome de outra classe entre parênteses para estendê-la

No exemplo, Gato herdou o construtor e o método anda da classe Animal

class Animal:

def __init__(self, nome):

self.nome = nome

def anda(self):

print('andando')

​

class Gato(Animal):

pass

​

g = Gato("Mingau")

print(g.nome)

g.anda()

5

Exercício: adicione métodos à classe Animal

6 of 40

O que você pode fazer na classe derivada

​

✅ Acessar atributos e métodos herdados

✅ Adicionar novos atributos e métodos

✅ Redefinir (sobrepor) métodos herdados

❌ Remover membros herdados: não pode

6

7 of 40

Sobreposição (override)

Ocorre quando uma subclasse fornece uma nova implementação para um método herdado

Override = sobrepor, passar por cima

​

OBS.: É mais comum traduzir override como sobrescrever (o que é um equívoco, pois essa seria a tradução de overwrite). Note que o método original não deixa de existir, então não se trata de sobrescrever.

7

8 of 40

Sobreposição em Python

No exemplo, Gato sobrepõe o método anda da classe Animal

8

class Animal:

def __init__(self, nome):

self.nome = nome

​

def anda(self):

print('andando')

​

class Gato(Animal):

def anda(self):

print('engatinhando')

​

g = Gato("Mingau")

print(g.nome)

g.anda()

​

9 of 40

super()

Em alguns casos queremos que a nova implementação de um método sobreposto chame a implementação original da superclasse; esta pode ser acessada com super()

9

class Animal:

def __init__(self, nome):

self.nome = nome

​

def anda(self):

print('andando')

�class Gato(Animal):

def anda(self):

super().anda()

print('engatinhando')

Exercício: crie uma classe Relatorio, com método imprime, e uma subclasse RelatorioProtegido, que recebe um usuário e só permite imprimir o relatório se o usuário for admin.

10 of 40

super().__init__

É uma boa prática chamar o construtor da superclasse, pois ele inicializa os atributos definidos na superclasse

10

class Animal:

def __init__(self, nome):

self.nome = nome

​

class Gato(Animal):

def __init__(self, nome):

super().__init__(nome + " Miau")

self.vidas = 7

​

g = Gato("Mingau")

print(g.nome)

11 of 40

Classe object

  • Toda classe declarada sem declaração de herança está implicitamente herdando da classe object
  • Métodos como __eq__ e __str__ são herdados de object e podem ser sobrepostos

11

12 of 40

Herança e subtipo (é-um)

A herança define uma relação de subtipo. No exemplo, Gato é um subtipo de Animal, ou seja, toda instância de Gato é também instância de Animal

Dizemos, ainda, que�todo gato é um animal

12

class Animal:

pass

class Gato(Animal):

pass

​

g = Gato()

print(isinstance(g, Gato)) # True

print(isinstance(g, Animal)) # True

print(isinstance(g, object)) # True

13 of 40

Herança múltipla

Em Python uma classe pode derivar de mais de uma classe (basta escrever a lista de classes separadas por vírgula), o que chamamos de herança múltipla (em Java, por exemplo, isso não é possível).

Não vamos abordar herança múltipla nesta disciplina.

13

14 of 40

Polimorfismo

15 of 40

Conceito: polimorfismo

  • Do grego, que tem muitas formas
  • Ideia de que um símbolo pode representar coisas diferentes a depender do contexto
  • Exemplo em linguagem natural (polissemia):�vela (de barco), vela (de bolo)

15

16 of 40

Polimorfismo de subtipo

No código ao lado, animal.anda() pode se referir ao método de cima ou ao método de baixo, pois animal pode ser um objeto da classe Animal ou de uma de suas subclasses*

Se determinado código manipula objetos de uma classe, ele também deve funcionar com objetos de subclasses*

* de forma geral, classes descendentes

16

class Animal:

def anda(self):

print('andando')

​

class Gato(Animal):

def anda(self):

print('engatinhando')

​

​

def passeia(animal):

for i in range(5):

animal.anda()

17 of 40

Duck typing

Em Python e outras linguagens de tipagem dinâmica, não importa o tipo do objeto, e sim quais métodos e atributos ele define. Isso significa que a função passeia funciona com qualquer objeto que possua o método anda(), independentemente de seu tipo.

17

class Animal:

def anda(self):

print('andando')

​

class Robo:

def anda(self):

print('tac-tac-tac')

​

def passeia(animal):

for i in range(5):

animal.anda()

​

​

“If it walks like a duck and it quacks like a duck, then it must be a duck"

18 of 40

Classes abstratas

19 of 40

Classes abstratas

  • São classes que não podem ser instanciadas, pois definem um ou mais métodos abstratos (i.e., sem implementação)
    • Útil quando você quer criar uma família de classes que diferem apenas pela implementação de um ou mais métodos
  • As demais classes são chamadas de concretas
  • Uma subclasse de uma classe abstrata pode ser
    • concretas, se implementarem todos os métodos abstratos
    • abstrata, caso contrário

19

20 of 40

Classes abstratas – exemplo

Tentar instanciar Forma resulta no erro:

TypeError: Can't instantiate abstract class Forma with abstract methods area, perimetro

20

from abc import ABC, abstractmethod

​

class Forma(ABC):

def __init__(self, cor):

self.cor = cor

@abstractmethod

def area(self):

pass

@abstractmethod

def perimetro(self):

pass

​

x = Forma("azul") # TypeError

​

21 of 40

Classes abstratas – exemplo

Quadrado estende e implementa todos os métodos abstratos de Forma. Por isso, é uma classe concreta

21

class Quadrado(Forma):

def __init__(self, cor, lado):

super().__init__(cor)

self.lado = lado

​

def area(self):

return self.lado ** 2

​

def perimetro(self):

return 4 * self.lado

​

q = Quadrado("azul", 10)

22 of 40

Classes abstratas – exemplo

O método concreto imprime_resumo pode chamar métodos abstratos, pois eles serão definidos em classes concretas

Isso permite definir um algoritmo no qual alguns passos ficam em aberto, podendo ser definidos por uma subclasse

22

class Forma(ABC):

def __init__(self, cor):

self.cor = cor

@abstractmethod

def area(self): pass

@abstractmethod

def perimetro(self): pass

​

def imprime_resumo(self):

print("Área:", self.area())

print("Perímetro:",� self.perimetro())

23 of 40

Protocolos

24 of 40

Protocolos

Ver slides sobre tipagem estática

Em Java e TypeScript é chamado de interface

24

25 of 40

Herança vs composição

26 of 40

Composição: “tem um"

  • Composição entre classes ocorre quando uma classe possui um atributo de uma outra classe
  • Composição define uma relação de “tem um"
  • Exemplos:
    • Círculo tem um Ponto (centro)
    • Palavra tem uma lista de Letras
    • Carro tem um Volante

26

27 of 40

Herança: “é um"

  • Não use herança somente para aproveitar código
  • Use herança quando há uma relação de “é um" entre classes. Exemplos:
    • Gato é um Animal
    • Quadrado é uma FormaGeométrica
  • Garanta que tudo o que foi herdado se aplica à subclasse

27

28 of 40

Herança: “é um"

  • Ex.: faz sentido Gato estender Animal, pois todo Gato é um Animal
  • A classe Animal define todos os comportamentos que são comuns a todos os animais (ex.: mover-se, reproduzir-se)
  • Gato herda esses comportamentos

28

Gato

Animal

29 of 40

Herança: contra-exemplos

  • Ponto define x, y e mover()
  • Círculo possui x, y, raio, mover() e area()
  • Círculo deve estender Ponto?
    • Conceitualmente, Círculo não é um ponto, e sim tem um ponto (centro)

29

30 of 40

Herança: contra-exemplos

  • Vetor é uma coleção de elementos
  • Pilha é uma coleção de elementos no qual o último a entrar é o primeiro a sair
  • Pilha deve estender Vetor?
    • Pilha pode ser implementado com outras estruturas, não apenas vetores
    • Vetor define operações (ex.: trocar elemento) que não se aplicam a Pilha

30

31 of 40

Herança: contra-exemplos

  • Quadrado deve estender Retangulo?
  • Até faz sentido: Quadrado é um tipo de Retangulo
  • Problema: Retangulo possui largura e altura

31

q = Quadrado()

q.largura = 3

q.altura = 4

# qual é o valor de q.largura?

32 of 40

Composição: “tem um"

  • Composição entre classes ocorre quando uma classe possui um atributo de uma outra classe
  • Composição define uma relação de “tem um"
  • Exemplos:
    • Círculo tem um Ponto (centro)
    • Palavra tem uma lista de Letras
    • Carro tem um Volante

32

33 of 40

Favoreça composição

  • Composição dá mais flexibilidade
    • Não é preciso usar todas as partes de um componente
    • Pode-se trocar os componentes em tempo de execução
    • Pode-se combinar diferentes componentes

33

34 of 40

Exemplo: jogo de estratégia em tempo real (com herança)

Classe Pessoa: pode mover-se e atacar

O ataque varia:

  • subclasse Arqueiro: ataque a longa distância
  • subclasse Guerreiro: com espada, curta distância

Um arqueiro nunca pode virar um guerreiro, pois�um objeto do tipo Arqueiro não pode mudar seu tipo para Guerreiro durante a execução do programa

​

34

35 of 40

Exemplo: jogo de estratégia em tempo real (com composição)

Agora considere que qualquer pessoa deve poder se locomover a pé ou a cavalo

Classe Pessoa: pode mover-se e atacar, e possui uma Arma

A ação de atacar consiste em usar a Arma

  • Arco estende Arma: permite atacar a longa distância
  • Espada estende Arma: permite atacar a curta distância

É possível trocar a arma durante o jogo

35

36 of 40

Exemplo

Vamos criar uma classe que imprime e salva o guia de usuário de uma determinada aplicação.

Esse guia pode ser gerado no formato HTML ou no formato Markdown.

36

37 of 40

Alguns padrões de projeto

38 of 40

Padrão de projeto

É uma solução genérica para um problema recorrente referente ao projeto (design) OO

38

🎨

39 of 40

Observer/listener

Problema: quero enviar uma informação para um objeto, mas não tenho referência para ele

Exemplo: quando o carro pega um item, quero avisar ao quadro de pontuação (interessado) que a pontuação deve ser atualizada

Solução: guarde uma lista na qual os interessados se inscrevem para receber avisos

39

🎨

40 of 40

Strategy

Problema: quero poder alterar o comportamento de um objeto em tempo de execução

Exemplo: ao bater em um obstáculo, o carro deve ser danificado; ao pegar um item de invencibilidade, no entanto, ele deve destruir o obstáculo

Solução: Carro deve possuir um objeto com um método que define o que deve ser feito ao bater em um obstáculo. Esse objeto pode ser trocado (por exemplo, através de um setter).

40

🎨