Capa do Ebook gratuito Analista Judiciário - Tecnologia da Informação: Preparação Completa para Concursos do Judiciário

Analista Judiciário - Tecnologia da Informação: Preparação Completa para Concursos do Judiciário

Novo curso

24 páginas

Programação orientada a objetos para Analista Judiciário - TI: classes, objetos e boas práticas

Capítulo 5

Tempo estimado de leitura: 9 minutos

+ Exercício

Em concursos para Analista Judiciário – TI, Programação Orientada a Objetos (OOP) costuma ser cobrada tanto em conceitos (definições e diferenças) quanto em leitura de código (identificar princípios e efeitos de design). O foco aqui é entender os pilares e aplicar em entidades típicas do Judiciário, evitando confusões comuns em questões.

Classe e objeto: o que são e como diferenciar em prova

Conceito

Classe é um “molde” (tipo) que define atributos (dados) e métodos (comportamentos). Objeto é uma instância concreta criada a partir da classe, com estado próprio.

  • Classe: definição (ex.: Processo).
  • Objeto: ocorrência (ex.: o processo de número 0001234-56.2025.8.01.0001).

Confusões frequentes em questões

  • Classe ≠ objeto: classe não “guarda dados de um caso real” por si; quem guarda é o objeto (instância).
  • Tipo ≠ instância: “variável do tipo Processo” pode estar null (sem instância) em linguagens que permitem referência nula.
  • Atributo de classe (estático) ≠ atributo de instância: estático é compartilhado por todas as instâncias; instância é individual.

Exemplo de modelagem (Processo, Parte, Usuário, Permissão)

// Exemplo conceitual (estilo Java/C#), focado em leitura de prova: atributos + métodos + invariantes simples
class Processo {
  String numero;
  String classeJudicial; // ex.: "Ação de Cobrança"
  List<Parte> partes;
  Usuario responsavel;

  void adicionarParte(Parte p) { /* ... */ }
  boolean podeSerAcessadoPor(Usuario u) { /* ... */ return false; }
}

class Parte {
  String nome;
  String tipo; // ex.: "Autor", "Réu", "Terceiro"
}

class Usuario {
  String login;
  Set<Permissao> permissoes;

  boolean temPermissao(Permissao p) { /* ... */ return false; }
}

class Permissao {
  String codigo; // ex.: "PROCESSO_VISUALIZAR"
}

Encapsulamento: proteger invariantes e reduzir dependências

Conceito

Encapsulamento é esconder detalhes internos e expor uma interface controlada de uso. Em prova, aparece como: uso de modificadores de acesso (private/protected/public), getters/setters com validação, e métodos que preservam regras do domínio (invariantes).

Passo a passo prático: encapsular o número do processo

  • 1) Identifique um dado que não pode ficar “livre” para qualquer alteração (ex.: número do processo).
  • 2) Torne o atributo privado.
  • 3) Exponha método de leitura e, se fizer sentido, método de alteração com validação (ou não exponha alteração).
  • 4) Centralize a regra no método, evitando validação duplicada fora da classe.
class Processo {
  private String numero;

  public Processo(String numero) {
    if (!validarNumero(numero)) {
      throw new IllegalArgumentException("Número inválido");
    }
    this.numero = numero;
  }

  public String getNumero() {
    return numero;
  }

  // Sem setNumero: evita que o número mude após criado (invariante)
  private boolean validarNumero(String n) {
    // validação simplificada para fins didáticos
    return n != null && n.length() >= 10;
  }
}

Diferenças conceituais cobradas

  • Encapsulamento ≠ ocultação total: você expõe operações necessárias, mas controla como o estado muda.
  • Getter/setter não é sinônimo de encapsular: se setters permitem qualquer valor sem regra, você só “escondeu” o campo, mas não protegeu invariantes.
  • Imutabilidade (quando aplicável) reforça encapsulamento: menos pontos de alteração, menos bugs.

Herança: generalização e especialização (e quando evitar)

Conceito

Herança modela relação “é-um” (is-a): uma classe derivada herda atributos/métodos de uma base e pode especializar comportamento. Em concursos, é comum cobrar: sobrescrita (override), visibilidade (protected), e substituição por polimorfismo.

Exemplo: tipos de usuário

class Usuario {
  protected String login;
  public boolean podeAssinar() { return false; }
}

class UsuarioServidor extends Usuario {
  @Override
  public boolean podeAssinar() { return true; }
}

class UsuarioAdvogado extends Usuario {
  @Override
  public boolean podeAssinar() { return true; }
}

Confusões frequentes

  • Herança ≠ composição: herança é “é-um”; composição é “tem-um”.
  • Reuso por herança pode aumentar acoplamento: mudanças na base impactam derivadas.
  • Herança múltipla: nem toda linguagem suporta; quando não suporta, interfaces costumam suprir parte da necessidade.

