Contador que que não funciona para um mini sistema de biblioteca

Fala galera!!! essa é a minha primeira pergunta então talvez não fique muito claro mas enfim.

Eu sou iniciante no JAVA (3 meses de estudo) e estou tendo um problema com um contador basicamente eu estou fazendo um mini sistema pra uma biblioteca e eu preciso por um limite de no máximo 3 livros alugados caso eles sejam alugados então o usuário precisa devolver um livro

meu código está dividido em três partes a Main (que é uma interface conectando o usuário e a funcionário), o problema esta no método alugar livro (classe MAIN) e a contagem que esta sendo feita na classe Funcionário. Eu tbm aceito criticas ao meu código! Obrigado pela atenção.

Essa é a MAIN:
import java.util.ArrayList;
import java.util.Scanner;

public interface Main {
static ArrayList livros = new ArrayList();
static ArrayList usuarios = new ArrayList();
Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
while (true) {
System.out.println(“== Biblioteca ==”);
System.out.println(“1 - Entrar”);
System.out.println(“2 - Registrar-se”);
System.out.println(“3 - Listar Usuarios”);
System.out.println(“4 - Adiconario Livro”);
System.out.println(“5 - Listar Livro”);
System.out.println(“6 - Alugar Livro”);
System.out.println(“7 - Devolver Livro”);
System.out.println(“0 - Sair”);
int opcao = scanner.nextInt();
switch (opcao) {
case 1:
entrar();
break;
case 2:
registrarse();
break;
case 3:
listarUsuarios();
break;
case 4:
adicionarLivro();
break;
case 5:
listarLivros();
break;
case 6:
alugarLivro();
break;
case 7:
devolverLivro();
break;
case 0:
System.out.println(“saindo…”);
}
}
}

static void entrar() {
	if (usuarios.isEmpty()) {
		System.out.println("Usuario não encontrado. Deseja registrar-se? S/N");
		String opcao = scanner.next();
		if (opcao.equalsIgnoreCase("Sim") || opcao.equalsIgnoreCase("S")) {
			registrarse();
		} else if (opcao.equalsIgnoreCase("nao") || opcao.equalsIgnoreCase("N") || opcao.equalsIgnoreCase("não")) {

		}
	} 
	else if (!usuarios.isEmpty()) {
		System.out.print("Digite seu nome: ");
		String existeNome = scanner.next();
		System.out.print("Digite seu cpf: ");
		String existeCpf = scanner.next();
		for (Usuario u : usuarios) {
			if (u.getNome().equals(existeNome) && u.getCpf().equals(existeCpf)) {
				System.out.println("Você entrou com sucesso");

			} else {
				System.out.println("usuario não encontrado tente novamente!");
				return;
			}
		}
	}
}

static void registrarse() {
	Usuario u = new Usuario(scanner);
	usuarios.add(u);
	System.out.println("Registrado com sucesso!");
}

static void listarUsuarios() {
	System.out.println("Digite a senha da biblioteca");
	String entrar = scanner.next();
	if (entrar.equals("Biblioteca123")) {
		for (Usuario encontrados : usuarios) {
			System.out.println(encontrados);
		}
	} else if (!entrar.equals("Biblioteca123")) {

	}
}
static void adicionarLivro() {
	while (true) {
		System.out.println("Digite a senha da biblioteca");
		String entrar = scanner.next();

		if (entrar.equals("Biblioteca123")) {
			Funcionario l = new Funcionario(scanner);
			livros.add(l);
			l.setAlugado(false);
			System.out.println("Deseja adicionar mais livros? S/N");
			String opcao = scanner.next();
			if (opcao.equalsIgnoreCase("Sim") || opcao.equalsIgnoreCase("S")) {

			} else if (opcao.equalsIgnoreCase("nao") || opcao.equalsIgnoreCase("N")
					|| opcao.equalsIgnoreCase("não")) {
				break;
			}
		}
	}
}

static void listarLivros() {
	for (Funcionario a : livros) {
		System.out.println(a);
	}
}

