Modelagem - Cliente/Vendedor como Pessoa Física ou Jurídica

Olá, li muitos tópicos sobre essa questão da PF e PJ (e herança em geral) aqui no GUJ e em outro lugares… Mas ainda me resta algumas dúvidas que não vi respostas que sejam consideradas “elegantes”. =)

De acordo com o que eu li, eu estou usando o padrão Strategy. Está ficando assim:

Tenho as classes Vendedor e Cliente.
Tenho a interface Pessoa que vai ser um atributo de Vendedor e Cliente.
Tenho as classes PessoaFisica e PessoaJuridica (Pessoa vai referenciar uma PF ou PJ).

Abaixo uma implementação básica delas para poder tirar minhas dúvidas.

public interface Pessoa { public String tipo(); // retornar se pessoa é jurídica ou física //outros métodos necessários }

[code]public class Cliente {
private int id;
private String nome;
private Pessoa p;

    // gets e sets

}

// Vendedor no mesmo padrão de classe do Cliente
[/code]

[code]public class PessoaJuridica implements Pessoa {

private String nomeFantasia;
private String cnpj;
    private String ie;
    private String dataFundacao;

    // gets e sets

public PessoaJuridica(String nomeFantasia, String cnpj, String ie, String dataFundacao) {
	this.nomeFantasia = nomeFantasia;
	this.cnpj = cnpj;
	this.ie = ie;
	this.dataFundacao = dataFundacao;
}

    public String tipo(){
            return "Pessoa Jurídica";
    }

}[/code]

[code]public class PessoaFisica implements Pessoa {
private String cpf;
private String rg;
private String sexo;
private String dataNascimento;

    // gets e sets

public PessoaFisica(String cpf, String rg, String sexo, String dataNascimento) {
	this.dataNascimento = dataNascimento;
	this.sexo = sexo;
	this.cpf = cpf;
	this.rg = rg;
}

    public String tipo(){
            return "Pessoa Física";
    }

}[/code]
Se eu for instanciar um Cliente PF e um PJ seria assim:

[code]//…
Cliente cPF = new Cliente(1, “Maria”, new PessoaFisica(“123”, “456”, “F”, “01/01/1990”));

    Cliente cPJ = new Cliente(1, "Pao com Ovo LTDA", new PessoaJuridica("PCO", "456789", "45678", "01/01/1994"));

//…[/code]

Bom, é o seguinte, se eu quiser saber o tipo de pessoa que meu cliente é só usar “cPF.getP().tipo();”. Mas se eu quiser recuperar os atributos que são específicos de PF?
Usaria cast? Ex.: ((PessoaFisica) cPF.getP()).getCPF();
Qual forma você acham mais “elegante”?

E no caso de métodos que fazem alterações no banco (seja update, insert, etc…)? Pois no meu caso o banco será representado por duas tabelas (ClientePF e ClientePJ), mas acho que não mudaria mesmo que fosse três). Pois os dados que serão armazenados e uma possível validação seria diferente no caso de um UPDATE em PF e PJ.
Seria melhor eu colocar esses métodos na interface Pessoa e implementá-los nas Classes PessoaJurida e PessoaFisica? Ex.:

public interface Pessoa { public String tipo(); // retornar se pessoa é jurídica ou física public boolean salvar(Cliente cliente); //outros métodos necessários }

// salvando... cPF.getP().salvar(cPF); //...

E então? Seria uma solução aceitável? Teria uma melhor opção? Talvez uma melhor modelagem para este caso?

Desde já o meu Muito Obrigado! Abraço!

Olá //west,

Vou tentar ajudar, dentro do possível:

  • não vejo problema no cast. Seria um problema se você estivesse sujeito a acrescentar novos tipos de pessoas (?) ao sistema. Mas como esse dois tipos (jurídica e fisíca) são bem estáveis, não vejo problema em usar o cast.

