Capa do Ebook gratuito Preparatório Caixa Econômica Federal - Técnico Bancário - Tecnologia da Informação

Preparatório Caixa Econômica Federal - Técnico Bancário - Tecnologia da Informação

Novo curso

20 páginas

Preparatório Caixa TI: Programação orientada a objetos e boas práticas

Capítulo 12

Tempo estimado de leitura: 11 minutos

+ Exercício

Classes e objetos: modelando o domínio

Em Programação Orientada a Objetos (POO), uma classe é um molde que define dados (atributos) e comportamentos (métodos). Um objeto é uma instância concreta dessa classe em execução. Em provas, é comum cobrarem a diferença entre “tipo” (classe) e “instância” (objeto), além de identificar responsabilidades bem definidas.

Exemplo conceitual

Em um contexto bancário, “Conta” pode ser uma classe; “Conta do cliente Maria, agência X, número Y” é um objeto. A classe define o que toda conta tem (saldo, número) e faz (depositar, sacar).

Passo a passo prático: do requisito ao modelo

  • 1) Liste substantivos do enunciado (candidatos a classes): Conta, Cliente, Transação.
  • 2) Liste verbos (candidatos a métodos): sacar, depositar, transferir, registrarTransacao.
  • 3) Defina atributos essenciais (evite “tudo que existe”): saldo, numero, agencia.
  • 4) Defina invariantes (regras que não podem ser violadas): saldo não pode ficar negativo (dependendo do produto), valor de depósito deve ser positivo.
  • 5) Distribua responsabilidades: Conta altera saldo; Transação registra histórico; Cliente agrega contas.
// Pseudocódigo (estilo OO genérico)  class Conta {   numero   agencia   saldo   metodo depositar(valor) { ... }   metodo sacar(valor) { ... } }  c1 = new Conta(numero=123, agencia=1) c1.depositar(100)

Encapsulamento: protegendo estado e regras

Encapsulamento é o princípio de esconder detalhes internos e expor apenas operações seguras. Em termos práticos: atributos não devem ser alterados diretamente por qualquer parte do sistema; as mudanças passam por métodos que validam regras.

O que a banca costuma explorar

  • Diferença entre interface pública (o que é exposto) e implementação (como funciona por dentro).
  • Uso de modificadores de acesso (privado/protegido/público) como mecanismo de encapsulamento.
  • Evitar “anemia de domínio”: classes que só têm getters/setters e nenhuma regra.

Passo a passo prático: encapsular saldo

  • 1) Torne o atributo saldo inacessível diretamente (privado).
  • 2) Exponha operações: depositar, sacar.
  • 3) Valide entradas e invariantes dentro desses métodos.
  • 4) Retorne erros via exceções (ou resultado) quando a regra for violada.
class Conta {   privado saldo   metodo depositar(valor) {     se valor <= 0 entao lancar ExcecaoValorInvalido     saldo = saldo + valor   }   metodo sacar(valor) {     se valor <= 0 entao lancar ExcecaoValorInvalido     se valor > saldo entao lancar ExcecaoSaldoInsuficiente     saldo = saldo - valor   }   metodo obterSaldo() { retorna saldo } }

Herança: generalização e especialização

Herança permite criar uma classe mais específica a partir de outra mais geral, reutilizando atributos e métodos. A classe base define comportamentos comuns; a derivada especializa ou estende.

Quando faz sentido

  • Existe relação “é um(a)” (is-a): ContaPoupanca é uma Conta.
  • Há comportamento comum significativo e estável.

Riscos comuns (cobrados indiretamente)

  • Herança usada apenas para “reaproveitar código” pode gerar hierarquias rígidas.
  • Quebra de substituição: se a subclasse não puder ser usada no lugar da superclasse sem causar erro conceitual, há problema de design.
class Conta {   metodo calcularTarifaMensal() { retorna 0 } }  class ContaCorrente herda Conta {   metodo calcularTarifaMensal() { retorna 20 } }

Polimorfismo: mesma interface, comportamentos diferentes

Polimorfismo é a capacidade de tratar objetos de classes diferentes de forma uniforme por meio de um tipo comum (superclasse ou interface), permitindo que a chamada de um método execute a versão correta conforme o objeto real.

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: identificando polimorfismo

// Uma lista de Contas pode conter diferentes tipos concretos contas = [new ContaCorrente(), new ContaPoupanca()] totalTarifas = 0 para cada c em contas {   totalTarifas = totalTarifas + c.calcularTarifaMensal() // chama a implementação específica } 

Em prova, isso costuma aparecer como: “qual conceito permite que calcularTarifaMensal() execute versões diferentes sem alterar o laço?” Resposta: polimorfismo (com despacho dinâmico).

Interfaces: contrato de comportamento

Interface define um contrato: quais métodos um tipo deve oferecer, sem impor como serão implementados. É útil para desacoplar código de implementações concretas.

Exemplo: integração com serviços

Em vez de o sistema depender de uma classe específica de notificação, ele depende de uma interface Notificador. Assim, pode-se trocar e-mail por SMS sem alterar regras de negócio.

interface Notificador {   metodo enviar(destino, mensagem) }  class NotificadorEmail implementa Notificador {   metodo enviar(destino, mensagem) { ... } }  class NotificadorSMS implementa Notificador {   metodo enviar(destino, mensagem) { ... } }  class ServicoAlerta {   privado notificador: Notificador   construtor(n: Notificador) { notificador = n }   metodo alertar(msg) { notificador.enviar("cliente", msg) } }

Passo a passo prático: aplicar interface para reduzir dependência

  • 1) Identifique um ponto de variação (ex.: canal de notificação).
  • 2) Extraia um contrato mínimo (método enviar).
  • 3) Faça o serviço depender da interface, não da classe concreta.
  • 4) Injete a implementação (no construtor, por exemplo).

Composição: montar objetos a partir de outros

Composição é criar classes que possuem outras classes como partes internas (relação “tem um(a)” / has-a). Em muitos cenários, composição é preferível à herança por ser mais flexível.

Exemplo: Conta e Histórico

Uma conta tem um histórico de transações. O histórico pode ser um objeto separado, responsável por armazenar e consultar transações.

class HistoricoTransacoes {   privado transacoes = []   metodo registrar(t) { transacoes.adicionar(t) }   metodo listar() { retorna transacoes } }  class Conta {   privado historico = new HistoricoTransacoes()   metodo depositar(valor) { ...; historico.registrar("DEP", valor) } }

Coesão e acoplamento: qualidade estrutural do design

Coesão (alta é desejável)

Coesão mede o quanto os elementos de um módulo/classe estão relacionados a uma única responsabilidade. Alta coesão significa foco: a classe faz “uma coisa” bem definida.

  • Alta coesão: HistoricoTransacoes só lida com transações.
  • Baixa coesão: uma classe Conta que calcula tarifa, envia e-mail, grava em arquivo e valida CPF.

Acoplamento (baixo é desejável)

Acoplamento mede o nível de dependência entre módulos. Baixo acoplamento facilita manutenção e testes, pois mudanças em um módulo impactam menos os outros.

  • Acoplamento alto: ServicoAlerta cria internamente NotificadorEmail e usa detalhes específicos dele.
  • Acoplamento baixo: ServicoAlerta depende de Notificador (interface).

Questão típica de prova (interpretação)

“Ao trocar o provedor de notificação, várias classes precisaram ser alteradas. Qual problema de design é mais provável?” Resposta esperada: acoplamento alto (e possível violação de princípios como DIP/OCP).

SOLID em nível objetivo (o que identificar em enunciados)

S — Single Responsibility Principle (SRP)

Uma classe deve ter um único motivo para mudar. Em questões, procure classes “faz-tudo”.

// Sinal de violação de SRP class Conta {   metodo sacar(...) { ... }   metodo enviarEmail(...) { ... }   metodo gerarRelatorioPDF(...) { ... } }

O — Open/Closed Principle (OCP)

Entidades devem estar abertas para extensão e fechadas para modificação. Em prova, aparece como “adicionar novo tipo sem alterar código existente”. Interfaces e polimorfismo ajudam.

// Em vez de if/else por tipo, use polimorfismo interface Tarifa { metodo calcular(conta) } class TarifaPadrao implementa Tarifa { ... } class TarifaPremium implementa Tarifa { ... }

L — Liskov Substitution Principle (LSP)