static void alugarLivro() {
	if (usuarios.isEmpty()) {
		System.out.println("Você precisar se cadastrar! deseja se cadastrar? S/N");
		String opcao = scanner.next();
		if (opcao.equalsIgnoreCase("Sim") || opcao.equalsIgnoreCase("S")) {
			registrarse();
		} else if (opcao.equalsIgnoreCase("nao") || opcao.equalsIgnoreCase("N") || opcao.equalsIgnoreCase("não")) {
			return;
		}
	}
	if (!usuarios.isEmpty())
		System.out.print("Digite a isbn do livro:");
	String ISBN = scanner.next();
	for (Funcionario livro : livros) {
		if (livro.getIsbn().contains(ISBN) && livro.isAlugado() == true) {
			System.out.println("Este livro ja foi alugado!");
			}
		if(livro.getIsbn().contains(ISBN) && livro.getContagem() > 3) {
			System.out.println("Você ja alcançou o maximo de livro deseja devolver algum livro? S/N");
			String opcao = scanner.next();
			if (opcao.equalsIgnoreCase("Sim") || opcao.equalsIgnoreCase("S")) {
				registrarse();
			} else if (opcao.equalsIgnoreCase("nao") || opcao.equalsIgnoreCase("N") || opcao.equalsIgnoreCase("não")) {
				break;
			}
		}
		else if (livro.getIsbn().equals(ISBN) && livro.isAlugado() == false) {
			System.out.println("Livro alugado!");
			livro.setAlugado(true);
			livro.contagem();
			System.out.println(livro.getContagem());
			break;
		}
	}

}

static void devolverLivro() {
	System.out.print("Digite a isbn do livro: ");
	String isbn = scanner.next();
	for (Funcionario livro : livros) {
		if (livro.getIsbn().equals(isbn) && livro.isAlugado() == false) {
			System.out.println("Este livro ja foi devolvido");
			break;
		} else if (livro.getIsbn().equals(isbn)) {
			System.out.println("Livro devolvido!");
			livro.setAlugado(false);
			break;
		}
	}
}

ESSA É A USUARIO :
import java.util.Scanner;
public class Usuario implements Main{
String nome;
String cpf;

Scanner scanner = new Scanner(System.in);

@Override
public String toString() {
	return "nome:" + nome + ", cpf:" + cpf;
}
Usuario(Scanner scanner) {
		System.out.print("Digite seu nome: ");
		this.nome = scanner.next();
		System.out.print("Digite seu cpf: ");
		this.cpf = scanner.next();
}

public String getNome() {
	return nome;
}
public String getCpf() {
	return cpf;
}

}

ESSA É A FUNCIONARIO
import java.util.Scanner;
public class Funcionario implements Main{

static Scanner scanner = new Scanner(System.in);
private String titulo;
private String Autor;
private String isbn;
private int ano;
private boolean alugado;
int contagem;
@Override
public String toString() {
	if(alugado) {																					
		return "titulo:" + titulo + ", Autor:" + Autor + ", isbn:" + isbn + ", ano:" + ano + ", indisponivel!";
	}
	if(!alugado) {																						
		return "titulo:" + titulo + ", Autor:" + Autor + ", isbn:" + isbn + ", ano:" + ano + ", disponivel!";
	}
	return titulo;
}
Funcionario(Scanner scanner){
	System.out.print("Digite o Titulo do livro: ");
	this.titulo = scanner.next();
	System.out.print("Digite o Autor do livro: ");
	this.Autor = scanner.next();
	System.out.print("Digite a isbn do livro: ");
	this.isbn = scanner.next();
	System.out.print("Digite o ano de publicação do livro: ");
	this.ano = scanner.nextInt();
	scanner.nextLine();
}	
public void contagem() {
	contagem++;
}
public int getContagem() {
	return contagem;
}
public boolean isAlugado() {
	return alugado;
}
public void setAlugado(boolean alugado) {
	this.alugado = alugado;
}
public String getTitulo() {
	return titulo;
}
public String getAutor() {
	return Autor;
}
public String getIsbn() {
	return isbn;
}
public int getAno() {
	return ano;
}

}

DESCOBRI ALGO!! na realidade ele conta apenas da ISBN de cada livro então ele esta funcionando mas não esta funcionando :smiley: kkkkkk

Porque diabos “Main” é uma interface?

