Fundamentos de Orientação a Objetos em Java: classes, objetos e responsabilidade

Capítulo 1

Tempo estimado de leitura: 9 minutos

+ Exercício

O que são classes e objetos (na prática)

Em Java, classe é um molde que descreve um tipo de coisa do domínio: quais dados ela guarda (atributos/campos) e o que ela sabe fazer (comportamentos/métodos). Um objeto é uma instância concreta criada a partir da classe, com valores próprios para seus campos.

Uma forma útil de pensar: a classe define responsabilidades. Se uma classe tem responsabilidades bem definidas, ela tende a ser mais fácil de entender, testar e evoluir.

Responsabilidade, coesão e dependências

  • Responsabilidade: aquilo que a classe deve fazer (e apenas isso).
  • Coesão (cohesion): quão “unidas” são as responsabilidades dentro da classe. Alta coesão significa que os métodos e campos da classe trabalham juntos para um objetivo claro.
  • Dependências: outras classes/serviços que uma classe precisa para cumprir seu papel. Dependências em excesso ou mal posicionadas aumentam acoplamento e dificultam mudanças.

Regra prática: se você descreve uma classe com “e” demais (ex.: “faz X e Y e Z”), provavelmente ela está assumindo responsabilidades demais.

Modelando um domínio simples: Biblioteca

Vamos modelar um domínio pequeno de biblioteca com foco em identificar entidades, atributos e comportamentos. Requisitos iniciais (simples e realistas):

  • Cadastrar livros com título, autor e quantidade de exemplares disponíveis.
  • Emprestar um livro (reduz disponibilidade) e devolver (aumenta).
  • Não permitir emprestar quando não há exemplares.
  • Gerar um identificador simples para cada livro.

Passo 1: identificar entidades

Entidades candidatas:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Livro: representa um item do acervo e controla sua disponibilidade.
  • (Opcional em versões futuras) Usuário, Empréstimo, Biblioteca/Catálogo.

Como estamos no começo, vamos começar com Livro e um pequeno programa para exercitar a classe.

Passo 2: listar atributos (estado)

  • id: identificador do livro.
  • titulo
  • autor
  • exemplaresDisponiveis

Passo 3: listar comportamentos (métodos)

  • emprestar(): tenta emprestar; se não houver exemplares, falha.
  • devolver(): devolve um exemplar.
  • getters para leitura (evitar expor campos diretamente).

Implementando as primeiras classes em Java

Campos, construtores e encapsulamento

Encapsulamento é manter os campos como private e expor operações seguras via métodos. Isso permite validar regras (ex.: não deixar disponibilidade negativa).

public class Livro {    private final String id;    private final String titulo;    private final String autor;    private int exemplaresDisponiveis;    public Livro(String id, String titulo, String autor, int exemplaresDisponiveis) {        if (id == null || id.isBlank()) {            throw new IllegalArgumentException("id obrigatório");        }        if (titulo == null || titulo.isBlank()) {            throw new IllegalArgumentException("título obrigatório");        }        if (autor == null || autor.isBlank()) {            throw new IllegalArgumentException("autor obrigatório");        }        if (exemplaresDisponiveis < 0) {            throw new IllegalArgumentException("exemplaresDisponiveis não pode ser negativo");        }        this.id = id;        this.titulo = titulo;        this.autor = autor;        this.exemplaresDisponiveis = exemplaresDisponiveis;    }    public String getId() {        return id;    }    public String getTitulo() {        return titulo;    }    public String getAutor() {        return autor;    }    public int getExemplaresDisponiveis() {        return exemplaresDisponiveis;    }    public boolean emprestar() {        if (exemplaresDisponiveis <= 0) {            return false;        }        exemplaresDisponiveis--;        return true;    }    public void devolver() {        exemplaresDisponiveis++;    }}

Observações importantes:

  • final em campos indica que o valor não muda após o construtor (bom para identidade e dados imutáveis).
  • O método emprestar() retorna boolean para indicar sucesso/falha sem lançar exceção em um fluxo esperado.
  • O método devolver() é simples; em um sistema real, você poderia validar limites, histórico etc.

Instanciando objetos e chamando métodos

