Skip to content

lucaspaixao-dev/Object-Oriented-Programming

Repository files navigation

🧠 Orientação a Objetos & SOLID em Kotlin

Projeto de estudo com exemplos práticos, comentados e executáveis dos conceitos fundamentais de Programação Orientada a Objetos (POO) e dos princípios SOLID. Cada conceito vive em seu próprio arquivo, com uma função main() que demonstra o comportamento no console.

💡 Todos os comentários do código estão em português, pensados para quem está aprendendo. Leia o código junto com este README.


📂 Estrutura do projeto

src/main/kotlin/
├── fundamentos/
│   ├── 01_ClassesEObjetos.kt     → Classe x Objeto
│   ├── 02_Encapsulamento.kt      → Encapsulamento
│   ├── 03_Heranca.kt             → Herança
│   ├── 04_Polimorfismo.kt        → Polimorfismo
│   ├── 05_Abstracao.kt           → Abstração (classe abstrata)
│   ├── 06_Interface.kt           → Interfaces
│   └── 07_Composicao.kt          → Composição ("TEM UM")
└── solid/
    ├── 01_SRP_SingleResponsibility.kt
    ├── 02_OCP_OpenClosed.kt
    ├── 03_LSP_LiskovSubstitution.kt
    ├── 04_ISP_InterfaceSegregation.kt
    └── 05_DIP_DependencyInversion.kt

▶️ Como executar

Cada arquivo tem sua própria função main(). A forma mais simples de rodar é pela IntelliJ IDEA: abra o arquivo e clique na ▶️ (seta verde) ao lado do fun main().

Pela linha de comando você pode compilar tudo com:

./gradlew compileKotlin

O projeto usa Kotlin 2.3.21 e a toolchain JDK 25 (configurada em build.gradle.kts). O Gradle baixa a JDK automaticamente na primeira execução.


🏛️ Parte 1 — Fundamentos da POO

Os 4 pilares da Orientação a Objetos

Os 4 pilares clássicos são Abstração, Encapsulamento, Herança e Polimorfismo. Junto deles, Interface e Composição completam o kit essencial de design. Abaixo, cada um com seu exemplo.


1. Classe e Objeto — 01_ClassesEObjetos.kt

Classe x Objeto

Conceito O que é
Classe O molde / a planta. Define os atributos (estado) e comportamentos (métodos).
Objeto A instância criada a partir da classe. Cada um tem seu próprio estado.

Classe do exemplo:

  • Carro — possui os atributos marca, modelo e velocidade, e os métodos acelerar(), frear() e descricao(). No main() criamos dois objetos (fusca e civic) e vemos que cada um guarda seu estado de forma independente.

2. Encapsulamento — 02_Encapsulamento.kt

Encapsulamento

Esconder os detalhes internos e proteger o estado do objeto, expondo apenas uma interface segura. Assim ninguém coloca o objeto em um estado inválido.

Classe do exemplo:

  • ContaBancaria — o saldo é private set (lê-se de fora, mas só muda por dentro). As únicas portas de entrada são depositar() e sacar(), ambas com validação (require, checagem de saldo). Tentar conta.saldo = 9999.0 nem compila — é exatamente essa a proteção do encapsulamento.

🔑 Modificadores de visibilidade: public (padrão), private, protected, internal.


3. Herança — 03_Heranca.kt

Herança

Criar uma classe nova reaproveitando atributos e comportamentos de outra, numa relação "É UM". Em Kotlin é preciso marcar a classe/método como open para permitir herança e usar override para especializar.

Classes do exemplo:

  • Animal (superclasse open) — tem nome, o método dormir() (comum a todos) e emitirSom() (marcado open para ser sobrescrito).
  • Cachorro — herda de Animal, faz override de emitirSom() ("Au au!") e adiciona o método próprio abanarRabo().
  • Gato — herda de Animal e usa super.emitirSom() para reaproveitar a implementação do pai antes de adicionar o "Miau!".

4. Polimorfismo — 04_Polimorfismo.kt

Polimorfismo

"Muitas formas": a mesma chamada se comporta de maneiras diferentes conforme o objeto real por trás dela.

Classes do exemplo:

  • Funcionario (base), Gerente (+50% de bônus) e Vendedor (base + comissão) — todos têm calcularSalario(). Ao percorrer uma List<Funcionario>, a mesma chamada f.calcularSalario() produz resultados diferentes → polimorfismo de sobrescrita (em tempo de execução).
  • Calculadora — três métodos somar() com parâmetros diferentes → polimorfismo de sobrecarga (overload, em tempo de compilação).

5. Abstração — 05_Abstracao.kt

Abstração

Focar no O QUE um objeto faz, escondendo o COMO. Uma classe abstrata não pode ser instanciada e pode obrigar as subclasses a implementarem certos métodos.

Classes do exemplo:

  • FormaGeometrica (abstrata) — define os métodos abstratos area() e perimetro() (sem corpo) e o método concreto descrever() (reaproveitado por todas).
  • Circulo, Retangulo, TrianguloEquilatero — cada uma implementa area()/perimetro() com sua própria fórmula. O código que chama descrever() não precisa saber qual forma é.

Abstração vs. Interface: a classe abstrata pode guardar estado e expressa "É UM"; a interface expressa uma capacidade ("consigo fazer X") e permite implementação múltipla.


