UC00608 · Programação orientada a objectos
Aulify · Componente Tecnológica

Programação orientada a objectos

Classes, herança, polimorfismo, encapsulamento
UC00608 · 50 horas · 4,5 pontos Nível 4 · TDS · Ano 2
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Resultados de aprendizagem

  1. Modelar problemas com classes e objectos.
  2. Aplicar herança, polimorfismo, encapsulamento.
  3. Usar bibliotecas OO em Python.
OO não é melhor que estruturada em absoluto. É melhor em certos problemas: simulações, GUIs, modelos de domínio.
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Bloco 1 · Classes

Do procedural ao OO

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Antes (procedural)

def criar_aluno(nome, idade):
    return {"nome": nome, "idade": idade, "notas": []}

def adicionar_nota(aluno, nota):
    aluno["notas"].append(nota)

def media(aluno):
    n = aluno["notas"]
    return sum(n) / len(n) if n else 0

ana = criar_aluno("Ana", 17)
adicionar_nota(ana, 15)
print(media(ana))
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Depois (OO)

class Aluno:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade
        self.notas = []

    def adicionar_nota(self, nota):
        self.notas.append(nota)

    def media(self):
        return sum(self.notas) / len(self.notas) if self.notas else 0

ana = Aluno("Ana", 17)
ana.adicionar_nota(15)
print(ana.media())

Dados + comportamento juntos. Encapsulamento.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Anatomia da classe

class Aluno:
    # Atributo de classe (partilhado)
    escola = "Aulify"

    # Construtor
    def __init__(self, nome, idade):
        # Atributos de instância
        self.nome = nome
        self.idade = idade

    # Método
    def saudar(self):
        return f"Olá, sou {self.nome}"

    # Representação
    def __repr__(self):
        return f"Aluno({self.nome!r}, {self.idade})"

self é a instância actual. Primeiro argumento de todos os métodos.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Bloco 2 · Encapsulamento

Esconder o que não interessa

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Atributos "privados"

Em Python, convenção com underscore:

class ContaBancaria:
    def __init__(self, titular, saldo_inicial=0):
        self.titular = titular
        self._saldo = saldo_inicial   # convenção: privado

    def depositar(self, valor):
        if valor <= 0:
            raise ValueError("Valor tem de ser positivo")
        self._saldo += valor

    def saldo(self):
        return self._saldo

Python não força privacidade (ao contrário de Java). É confiança e convenção.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Properties

Acesso "tipo atributo" com lógica:

class Aluno:
    def __init__(self, nome, idade):
        self.nome = nome
        self._idade = idade

    @property
    def idade(self):
        return self._idade

    @idade.setter
    def idade(self, valor):
        if valor < 0:
            raise ValueError("Idade não pode ser negativa")
        self._idade = valor

ana = Aluno("Ana", 17)
print(ana.idade)    # 17
ana.idade = 18      # OK, passa pelo setter
# ana.idade = -1    # ValueError
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Bloco 3 · Herança

Reutilizar e especializar

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Herança simples

class Pessoa:
    def __init__(self, nome, idade):
        self.nome = nome
        self.idade = idade

    def saudar(self):
        return f"Olá, sou {self.nome}"

class Aluno(Pessoa):
    def __init__(self, nome, idade, numero):
        super().__init__(nome, idade)
        self.numero = numero
        self.notas = []

    def adicionar_nota(self, nota):
        self.notas.append(nota)

ana = Aluno("Ana", 17, 42)
print(ana.saudar())          # herda de Pessoa
ana.adicionar_nota(15)       # próprio
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Override de métodos

class Animal:
    def som(self):
        return "..."

class Cao(Animal):
    def som(self):
        return "Au au!"

class Gato(Animal):
    def som(self):
        return "Miau!"

animais = [Cao(), Gato(), Animal()]
for a in animais:
    print(a.som())

Cada subclasse redefine som(). Polimorfismo.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Herança múltipla

class Voador:
    def voar(self):
        return "Voando!"

class Nadador:
    def nadar(self):
        return "Nadando!"

class Pato(Voador, Nadador):
    pass

donald = Pato()
print(donald.voar())     # "Voando!"
print(donald.nadar())    # "Nadando!"

Possível em Python (não em Java). Cuidado com diamond problem.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Bloco 4 · Conceitos

Abstracção e composição

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Classes abstractas

Não devem ser instanciadas; só servem para herdar.

from abc import ABC, abstractmethod

class Figura(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangulo(Figura):
    def __init__(self, l, a):
        self.l, self.a = l, a
    def area(self):
        return self.l * self.a

# Figura()        # erro: classe abstracta
r = Rectangulo(3, 4)
print(r.area())   # 12
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Composição vs herança

Herança: "é um" (Aluno é Pessoa).
Composição: "tem um" (Carro tem Motor).

class Motor:
    def ligar(self):
        return "Vrrum"

class Carro:
    def __init__(self):
        self.motor = Motor()   # composição

    def arrancar(self):
        return self.motor.ligar()

c = Carro()
print(c.arrancar())   # "Vrrum"

"Prefere composição a herança." — princípio clássico.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Bloco 5 · Casos de uso

OO na prática

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Modelar domínio

OO brilha em modelos de domínio:

class Encomenda:
    def __init__(self, cliente):
        self.cliente = cliente
        self.itens = []
        self.estado = "pendente"

    def adicionar_item(self, produto, qty):
        self.itens.append((produto, qty))

    def total(self):
        return sum(p.preco * q for p, q in self.itens)

    def confirmar(self):
        if not self.itens:
            raise ValueError("Sem itens")
        self.estado = "confirmada"
Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos

Quando NÃO usar OO

Para scripts curtos, OO é overkill:

# Errado (OO desnecessário)
class Calculadora:
    def somar(self, a, b):
        return a + b

calc = Calculadora()
calc.somar(2, 3)

# Certo (função simples)
def somar(a, b):
    return a + b

somar(2, 3)

Regra: objectos quando tens estado que muda ao longo do tempo. Funções quando só transformas inputs em outputs.

Aulify · Componente Tecnológica · Cursos Profissionais
UC00608 · Programação orientada a objectos
Aulify · UC00608 · Fim

Pensa em objectos quando o problema tem entidades com vida.

📧 geral@aulify.pt
Aulify · Componente Tecnológica · Cursos Profissionais