Interfaces são tipos abstratos de dados e servem para definir contratos de operações que determinadas classes devem realizar.

Você criou uma interface e entupiu de metros estáticos e concretos, está errado.

3 curtidas

Além do que já foi dito (não faz sentido Main ser uma interface, e menos sentido ainda Usuario e Funcionario implementarem esta interface), tem outros detalhes aí.

Tem bastante coisa que daria para melhorar, mas para não ficar muita informação de uma vez (que pode acabar te confundindo), vou citar apenas algumas.

Pra começar, este loop:

for (Usuario u : usuarios) {
    if (u.getNome().equals(existeNome) && u.getCpf().equals(existeCpf)) {
        System.out.println("Você entrou com sucesso");

    } else {
        System.out.println("usuario não encontrado tente novamente!");
        return;
    }
}

A lógica está errada. Vamos supor que a lista usuarios tem 2 usuários: o primeiro tem nome “Fulano” e CPF “12345678909”, o segundo tem nome “Ciclano” e CPF “11122233396”.

Suponha que foi digitado “Ciclano” e “11122233396” (são os respectivos valores de existeNome e existeCpf). Ou seja, é um usuário que deveria ser encontrado.

Só que na primeira iteração do loop vai entrar no else, vai imprimir que não foi encontrado e o return encerra a execução do método. Ou seja, também vai interromper o for, e ele não vai ter a chance de verificar o segundo elemento, que seria o usuário correto.


O certo seria ir verificando os usuários, até encontrar algum:

// indica se conseguiu entrar (começa com "não")
boolean entrou = false;
for (Usuario u : usuarios) {
    if (u.getNome().equals(existeNome) && u.getCpf().equals(existeCpf)) {
        entrou = true;
        // encontrei, pode interromper o loop
        break;
    }
}

// só depois que o loop terminou, verifica se encontrou ou não
if (entrou) {
    System.out.println("Você entrou com sucesso");
} else {
    System.out.println("usuario não encontrado tente novamente!");
}

Ou seja, se eu encontrar o usuário, aí sim eu interrompo o loop usando break (não usei return porque isso interrompe o método inteiro, e ele não executaria o código que está depois do for).

E só depois do for eu verifico se encontrou ou não, e imprimo a mensagem correspondente.


Por fim, só mais uma coisa. Neste método tem o seguinte:

if (usuarios.isEmpty()) {
    // faz uma coisa
} else if (!usuarios.isEmpty()) {
    // faz outra coisa
}

Só que neste caso, o método isEmpty() retorna um boolean. Ou seja, ele tem apenas dois retornos possíveis: true ou false. Então se não entrou no if é porque não é true, portanto a única opção restante é false e por isso não precisa verificar no else, é redundante.

Poderia ser apenas assim:

if (usuarios.isEmpty()) {
    // faz uma coisa
} else {
    // faz outra coisa
}

Ou seja, se isEmpty() retorna true, entra no if. Se não entrou no if é porque não retornou true, mas como é um boolean a única possibilidade é que retornou false, então não precisa verificar if (!usuarios.isEmpty()) no else.


Obs: na verdade eu faria o método entrar() retornar true ou false:

// retorna um boolean indicando se conseguiu entrar
static boolean entrar() {
    // ...

    for (Usuario u : usuarios) {
        if (u.getNome().equals(existeNome) && u.getCpf().equals(existeCpf)) {
            // encontrei, posso retornar direto
            return true;
        }
    }
    // se chegou aqui é porque percorreu todos os usuários e não encontrou
    return false;
}

E quem chama o método faz o que precisar com o resultado (como imprimir mensagens, etc).

if (entrar()) {
    System.out.println("Você entrou com sucesso");
} else {
    System.out.println("usuario não encontrado tente novamente!");
}

Além disso, a parte do cadastro eu acho que deveria ficar fora, pois o método que verifica se o usuário está cadastrado não deveria ser responsável por também chamar o registro. Mas aí já começa a alterar demais o código, e como já dito, acho que fica informação demais e pode acabar te confundindo.


Mas só pra finalizar, não faz sentido um funcionário ter título, autor e ISBN. Será que esta classe não deveria se chamar Livro?

