POO: o que é programação orientada a objetos? Entenda o conceito e veja exemplos práticos

Se você quer saber o que significa POO e por que tantas pessoas buscam entender a orientação a objetos, este artigo é para você. Veja a seguir uma explicação completa sobre o que é programação orientada a objetos, como funciona, quais suas principais características e exemplos práticos em diferentes linguagens de programação orientada a objetos, como Java e Python.
Assim como diversas atividades do dia a dia podem ser realizadas de formas diferentes, na programação também existem diferentes modos de desenvolver soluções, que são chamados de paradigmas de programação. Entre os principais paradigmas estão a programação orientada a objetos (POO) e a programação estruturada.
Atualmente, a programação orientada a objetos é um dos paradigmas mais populares, principalmente em projetos que demandam flexibilidade e reaproveitamento de código.
Este artigo tem como objetivo explicar o que é programação orientada a objetos, suas bases e principais características.
O que é programação orientada a objetos?
De maneira geral, a programação orientada a objetos (POO) é um paradigma que organiza o código de acordo com o conceito de objetos. Ou seja, em uma linguagem orientada a objetos, o código é estruturado em torno de classes e objetos que representam entidades do mundo real.
Ou seja, é uma abordagem que simplifica programas complexos ao dividi-los em unidades menores chamadas de objetos.
Esse paradigma foi criado para aproximar o modo como lidamos com programas ao modo como interagimos com objetos do mundo real. O nome "objeto" faz referência a algo genérico, que pode representar qualquer entidade tangível ou conceitual.
Esse novo paradigma da orientação a objetos se baseia principalmente em dois conceitos-chave: classes e objetos. Todos os outros conceitos, igualmente importantes, são construídos em cima desses dois.
| O próximo nível depende da performance de hoje. Você consome conteúdo de tech porque quer crescer, e isso já é um passo. Mas consumir não é o mesmo que treinar. Junho marca a metade do ano, e ainda há tempo de fechar 2026 em outro patamar, começando agora! Na Alura, você desenvolve as habilidades que o mercado está procurando com cursos práticos, conteúdo atualizado e uma comunidade de profissionais em evolução contínua. Aproveite os planos 2 anos com 35% off + 2 meses grátis + ebook exclusivo. Válido até 10/06. Ver planos com 35% off → |
O que são objetos na programação orientada a objetos?
Os objetos são unidades que reúnem dados (atributos) e comportamentos (métodos).
Imagine que você comprou um carro e decide modelar esse carro usando programação orientada a objetos. O seu carro tem as características que você estava procurando: um motor 2.0 híbrido, cor azul escuro, quatro portas, câmbio automático.
Ele também possui comportamentos que, provavelmente, foram o motivo de sua compra, como acelerar, desacelerar, acender os faróis, buzinar e tocar música.
Podemos dizer que o carro novo é um objeto, em que suas características são seus atributos (dados atrelados ao objeto) e seus comportamentos são ações ou métodos.
Os elementos de um objeto podem ser divididos em duas categorias:
Atributos ou propriedades
Os atributos ou propriedades se referem às informações do objeto. Então, por exemplo, se considerar um objeto "celular", suas propriedades serão: cor, marca, modelo, ano de fabricação e daí por diante.
Métodos
Os métodos, por outro lado, são as operações que podem ser feitas no objeto. Se seguir no exemplo de celular, os métodos podem ser ligar, enviar mensagem, tirar uma foto.
O que são classes na orientação a objetos?
A classe é um conjunto de características e comportamentos que definem um grupo de objetos pertencentes a ela.
Seu carro é um objeto seu mas na loja onde você o comprou existiam vários outros, muito similares, com quatro rodas, volante, câmbio, retrovisores, faróis, dentre outras partes.
Observe que, embora seu carro seja único, podem existir outros com exatamente os mesmos atributos, e todos ainda assim são considerados carros.
Podemos dizer então que seu objeto pode ser classificado (isto é, seu objeto pertence à uma classe) como um carro.
Nesse caso, o carro que você comprou nada mais é que uma instância dessa classe chamada "carro".