Polimorfismo: mesma interface, comportamentos diferentes

Conceito

Polimorfismo permite tratar objetos de classes diferentes por um tipo comum (classe base ou interface), chamando o mesmo método e obtendo comportamentos distintos (despacho dinâmico). Em prova, aparece em trechos onde uma variável do tipo base aponta para objeto derivado.

Continue em nosso aplicativo

Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.

ou continue lendo abaixo...
Download App

Baixar o aplicativo

Leitura de código: identificar polimorfismo e sobrescrita

Usuario u = new UsuarioServidor();
boolean pode = u.podeAssinar();
  • O que a variável “é” (tipo estático): Usuario.
  • O que o objeto “é” (tipo dinâmico): UsuarioServidor.
  • Método executado: o podeAssinar() sobrescrito em UsuarioServidor.

Confusões frequentes

  • Polimorfismo ≠ sobrecarga: sobrecarga (overload) é escolher método por assinatura em tempo de compilação; polimorfismo (override) é escolher implementação em tempo de execução.
  • Cast: converter referência não muda o objeto; só muda a “visão” do tipo, e pode falhar se incompatível.

Interfaces: contrato de comportamento e desacoplamento

Conceito

Interface define um contrato (assinaturas) que classes implementam. Ajuda a reduzir acoplamento: o código depende do contrato, não de uma classe concreta. Em prova, é comum cobrar: “programar para interface”, implementação múltipla e substituição de implementações.

Exemplo: autorização por política

interface PoliticaDeAcesso {
  boolean podeVisualizar(Processo p, Usuario u);
}

class PoliticaPadrao implements PoliticaDeAcesso {
  public boolean podeVisualizar(Processo p, Usuario u) {
    return u.temPermissao(new Permissao("PROCESSO_VISUALIZAR"));
  }
}

class ServicoProcesso {
  private final PoliticaDeAcesso politica;

  ServicoProcesso(PoliticaDeAcesso politica) {
    this.politica = politica;
  }

  public void abrir(Processo p, Usuario u) {
    if (!politica.podeVisualizar(p, u)) {
      throw new SecurityException("Acesso negado");
    }
    // ...
  }
}

Passo a passo prático: “programar para interface”

  • 1) Identifique um ponto que varia (ex.: regra de acesso).
  • 2) Extraia um contrato (interface) com o método necessário.
  • 3) Crie implementações (ex.: política padrão, política restrita).
  • 4) Injete a interface no serviço (construtor/parâmetro), evitando dependência direta da implementação.

Diferenças cobradas

  • Interface ≠ classe abstrata: interface é contrato; classe abstrata pode trazer estado e implementação parcial (depende da linguagem), além de restringir herança a uma única base em muitas linguagens.
  • Implementar interface não implica herdar código; implica cumprir assinaturas.

Composição: “tem-um” e modelagem de entidades do domínio

Conceito

Composição é construir objetos a partir de outros objetos, representando relação “tem-um” (has-a). Em design, costuma ser preferida ao reuso por herança quando o objetivo é montar comportamentos/estruturas sem criar hierarquias rígidas.

Exemplo: Processo tem Partes e pode ter Responsável

class Processo {
  private List<Parte> partes = new ArrayList<>();
  private Usuario responsavel;

  public void definirResponsavel(Usuario u) {
    this.responsavel = u;
  }

  public void adicionarParte(Parte p) {
    if (p == null) throw new IllegalArgumentException();
    partes.add(p);
  }
}

Confusões frequentes

  • Composição ≠ agregação: em UML, agregação é relação mais “fraca” (ciclo de vida independente); composição é mais “forte” (parte depende do todo). Em questões, o examinador pode usar a ideia de ciclo de vida para diferenciar.
  • Composição ≠ herança: “Processo tem Partes” não significa que Processo “é uma” Parte.

Coesão e acoplamento: boas práticas cobradas indiretamente

Conceito

Coesão mede o quanto as responsabilidades de uma classe estão relacionadas entre si. Acoplamento mede o quanto uma classe depende de detalhes de outra. Boa prática: alta coesão e baixo acoplamento.

Exemplo de baixa coesão (sinal de alerta em prova)

class Processo {
  // Mistura domínio + persistência + segurança + formatação
  void salvarNoBanco() { /* ... */ }
  boolean podeAcessar(Usuario u) { /* ... */ return false; }
  String gerarRelatorioPDF() { /* ... */ return "..."; }
}

Em leitura de prova, isso sugere violação de separação de responsabilidades: a classe está fazendo “coisas demais”.

Exemplo com melhor coesão e menor acoplamento

class Processo {
  private String numero;
  // regras do domínio do processo
}

interface RepositorioDeProcessos {
  void salvar(Processo p);
}

