Devo usar uma classe especializada?

Tenho o seguinte codigo:

@Entity
public class Reserva implements Criptografavel {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
@OneToOne
private Estadia estadia;
@Enumerated(EnumType.STRING)
private ReservaStatus status;
@ManyToOne
private Cliente cliente;
private String contacto;
private String recebidaPor;
private String feitaPor;
private String agencia;
@OneToMany(mappedBy = "reserva", cascade = CascadeType.ALL)
private List<Pagamento> pagamentos;
@Transient
private BigDecimal totalEfetuado;
@Transient
private BigDecimal totalPendente;

public Reserva(String nome, Estadia estadia, String recebidaPor, String feitaPor, String agencia) {
    this();
    this.nome = nome;
    this.estadia = estadia;
    this.recebidaPor = recebidaPor;
    this.status = ReservaStatus.ESPERA;
    this.feitaPor = feitaPor;
    this.agencia = agencia;
}

@Deprecated
public Reserva() {
    totalEfetuado = calcularPrecoTotal(getPagamentos(PagamentoStatus.PAGO));
    totalPendente = calcularPrecoTotal(getPagamentos(PagamentoStatus.PENDENTE));
}

//GETTER E SETTER OCULTADOS

public void estadoAnterior() {
    this.status = this.status.anterior();
}

public void proximoEstado() {
    this.status = this.status.proximo();
}

public List<Pagamento> getPagamentos() {
    return Collections.unmodifiableList(pagamentos);
}

public void setPagamentos(List<Pagamento> pagamentos) {
    this.pagamentos = pagamentos;
}

@Override
public void criptografa() {
    Criptografador criptografador = new Criptografador();
    this.nome = criptografar(criptografador, this.nome);
    this.contacto = criptografar(criptografador, this.contacto);
    this.feitaPor = criptografar(criptografador, this.feitaPor);
    this.recebidaPor = criptografar(criptografador, this.recebidaPor);
}

@Override
public void descriptografa() {
    Criptografador criptografador = new Criptografador();
    this.nome = descriptografar(criptografador, this.nome);
    this.contacto = descriptografar(criptografador, this.contacto);
    this.feitaPor = descriptografar(criptografador, this.feitaPor);
    this.recebidaPor = descriptografar(criptografador, this.recebidaPor);
}

private boolean textoVazio(String msg) {
    return msg.trim().isEmpty();
}

public BigDecimal getTotalEfetuado() {
    return totalEfetuado;
}

public BigDecimal getTotalPendente() {
    return totalPendente;
}

public void adicionarDivida(BigDecimal quantia, String descricao) {
    Pagamento pagamento = new Pagamento(quantia, descricao, this, PagamentoStatus.PENDENTE);
    
    guardarPagamento(pagamento);
    this.pagamentos.add(pagamento);
    this.totalPendente = this.totalPendente.add(quantia);
}

public void pagar(BigDecimal quantia, String descricao) {
    Pagamento pagamento = new Pagamento(quantia, descricao, this, PagamentoStatus.PAGO);
    if (getDivida().equals(BigDecimal.ZERO)){
        throw new RuntimeException("Não existem dividas");
    }else if (this.totalPendente.compareTo(this.totalEfetuado.add(quantia)) == -1){
        throw new RuntimeException("Pagamento maior que a divida");
    }
    
    this.totalEfetuado = this.totalEfetuado.add(quantia);
    guardarPagamento(pagamento);
    this.pagamentos.add(pagamento);
}

private void guardarPagamento(Pagamento pagamento) {
    PagamentoDao pagamentoDao = new PagamentoDao(new JPAUtil().getEntityManager());
    int idPagamento = pagamentoDao.adicionar(pagamento);
    pagamento.setId(idPagamento);
}

public BigDecimal getDivida() {
    return this.totalPendente.subtract(this.totalEfetuado);
}

private BigDecimal calcularPrecoTotal(List<Pagamento> pagamentos) {
    BigDecimal total = BigDecimal.ZERO;
    for (Pagamento pagamento : pagamentos) {
        total = total.add(pagamento.getQuantia());
    }

    return total;
}

private List<Pagamento> getPagamentos(PagamentoStatus status) {
    List<Pagamento> pays = this.pagamentos.stream().filter(pay -> pay.getStatus() == status)
            .collect(Collectors.toList());
    return pays;
}
}

A minha duvida é relativamente aos pagamentos, devo utilizar uma classe apenas com a função de gerir os pagamentos? Se sim como? Já tentei fazer, mas para isso teria que quebrar um pouco o encapsulamento

Bem, se voce vai receber os pagamentos, sim tem de ter um metodo pra isso, e nem tem isso de quebrar o encapsulamento, pois isso é ocultar partes desnecessarias ao codigo, voce da uma entrada de um valor na classe main e chama o metodo de pagamento

1 curtida

Se você está preocupado com não quebrar o Encapsulamento, então está preocupado com os conceitos de OO, e já que é assim, antes de pensar em encapsular seus atributos e comportamentos, pense na Coesão e acoplamento de seu sistema.