  • não é boa política acrescentar os métodos de persistência nas próprias classes de entidade. Isso representaria que essas classes passariam a ter mais uma atribuição, mais ou menos complexa, resultando em perda de coesão. Uma classe deve ser coesa, ter poucas atribuições. Para resolver isso, recomendo que você escolha um modelo de persistência - como DAO, ou outro.

Bem, espero ter ajudado.

Hel_all

A minha ideia era dentro de Cliente ter um método salvar, nele ia ser chamado o método “cPF.getP().salvar(cPF);” e dentro do salvar de PF e PJ eu fazer determinado tratamento, caso necessário, e instanciar um DAO e ele faz o insert.

Seria aceitável seguindo os padrões OO?

Olá,

Olha, eu não gosto muito da idéia de cPF.getP().salvar(cPF). Ele faz Pessoa depender de Cliente, sem que isso seja justificado. E para o Vendedor, como seria? Haveria um método Pessoa.salvar(Vendedor) também? E se fosse necessário acrescentar outra categoria - Fornecedor, por exemplo - a interface Pessoa teria que ser alterada?

Se a crítica é válida, o caminho é pensar por que o Cliente é necessário para salvar a Pessoa, Fisíca ou Jurídica. Desconfio que pode ser por causa do campo nome, e talvez de outros atributos que estão naquela classe. Mas o nome é nome da Pessoa ou do Cliente? O nome é da Pessoa, acho, porque a mesma pessoa tem o mesmo nome, quer esteja como Vendedor, Cliente, ou Fornecedor, etc. Tenho razão? A sugestão é fazer esse pente fino em todos os atributos de Cliente. Se sobrarem atributos que não podem migrar de Cliente para Pessoa, são atributos próprios de Cliente e justifica-se abrir uma tabela lógica para Cliente no BD, tabela que se relaciona com Pessoa.

Uma pequena modificação, conservando a sua solução, é fazer o método salvar, em Pessoa, receber como parâmetro um objeto Pessoa:

public interface Pessoa {
    .....
    void salvar(Pessoa pessoa); 

}


public class Cliente {
     .....
     public void salvar(Cliente cliente) {
          cliente.getP().salvar(p);  //salva Pessoa
          //faz tudo o que tem que fazer
     }

}

Essa solução conserva o modelo inicial que você propôs. Eu tenho uma visão um pouco diferente do DAO, diferente do que você mencionou na mensagem. A meu ver, se você usar o DAO, os métodos salvar() migram de Cliente e de Pessoa, onde estão, para, digamos, ClienteDAO e PessoaDAO. Você então chamaria, no seu programa, para salvar cliente:

DAOCliente dao = new DAOCliente(); dao.salvar(cPF);

O DAOCliente faria o salvamento de Pessoa:

public class DAOCliente { ..... public void salvar(Cliente cliente) { Pessoa p = cliente.getP(); //instancia o DAO adequado ao tipo dao.salvar(p); } }

A escolha do DAO adequado poderia ser feita por uma Fábrica.

É assim que eu vejo esse caminho.

Espero ter sido útil.

Hel_all

Outra coisa chata é que o pessoal costuma modelar Pessoa Física e Jurídica como sendo classes que têm um ancestral comum, que é “Pessoa”.
Acho que ambas as coisas são muito distintas e não merecem ser “aparentadas” porque têm comportamentos muito distintos, e compartilham relativamente poucos atributos.

Olha, primeiro queria dizer que eu não entendo muito dessas categorias (Pessoa Fisica, Pessoa Juridica). Minha visão é a do senso comum. Acho que seus argumentos são válidos, mas queria fazer uma observação baseada apenas na forma das expressões. A expressão Pessoa jurídica talvez possa ser lida indicando que “Pessoa jurídica é uma Pessoa”. O mesmo para Pessoa física. Se isso está certo, a relação é um poderia ser representada como herança, não é?

Me ocorre a possibilidade de ambas a soluções serem aceitáveis, cada qual justificada a seu modo… pirei? :smiley:

Olha, primeiro queria dizer que eu não entendo muito dessas categorias (Pessoa Fisica, Pessoa Juridica). Minha visão é a do senso comum. Acho que seus argumentos são válidos, mas queria fazer uma observação baseada apenas na forma das expressões. A expressão Pessoa jurídica talvez possa ser lida indicando que “Pessoa jurídica é uma Pessoa”. O mesmo para Pessoa física. Se isso está certo, a relação é um poderia ser representada como herança, não é?

Me ocorre a possibilidade de ambas a soluções serem aceitáveis, cada qual justificada a seu modo… pirei? :smiley: [/quote]

Usar herança neste caso é o exemplo clássico de como não se deve modelar classes. Você pode chagar a conclusão neste mapeamento que Ford Motors é uma pessoa tão quanto João Silva é, o que é no mínimo estranho para qualquer domain model. Se vc quer apenas usar atributos em comum entre pessoa juridica e pessoa física use composição, não herança.