Repare que a classe em si é um conceito abstrato, como um molde, que se torna concreto e palpável através da criação de um objeto. Chamamos essa criação de instanciação da classe, como se estivéssemos usando esse molde (classe) para criar um objeto.
Exemplo em Java
public class Carro {
Double velocidade;
String modelo;
public Carro(String modelo) {
this.modelo = modelo;
this.velocidade = 0;
}
public void acelerar() {
/* código do carro para acelerar */
}
public void frear() {
/* código do carro para frear */
}
public void acenderFarol() {
/* código do carro para acender o farol */
}
}Exemplo em Python
class Carro:
def __init__(self, modelo):
self.modelo = modelo;
self.velocidade = 0
def acelerar(self):
# Codigo para acelerar o carro
def frear(self):
# Codigo para frear o carro
def acenderFarol(self):
# Codigo para acender o farol do carroPrincipais características e conceitos da programação orientada a objetos (POO)
As duas bases da POO são os conceitos de classe e objeto. Desses conceitos, derivam alguns outros extremamente importantes. Dentre eles estão: o encapsulamento, a herança, as interfaces e o polimorfismo.
Esses conceitos são fundamentais para entender como funciona a programação orientada a objetos e são muito cobrados em entrevistas para quem busca atuar com linguagens orientadas a objetos, como Java, Python, C# e outras.
Encapsulamento
O encapsulamento de atributos e métodos impede o vazamento de escopo, em que um atributo ou método é visível por alguém que não deveria vê-lo, como outro objeto ou classe.
Ainda usando a analogia do carro, sabemos que ele possui atributos e métodos. Ou seja, características e comportamentos.
Os métodos do carro, como acelerar e freiar, podem usar atributos e outros métodos do carro como o tanque de gasolina e o mecanismo de injeção de combustível.
Afinal de contas, acelerar o carro gasta combustível.

No entanto, se alguns desses atributos ou métodos forem facilmente visíveis e modificáveis, como o mecanismo de aceleração do carro, é possível fazer alterações com efeitos colaterais imprevisíveis.
Nessa analogia, uma pessoa pode não estar satisfeita com a aceleração do carro e modifica a forma como ela acontece, criando efeitos colaterais que podem, por exemplo, fazer o carro nem andar.
Nesse caso, o método de aceleração do carro não é visível externamente, apenas para o próprio objeto.
Na POO, um atributo ou método que não é visível de fora do próprio objeto é chamado de "privado" e quando é visível, é chamado de "público".

Mas afinal, como sabemos como o carro acelera? Simples: não precisamos saber.
Nós só sabemos que para acelerar, devemos pisar no acelerador e de resto o objeto sabe como executar essa ação sem expor como o faz.
Dizemos que a aceleração do carro está encapsulada, pois sabemos o que ele vai fazer ao executarmos esse método, mas não sabemos como - e na verdade, não importa para o programa como o objeto o faz, só que ele o faça.
O mesmo vale para atributos. Por exemplo, não sabemos como o carro sabe qual velocidade mostrar no velocímetro ou como ele calcula sua velocidade, mas não precisamos saber como isso é feito.
Só precisamos saber que ele vai nos dar a velocidade certa. Ler ou alterar um atributo encapsulado geralmente é feito por meio de métodos especiais, conhecidos como getters (para acessar valores) e setters (para modificar valores).
Isso evita a confusão do uso de variáveis globais no programa, deixando mais fácil de identificar em qual estado cada variável vai estar a cada momento do programa, já que a restrição de acesso nos permite identificar quem consegue modificá-la.
Exemplo em Java
public class Carro {
private Double velocidade;
private String modelo;
private MecanismoAceleracao mecanismoAceleracao;
private String cor;
/* Repare que o mecanismo de aceleração é inserido no carro ao ser construído, e
não o vemos nem podemos modificá-lo, isto é, não tem getter nem setter.
Já o "modelo" pode ser visto, mas não alterado. */
public Carro(String modelo, MecanismoAceleracao mecanismoAceleracao) {
this.modelo = modelo;
this.mecanismoAceleracao = mecanismoAceleracao;
this.velocidade = 0;
}
public void acelerar() {
this.mecanismoAceleracao.acelerar();
}
public void frear() {
/* código do carro para frear */
}
public void acenderFarol() {
/* código do carro para acender o farol */
}
public Double getVelocidade() {
return this.velocidade
}
private void setVelocidade() {
/* código para alterar a velocidade do carro */
/* Como só o próprio carro deve calcular a velocidade,
esse método não pode ser chamado de fora, por isso é "private" */
}
public String getModelo() {
return this.modelo;
}
public String getCor() {
return this.cor;
}
/* podemos mudar a cor do carro quando quisermos */
public void setCor(String cor) {
this.cor = cor;
}
}Exemplo em Python
# Exemplo da classe Carro em Python
class Carro:
def __init__(self, modelo, mecanismoAceleracao):
self._modelo = modelo
self._velocidade = 0
self._mecanismoAceleracao = mecanismoAceleracao
def acelerar(self):
mecanismoAceleracao.acelerar()
def frear(self):
# Codigo para frear o carro
def acenderFarol(self):
# Codigo para acender o farol do carro
def getVelocidade(self):
return self._velocidade
def _setVelocidade(self):
# Codigo para alterar a velocidade por dentro do objeto
def getModelo(self):
return self._modelo
def getCor(self):
return self._cor
def setCor(self, cor):
Self._cor = corHerança na orientação a objetos: como funciona esse conceito?
No nosso exemplo, você acabou de comprar um carro com os atributos que procurava.
Apesar de ser único, existem carros com exatamente os mesmos atributos ou formas modificadas.
Digamos que você tenha comprado o modelo Fit, da Honda. Esse modelo possui uma outra versão, chamada WR-V (ou "Honda Fit Cross Style"), que mantém muitos atributos da versão clássica, mas apresenta diferenças significativas, como adaptações para estradas de terra.
Diferente da versão clássica, o motor é híbrido, possui um sistema de suspensão diferente tração nas quatro rodas, por exemplo.
Vemos então que não só alguns atributos como também alguns mecanismos (ou métodos, traduzindo para POO) mudam.
No entanto, a versão "cross" ainda é do modelo Honda Fit, ou melhor, é um tipo do modelo.