Em termos gerais a coesão (alta coesão) diz que cada objeto deve ter um único objetivo claro e COESO para existir.

E o acoplamento (baixo acoplamento no caso) cuida de fazer com que seus objetos não sejam ACOPLADOS uns aos outros de forma que não possam ser reaproveitados por outros componentes do seu Software. No caso acima, seu Pagamento está fortemente acoplado na sua Reserva. Tão fortemente, que só existe Pagamento se existir uma Reserva. Quando você estender seu Software pra receber pagamentos de produtos servidos no quarto ou vendidos no restaurante do hotel, teria que criar outro pagamento em outras classes.

Na mesma linha está indo sua criptografia, você está colocando o método de criptografia dentro da classe de reserva, de modo que agora se, em qualquer outro lugar você precisar criptografar, terá que instanciar uma Reserva ou implementar a interface Criptografavel.

Embora funcione na prática o que você está fazendo, sua preocupação de que separar o pagamento vai quebrar o encapsulamento não faz sentido, afinal você estará dando mais coesão pro seu programa e diminuindo o acoplamento drasticamente. Então vá fundo, você já teve uma boa ideia e está em duvida por conta de um outro conceito que não foi bem entendido, que é o encapsulamento.

Mesmo que fosse verdade, ainda assim sempre prefira alta coesão e baixo acoplamento no seu software, assim ele sempre será mais fácil de manter.

Abraços e sucesso nos estudos… Qualquer coisa avisa aí… :wink:

1 curtida

Sim. Como? Criando uma classe/arquivo que seja responsável somente por gerir pagamento. O requisito é simplesmente esse e não aumentar a complexidade por firulas de OOP.

1 curtida

Eu tenho uma classe de criptografia, porem ela so criptografa strings. Por isso na classe reserva possuo um metodo criptografa para criptografar todos os atributos. Existe uma maneira melhor de fazer isso?

Já agora outra pergunta: devo criar uma interface para todos os objetos que sejam “pagaveis”?

Quanto ao resto, obrigado pela ajuda!

Só se precisar estabelecer contrato para várias implementações de uma mesma funcionalidade. Se forem funcionalidades diferentes, evite essas burocracias, cada funcionalidade com sua vida própria, evitando amarrações. Raramente uso Interface para regras de negócio, pois os requisitos são muito dinâmicos.

Bem… pela natureza do que você está fazendo, acaba sendo uma maneira mais “fácil”, porém mais braçal. Você poderia usar Reflection pra fazer isso toda vez que for setar seus objetos e, como você provavelmente vai salvar essas informações em alguma base de dados, usar um Listener pra transformar esses dados em dados criptografados. Mas… lembre que toda a solução mais robusta tem um custo, logo sua performance tende a ser prejudicada.

Porém vendo você fazer isso, me pergunto porque você está criptografando todas as informações? Se for porque você viu em algum lugar que é seguro ter esses dados criptografados, então vou dar algumas dicas que podem lhe ser úteis:

Se for apenas pra testar o recurso:

  • Ok, continue, teste de todas as formas possíveis e avalie o recurso;

Se for pra algo que já vai pra produção

  • Esqueça isso. Se você tem um SGBD que armazena suas informações, é bem possível que o mesmo já tenha o recurso de criptografar seus dados sem você precisar mexer em nada, ainda tem um custo de performance, mas jogar isso pro banco, deixa esse custo ínfimo;
  • Avalie testar um framework que já faça isso pra você, caso o recurso não esteja dispponível no seu SGBD;
  • Avalie também se é interessante ter todos os seus dados criptografados. Talvez dados de documentos e financeiros façam sentido, já datas, descrições, nomes e campos de controle, dependendo do seu negócio, seja um exagero.

Quanto à interface de pagamento, não precisa atrelar nela nenhum dos seus objetos através de dependência hierárquica. Minha sugestão (no escuro sem conhecer seu objetivo e sua estrutura completa) é pra criares uma interface MeioPagamento e as classes PagamentoCartao, PagamentoDinheiro por exemplo, herdando a interface de Meio de pagamento que irá ter um contrato padrão que todos os seus meios de pagamentos posteriores (cashback, cheque, etc.) terão que ter.

Para relacionamento de tudo que é pagável, você pode fazer um relacionamento de OneToMany. Para exemplificar, seu objeto reserva teria a seguinte configuração:

class Reserva {
     ...
    private List<Pagamento> pagamentos;
     ...
}

Assim você está associando os seus pagamentos a sua reserva, já que provavelmente seu hotel permitirá que você pague sua reserva de várias maneiras, com mais de 1 cartão, etc.

Claro, se tudo isso for apenas para um trabalho de facul, simplifique tirando tudo que você acha que não vai caber a tempo de entregar, mas entenda as possibilidades.

É isso, espero ter lhe ajudado mais um pouco. :wink:

1 curtida

Você já ouviu falar (da hinode) dos princípios SOLID? Se não, dá uma olhada
Em resumo, o princípio da Single Responsibility (responsabilidade única) diz que sim, toda classe só deve existir com um único propósito.
Nada de criar classes que batem o escanteio e cabeceiam.

3 curtidas