Melhor forma de modelar/programar cliente, fornecedor e pessoa

Tenho os seguintes papeis: Cliente, Fornecedor, Vendedor

Essas classes tem em comum, o endereço, como rua, telefone, e-mail, etc (uns 20 atributos).

Esses dados poderiam estar em uma classe Pessoa?

Mas aqui eu tenho o caso de por exemplo um Vendedor também poder assumir o papel de Cliente.

Qual a melhor forma de modelar isso? Eu criaria a classe Pessoa referentes a dados pessoais?
Quais a relação de classes que eu faria a herença ou agregação?

Usaria polimorfismo? Também não seria o caso, pois Cliente, Fornecedor e Vendedor possuem alguns atributos e métodos específicos.

Além disso, Cliente e Fornecedor possui outro atributos em comum mas que não fa parte de Vendedor. Nesse caso (como é somente um atributo), acredito que seria melhor que ele fique em Cliente e Fornecedor.

Essa abordagem é necessária por que minha aplicação possui um método que é responsável importar dados em comum, ou seja: O usuário da aplicação cadastra um Cliente, mas se ele quiser pode recuperar os dados em comum (como endereço, tel, etc), para não ter que digitar tudo novamente para efetuar o cadastro de Vendedor ou Fornecedor.

[quote=hugleo]Tenho os seguintes papeis: Cliente, Fornecedor, Vendedor

Essas classes tem em comum, o endereço, como rua, telefone, e-mail, etc (uns 20 atributos).

Esses dados poderiam estar em uma classe Pessoa?

Mas aqui eu tenho o caso de por exemplo um Vendedor também poder assumir o papel de Cliente.

Qual a melhor forma de modelar isso? Eu criaria a classe Pessoa referentes a dados pessoais?
Quais a relação de classes que eu faria a herença ou agregação?

Usaria polimorfismo? Também não seria o caso, pois Cliente, Fornecedor e Vendedor possuem alguns atributos e métodos específicos.

Além disso, Cliente e Fornecedor possui outro atributos em comum mas que não fa parte de Vendedor. Nesse caso (como é somente um atributo), acredito que seria melhor que ele fique em Cliente e Fornecedor.

Essa abordagem é necessária por que minha aplicação possui um método que é responsável importar dados em comum, ou seja: O usuário da aplicação cadastra um Cliente, mas se ele quiser pode recuperar os dados em comum (como endereço, tel, etc), para não ter que digitar tudo novamente para efetuar o cadastro de Vendedor ou Fornecedor.
[/quote]

Oi,

Bom, eu faria da seguinte forma, não sei se é a ideal para o seu caso e nem a mais correta, mas vamos lá:

Cliente, Fornecedor e Vendedor são papeis, portanto eu criaria interfaces representando os métodos de cada um, lembre que você pode ter também constantes nas interfaces caso precise identificar atributos

Pessoa possui os atributos comuns aquela lista de que falou

Ai para usar voce poderia ter algo do tipo

[code]class SuperPessoa extends Pessoa implements Fornecedor, Vendedor, Cliente {
// SuperPessoa possui todos os atributos de Pessoa que devem ser declarados como protected de preferência

// atributos específicos de SuperPessoa
private String nomeDeSuperPessoa;
private int idadeDeSuperPessoa;

// método da interface Cliente, você deve colocar a lógica aqui
public int fazAlgumaCoisaComCliente() {
   // TODO
}

// idem para Fornecedor
public boolean fazAlgumaCoisaComFornecedor() {

}

...

}
[/code]

A idéia de uma interface é ser um contrato que define um comportamento o que é o caso de Cliente, Fornecedor e Vendedor

A herança cuida dos casos de especialização, no caso SuperPessoa é um pouco mais do que uma Pessoa

Caso você precise ou ache necessário pode definir um interface básica que todos devem implementar…

Abs

Então, nesse caso eu necessitaria de implementar todos os métodos em SuperPessoa.

Se por virtude eu precisar modificar a classes Cliente, eu teria que modificar a SuperPessoa também. Aí seria pareciso se eu nem tivesse Cliente, Fornecedor, Vendedor… Criaria tudo em SuperPessoa.

Talvez seja mesmo o único jeito, pois já que um Cliente pode ser também Vendedor, Fornecedor… E SuperPessoa cuidaria disso… De alguma forma seria obrigatório existir uma classe única que integrasse Cliente, Fornecedor, Vendedor…

Acredito que é mesmo o melhor jeito.

:wink:

Eu usaria o padrao decorator nesse caso.

André Fonseca, nesse caso eu não teria que cadastratar obrigatoriamente que cadastar uma Cliente, Fornecedor e Vendedor para que seja possível montar a SuperPessoa? Então se eu cadastrar um Cliente ele seria também um Vendedor, o que nem sempre é verdade.