Subtipos devem poder substituir seus tipos base sem quebrar expectativas. Indício de violação: subclasse que lança exceções inesperadas para operações válidas na base ou muda significado do método.

I — Interface Segregation Principle (ISP)

Prefira interfaces pequenas e específicas. Indício de violação: interface “gigante” que obriga classes a implementar métodos que não usam.

// Ruim: interface grande interface OperacoesConta {   sacar(); depositar(); investir(); contratarSeguro(); } // Melhor: separar por capacidade interface Movimentavel { sacar(); depositar(); } interface Investivel { investir(); }

D — Dependency Inversion Principle (DIP)

Dependa de abstrações (interfaces), não de concretizações. Em prova, isso se conecta a injeção de dependência e testabilidade.

Padrões simples: reconhecer Factory e Strategy

Factory (fábrica): centralizar criação

Factory encapsula a lógica de criação de objetos, evitando espalhar new e regras de instanciação pelo sistema. Em questões, aparece como “método que retorna instâncias conforme um parâmetro”.

class ContaFactory {   metodo criar(tipo) {     se tipo == "CORRENTE" retorna new ContaCorrente()     se tipo == "POUPANCA" retorna new ContaPoupanca()     senao lancar ExcecaoTipoInvalido   } }

Strategy (estratégia): variar algoritmo em tempo de execução

Strategy define uma família de algoritmos (ex.: cálculo de tarifa, cálculo de juros), encapsula cada um e permite trocar sem alterar o cliente.

interface RegraTarifa { metodo calcular(conta) } class TarifaIsenta implementa RegraTarifa { ... } class TarifaPadrao implementa RegraTarifa { ... }  class Conta {   privado regra: RegraTarifa   metodo setRegraTarifa(r) { regra = r }   metodo tarifaMensal() { retorna regra.calcular(this) } }

Tratamento de exceções: falhas previsíveis e fluxo seguro

Exceções representam situações anormais que interrompem o fluxo normal. Em sistemas bancários, validações e regras de negócio frequentemente geram exceções (valor inválido, saldo insuficiente), e integrações podem gerar falhas técnicas (timeout, indisponibilidade).

Conceitos cobrados

  • Exceções de negócio: violação de regra (ex.: saque maior que saldo).
  • Exceções técnicas: falhas de infraestrutura (ex.: rede, banco indisponível).
  • Tratamento: capturar onde é possível decidir (repetir? informar? registrar?).
  • Não engolir exceção: capturar e ignorar sem ação dificulta diagnóstico.

Leitura de código: onde tratar

metodo realizarSaque(conta, valor) {   tentar {     conta.sacar(valor)     repositorio.salvar(conta)   } capturar (ExcecaoSaldoInsuficiente e) {     retornar "Negado: saldo insuficiente"   } capturar (ExcecaoInfraestrutura e) {     // decisão: reprocessar, registrar, retornar erro genérico     registrarLog(e)     retornar "Serviço indisponível"   } }

Coleções: listas, conjuntos e mapas (visão conceitual)

Coleções armazenam e organizam múltiplos objetos. Em prova, é comum cobrarem qual estrutura escolher para cada necessidade e impactos de desempenho e semântica.

Principais tipos e quando usar

  • Lista (List): mantém ordem e permite duplicados. Ex.: extrato com transações em ordem cronológica.
  • Conjunto (Set): não permite duplicados. Ex.: conjunto de permissões de um usuário.
  • Mapa/Dicionário (Map): chave → valor. Ex.: buscar conta por número; buscar cliente por CPF.

Leitura de código: identificar a estrutura

// Lista: extrato transacoes = [] transacoes.adicionar(t1) transacoes.adicionar(t2)  // Set: permissões (sem repetição) permissoes = set() permissoes.adicionar("CONSULTAR_SALDO") permissoes.adicionar("CONSULTAR_SALDO") // continua 1 item  // Map: conta por número contasPorNumero = map() contasPorNumero[123] = contaA conta = contasPorNumero[123]

Leitura de trechos pseudossintáticos: identificando conceitos

Trecho 1

interface Autenticador { metodo autenticar(usuario, senha) } class AutenticadorLDAP implementa Autenticador { ... } class AutenticadorLocal implementa Autenticador { ... }  class LoginService {   privado auth: Autenticador   construtor(a: Autenticador) { auth = a }   metodo login(u, s) { retorna auth.autenticar(u, s) } }
  • Conceitos: interface (contrato), DIP (depender de abstração), baixo acoplamento, possibilidade de Strategy (trocar autenticador).

Trecho 2

class Relatorio {   metodo gerarPDF() { ... }   metodo enviarEmail() { ... }   metodo calcularTarifas() { ... } }
  • Conceitos: baixa coesão, provável violação de SRP; manutenção difícil (mudanças em e-mail podem afetar PDF).

Trecho 3

class TarifaFactory {   metodo obterTarifa(perfil) {     se perfil == "BASICO" retorna new TarifaPadrao()     se perfil == "VIP" retorna new TarifaIsenta()   } }
  • Conceitos: Factory; pode evoluir para OCP com registro de estratégias para reduzir modificações.

Questões (identificação de conceitos e impactos de design)

1) (Encapsulamento)

Uma classe expõe o atributo saldo como público e permite alteração direta por qualquer módulo. Qual princípio está sendo violado e qual impacto provável?

  • Resposta esperada: violação de encapsulamento; impacto: regras de negócio podem ser burladas, maior chance de inconsistência e bugs difíceis de rastrear.

2) (Herança vs composição)