1 curtida

Ah, outra dica rápida é usar sempre scanner.nextLine() ao ler dados do teclado. Tem vários posts no fórum que explicam o motivo, mas - só pra fazer uma propagandinha - eu escrevi um post bem detalhado sobre isso:

1 curtida

eu realmente não sei pq eu fiz isso :no_mouth:? Mas eu arrumei essa parte e dividi em duas classes uma main e uma biblioteca, ai na main eu faço os códigos dos metodos e na biblioteca eu apenas chamo os metodos acho que a leitura do código fica melhor né ?

Nossa! eu realmente não estava considerando usar o scanner.nextLine() em nenhum momento do meu código mas depois de ler esse artigo vou começas a usar mais.(vou logo trocar meus scanner.next() kkkkk) Valeu pela ajuda!

Complementando…

Em termos de organização das classes, eu faria diferente:

  • manter a classe Usuario
  • mudar o nome da classe Funcionario para Livro, já que esta classe tem título, autor, ISBN, ou seja, todos os dados de um livro
  • criar a classe Biblioteca, que contém um registro de usuários e respectivos livros alugados, além dos livros disponíveis

Assim, quem sabe se um livro está alugado ou não é a biblioteca (e não o livro). Além disso, pode existir mais de um exemplar do livro, não? E pra alugar e devolver, precisa saber também qual o usuário que está fazendo isso.


Para o usuário e livro, usei record, que é um recurso introduzido no Java 16 (apesar de ser possível usar no Java 14 e 15 como preview). Fica assim:

Arquivo Usuario.java:

public record Usuario(String nome, String cpf) {}

Arquivo Livro.java:

public record Livro(String titulo, String autor, String isbn, int ano) {}

Nada impede que vc use classes normais com construtor, getters, etc. É que o record cria tudo isso automaticamente, incluindo os métodos equals e hashCode que são importantes para adicionarmos estas classes em maps (que será feito na classe Biblioteca abaixo).

Como está aprendendo, eu sugiro já começar com versões mais recentes da linguagem, até para ir se acostumando com os novos recursos, como os records.


Enfim, a lógica de quais livros estão alugados para quais usuários fica a cargo da biblioteca.

Arquivo Biblioteca.java:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;

public class Biblioteca {
    // registro de usuários e os respectivos livros que estão alugados
    private Map<Usuario, List<Livro>> registro;
    // catálogo dos livros disponíveis e suas respectivas quantidades (pode ter mais de um exemplar de cada livro)
    private Map<Livro, Integer> catalogo;
    // quantidade máxima de livros que cada usuário pode ter por vez
    private int maxLivrosPorUsuario;
    private Scanner scanner;

    public Biblioteca(int maxLivrosPorUsuario, Scanner scanner) {
        this.registro = new HashMap<>();
        this.catalogo = new HashMap<>();
        this.maxLivrosPorUsuario = maxLivrosPorUsuario;
        this.scanner = scanner;
    }

    // mostra o status atual
    public void status() {
        System.out.println("Catálogo:");
        for (var e : this.catalogo.entrySet()) {
            System.out.println("- " + e.getValue() + " unidade(s) de '" + e.getKey().titulo() + "'");
        }
        System.out.println("\n\nLivros alugados:");
        for (var e : this.registro.entrySet()) {
            System.out.println("- " + e.getKey().nome() + " -> " + e.getValue().stream().map(Livro::titulo).collect(Collectors.joining(", ")));
        }
    }

    public void registrar(Usuario usuario) {
        if (this.registro.containsKey(usuario)) {
            System.out.println("Usuário já está cadastrado");
        } else {
            this.registro.put(usuario, new ArrayList<>());
        }
    }

    public void listarUsuarios() {
        for (Usuario usuario : this.registro.keySet()) {
            System.out.println(usuario);
        }
    }

    public void adicionarLivro(Livro livro) {
        // Se o livro já existe no catálogo, incrementa a quantidade. Senão adiciona ao catálogo
        this.atualizarCatalogo(livro, 1);
    }

    private void atualizarCatalogo(Livro livro, int qtd) {
        this.catalogo.put(livro, this.catalogo.getOrDefault(livro, 0) + qtd);
    }