6. Interface — 06_Interface.kt

Interface

Um contrato de capacidades. Uma classe pode implementar várias interfaces (diferente da herança, que permite só uma superclasse).

Tipos do exemplo:

  • Voador — contrato com voar() e um método default aterrissar().
  • Nadador — contrato com nadar().
  • Pato — implementa as duas (voa e nada).
  • Pinguim — implementa Nadador (não voa).
  • Aviao — implementa Voador (mesmo não sendo um animal — a interface conecta tipos sem relação de herança).

7. Composição — 07_Composicao.kt

Composição

Montar um objeto complexo a partir de outros, numa relação "TEM UM". Princípio de ouro: "prefira composição à herança" — é mais flexível e tem menor acoplamento.

Classes do exemplo:

  • CPU, MemoriaRAM, ArmazenamentoSSD — peças pequenas, cada uma com responsabilidade única.
  • Computador — é composto por essas peças (recebidas no construtor) e delega o trabalho a elas em ligar(). Trocamos um "PC básico" por um "PC gamer" só mudando as peças, sem criar novas classes via herança.

🧱 Parte 2 — Princípios SOLID

Princípios SOLID

SOLID são 5 princípios para escrever código orientado a objetos mais limpo, flexível e fácil de manter. Em cada arquivo você encontra a versão ❌ errada (com sufixo Ruim) e a ✅ correta, lado a lado, para comparar.


S — Responsabilidade Única (SRP) — 01_SRP_SingleResponsibility.kt

"Uma classe deve ter apenas um motivo para mudar."

  • RelatorioRuim — calcula, formata em texto e salva em arquivo (três responsabilidades = três motivos para mudar).
  • ✅ Separadas em Relatorio (dados/regra), FormatadorRelatorio (formatação) e RepositorioRelatorio (persistência). Cada uma muda e é testada de forma independente.

O — Aberto/Fechado (OCP) — 02_OCP_OpenClosed.kt

"Aberto para extensão, fechado para modificação."

  • CalculadoraDescontoRuim — um when por tipo de cliente; cada novo tipo exige editar a classe.
  • ✅ Contrato RegraDesconto implementado por ClienteComum, ClientePremium, ClienteVip e ClienteParceiro. Um novo desconto = uma nova classe, sem tocar no código existente. A CalculadoraDesconto só conhece a abstração.

L — Substituição de Liskov (LSP) — 03_LSP_LiskovSubstitution.kt

"Um subtipo deve poder substituir o tipo base sem quebrar o programa."

  • AveRuim assume que toda ave voa; PinguimRuim é forçado a lançar exceção em voar() → substituir a base por ele quebra o programa.
  • Ave só define o que toda ave faz (comer()); a capacidade de voar fica na interface PodeVoar, implementada por Pardal e Aguia. Pinguim é uma Ave que simplesmente não implementa PodeVoar — sem mentiras, sem exceções.

I — Segregação de Interfaces (ISP) — 04_ISP_InterfaceSegregation.kt

"Ninguém deve ser forçado a depender de métodos que não usa."

  • TrabalhadorRuim é uma interface "gorda" (trabalhar + comer + dormir). RoboRuim é obrigado a implementar comer()/dormir() que não fazem sentido para ele.
  • ✅ Interfaces pequenas e coesas: Trabalhavel, Alimentavel, Descansavel. Humano implementa as três; Robo implementa só Trabalhavel.

D — Inversão de Dependência (DIP) — 05_DIP_DependencyInversion.kt

Inversão de Dependência

"Dependa de abstrações, não de implementações concretas."

  • NotificadorRuim instancia e depende diretamente de EmailService. Trocar o canal exige editar o notificador.
  • ✅ A abstração CanalDeMensagem é implementada por EmailSender, SmsSender e WhatsappSender. A classe Notificador recebe o canal pelo construtor (injeção de dependência) e funciona com qualquer um — inclusive um fake em testes — sem nunca ser modificada.

🗺️ Resumo rápido

# Conceito Ideia em uma frase Arquivo
1 Classe/Objeto Molde × instância fundamentos/01_ClassesEObjetos.kt
2 Encapsulamento Proteger o estado interno fundamentos/02_Encapsulamento.kt
3 Herança Reaproveitar ("É UM") fundamentos/03_Heranca.kt
4 Polimorfismo Mesma chamada, comportamentos diferentes fundamentos/04_Polimorfismo.kt
5 Abstração O QUE, não o COMO fundamentos/05_Abstracao.kt
6 Interface Contrato de capacidades fundamentos/06_Interface.kt
7 Composição Montar com peças ("TEM UM") fundamentos/07_Composicao.kt
S SRP Um motivo para mudar solid/01_SRP_SingleResponsibility.kt
O OCP Estender sem modificar solid/02_OCP_OpenClosed.kt
L LSP Subtipo substitui a base solid/03_LSP_LiskovSubstitution.kt
I ISP Interfaces pequenas solid/04_ISP_InterfaceSegregation.kt
D DIP Depender de abstrações solid/05_DIP_DependencyInversion.kt

Bons estudos! 🚀 Leia o código, rode os main() e experimente modificar os exemplos para fixar cada conceito.

About

This repo shows the mainly object oriented programming principals

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages