Programação orientada a objetos: princípios e boas práticas

Mayk Brito

Acelere sua carreira com a Rocketseat
Aproveite a black month para garantir seu plano na melhor condição!
Faaaaaala, dev! Mayk Brito na área para falar sobre um tema que muita gente ouve falar, mas que às vezes fica meio nebuloso: a programação orientada a objetos. Se você já começou a explorar o mundo da programação, pode ter trombado com esse termo, conhecido também como POO. E olha, deixa eu te contar uma coisa: entender orientação a objetos muda a nossa forma de programar. Se você quer organizar melhor o seu código, deixar ele mais fácil de entender e até de reaproveitar em outros projetos, cola aqui comigo que eu vou te mostrar como a POO pode transformar sua vida como dev!
Mas... o que é programação orientada a objetos?
Imagina que você tem que organizar todas as suas ideias de programação. Um jeito de fazer isso é pensar em termos de objetos – coisas que têm características (ou propriedades) e funcionalidades (ou métodos). Assim como no mundo real, onde uma caneta, por exemplo, tem uma cor, um tipo de tinta e uma função bem específica (escrever!), na POO, tudo no nosso código pode ser estruturado dessa maneira.
A POO é um paradigma – ou seja, um jeito de pensar na programação – e foi criada para organizar o código em torno de objetos. Ela surgiu para facilitar o desenvolvimento de sistemas complexos, permitindo que a gente consiga dividir o código em pequenas partes que se comunicam e funcionam de forma mais independente. O objetivo deste artigo é simplificar esses conceitos e mostrar como aplicá-los na prática. Vem comigo!
Os 4 pilares da programação orientada a objetos
Para a POO fazer sentido, a gente precisa entender seus quatro pilares: encapsulamento, herança, polimorfismo e abstração. Eles são as peças fundamentais que fazem a mágica acontecer. Vamos conhecer cada um deles.
1. Encapsulamento: guardando os segredos
O encapsulamento é como uma “caixinha” onde você guarda tudo o que é importante para o funcionamento de um objeto, mas sem deixar todo mundo ver ou mexer. Por exemplo, pensa no motor de um carro: você só precisa girar a chave para ele funcionar. Todo o funcionamento do motor está escondido; só você sabe que ele está ali, encapsulado.
Em programação, isso significa proteger certas partes do código para que apenas o que é realmente necessário seja acessível do lado de fora do objeto. Imagine uma classe
Curso na Rocketseat, onde o material completo está protegido e só é acessível para alunos que se inscreveram.class Curso { #conteudoCompleto; constructor(nome, duracao) { this.nome = nome; this.duracao = duracao; this.#conteudoCompleto = "Material exclusivo para alunos"; } acessarConteudo() { return `Acessando conteúdo do curso ${this.nome}: ${this.#conteudoCompleto}`; } } const cursoJavaScript = new Curso("JavaScript", "3 meses"); console.log(cursoJavaScript.acessarConteudo());
No exemplo acima, o
conteudoCompleto está encapsulado com #, então ele não pode ser acessado diretamente de fora da classe. Somente o método acessarConteudo consegue usá-lo, garantindo que o material do curso esteja acessível apenas da forma planejada. Esse tipo de encapsulamento ajuda a proteger informações e manter o controle sobre o que pode ser acessado.2. Herança: aproveitando o que já existe
Herança é sobre reaproveitar. Imagine que você tem uma estrutura base para um curso, chamada
Curso, e quer criar um curso específico para Node.js. Em vez de recriar tudo do zero, você usa a herança para que o curso de Node.js aproveite as propriedades e métodos da classe Curso, adicionando apenas o que é específico para ele.class Curso { iniciar() { console.log("Iniciando o curso..."); } } class CursoNode extends Curso { assistirAula() { console.log("Assistindo aula de Node.js!"); } } const cursoNode = new CursoNode(); cursoNode.iniciar(); // Herda o método iniciar da classe Curso cursoNode.assistirAula(); // Método específico da classe CursoNode
Aqui, a classe
CursoNode herda o método iniciar da classe base Curso. Assim, podemos adicionar funcionalidades específicas, como assistirAula, que é exclusiva do curso de Node.js. A herança permite reaproveitar o que já foi feito, organizando o código de forma eficiente e evitando redundâncias.3. Polimorfismo: várias formas de ser
Polimorfismo permite que métodos em classes diferentes respondam de maneira distinta a uma mesma chamada. Imagine que estamos desenvolvendo uma plataforma de cursos para a Rocketseat, onde diferentes formações, como React e Java, possuem o método
iniciar. Cada formação inicia de maneira única, mas o método iniciar é o mesmo.class Formacao { iniciar() { console.log("Iniciando a formação..."); } } class FormacaoReact extends Formacao { iniciar() { console.log("Iniciando a formação de React!"); } } class FormacaoJava extends Formacao { iniciar() { console.log("Iniciando a formação de Java!"); } } const react = new FormacaoReact(); const java = new FormacaoJava(); react.iniciar(); // Inicia a formação de React java.iniciar(); // Inicia a formação de Java
No exemplo acima, tanto
FormacaoReact quanto FormacaoJava possuem o método iniciar, mas cada um responde de forma única, respeitando o contexto da formação específica. Esse é o poder do polimorfismo: adaptar o comportamento de um método conforme a necessidade de cada classe.4. Abstração: só o que importa
Abstração ajuda a focar nos aspectos essenciais e a esconder os detalhes complexos. Imagine que estamos criando um sistema para gerenciar cursos da Rocketseat, onde cada curso pode ser específico, como Go ou C#, mas todos compartilham características comuns de um
Curso. Com a abstração, definimos o que é necessário em cada curso sem detalhar a implementação de cada um.class Curso { constructor(nome) { if (new.target === Curso) { throw new Error("Curso é uma classe abstrata e não pode ser instanciada diretamente"); } this.nome = nome; } iniciarCurso() { throw new Error("Método iniciarCurso() deve ser implementado"); } } class CursoGo extends Curso { iniciarCurso() { return `${this.nome} está iniciando, se prepare para aprender Go!`; } } class CursoCSharp extends Curso { iniciarCurso() { return `${this.nome} está iniciando, se prepare para aprender C#!`; } } const go = new CursoGo("Curso de Go"); const csharp = new CursoCSharp("Curso de C#"); console.log(go.iniciarCurso()); console.log(csharp.iniciarCurso());
Nesse código,
Curso é uma classe abstrata que define o método iniciarCurso, mas não o implementa. As classes CursoGo e CursoCSharp herdam de Curso e implementam o método conforme suas necessidades específicas. A abstração permite criar um modelo geral e deixar que cada curso detalhe sua implementação – simplificando a estrutura do sistema e mantendo a flexibilidade.Princípios SOLID: levando a POO ao próximo nível
Agora que já falamos dos pilares, vamos explorar o SOLID. Esses cinco princípios elevam a POO, ajudando a manter o código mais limpo, organizado e fácil de expandir.
- SRP (single responsibility principle): cada classe deve ter uma única responsabilidade. Isso evita que uma classe fique “fazendo tudo” e facilita a manutenção do código.
- OCP (open/closed principle): classes devem estar abertas para extensão, mas fechadas para modificação. Ou seja, se você precisar adicionar funcionalidades, estenda a classe ao invés de modificá-la diretamente.
- LSP (Liskov substitution principle): subclasses devem ser substituíveis por suas superclasses. Se você substitui uma classe base por uma derivada, o código ainda deve funcionar sem problemas.
- ISP (interface segregation principle): interfaces específicas para cada tipo de cliente. Ou seja, as classes não devem depender de métodos que não usam.
- DIP (dependency inversion principle): dependências devem apontar para abstrações, e não para implementações concretas.
Boas práticas de POO
Aqui vão algumas dicas para escrever código orientado a objetos que vai deixar sua vida e a de quem for ler seu código mais fácil, especialmente em sistemas robustos e escaláveis como os usados na Rocketseat:
- Nomeação clara e direta: escolha nomes que sejam claros e significativos para variáveis, métodos e classes. Em vez de abreviações ou siglas, use nomes descritivos, como
AlunoCursoNodeouFormacaoJavaScript. Nomes claros ajudam o time a entender o código sem precisar de muita documentação extra.
- Comente apenas o essencial: escreva o código de forma que ele “fale por si”. Comentários devem esclarecer o que não é óbvio e explicar lógicas complexas ou específicas. Em uma formação da Rocketseat, por exemplo, ao definir um método
iniciarModulo, deixe claro o que cada etapa do módulo faz, mas sem sobrecarregar o código com comentários excessivos.
- Escreva testes unitários: testes são a garantia de que o código está funcionando como esperado e de que mudanças futuras não quebrem funcionalidades. Imagine um sistema de acesso a cursos onde os alunos podem iniciar e pausar aulas. Ter testes que garantem que os alunos só conseguem acessar o conteúdo correto é essencial para um sistema funcional e seguro.
- Refatore sempre que possível: não tenha medo de revisar e reescrever partes do código para deixá-lo mais claro ou eficiente. Se você percebeu uma oportunidade de simplificar uma função
acessarCursoougerarCertificado, por exemplo, faça a refatoração e compartilhe com o time. Um código limpo é a base para um sistema sustentável e fácil de manter.
Exemplo prático: sistema de formações na Rocketseat com herança
Imagine que estamos criando um sistema para gerenciar diferentes formações da Rocketseat, como Node.js, React, e JavaScript. Cada formação tem algumas características específicas, mas todas compartilham comportamentos e propriedades comuns que podemos definir em uma classe
Formacao.class Formacao { constructor(nome, duracao) { this.nome = nome; this.duracao = duracao; } iniciar() { console.log(`Iniciando a formação ${this.nome} com duração de ${this.duracao} meses.`); } } class FormacaoNode extends Formacao { projetoFinal() { console.log(`Projeto final da formação ${this.nome}: Criar uma API completa com Node.js!`); } } class FormacaoReact extends Formacao { projetoFinal() { console.log(`Projeto final da formação ${this.nome}: Desenvolver uma aplicação web completa com React!`); } } const formacaoNode = new FormacaoNode("Node.js", 3); formacaoNode.iniciar(); // Método herdado da classe Formacao formacaoNode.projetoFinal(); // Método específico de FormacaoNode const formacaoReact = new FormacaoReact("React", 3); formacaoReact.iniciar(); formacaoReact.projetoFinal();
Neste exemplo, a classe
Formacao define métodos e propriedades comuns para todas as formações, enquanto FormacaoNode e FormacaoReact adicionam comportamentos específicos, como o projeto final de cada curso. Esse tipo de estrutura permite que o sistema seja expandido facilmente, possibilitando que novas formações compartilhem a mesma base e ganhem novos métodos conforme necessário.Imagine agora que esse sistema se expanda para incluir formações de Java ou Python. O padrão de herança permite que você adicione novos cursos sem duplicar código, mantendo tudo organizado e claro para o time. É aqui que a Programação Orientada a Objetos realmente brilha!
Conclusão
Agora que você deu esse primeiro passo no mundo da Programação Orientada a Objetos, quero te convidar a dar continuidade nos estudos e explorar ainda mais esse universo. Afinal, com a prática, tudo fica mais claro, e a POO se torna uma ferramenta poderosa nas suas mãos!
Para te ajudar com seus estudos em POO, que tal descobrir estratégias e técnicas para gerenciar seu próprio aprendizado, definir objetivos e alcançar sucesso no desenvolvimento pessoal e profissional? Acesse o nosso material exclusivo de Educação Autodirigida e leve sua jornada de estudos para o próximo nível: Acesse aqui.
Outra ferramenta incrível que vai te ajudar a organizar seus estudos é o nosso Calendário de Estudos Personalizável. Com ele, você pode definir suas metas, organizar o tempo e maximizar sua produtividade, planejando cada etapa da sua jornada no aprendizado de POO e outras tecnologias. Acesse e crie seu próprio calendário agora: Baixe o calendário.
E se você gostou desse conteúdo e quer saber ainda mais sobre POO, venha conferir um vídeo especial que gravei sobre esse tema no meu canal! Nele, explico com detalhes cada conceito da Programação Orientada a Objetos, como encapsulamento, herança, polimorfismo e abstração, com exemplos práticos e linguagem acessível. Você vai entender, de uma vez por todas, como esses pilares podem transformar o jeito que você escreve e organiza seu código. Não perca essa oportunidade de aprender de forma prática e divertida!

Acelere sua carreira com a Rocketseat
Aproveite a black month para garantir seu plano na melhor condição!
Artigos_
Explore conteúdos relacionados
Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.