# public int fazAlgumaCoisaComCliente() {  
#        // TODO  
#     }  
#   
#     // idem para Fornecedor  
#     public boolean fazAlgumaCoisaComFornecedor() {  
#   
#     }  

Desculpa, Quis disse strategy e nao decorator.

Se vc fizer isso, Se um dia aparecer uma nova entidade no seu sistema vc ta “pego” pra alterar todas as interfaces, a classe pessoa e a SuperPessoa. Fora que fica um codigo totalmente feio e acoplado.

Ai vai um exemplo usando strategy.

public class StrategyEx {

	public static void main(String[] args) {
		
		
		PessoaContext pessoa1 = new PessoaContext( new Cliente() );
		PessoaContext pessoa2 = new PessoaContext( new Vendedor() );
		PessoaContext pessoa3 = new PessoaContext( new Fornecedor() );
				
		System.out.println( pessoa1.getType());
		System.out.println( pessoa2.getType());
		System.out.println( pessoa3.getType());						
		
	}

}

interface Pessoa {	
	String getType();	
}

class PessoaContext {

	protected Pessoa pessoa;
	
	PessoaContext(Pessoa pessoa) {
		this.pessoa = pessoa ;	
	}
	
	public String getType() {
		return pessoa.getType();		
	}	
	
}

class Cliente implements Pessoa {
		
	public String getType() {		
		return "Tipo Cliente";
	}
	
}

class Vendedor implements Pessoa {
	
	public String getType() {		
		return "Tipo Vendedor";
	}
	
}

class Fornecedor implements Pessoa {
	
	public String getType() {		
		return "Tipo Fornecedor";
	}
	
}

Obrigado, parece bem interessante.

Oi,

Respondendo a sua dúvida: você não irá cadastrar/persistir um Cliente, você irá cadastar uma SuperPessoa que pode ser ou não um Cliente, ai depende da sua necessidade.

Como o windsofhell falou usando o strategy facilita quando for necessária uma alteração na entidade Pessoa

Abs

# interface Pessoa {    
#     String getType();     
# }  

Tá, você criou interface Pessoa, então como fica o caso de: “Uma interface não pode possuir atributos de instância”
?
Já que existem atributos comuns entre Cliente, Fornecedor, Vendedor.

Talvez fosse ideal criar uma classe agregada, onde teria um tipo de dados.

# class Cliente {    
#     DadosBasicos basicos;     
# }  

[quote=hugleo][code]

interface Pessoa {

String getType();

}

[/code]

Tá, você criou interface Pessoa, então como fica o caso de: “Uma interface não pode possuir atributos de instância”
?
Já que existem atributos comuns entre Cliente, Fornecedor, Vendedor.

Talvez fosse ideal criar uma classe agregada, onde teria um tipo de dados.

[code]

class Cliente {

DadosBasicos basicos;

}

[/code][/quote]

Ai eh que ta, a interface Pessoa eh somente pra quando vc criar um novo personagem vc tem que implementar essa classe. Por exemplo, se vc quer adicionar um gerente, ai ficaria class Gerente implements Pessoa.
Os atributos vc pode colocar na classe PessoaContext, por exemplo, Se vc tem um atributo q todo mundo tem tipo “nome” ai ficaria :

class PessoaContext {

	Pessoa pessoa;
	String nome;
 
	
	PessoaContext(Pessoa pessoa) {
		this.pessoa = pessoa ;	
	}
	
	public String getType() {
		return pessoa.getType();		
	}	
	
	public void setName(String name) {
		this.nome = name;	
	}
	
	public String getName() {
		return nome;
	}	
	
}

ai vc faz :

PessoaContext pessoa1 = new PessoaContext( new Cliente() );			
pessoa1.setName("Daniel"); 		
System.out.println( pessoa1.getType() + "," + pessoa1.getName() ); 

Isso facilita porque qualquer novo atributo eh so adicionar na class contexto e vai surtir efeito em todos os seus personagens.

//Daniel

Entendido.

Qualquer modelagem que tenha uma entidade “Pessoa”, pra mim é duvidosa.
A modelagem tem que expressar seu domínio e uma entidade pessoa é maior generalização de uma entidade referente ao negócio que se pode ter… (aka apelação).

Se o seu fornecedor, por exemplo, é uma empresa e não um “ser-humano” (mesmo essa empresa tendo nome, email, telefone, etc) seu domínio começa a ficar estranho… e classe como “SuperPessoa” (a não ser que seja um super herói) ou outras como “PessoaContext” só mostram como sua modelagem esta se distanciando de um modelo mais like domain.