interface GeradorDeRelatorio {
  byte[] gerar(Processo p);
}

class ServicoDeProcessos {
  private final RepositorioDeProcessos repo;
  private final GeradorDeRelatorio relatorios;

  ServicoDeProcessos(RepositorioDeProcessos repo, GeradorDeRelatorio relatorios) {
    this.repo = repo;
    this.relatorios = relatorios;
  }
}
  • Coesão: Processo foca no domínio; serviços focam em casos de uso.
  • Acoplamento: serviços dependem de interfaces, não de classes concretas.

Diferenças conceituais que mais caem (mapa rápido)

  • Classe vs Objeto: definição vs instância.
  • Encapsulamento vs Abstração: encapsulamento controla acesso e protege invariantes; abstração seleciona o que é relevante e omite detalhes.
  • Herança vs Composição: “é-um” vs “tem-um”.
  • Polimorfismo (override) vs Sobrecarga (overload): escolha em tempo de execução vs escolha por assinatura.
  • Interface vs Classe abstrata: contrato vs base com possível implementação parcial/estado (dependendo da linguagem).
  • Associação vs Agregação vs Composição: vínculo genérico vs vínculo fraco vs vínculo forte (ciclo de vida).

Exercícios de leitura e interpretação (estilo concurso)

Exercício 1: identificar encapsulamento e invariantes

class Usuario {
  private String login;
  private Set<Permissao> permissoes = new HashSet<>();

  public void conceder(Permissao p) {
    if (p == null) throw new IllegalArgumentException();
    permissoes.add(p);
  }

  public boolean temPermissao(String codigo) {
    for (Permissao p : permissoes) {
      if (p.getCodigo().equals(codigo)) return true;
    }
    return false;
  }
}
  • a) O que está encapsulado? Identifique membros privados e como o acesso é controlado.
  • b) Qual invariante simples é protegido em conceder?
  • c) Por que não expor diretamente permissoes como público reduz riscos?

Exercício 2: herança, polimorfismo e método executado

class Parte {
  public String tipo() { return "GENERICA"; }
}

class ParteAutor extends Parte {
  @Override
  public String tipo() { return "AUTOR"; }
}

Parte p = new ParteAutor();
String t = p.tipo();
  • a) Há herança? Justifique pela relação entre classes.
  • b) Há polimorfismo? Explique usando tipo estático e dinâmico.
  • c) Qual valor vai para t e por quê?

Exercício 3: interface e desacoplamento

interface Auditoria {
  void registrar(String evento);
}

class AuditoriaArquivo implements Auditoria {
  public void registrar(String evento) { /* grava em arquivo */ }
}

class ServicoPermissao {
  private final Auditoria auditoria;
  ServicoPermissao(Auditoria auditoria) { this.auditoria = auditoria; }

  void conceder(Usuario u, Permissao p) {
    u.conceder(p);
    auditoria.registrar("Concedida: " + p.getCodigo());
  }
}
  • a) Onde está o “programar para interface”?
  • b) Se trocar AuditoriaArquivo por AuditoriaBanco, o que muda no ServicoPermissao?
  • c) Isso reduz acoplamento? Explique.

Exercício 4: composição vs herança (pegadinha)

class Processo {
  private Usuario responsavel;
}

class UsuarioResponsavel extends Usuario {
  private Processo processo;
}
  • a) Qual modelagem faz mais sentido para “responsável pelo processo”: composição no Processo ou herança em UsuarioResponsavel? Justifique usando “é-um” vs “tem-um”.
  • b) Que problema de acoplamento pode surgir ao criar muitos “subtipos por papel” (ex.: UsuarioResponsavel, UsuarioDistribuidor, UsuarioAssinante)?

Exercício 5: coesão e acoplamento em trecho de código

class ProcessoController {
  public void abrirTela(Processo p) { /* UI */ }
  public void salvar(Processo p) { /* SQL direto aqui */ }
  public boolean validarAcesso(Usuario u) { /* regra de permissão */ return true; }
}
  • a) A coesão está alta ou baixa? Por quê?
  • b) Cite duas responsabilidades misturadas indevidamente.
  • c) Reescreva (apenas em termos de classes/nomes) uma separação melhor: quais classes/serviços você criaria?

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

Ao aplicar encapsulamento ao atributo numero em uma classe Processo, qual abordagem melhor preserva o invariante de “número válido” e evita alterações indevidas após a criação do objeto?

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

Você errou! Tente novamente.

Encapsular significa controlar como o estado muda. Ao tornar numero privado, validar no construtor e evitar set, a classe centraliza a regra e impede que qualquer código altere o valor sem passar pela validação, preservando o invariante.

Próximo capitúlo

Banco de Dados para Analista Judiciário - TI: modelagem conceitual e lógica (ER e relacional)

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