    public void alugar(Usuario usuario, Livro livro) {
        if (!this.registro.containsKey(usuario)) {
            System.out.println("Usuário não cadastrado");
            return;
        }
        if (this.catalogo.get(livro) == 0) {
            System.out.println("Livro não está disponível para alugar");
            return;
        }

        List<Livro> alugados = this.registro.get(usuario);
        if (alugados.size() == this.maxLivrosPorUsuario) {
            if (this.simOuNao("Você ja alcançou o máximo de livros, deseja devolver algum livro?")) {
                if (!this.devolucaoEscolha(alugados)) {
                    System.out.println("Você optou por não devolver, então não poderá alugar outro livro");
                    return;
                }
            } else {
                System.out.println("Você optou por não devolver, então não poderá alugar outro livro");
                return;
            }
        }

        alugados.add(livro);
        // diminui a quantidade disponível no catálogo
        this.atualizarCatalogo(livro, -1);
    }

    public void devolver(Usuario usuario, Livro livro) {
        if (!this.registro.containsKey(usuario)) {
            System.out.println("Usuário não cadastrado");
            return;
        }

        this.devolucao(this.registro.get(usuario), livro);
    }

    private void devolucao(List<Livro> list, Livro livro) {
        if (list.remove(livro)) {
            this.atualizarCatalogo(livro, 1);
            System.out.println("Livro devolvido: " + livro);
        } else {
            System.out.println("Livro não estava alugado para este usuário");
        }
    }

    private boolean devolucaoEscolha(List<Livro> alugados) {
        while (true) {
            System.out.println("Livros alugados atualmente:");
            for (int i = 0; i < alugados.size(); i++) {
                System.out.printf("%2d - %s\n", i + 1, alugados.get(i).toString());
            }
            System.out.println("Escolha o número do livro que vc quer devolver, ou 0 para sair sem devolver nenhum");
            try {
                int opt = Integer.parseInt(this.scanner.nextLine()) - 1;
                if (opt == -1) {
                    return false; // nenhum foi devolvido
                }
                if (opt < 0 || opt >= alugados.size()) {
                    System.out.println("número inválido");
                } else {
                    this.devolucao(alugados, alugados.get(opt));
                    return true; // livro devolvido
                }
            } catch (Exception e) {
                System.out.println("Digite um número válido");
            }
        }
    }

    private boolean simOuNao(String mensagem) {
        while (true) {
            System.out.println(mensagem);
            String opcao = this.scanner.nextLine().toLowerCase();
            switch (opcao) {
                case "s", "sim":
                    return true;
                case "n", "nao", "não":
                    return false;
                default:
                    System.out.println("Digite apenas s/sim/n/nao/não");
            }
        }
    }
}

Aqui eu fiz uma versão bem simplificada da biblioteca, mas basicamente ela possui:

  • Map<Usuario, List<Livro>> registro - registro de usuários e os respectivos livros que cada um alugou
  • Map<Livro, Integer> catalogo - catálogo de livros e a respectiva quantidade disponível de cada um
  • maxLivrosPorUsuario - quantidade máxima de livros que cada usuário pode alugar

A biblioteca também tem uma referência ao Scanner. Para esta versão inicial fiz assim para simplificar o exemplo, mas isso pode ser melhorado para ficar em outra classe que controla as interações com o usuário. Assim deixa a biblioteca somente com a responsabilidade de gerenciar livros (disponibilidade e aluguéis). Por ora, deixei assim mesmo :slight_smile:


Por fim, no main vc cria a biblioteca e faz as interações com o usuário. No código abaixo eu não criei todos os métodos, só coloquei alguns pra te dar a ideia geral.