public class ProgramaBiblioteca {    public static void main(String[] args) {        Livro livro = new Livro("L-001", "Java para Domínios", "Ana Silva", 2);        System.out.println(livro.getTitulo() + " - disponíveis: " + livro.getExemplaresDisponiveis());        boolean ok1 = livro.emprestar();        boolean ok2 = livro.emprestar();        boolean ok3 = livro.emprestar();        System.out.println("Empréstimo 1: " + ok1);        System.out.println("Empréstimo 2: " + ok2);        System.out.println("Empréstimo 3: " + ok3);        System.out.println("Disponíveis agora: " + livro.getExemplaresDisponiveis());        livro.devolver();        System.out.println("Disponíveis após devolução: " + livro.getExemplaresDisponiveis());    }}

Esse exemplo já mostra o essencial: criar objeto com new, inicializar via construtor e usar métodos para alterar o estado com regras.

Membros static e constantes: quando usar

static significa “pertence à classe, não ao objeto”. Use quando a informação/ação é compartilhada por todas as instâncias ou quando você precisa de um utilitário sem estado de instância.

Constantes com static final

Constantes são valores fixos, normalmente public static final. Exemplo: um prefixo padrão de ID.

public class Livro {    public static final String PREFIXO_ID = "L-";    private static int sequencia = 1;    private final String id;    private final String titulo;    private final String autor;    private int exemplaresDisponiveis;    public Livro(String titulo, String autor, int exemplaresDisponiveis) {        this(gerarId(), titulo, autor, exemplaresDisponiveis);    }    public Livro(String id, String titulo, String autor, int exemplaresDisponiveis) {        if (id == null || id.isBlank()) {            throw new IllegalArgumentException("id obrigatório");        }        if (titulo == null || titulo.isBlank()) {            throw new IllegalArgumentException("título obrigatório");        }        if (autor == null || autor.isBlank()) {            throw new IllegalArgumentException("autor obrigatório");        }        if (exemplaresDisponiveis < 0) {            throw new IllegalArgumentException("exemplaresDisponiveis não pode ser negativo");        }        this.id = id;        this.titulo = titulo;        this.autor = autor;        this.exemplaresDisponiveis = exemplaresDisponiveis;    }    private static String gerarId() {        return PREFIXO_ID + (sequencia++);    }    public String getId() {        return id;    }    public String getTitulo() {        return titulo;    }    public String getAutor() {        return autor;    }    public int getExemplaresDisponiveis() {        return exemplaresDisponiveis;    }    public boolean emprestar() {        if (exemplaresDisponiveis <= 0) {            return false;        }        exemplaresDisponiveis--;        return true;    }    public void devolver() {        exemplaresDisponiveis++;    }}

Pontos de atenção:

  • sequencia é static porque é compartilhada por todos os livros para gerar IDs.
  • gerarId() é static porque não depende de um objeto já existente.
  • Esse gerador é simples e serve para exercício; em aplicações reais, IDs podem vir de banco, UUID etc.

Transformando requisitos em classes: roteiro guiado

Checklist de modelagem (rápido e prático)

PerguntaO que você produzExemplo (Biblioteca)
Quais são os “substantivos” do domínio?Entidades/classes candidatasLivro, Usuário, Empréstimo
O que cada entidade precisa lembrar?Atributos/campostítulo, autor, disponíveis
O que cada entidade precisa fazer?Métodosemprestar, devolver
Quais regras não podem ser quebradas?Validações e invariantesdisponíveis nunca negativo
Quem deve conhecer quem?DependênciasLivro não precisa conhecer Usuário (ainda)

Exercício guiado 1: do requisito à classe

Requisito: “Uma conta bancária tem número, titular e saldo. Deve permitir depositar e sacar. Não permitir saque maior que o saldo.”

Passo a passo:

  • Entidade: ContaBancaria
  • Atributos: numero, titular, saldo
  • Métodos: depositar(valor), sacar(valor), getters
  • Invariantes: saldo >= 0; valor > 0
public class ContaBancaria {    private final String numero;    private final String titular;    private double saldo;    public ContaBancaria(String numero, String titular, double saldoInicial) {        if (numero == null || numero.isBlank()) {            throw new IllegalArgumentException("número obrigatório");        }        if (titular == null || titular.isBlank()) {            throw new IllegalArgumentException("titular obrigatório");        }        if (saldoInicial < 0) {            throw new IllegalArgumentException("saldoInicial não pode ser negativo");        }        this.numero = numero;        this.titular = titular;        this.saldo = saldoInicial;    }    public String getNumero() {        return numero;    }    public String getTitular() {        return titular;    }    public double getSaldo() {        return saldo;    }    public void depositar(double valor) {        if (valor <= 0) {            throw new IllegalArgumentException("valor de depósito deve ser positivo");        }        saldo += valor;    }    public boolean sacar(double valor) {        if (valor <= 0) {            throw new IllegalArgumentException("valor de saque deve ser positivo");        }        if (valor > saldo) {            return false;        }        saldo -= valor;        return true;    }}