Quando dizemos que uma classe A é um tipo de classe B, dizemos que a classe A herda as características da classe B e que a classe B é mãe da classe A, estabelecendo então uma relação de herança entre elas.
No caso do carro, dizemos então que um Honda Fit "Cross" é um tipo de Honda Fit e o que muda são alguns atributos e um dos métodos da classe.
Mas todo o resto permanece o mesmo, e o novo modelo recebe os mesmos atributos e métodos do modelo clássico.
Exemplo em Java
// "extends" estabelece a relação de herança dom a classe Carro
public class HondaFit extends Carro {
public HondaFit(MecanismoAceleracao mecanismoAceleracao) {
String modelo = "Honda Fit";
// chama o construtor da classe mãe, ou seja, da classe "Carro"
super(modelo, mecanismoAceleracao);
}
}Exemplo em Python
# As classes dentro do parênteses são as classes mãe da classe sendo definida
class HondaFit(Carro):
def __init__(self, mecanismoAceleracao):
modelo = "Honda Fit"
# chama o construtor da classe mãe, ou seja, da classe "Carro"
super().__init__(modelo, mecanismoAceleracao)O que é interface na programação orientada a objetos?
Muitos dos métodos dos carros são comuns em vários automóveis.
Tanto um carro quanto uma motocicleta são classes cujos objetos podem acelerar, parar, acender o farol etc, pois são coisas comuns a automóveis.
Podemos dizer, então, que ambas as classes "carro" e "motocicleta" são "automóveis".
Quando duas (ou mais) classes possuem comportamentos comuns, esses comportamentos podem ser reunidos em uma interface, que será implementada pelas demais classes.
Note que colocamos a interface como "classe comum", que pode ser "herdada" (com aspas), porque uma interface não é exatamente um classe, mas sim um conjunto de métodos que todas as classes que herdarem dela devem possuir (implementar) - portanto, uma interface não é "herdada" por uma classe, mas sim implementada.
No contexto do desenvolvimento de software, uma interface funciona como um "contrato": qualquer classe que implementa uma interface se compromete a fornecer implementações para todos os métodos definidos pela interface, podendo assim ser tratada como pertencente ao mesmo tipo da interface.
No nosso exemplo, "carro" e "motocicleta" são classes que implementam os métodos da interface "automóvel", logo podemos dizer que qualquer objeto dessas duas primeiras classes, como um Honda Fit ou uma motocicleta da Yamaha, são automóveis.
Importante: uma interface não é herdada por uma classe, e sim implementada por ela.
No entanto, uma interface pode herdar de outra interface, criando uma hierarquia de interfaces.
Usando um exemplo completo com carros, dizemos que a classe "Honda Fit Cross" herda da classe "Honda Fit", que por sua vez herda da classe "Carro".
A classe "Carro" implementa a interface "Automóvel" que, por sua vez, pode herdar (por exemplo) uma interface chamada "MeioDeTransporte", uma vez que tanto um "automóvel" quanto uma "carroça" são meios de transporte, ainda que uma carroça não seja um automóvel.
Exemplo em Java
public interface Automovel {
void acelerar();
void frear();
void acenderFarol();
}
public class Carro implements Automovel {
/* ... */
@Override
public void acelerar() {
this.mecanismoAceleracao.acelerar();
}
@Override
public void frear() {
/* código do carro para frear */
}
@Override
public void acenderFarol() {
/* código do carro para acender o farol */
}
/* ... */
}
public class Moto implements Automovel {
/* ... */
@Override
public void acelerar() {
/* código específico da moto para acelerar */
}
@Override
public void frear() {
/* código específico da moto para frear */
}
@Override
public void acenderFarol() {
/* código específico da moto para acender o farol */
}
/* ... */
}Exemplo em Python
class Automovel():
def acelerar(self):
raise NotImplementedError()
def frear(self):
raise NotImplementedError()
def acenderFarol(self):
raise NotImplementedError()
class Carro(Automovel):
# ...
def acelerar(self):
# Codigo para acelerar o carro
def frear(self):
# Codigo para frear o carro
def acenderFarol(self):
# Codigo para acender o farol do carro
# ...
class Moto(Automovel):
# ...
def acelerar(self):
# Codigo para acelerar a moto
def frear(self):
# Codigo para frear a moto
def acenderFarol(self):
# Codigo para acender a moto
# ...Nota: criar um erro do tipo NotImplementedError é apenas uma convenção para que, caso uma classe filha tente executar um método da classe mãe sem tê-la implementado, ocorra o erro.
Em Python, as interfaces são criadas como classes normais que são herdadas pelas classes filhas. Existem formas de forçar a implementação por parte das classes filhas, mas para nosso exemplo essa abordagem é suficiente.
Nota: A partir do Python 3.4+, existe o módulo 'abc' (Abstract Base Classes) que é a forma recomendada para criar interfaces/classes abstratas.
O que é polimorfismo na programação orientada a objetos (POO)?
Vamos dizer que um dos motivos de você ter comprado um carro foi a qualidade do sistema de som dele.
Mas, no seu caso, digamos que a reprodução só pode ser feita via rádio ou bluetooth, enquanto que no seu antigo carro, podia ser feita apenas via cartão SD e pendrive.
Em ambos os carros está presente o método "tocar música" mas, como o sistema de som deles é diferente, a forma como o carro toca as músicas é diferente.
Esse é um exemplo de polimorfismo, pois objetos de diferentes classes podem ter um mesmo método, porém com implementações distintas. Ou seja, um mesmo nome de método pode ter várias formas de funcionamento conforme a classe. O termo polimorfismo vem do grego: 'poli' = muitas e 'morphos' = forma.
Exemplo em Java
public class Main {
public static void main(String[] args) {
Automovel moto = new Moto("Yamaha XPTO-100", new MecanismoDeAceleracaoDeMotos())
Automovel carro = new Carro("Honda Fit", new MecanismoDeAceleracaoDeCarros())
List<Automovel> listaAutomoveis = Arrays.asList(moto, carro);
for (Automovel automovel : listaAutomoveis) {
automovel.acelerar();
automovel.acenderFarol();
}
}
}Exemplo em Python
def main():
moto = Moto("Yahama XPTO-100", MecanismoDeAceleracaoDeMotos())
carro = Carro("Honda Fit", MecanismoDeAceleracaoDeCarros())
listaAutomoveis = [moto, carro]
for automovel in listaAutomoveis:
automovel.acelerar()
automovel.acenderFarol()Observe que, mesmo sendo objetos diferentes, "moto" e "carro" possuem os mesmos métodos "acelerar" e "acenderFarol". Esses métodos podem ser chamados da mesma forma, embora cada classe tenha sua própria implementação.
Programação estruturada x Programação orientada a objetos: principais diferenças
Ao iniciar em linguagens de programação como Java, C# ou Python, geralmente é comum aplicar a programação estruturada antes de utilizar recursos de orientação a objetos.
Diferente da programação orientada a objetos, na programação estruturada, um programa é composto por três tipos básicos de estruturas:
- Sequências: são os comandos a serem executados
- Condições: sequências que só devem ser executadas se uma condição for satisfeita (exemplos:
if-else,switche comandos parecidos) - Repetições: sequências que devem ser executadas repetidamente até uma condição for satisfeita (
for,while,do-whileetc)
Essas estruturas são usadas para processar as entradas do programa e gerar a saída esperada, por meio da alteração dos dados.
Até aí, nada que a programação orientada a objetos não faça, também, certo?
A principal diferença é que, na programação estruturada, os programas costumam ser escritos em uma única rotina principal (ou função), que pode ser dividida em sub-rotinas ou funções auxiliares.
Mas o fluxo do programa continua o mesmo, como se pudéssemos copiar e colar o código das subrotinas diretamente nas rotinas que as chamam.
Dessa forma, normalmente há uma grande rotina principal que executa todo o programa.