Arquivo Main.java:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // cada usuário pode ter no máximo 3 livros por vez
        Biblioteca biblioteca = new Biblioteca(3, scanner);

        String mensagem = "== Biblioteca ==\n1 - Entrar\n2 - Registrar-se\n3 - Listar Usuarios\n4 - Adiconario Livro\n5 - Listar Livro\n6 - Alugar Livro\n7 - Devolver Livro\n0 - Sair\n";
        while (true) {
            int opcao = lerNumero(scanner, mensagem);
            switch (opcao) {
                case 2:
                    cadastrarUsuario(biblioteca, scanner);
                    break;

                case 4:
                    adicionarLivro(biblioteca, scanner);
                    break;
                // etc...
            }
        }
    }

    // método auxiliar para ler um número
    static int lerNumero(Scanner scanner, String mensagem) {
        // enquanto não digitar um número, pede para digitar novamente
        while (true) {
            try {
                System.out.print(mensagem);
                return Integer.parseInt(scanner.nextLine());
            } catch (Exception e) {
                System.out.println("Digite um número válido");
            }
        }
    }

    static void cadastrarUsuario(Biblioteca biblioteca, Scanner scanner) {
        System.out.print("Digite seu nome: ");
        String nome = scanner.nextLine();
        System.out.print("Digite seu CPF: ");
        String cpf = scanner.nextLine();

        biblioteca.registrar(new Usuario(nome, cpf));
    }

    private static void adicionarLivro(Biblioteca biblioteca, Scanner scanner) {
        System.out.print("Digite o Titulo do livro: ");
        String titulo = scanner.nextLine();
        System.out.print("Digite o Autor do livro: ");
        String autor = scanner.nextLine();
        System.out.print("Digite o ISBN do livro: ");
        String isbn = scanner.nextLine();
        int ano = lerNumero(scanner, "Digite o ano de publicação do livro: ");

        biblioteca.adicionarLivro(new Livro(titulo, autor, isbn, ano));
    }
}

Acho que assim as responsabilidades ficam bem separadas, em vez de ter coisas como o Usuario recebendo um Scanner no construtor, e ele mesmo fazendo a leitura dos dados. A meu ver, ler os dados é uma tarefa separada, e o construtor apenas recebe as informações e cria um usuário. Já os dados, podem vir de qualquer lugar (do Scanner, de um arquivo, de uma chamada HTTP para uma API, do banco de dados, etc), e não é responsabilidade da classe Usuario saber desses detalhes.

Separando as responsabilidades (uma classe lê os dados, outra usa esses dados para criar uma instância, etc) fica bem mais organizado, facilitando a manutenção e evolução do código.

Aprendeu isso no curso? Caso afirmativo, então está aprendendo bobagem.

Fica?
Cada classe tem que ter suas responsabilidades.
Faz sentido a Biblioteca depender de métodos da Main?

Negativo, na realidade foi um erro meu mesmo basicamente falta de leitura e entendimento do assunto.

Bem Eu estudei mais sobre nomear classes e agora que eu estou fazendo esse mini sistema conectado com banco de dados percebi que é muito importante o nome/código que tem dentro de uma classe e que também não tem necessidade de jogar um monte de extends entre as classes e enfim, valeu staroski vi no teu perfil que tu é de Blumenau, eu sou de Gaspar cidade vizinha da sua kkkkk, inclusive eu faço Jovem Programador no SENAC de Blumenau e ai acabei entendendo errado quando usar interface e herança kkkkk.

Agradeço se puder nos explicar o que te ensinaram sobre interfaces e sobre herança.
Isso vai ajudar bastante a te explicar melhor.

O professor (Vilson Mouro que talvez tu conheça ele) me ajudou bastante a entender herança que é quando a classe herda atributos e métodos de outra classe mas não necessariamente você precisa dela pra tudo ainda mais quando o código é bem organizado e estruturado não tem necessidade de usar tanto (igual eu usava) até pq vc acaba usando apenas apenas os campos de cada classe e em uma outra vc cria os métodos, e a interface é tipo vc forçar que outras classes sejam sigam os métodos dela, como se fosse um contrato (igual vc disse) pra mim é isso caso eu tenha escrito algo errado por favor me corrija . E eu tenho uma pergunta, a gente usa interface tipo em grandes sistemas ou não?

Sim, mas você já parou para pensar em que situação você vai usar isso?
Você vai usar herança em uma situação onde a classe filha é uma especialização da classe pai.

Detesto exemplos abstratos usando animais e frutas, ninguém implementa isso profissionalmente.
Então vou exemplificar como poderia usar a herança num sistema hospitalar.