Validação de responsabilidade: a classe cuida do saldo e das regras de movimentação. Ela não imprime extrato, não salva em arquivo, não acessa banco. Isso mantém alta coesão.

Exercício guiado 2: melhorando coesão (separando responsabilidades)

Cenário: você criou uma classe Biblioteca que “cadastra livro, empresta, devolve, imprime relatórios e também lê dados do teclado”.

Problema: responsabilidades demais (baixa coesão). Misturar regras de domínio com entrada/saída (I/O) torna o código difícil de testar.

Tarefa: separe em pelo menos duas classes:

  • Biblioteca: regras de negócio (cadastrar, emprestar, devolver).
  • RelatorioBiblioteca (ou Formatador): apenas formatação/relatório.
  • (Opcional) ProgramaBiblioteca: interação com usuário (main).

Esqueleto sugerido:

import java.util.ArrayList;import java.util.List;public class Biblioteca {    private final List<Livro> acervo = new ArrayList<>();    public void cadastrar(Livro livro) {        if (livro == null) {            throw new IllegalArgumentException("livro obrigatório");        }        acervo.add(livro);    }    public List<Livro> getAcervo() {        return List.copyOf(acervo);    }    public boolean emprestarPorId(String id) {        Livro livro = encontrarPorId(id);        if (livro == null) return false;        return livro.emprestar();    }    public boolean devolverPorId(String id) {        Livro livro = encontrarPorId(id);        if (livro == null) return false;        livro.devolver();        return true;    }    private Livro encontrarPorId(String id) {        for (Livro l : acervo) {            if (l.getId().equals(id)) {                return l;            }        }        return null;    }}
public class RelatorioBiblioteca {    public String gerarResumo(Biblioteca biblioteca) {        StringBuilder sb = new StringBuilder();        for (Livro l : biblioteca.getAcervo()) {            sb.append(l.getId())              .append(" | ")              .append(l.getTitulo())              .append(" | ")              .append(l.getAutor())              .append(" | disponíveis: ")              .append(l.getExemplaresDisponiveis())              .append("\n");        }        return sb.toString();    }}

Checagem de dependências: RelatorioBiblioteca depende de Biblioteca e Livro para ler dados, mas não altera regras. Biblioteca depende de Livro (faz sentido), mas não depende de relatório nem de console.

Exercício guiado 3: identificando static mal utilizado

Requisito: “Cada livro tem sua própria disponibilidade.”

Erro comum: declarar exemplaresDisponiveis como static, fazendo com que todos os livros compartilhem o mesmo valor.

Tarefa: explique por que exemplaresDisponiveis deve ser campo de instância (sem static) e cite um exemplo de campo que faz sentido ser static no domínio (por exemplo, um gerador de sequência, uma constante de prefixo, ou uma política global).

Prática orientada: mini-desafio de modelagem (Pedidos)

Requisitos:

  • Um pedido tem número e itens.
  • Um item tem descrição, quantidade e preço unitário.
  • O pedido calcula o total (soma de quantidade * preço).
  • Não permitir quantidade menor ou igual a zero, nem preço negativo.

Tarefa 1: liste as classes e responsabilidades (duas classes são suficientes).

Tarefa 2: implemente:

  • ItemPedido com validações no construtor.
  • Pedido com uma lista de itens, método adicionarItem e método getTotal().

Tarefa 3 (cohesion): decida onde deve ficar o cálculo do subtotal do item: em ItemPedido (ex.: getSubtotal()) ou em Pedido. Justifique com base em responsabilidade e coesão.

Agora responda o exercício sobre o conteúdo:

Ao notar que uma classe está sendo descrita com muitos “e” (faz X e Y e Z), qual refatoração melhora a coesão e reduz acoplamento, mantendo as responsabilidades bem definidas?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Muitos “e” indicam responsabilidades demais (baixa coesão). Separar regras de negócio de formatação/relatórios ou I/O reduz dependências desnecessárias, melhora testabilidade e facilita evoluir o código.

Próximo capitúlo

Encapsulamento em Java OOP: visibilidade, invariantes e API pública

Arrow Right Icon
Capa do Ebook gratuito Java Orientado a Objetos: Do Conceito ao Código com Padrões de Projeto Básicos
6%

Java Orientado a Objetos: Do Conceito ao Código com Padrões de Projeto Básicos

Novo curso

17 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.