9. Programação Orientada a Objetos (POO)#
Chegamos a um dos conceitos mais transformadores da computação moderna. Se até agora você aprendeu a criar ferramentas isoladas (Funções) e a organizar gavetas de dados (Listas e Dicionários), a Programação Orientada a Objetos (POO) ensinará você a construir sistemas inteligentes.
A POO é um paradigma que permite estruturar o código de maneira mais próxima de como enxergamos o mundo real: focado em “coisas” (Objetos) e em como elas interagem entre si, unindo dados e ações em um só lugar.
Classes vs. Objetos#
Para entender a diferença de forma simples, esqueça a tecnologia por um momento e pense em uma Forma de fazer biscoitos.
Classe (O Molde): A classe é a forma de metal. Ela define o formato (digamos, um boneco ou uma estrela) e dita as regras do que o biscoito vai ser. No entanto, você não pode comer a forma. Ela não é o biscoito real, é apenas o molde que usamos para criá-lo.
Objeto (O Biscoito Real): O objeto é o biscoito que saiu do forno. Usando aquela única forma de metal (a Classe), você pode criar dezenas de biscoitos reais (Objetos). E o mais legal: um biscoito pode ter cobertura de chocolate, outro de morango e outro de baunilha. Cada biscoito é único e tem suas próprias características (Atributos), mas todos foram criados a partir do mesmo molde.
Na programação, chamamos o ato de “assar um novo biscoito usando a forma” de Instanciar um Objeto.
Outro exemplo prático é um Formulário de Cadastro em branco:
O papel em branco com os campos “Nome” e “Idade” é a Classe.
O papel preenchido com “Maria, 25 anos” é o Objeto.
Criando sua Primeira Classe#
No Python, utilizamos a palavra reservada class (com a primeira letra sempre maiúscula por convenção) para definir o nosso molde.
O Construtor (__init__) e o self#
Toda classe costuma ter um método especial chamado __init__ (inicializador). Ele é executado automaticamente assim que o objeto “nasce”.
A palavra self é obrigatória e representa “este objeto em particular”. É a forma de a classe dizer: “estou guardando esta cor neste carro específico que acabei de criar”.
class Carro:
# O método __init__ inicializa as características (Atributos)
def __init__(self, marca, modelo, cor):
self.marca = marca # Atributo (Dado / Estado)
self.modelo = modelo # Atributo
self.cor = cor # Atributo
self.velocidade = 0 # Valor padrão
# As ações que o objeto pode fazer (Métodos / Comportamentos)
def acelerar(self, incremento):
self.velocidade += incremento
print(f"O {self.modelo} acelerou. Velocidade: {self.velocidade} km/h")
Instanciando e Usando Objetos#
Agora que temos o “projeto” do carro, vamos fabricar (instanciar) dois carros diferentes e testar suas funções.
# Criando os objetos (Instanciação)
carro1 = Carro("Toyota", "Corolla", "Prata")
carro2 = Carro("Ford", "Mustang", "Vermelho")
# Acessando os atributos e métodos
print(f"Meu carro é um {carro1.marca} da cor {carro1.cor}.")
carro1.acelerar(30)
carro2.acelerar(80) # Cada objeto mantém sua própria velocidade de forma independente
Meu carro é um Toyota da cor Prata.
O Corolla acelerou. Velocidade: 30 km/h
O Mustang acelerou. Velocidade: 80 km/h
Os Quatro Pilares da POO#
Se criar Classes e Objetos é o vocabulário básico, os Quatro Pilares são as leis da física desse nosso universo virtual. Eles definem como os objetos devem ser construídos para garantir segurança, reutilização e flexibilidade.
Encapsulamento#
É a prática de esconder os detalhes internos de funcionamento de um objeto e proteger seus dados contra modificações indevidas feitas de fora da classe. Imagine o motor de um carro: você interage com o pedal (interface), mas os pistões e as correias ficam escondidos e protegidos sob o capô.
Em Python, indicamos que um atributo não deve ser acessado diretamente usando underscores (_ ou __):
class ContaBancaria:
def __init__(self, titular, saldo_inicial):
self.titular = titular
# O duplo underscore indica que este atributo é "privado"
self.__saldo = saldo_inicial
def depositar(self, valor):
if valor > 0:
self.__saldo += valor
def exibir_saldo(self):
# O saldo só pode ser lido de forma controlada através deste método
print(f"Saldo: R$ {self.__saldo}")
conta = ContaBancaria("Ana", 1000)
conta.depositar(500)
conta.exibir_saldo() # Correto.
# print(conta.__saldo) -> Erro! O Python protege o acesso direto a este atributo.
Saldo: R$ 1500
Herança#
A Herança permite criar uma nova classe (Subclasse / Filha) baseada em uma classe já existente (Superclasse / Pai). A classe filha herda os atributos e métodos do pai, podendo adicionar coisas novas. Isso evita que você tenha que reescrever código do zero.
# Superclasse (Pai)
class Animal:
def __init__(self, nome):
self.nome = nome
def comer(self):
print(f"{self.nome} está se alimentando.")
# Subclasse (Filha) - Recebe (Animal) entre parênteses
class Cachorro(Animal):
def latir(self):
print("Au au!")
rex = Cachorro("Rex")
rex.comer() # Método herdado da classe pai
rex.latir() # Método exclusivo da classe filha
Rex está se alimentando.
Au au!
Polimorfismo#
Polimorfismo significa que objetos de classes diferentes podem responder ao mesmo comando (método) de formas diferentes. Geralmente, ocorre quando as classes filhas sobrescrevem um método do pai para adaptá-lo às suas próprias necessidades.
class Passaro(Animal):
def mover(self):
return "Voando pelo céu."
class Peixe(Animal):
def mover(self):
return "Nadando no oceano."
# Lista com objetos de tipos diferentes
animais = [Passaro("Piu"), Peixe("Nemo")]
# O mesmo comando (.mover()) gera resultados específicos para cada objeto
for animal in animais:
print(f"{animal.nome} está {animal.mover()}")
Piu está Voando pelo céu.
Nemo está Nadando no oceano.
Abstração#
A Abstração significa focar apenas nas características essenciais de um objeto e esconder a complexidade (“o como” ele faz as coisas) por trás de comandos simples e diretos.
Pense no exemplo do nosso Carro: quando você senta no banco do motorista e gira a chave, o carro liga. Você não precisa saber como o motor injeta o combustível, como as velas geram a faísca ou como a bateria distribui a energia. O carro abstrai toda essa engenharia complexa e te entrega apenas uma interface simples: a chave na ignição.
Na programação, fazemos a mesma coisa. Criamos classes com métodos simples para quem vai usar o código, enquanto escondemos as regras difíceis em métodos e atributos privados (usando o Encapsulamento que vimos acima).
Veja como podemos abstrair a lógica de ligar um carro no código:
class Carro:
def __init__(self, modelo):
self.modelo = modelo
self.__combustivel = 100 # Atributo privado
# Métodos privados: A engenharia interna que o motorista não precisa ver
def __injetar_combustivel(self):
self.__combustivel -= 2
def __gerar_faisca(self):
pass # Lógica complexa do motor...
# Método público: A "chave" que o motorista usa (A Abstração)
def ligar(self):
if self.__combustivel > 0:
self.__injetar_combustivel()
self.__gerar_faisca()
print(f"Vrum! O {self.modelo} está ligado e pronto para andar.")
else:
print("Sem combustível!")
meu_carro = Carro("Mustang")
# O usuário só precisa chamar um comando simples:
meu_carro.ligar()
# Ele não precisa (e nem deve) chamar meu_carro.__injetar_combustivel()
Vrum! O Mustang está ligado e pronto para andar.
Neste exemplo, o método ligar() é a nossa abstração. Quem usa a classe Carro só precisa saber que esse método existe, sem precisar se preocupar com as etapas internas de injeção e faísca. Isso torna o código extremamente fácil de usar e diminui as chances de erros!
🎉 Parabéns! Você finalizou a introdução à Programação Orientada a Objetos. Compreender classes, objetos e os quatro pilares muda a forma como modelamos problemas, permitindo criar sistemas mais robustos e profissionais.