Exemplo

  • Classe Pai: Pessoa
  • Classe Filha: Paciente, Medico, Enfermeiro

Explicação:
Todas essas pessoas têm nome e CPF.
Mas o Paciente tem o convênio.
O Médico tem CRM.
O Enfermeiro tem COREN.

public class Pessoa {
    protected String nome;
    protected String cpf;
}

public class Paciente extends Pessoa {
    private String convenio;
}

public class Medico extends Pessoa {
    private String crm;
}

public class Enfermeiro extends Pessoa {
    private String coren;
}

Dica de ouro:

“Toda vez que você puder dizer X é um Y, então você pode usar herança e X pode estender Y.”

Por exemplo:

  • Um Carro é um Veiculo então
    class Carro extends Veiculo

  • Um Funcionario CLT é um Funcionario então
    class FuncionarioCLT extends Funcionario

  • Um Medico é uma Pessoa então
    class Medico extends Pessoa

Exatamente.

Não importa o tamanho do sistema, se programar de forma decente, usa bastante interface.

:mag: O que é uma interface (em termos simples)?

Uma interface define um contrato: métodos que uma classe deve implementar.
Não diz como fazer, mas o que deve ser feito.


:white_check_mark: Por que usar interfaces?

  1. Para garantir que diferentes classes possam ser usadas da mesma forma.
  2. Para facilitar trocas e evoluções no sistema (baixo acoplamento).
  3. Para permitir múltiplas implementações.
  4. Para aplicar os princípios SOLID (especialmente o “D” - Inversão de Dependência).

:brain: Analogia rápida (sem frutas ou animais :wink:)

Pensa num sistema de pagamento online. Não importa se o pagamento é via PIX, Cartão, ou Boleto
O sistema só precisa saber que consegue chamar o método pagar().


:credit_card: Exemplo Módulo de Pagamentos

Interface:

public interface MetodoDePagamento {

    void pagar(double valor);
}

Implementações:

public class PagamentoCartao implements MetodoDePagamento {

    public void pagar(double valor) {
        // lógica com operadora
    }
}

public class PagamentoPix implements MetodoDePagamento {

    public void pagar(double valor) {
        // lógica com chave Pix
    }
}

public class PagamentoBoleto implements MetodoDePagamento {

    public void pagar(double valor) {
        // gera código de barras
    }
}

Exemplo de Uso:

public class Checkout {

    public void finalizarCompra(MetodoDePagamento metodo, double valor) {
        metodo.pagar(valor);
    }
}

:white_check_mark: Aqui, o Checkout não precisa saber como é feito o pagamento. Isso pode mudar amanhã (ex: adicionar Google Pay), sem tocar nessa classe.

Perceba que a implementação do método finalizarCompra recebe como parâmetro um objeto do tipo MetodoDePagamento.

Isso significa que vai funcionar com qualquer implementação da interface MetodoDePagamento, indiferente se for um objeto da classe PagamentoCartao, PagamentoPix, PagamentoBoleto ou qualquer implementação futura da interface, por exemplo PagamentoGooglePay.


:hammer_and_wrench: Quando usar interfaces

:white_check_mark: Use interface quando:

Situação Exemplo
Você precisa de flexibilidade para múltiplas implementações Vários tipos de pagamento, envio de e-mails, armazenamento (S3, disco, banco)
Você quer testar com mocks/fakes Interface RepositorioDeUsuarios, com uma versão real e uma versão fake para testes
Você quer permitir substituição fácil de comportamento Envio de notificações: Notificador pode ter envio por email, SMS, push…
Você precisa de baixo acoplamento Seu código principal depende da interface, não da implementação concreta

:x: Quando não usar interfaces

  • Se você tem uma classe que nunca terá variação de comportamento.
  • Para fazer tudo “genérico demais” sem necessidade real.
  • Se você não precisa substituir a implementação ou reaproveitá-la.

:dart: Dica final

Herança modela o “é um tipo de…”
Interface modela o “pode fazer tal coisa…”

Nossa esse exemplo do sistema hospitalar e da conta bancaria me ajudou a entender MUITO melhor a questão de interface. Obrigado Staroski

1 curtida