Se são dados cadastrais gerais que você quer utilizar para esses caras, use uma classe que os agrupe da melhor forma pra você. Ela pode se chamar DadosCadastrais, CadastroBasico ou qualquer coisa mais criativa e compor as classes Cliente, Vendedor ou Fornecedor. Se mudar um atributo de cadastro, mudou para todos os outros.

A idéia da interface para este caso, tbm não me parece interessante, não vejo pq manter um contrato de comportamentos para tipos diferentes como Fornecedor e Cliente. Cada classe tem seu comportamento e não pertencem a mesma hierarquia ou tipo. O fato é que um Vendedor “tbm” pode ser um Cliente. Eu prefereria compor o vendedor de um cliente, se caso ele tbm é um cliente. Um metodo como isCliente() pode ajudar a definir os comportamentos em seu vendedor. Uma outra apelativa, mais técnica do que teórica, é que vc vai encontrar sobre tudo algumas dificuldades em mapeamento com o banco (ORM) quando decide optar pelo polimorfismo baseado em interfaces.

[quote=Alessandro Lazarotti]Qualquer modelagem que tenha uma entidade “Pessoa”, pra mim é duvidosa.
A modelagem tem que expressar seu domínio e uma entidade pessoa é maior generalização de uma entidade referente ao negócio que se pode ter… (aka apelação).

Se o seu fornecedor, por exemplo, é uma empresa e não um “ser-humano” (mesmo essa empresa tendo nome, email, telefone, etc) seu domínio começa a ficar estranho… e classe como “SuperPessoa” (a não ser que seja um super herói) ou outras como “PessoaContext” só mostram como sua modelagem esta se distanciando de um modelo mais like domain.

Se são dados cadastrais gerais que você quer utilizar para esses caras, use uma classe que os agrupe da melhor forma pra você. Ela pode se chamar DadosCadastrais, CadastroBasico ou qualquer coisa mais criativa e compor as classes Cliente, Vendedor ou Fornecedor. Se mudar um atributo de cadastro, mudou para todos os outros.

A idéia da interface para este caso, tbm não me parece interessante, não vejo pq manter um contrato de comportamentos para tipos diferentes como Fornecedor e Cliente. Cada classe tem seu comportamento e não pertencem a mesma hierarquia ou tipo. O fato é que um Vendedor “tbm” pode ser um Cliente. Eu prefereria compor o vendedor de um cliente, se caso ele tbm é um cliente. Um metodo como isCliente() pode ajudar a definir os comportamentos em seu vendedor. Uma outra apelativa, mais técnica do que teórica, é que vc vai encontrar sobre tudo algumas dificuldades em mapeamento com o banco (ORM) quando decide optar pelo polimorfismo baseado em interfaces.[/quote]

Usei Pessoa porque o hugleo colocou Pessoa no inicio do topico, Mas na verdade nao precisa ser necessariamente pessoa pode ser outro nome, ainda pra complementar poderia ter um metodo que retorna se eh pessoa juridica ou fisica. Nao funcionaria?

//Daniel

Tudo bem, um vendedor pode assumir o papel de cliente. Mas ele pode ser os dois ao mesmo tempo?
Acredito que não, ele assumirá apenas um papel por transação.
Nesse caso, você pode implementar Strategy ( como sugerido acima ) da seguinte forma:

[code]public enum Papel {
FORNECEDOR {
public void doPapel() {
System.out.println(“Sou um fornecedor”);
}
},

CLIENTE {
	public void doPapel() {
		System.out.println("Sou um cliente");
	}
};

public abstract void doPapel();

}[/code]
Aí toda pessoa terá um papel ( bastante natural ):