Além disso, na programação estruturada, o acesso às variáveis não possuem muitas restrições.
Em linguagens fortemente baseadas nesse paradigma, restringir o acesso à uma variável se limita a dizer se ela é visível ou não dentro de uma função (ou módulo, como no uso da palavra-chave static, na linguagem C).
Mas não se consegue dizer de forma nativa que uma variável pode ser acessada por apenas algumas rotinas do programa.
O contorno para situações como essas envolve práticas de programação danosas ao desenvolvimento do sistema, como o uso excessivo de variáveis globais.
Vale lembrar que variáveis globais são usadas tipicamente para manter estados no programa, marcando em qual parte dele a execução se encontra.
A programação orientada a objetos surgiu como uma alternativa a essas características da programação estruturada.
Design Patterns em linguagens orientadas a objetos
Alguns problemas aparecem com tanta frequência em POO que suas soluções se tornaram padrões de design de sistemas e modelagem de código orientado a objeto, a fim de resolvê-los.
Esses padrões de projeto, (ou design patterns) nada mais são do que formas padronizadas de resolver problemas comuns em linguagens orientadas a objetos.
O livro "Design Patterns", conhecido como Gof:Gang of Four, é a principal referência nesse assunto, contendo os principais padrões usados em grandes projetos.
A Alura também oferece cursos de Design Patterns em linguagens de programação como Java, Python e C#.
Clean code, SOLID e boas práticas em POO
Em projetos desenvolvidos com POO, assim como em qualquer paradigma, o código pode se tornar desorganizado e difícil de manter a médio e longo prazo.
Em vista dessa situação, alguns princípios de boas práticas de programação e código limpo foram desenvolvidos, como por exemplo:
- KISS (Keep It Simple, Stupid, "Mantenha as coisas simples"): Sempre que um código for escrito, ele deve ser escrito da forma mais simples possível, para manter o código mais legível. Códigos excessivamente complexos são mais difíceis de manter, pois é mais complicado compreender seu funcionamento e lógica.
- DRY (Don't Repeat Yourself, "Não se repita"): Todo código escrito para resolver um problema deve ser escrito apenas uma vez, a fim de evitar repetição de código. É quase uma variação do KISS, pois a repetição de código torna o sistema mais difícil de manter e corrigir.
Alguns cursos da Alura abordam esses assuntos, como o curso de SOLID com Java, SOLID com PHP, e também nosso artigo sobre o que é Clean Code.
Além dos design patterns e dos princípios de código limpo existe um conjunto de técnicas, mais generalizadas que os design patterns, que ajudam a criar código orientado a objeto de forma a deixá-lo mais flexível, possibilitando uma manutenção e expansão mais suave e descomplicada do código ao longo do tempo.
Se você quer dominar de vez a orientação a objetos e aprender sobre design patterns, clean code e os pilares da POO, explore a Carreira Desenvolvimento Back-End Java para sua próxima etapa na carreira.