Um sistema criou ContaComHistorico herdando de Conta apenas para adicionar uma lista de transações. Qual alternativa tende a ser melhor e por quê?

  • Resposta esperada: composição (Conta tem um Histórico); reduz rigidez da hierarquia e melhora coesão.

3) (Polimorfismo)

Um laço chama calcularTarifaMensal() em objetos de tipos diferentes sem usar if por tipo. Qual conceito permite isso?

  • Resposta esperada: polimorfismo.

4) (SOLID — SRP)

Uma classe ClienteService valida CPF, envia e-mail, gera relatório e faz persistência. Qual princípio do SOLID é mais diretamente violado e qual refatoração típica?

  • Resposta esperada: SRP; separar em serviços/componentes (validador, notificador, repositório, gerador de relatório).

5) (SOLID — DIP)

Um serviço instancia diretamente new NotificadorEmail() internamente. Qual princípio é afetado e qual melhoria objetiva?

  • Resposta esperada: DIP; depender de interface Notificador e receber a implementação por injeção.

6) (Factory)

Um método central escolhe e cria objetos de tipos diferentes com base em um parâmetro (ex.: “CORRENTE”, “POUPANCA”). Qual padrão está sendo aplicado?

  • Resposta esperada: Factory.

7) (Strategy)

O cálculo de tarifa varia conforme o perfil e pode ser trocado sem alterar a classe Conta, apenas substituindo um objeto de regra. Qual padrão é esse e qual benefício?

  • Resposta esperada: Strategy; benefício: extensão sem modificar código cliente, melhor testabilidade e aderência ao OCP.

8) (Exceções)

Um código captura uma exceção genérica e não faz nada (nem log, nem retorno). Qual impacto para manutenção e operação?

  • Resposta esperada: falhas silenciosas, dificuldade de diagnóstico, comportamento imprevisível e aumento de tempo de correção.

9) (Coleções)

Para buscar rapidamente uma conta pelo número, qual coleção é mais adequada: lista, conjunto ou mapa? Justifique.

  • Resposta esperada: mapa/dicionário (chave → valor), pois permite acesso direto por chave.

10) (Coesão e acoplamento)

Ao alterar o provedor de autenticação, várias classes precisam mudar. Isso indica mais fortemente baixa coesão ou alto acoplamento? Qual ação reduz o problema?

  • Resposta esperada: alto acoplamento; reduzir dependência de concretos usando interfaces e injeção de dependência.

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

Um serviço de alerta precisa permitir a troca do canal de notificação (e-mail/SMS) sem exigir alterações na lógica do serviço. Qual abordagem melhor atende a esse objetivo, reduzindo o acoplamento?

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

Você errou! Tente novamente.

Depender de uma interface e injetar a implementação reduz o acoplamento com classes concretas, facilitando trocar e-mail por SMS sem modificar a regra do serviço. Isso segue o princípio de depender de abstrações (DIP).

Próximo capitúlo

Preparatório Caixa TI: Desenvolvimento Web e APIs para ambientes corporativos

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