Como transformar uma instância de uma classe mutável em uma instância imutável

Pessoal,

Não sei se tem como fazer isso, mas minha situação é a seguinte. Imagine que existe uma classe Cliente que possui 0 ou vários dependentes. Essas classes são mutáveis e são persistidas em banco.
Em um dado momento, eu possuo uma instância da classe Cliente em mãos e eu preciso fazer um “filtro” e remover alguns dependentes da associação, mas somente em memória, e queria que essa instância fosse somente leitura, ou seja, o cara que receber essa instância só pode ler os dados, não pode dar um set em nada (nem que isso causasse um erro em runtime, como Collections.unmodifiableList faz com o add).

Um jeito de fazer isso seria ter um atributo na classe que definisse que a instância é read-only e em todos métodos que alterem o valor do objeto, verificar se esse atributo não está setado, mas não acho que seja uma boa solução! :lol:

Alguém já teve que fazer algo do tipo ou sabe uma maneira mais “elegante” e eficiente de fazer isso?

Obrigado
Luciano

Oi Luciano,

Honestamente não me veio nada na cabeça diferente disto que você falou (bloqueio utilizando um atributo).

Porem seria interessante vc dizer o porque vc precisa deste recurso para que a gente tenha condições de lhe oferecer alternativas.

Por um acaso é para bloquear a ação de usuários baseado em seus perfis?

flws

Oi fantomas,

Vou tentar explicar a necessidade. Não é para bloquear a ação de usuários não. É o seguinte:

-Tenho um “blocão” (war) com várias interfaces web services (jar), sendo que cada interface dessa fala com um sistema externo diferente e consequentemente possuem um “contrato” diferente. Porém, todas elas recebem o objeto Cliente (que é genérico) e à partir dele cria seus objetos específicos.

-E tenho um bloco central (outro .war) que trabalha o objeto Cliente, persiste e faz os filtros necessários antes de enviá-lo para o bloco das interfaces.

Como tem bastante gente mexendo no projeto, preciso garantir que nenhuma interface vai querer atualizar e persistir o Cliente (que não está completo nesse ponto).

Espero que tenha sido pelo menos um pouco claro…rs

Abraços!

Eu não entendi direito, mas se você não quer que um objeto seja sobrescrito, utilize final como chave na declaração.

wellington, você entendeu corretamente. Sua solução é boa, mas o refactoring seria muito grande realmente, não sei se ela será possível.

Por que você não adota a mesma solução do Collections.unmodifiableList e faz um proxy para sua classe?

Separe a interface da classe do Cliente, e passe a usar essa interface. Depois, crie uma classe chamada ClienteReadOnly, que receba um cliente, também implemente a interface, e que só delegue a classe cliente que ela encapsula os métodos getters. No caso dos setters, você pode lançar uma UnsupportedOperationException.

Um exemplo trivial:

[code]public interface Cliente {
String getNome();
void setNome(String nome);
}

public class ClienteImpl implements Cliente {
private String nome;

public String getNome() { return nome; }
public void setNome(String nome) { this.nome = nome; }

}

public final class ClienteReadOnly implements Cliente {
private Cliente cliente;

public ClienteReadOnly(Cliente cliente) {
    this.cliente = cliente;
}

private String getNome() { return cliente.getNome(); }
private void setNome(String nome) { throw new UnsupportedOperationException(); }

}[/code]

O uso fica igual ao com a Collections. Vamos supor que você tenha um ClienteImpl, mas queira retorna-lo como um cliente imodificável:

public Cliente getCliente() { //Retornamos a versão read only desse cliente return new ReadOnlyCliente(cliente); }

Note que o ideal é que todos os seus métodos tenham como valor de retorno interface Cliente, não a classe concreta que implementa cliente. Assim você sempre terá poder de escolha sobre qual implementação concreta usar.

ViniGodoy e wellington.nogueira.

Queria evitar ter que fazer mutio refactoring, mas acho que vai ser necessário de qualquer forma.

Obrigado pela ajuda!

Abraços
Luciano

Pessoal,

Comecei a fazer um teste com a abordagem do throw new UnsupportedOperationException() nos métodos set, mas tem um porém.

Se o cara tem uma instância do Cliente que é imutável (ClienteReadOnly) e pede pro Hibernate inserí-la, ela vai ser inserida na boa, pois pelo visto o Hibernate não chama o setId da classe.

Para resolver esse problema,estava pensando em colocar uma verifição nos métodos que insere e atualiza Cliente no banco. Essa verificação teria que checar se a instância do Cliente é ClienteImpl ou ClienteReadOnly. No primeiro caso deixaria fazer a operação, no segundo dispararia uma UnsupportedOperationException.

O que acham?

Att.
Luciano

Me parece ser por aí mesmo. :slight_smile:

Legal ViniGodoy, Obrigado!Vou seguir essa linha mesmo.

wellington.nogueira, não quero que alguém salve os dados “por acidente”, pois o meu problema é que os dados vão estar filtrados, ou seja, incompletos, e isso poderia causar uma inconsistência no BD e zicar com a lógica de negócio.

Uma opção seria criar uma interface ClienteImutavel a ser implementada pela classe Cliente que contivesse apenas as assinaturas getXXX() de cliente e você disponibilizasse essa interface para quem deve usá-lo como somente leitura.

Uma alternativa seria criar interfaces Cliente que herdasse ([size=7][color=green]extendesse[/color][/size]) ClienteImutavel e ClienteMutavel (que possuiria as assinaturas dos setXXX() ) e a classe Cliente atual fosse renomeada para ClienteImpl

Quando o sistema puder ler e escrever, você disponibilizaria a interface Cliente, qdo fosse apenas leitura, forneceria ClienteImutavel. Qdo o Cliente fosse somente escrita, forneceria ClienteMutavel

É um refactoring razoável mas impede erros relativos ao uso de um atributo de controle de leitura que pode ter um controle, as vezes, inviável se disseminado em diversos pontos.

[size=9][color=red]PS: Os nomes são apenas sugestões para clareza :P[/color]

Edit: extendesse não seria a palavra adequada. O certo é herdasse.[/size]

Eu entendi que ele não quer que alguns sistemas utilizem os setters do objeto.

As vezes um grande refactoring previne problemas futuros muito maiores. Não sei qual a ide que usa, mas a do eclipse consegue ajudar muito nesse tipo de alteração.

A solução do ViniGodoy tbm é muito boa e, apesar de possivelmente causar um pouco de refactoring, internamente você passaria a tratar o objeto Cliente através de sua interface (ao invés de uma classe concreta que seria ClienteImpl) causando poucas mudanças.

Só não acho muito legal pois está deixando explícita a classe concreta (não que eu, as vezes não faça :oops: ), mas é uma solução simples.

Dúvida [size=9]de curioso[/size]: Você não quer que o dado seja gravado ou não quer dar margem a erros numa eventual recuperação dos mesmos?
E, em relação aos dados já existentes? (Se existirem). Não haverá mais chances de recuperação, se for uma parte que deve usar o objeto imutável?