public class Pessoa { // Dados da pessoa ( nome, endereço, etc ) Papel papel; // Toda pessoa tem um papel. }
Veja se essa solução pode ser adaptada pro seu caso.
[EDIT]
Não vejo problema nenhum em ter uma classe Pessoa no domínio. Caso você precise um dia distinguir pessoa física de pessoa jurídica, basta usar Strategy de novo e adicionar mais um atributo na classe pessoa para comportar seu tipo.
[/EDIT]
Abraços.

Um é distinto do outro, não tem pq distinguir… eles são assim por natureza. É como distinguir pedra de fruta em um ancestral em comum, apenas para utilizar de atributos como “descrição, nomeCientifico, blabla”.
Os dois não devem pertencer uma mesma hierarquia “Pessoa” (a jurídica e física), uma vez que Pessoa Jurídica não é necessariamente uma pessoa.


.( pessoa jurídica é a entidade abstrata com existência e responsabilidade jurídicas como, por exemplo, uma associação, empresa, companhia, legalmente autorizadas)

ja tinhamos discutido isso antes neste tópico.

[quote=tnaires]Tudo bem, um vendedor pode assumir o papel de cliente. Mas ele pode ser os dois ao mesmo tempo?
Acredito que não, ele assumirá apenas um papel por transação. [/quote]

Bom, não conheço mais detalhes da aplicação do hugleo, mas imagino que possa ser possível sim um vendedor ser um cliente em uma mesma transação. A empresa “Some CO” repassa produtos a vendedores autorizados, que dado a isso recebem descontos bem diferenciados além de detalhes tbm diferentes na nota. A entidade em questão esta em seu papel de cliente direto da fábrica mas tbm é vendedor, uma vez que esse é um dos critérios do negócio.

Não estou afirmando que qualquer modelagem descrita aqui é invalida, mas generalizar “Pessoas” por papéis ao meu ver é uma generalização difícil de se manter no sistema. Imagine a fluência do negócio no código, quando “ler” que pessoa.getNome() retornar “Petroquímica Danger LTDA”.

Claro que funcionar, de fato funciona. Mas não é algo natural.

PS: O link que postei do Sebraesp na minha máquina só abriu no IE, estranho…

[quote=Alessandro Lazarotti]Um é distinto do outro, não tem pq distinguir… eles são assim por natureza. É como distinguir pedra de fruta em um ancestral em comum, apenas para utilizar de atributos como "descrição, nomeCientifico, blabla".
Os dois não devem pertencer uma mesma hierarquia "Pessoa" (a jurídica e física), uma vez que Pessoa Jurídica não é necessariamente uma pessoa.


.( pessoa jurídica é a entidade abstrata com existência e responsabilidade jurídicas como, por exemplo, uma associação, empresa, companhia, legalmente autorizadas)

ja tinhamos discutido isso antes neste tópico.[/quote]
Entendo perfeitamente que são distintos. Quando falei "caso seja necessário distinguir um ou outro", na verdade quis dizer "caso seja necessário no seu sistema utilizar cpf ou cnpj". Quis dizer apenas que, dependendo do problema, pode ou não ser necessário atribuir CPF ou CNPJ às entidades.
Mas aí é que tá, no caso dele que outra palavra poderíamos utilizar para designar alguém que pode ter vários papéis no sistema? Pra encontrar um modelo melhor, precisaria conversar com os clientes dele.
O post do GUJ que você linkou critica a utilização de uma entidade Pessoa em uma hierarquia Pessoa -> Pessoa Fìsica -> Pessoa Jurídica. Com isso aí eu concordo plenamente.

Não sei se entendi muito bem o problema, mas talvez na entidade Transação ( por assim dizer ) deveríamos ter dois atributos distintos: cliente e vendedor, por exemplo. Assim, bastaria ter dois objetos distintos da mesma “pessoa” ( ou como queira ), porém cada um com um papel diferente.

Realmente, não tá muito legal. Mas não consigo pensar em uma generalização melhor sem conhecer o domínio do problema dele.

A entidade Pessoa pode ser mudado para um nome melhor como DadosBasicos, pois não refleteria nenhum pessoa em si.
Tanto é que até pensei em colocar esses atributos como uma agregação, mas essa classe teria métodos de pesquisa no banco de dados. Aqui teria que usar SQL (e não persistência).
Por isso Dados básicos teria métodos que inserisse dados, cliente teria métodos que inserisse dados. E algum método (Transacao) faria em join nisso dado commit.

Cliente é Cliente, Fornecedor é Fornecedor, Vendedor é Vendedor. Eles podem sim assumir eventualmente o mesmo pepel. Ex: Eu sou Um Vendedor e vou vender um produto pra mim mesmo.
O que temos também é a Entidade Usuário, responsável por fazer login no sistema, mas não necessarimaente é um Vendedor.

Pessoa Jurídica, e Física também deveram ser distinguidadas. Algumas funções somente ficarão disponíveis pra PJ por exemplo.

Esses dados em comuns incluíria CPJ e CNPJ que teria o valor NULL (no banco) dependendo do caso.

Linkei o topico Tarso, por causa daquele ultimo comentario sobre PF e PJ do post anterior.

Mas concordo que esta modelagem esta estranha e que o segredo deve estar mesmo em outros detalhes do domínio.

hmm, acho melhor vc realmente repensar no rumo que a modelagem esta tomando hugleo.

Se a classe Pessoa você cogita mudar ela para outro nome como “DadosBasicos”, contudo tbm gostaria de adicionar métodos de pesquisa no banco de dados, talvez muitos conceitos estejam emaranhados por aí.

Uma classe do tipo “DadosBasicos” não deveria ser responsável por acesso ao banco, é uma responsabilidade fora de sua fronteira. Eu imagino ela como uma classe anêmica que simplesmente serviria como composição para outras classes que possuem esses atributos em comum.

A questão de: “eu sou Um Vendedor e vou vender um produto pra mim mesmo.” parece se enquadrar bem no que o tarso falou, eles não compartilham papéis simultaneos em uma mesma transação.