Como o tópico sugere este é um problema que envolve Design Pattern, vou dar um exemplo meramente ilustrativo de um sistema com conexão ao banco de dados, tenham em mente que o foco é a lógica.
Quando fazemos um sistema que se conecta a um banco de dados é comum que se faça da classe de conexão um Singleton, dessa forma mais elegante que um estático “global”.
Imagine então que temos uma classe qualquer com o método salvar(), esse método usa o Singleton da para obter a única instancia de conexão existente e salvar.
O problema é: e se eu quiser várias instancias dessa conexão? Não duas ou três, mas uma quantidade indeterminada. Bom, filosofei comigo mesmo um bocado, pesquisei alguns padrões que não estavam tão frescos na minha memória, mas sinto que deixei algo escapar.
Até poderia fazer um Builder, mas não seria nada elegante, pois se trata de um conjunto muito grande de classes e a maioria não tem nada em comum umas com as outras, a solução provisória que encontrei foi simplesmente passar a instâcia da conexão pelo construtor de cada classe toda vez que uma fosse criada.
Mas algumas classes tem relações com outras classes, então novamente deveriam repassar a instância da conexão vigente para as classes subsequentes, enfim, esta solução não é nada elegante, será que alguém pode me dar uma luz? Exite algum método decente de fazer isto?
Você precisa de um pool de conexões.
Veja o c3p0, nano-pool, etc…
Lembre-se: use singletons apenas na presença de adultos (não se ofenda, por favor).
Fazer adaptações para que se tenha mais de uma instância leva a aberrações documentadas como anti-padrões conhecidas como “Doubleton” e assim por diante.
Obrigado pela iniciativa, mas o sistema em questão é apenas hipotético, de fato meu problema se trata de conexões, mas não com um simples banco de dados, é um protocolo próprio, não há framework que possa me ajudar.
Embora eu já tenha usado o c3p0 em um projeto que participei, não sei se me recordo com clareza, se não me engano o objetivo dele é manter certo número de conexões ativas de acordo com a demanda, certo? Mas para que fique claro no exemplo que dei imagine que cada conexão é feita a um servidor diferente, ou o usuário e senha para o acesso seja diferente, embora utilize o mesmo conjunto de classes as instâncias não são equivalentes, é isto o que quero fazer.
Cara, você travou no problema porque você quer encontrar um pattern logo de cara, e isso não funciona. Essa abordagem de pensar antecipadamente no pattern é prejudicial. Esse exemplo de encapsular uma conexão com o banco em uma Singleton é terrível (a não ser que todo o sistema tenha uma única thread).
Ao invés de tentar encontrar um pattern, apenas aplique os princípios de OO: programe para interfaces, favoreça composição ao invés de herança, etc. Assim é muito mais provável você chegar em um solução simples, que resolva o seu problema do que tentar forçar um pattern conhecido.
Agradeço novamente a iniciativa e me desculpe por não me expressar com clareza, deixei de dar alguns detalhes cruciais sobre o caso.
O intuito do meu projeto é justamente trabalhar com multi-theading, citei o Singleton pois seria uma solução plausível se meu projeto fosse mono-thread e sequencial, já que não é…
Talvez um exemplo esclareça melhor as coisas:[code]class Sessao {
public Sessao(String servidor, String usuario, String senha) {
//…
}
//...
}
class Pessoa {
public Pessoa(Sessao sessao, /* outros parâmetros…*/) {
this.sessao = sessao;
//…
}
//...
}
class Grupo {
private List pessoas;
public Grupo(Sessao sessao, /* outros parâmetros...*/) {
this.sessao = sessao;
for (int i = 0; i < /* máximo de pessoas*/; i++) {
dados = sessao.comando(Sessao.OBTER_DADOS_PESSOA, /* parâmetos */);
Pessoa pessoa = new Pessoa(sessao, dados);
pessoas.add(pessoa);
}
//...
}
//...
}
class App {
public static void main(String[] args) {
Sessao sessaoA = new Sessao(servidorA, usuarioA, senhaA);
Grupo grupoX = new Grupo(sessaoA, /…/);
Grupo grupoY = new Grupo(sessaoA, /…/);
Grupo grupoZ = new Grupo(sessaoA, /…/);
Sessao sessaoB = new Sessao(servidorB, usuarioB, senhaB);
Grupo grupoM = new Grupo(sessaoB, /*...*/);
Grupo grupoN = new Grupo(sessaoB, /*...*/);
grupoX.getPessoas();
//...
grupoM.getPessoas();
//...
}
}[/code]Neste caso toda santa classe que criar vou ter que ficar passando a sessão, poderia fazer mais OO, em vez da sessão retornar os parâmetros para criação de novas classes, retornar as próprias, mas tem dois porém:
[list]Ainda assim o primeiro parâmetro do construtor iria ser a sessão;[/list][list]Existem uma infinidade de classes, o código da classe Sessão iria ser enorme, desorganizado e asqueroso.[/list]
Alguém tem alguma ideia melhor para deixar este código menos repetitivo? Ou não tem jeito, tem que ser assim mesmo?
[quote=tiagopimenta]Agradeço novamente a iniciativa e me desculpe por não me expressar com clareza, deixei de dar alguns detalhes cruciais sobre o caso.
O intuito do meu projeto é justamente trabalhar com multi-theading, citei o Singleton pois seria uma solução plausível se meu projeto fosse mono-thread e sequencial, já que não é…
Talvez um exemplo esclareça melhor as coisas:[code]class Sessao {
public Sessao(String servidor, String usuario, String senha) {
//…
}
//...
}
class Pessoa {
public Pessoa(Sessao sessao, /* outros parâmetros…*/) {
this.sessao = sessao;
//…
}
//...
}
class Grupo {
private List pessoas;
public Grupo(Sessao sessao, /* outros parâmetros...*/) {
this.sessao = sessao;
for (int i = 0; i < /* máximo de pessoas*/; i++) {
dados = sessao.comando(Sessao.OBTER_DADOS_PESSOA, /* parâmetos */);
Pessoa pessoa = new Pessoa(sessao, dados);
pessoas.add(pessoa);
}
//...
}
//...
}
class App {
public static void main(String[] args) {
Sessao sessaoA = new Sessao(servidorA, usuarioA, senhaA);
Grupo grupoX = new Grupo(sessaoA, /…/);
Grupo grupoY = new Grupo(sessaoA, /…/);
Grupo grupoZ = new Grupo(sessaoA, /…/);
Sessao sessaoB = new Sessao(servidorB, usuarioB, senhaB);
Grupo grupoM = new Grupo(sessaoB, /*...*/);
Grupo grupoN = new Grupo(sessaoB, /*...*/);
grupoX.getPessoas();
//...
grupoM.getPessoas();
//...
}
}[/code]Neste caso toda santa classe que criar vou ter que ficar passando a sessão, poderia fazer mais OO, em vez da sessão retornar os parâmetros para criação de novas classes, retornar as próprias, mas tem dois porém:
[list]Ainda assim o primeiro parâmetro do construtor iria ser a sessão;[/list][list]Existem uma infinidade de classes, o código da classe Sessão iria ser enorme, desorganizado e asqueroso.[/list]
Alguém tem alguma ideia melhor para deixar este código menos repetitivo? Ou não tem jeito, tem que ser assim mesmo?[/quote]
Bom, deixa ver se eu entendi: pelo jeito, a classe Sessao representa alguma conexão com um serviço remoto, certo ? E dentro do seu domínio, diversos tipos de classe precisam acessar esses serviços, correto ?
Ok, agora ficou mais claro.
Mas ainda não ficou claro qual o critério de distribuição das sessões para cada grupo.
Seria possível descrever melhor como o algoritmo deveria funcionar nesse quesito?
Quanto mais informações melhor, tenho algumas ideias que podem ajudar.
[quote=rmendes08]Bom, deixa ver se eu entendi: pelo jeito, a classe Sessao representa alguma conexão com um serviço remoto, certo ? E dentro do seu domínio, diversos tipos de classe precisam acessar esses serviços, correto ?[/quote]É isto mesmo, só não me agrada a repetição de código, será que tem como otimizar isto?
[quote=Tchello]Ok, agora ficou mais claro.
Mas ainda não ficou claro qual o critério de distribuição das sessões para cada grupo.
Seria possível descrever melhor como o algoritmo deveria funcionar nesse quesito?
Quanto mais informações melhor, tenho algumas ideias que podem ajudar.[/quote]Cada classe que fizer comunicação com servidor vai ter que ter a sessão a que pertence atrelada, Grupo e Pessoa foi só um exemplo do que acontece no meu código: relações, há muitas delas, e assim como Grupo fez uso da sessão, Pessoa também fará, por isso o parâmetro sessão é passado, existem relações de um para um, de muitos para um, de um para muitos, assim como num banco de dados relacional, o grande problema é que todas essas classes tem o primeiro parâmetro do construtor obrigatoriamente sendo a sessão, portanto sempre que forem criadas novas classes devo me preocupar em passar a sessão daquele segmento.
[quote=tiagopimenta][quote=rmendes08]Bom, deixa ver se eu entendi: pelo jeito, a classe Sessao representa alguma conexão com um serviço remoto, certo ? E dentro do seu domínio, diversos tipos de classe precisam acessar esses serviços, correto ?[/quote]~
É isto mesmo, só não me agrada a repetição de código, será que tem como otimizar isto?
[/quote]
Bom, uma solução possível é utilizar um conteiner de injeção de dependências como o Spring, Guice ou CDI. Na verdade, é algo muito parecido com o que você está fazendo, com a diferença de os frameworks usam um pouco de “mágica” para você não ficar declarando a dependência no construtor.
Uma outra solução, para que você não tenha que apelar para frameworks de terceiros é criar um factory method estático. Algo como:
Sessao sessao = Sessao.buildSessao();
ou seja, é parecido com o singleton, com a diferença de que você não vai controlar o número de instâncias. Você pode também delegar a tarefa para um outro objeto:
Ou seja, existem 1001 maneiras para criar um objeto, acho que a grande questão é: como um objeto qualquer do sistema sabe qual configuração ele deve utilizar ?
Enfim, a conclusão que eu estou chegando é que:
você tem várias configurações de serviço para usar na mesma aplicação
você deve distribuir essas configurações para objetos seguindo algum critério
ou seja, temos aí uma responsabilidade única, mas não temos nenhuma classe assumindo essa responsabilidade, estou correto ?
[quote=rmendes08]Ou seja, existem 1001 maneiras para criar um objeto, acho que a grande questão é: como um objeto qualquer do sistema sabe qual configuração ele deve utilizar ?[/quote]Pois é, tirou as palavras da minha boca, o único meio que encontrei de fazer isso foi passando adiante a mesma instância criada nas relações anteriores.
[quote=rmendes08]ou seja, temos aí uma responsabilidade única, mas não temos nenhuma classe assumindo essa responsabilidade, estou correto ?[/quote]Acho que entendi sua dúvida, você quer saber onde tudo começa certo? Bom, na verdade tudo parte da Sessão, alguns objetos chaves podem ser obtidos diretamente dela, e os demais são criados sob demanda.
Em uma hierarquia de empresas imagine que eu sei que existe somente uma empresa para cada sessão, mas para cada usuário e senha usados na conexão é retornada uma empresa diferente, eu poderia fazer algo assim:Empresa empresaA = sessaoA.getEmpresa(); // a própria sessão faz o new Empresa(this /* a própria sessão */, /* outros parâmetros */)
// A empresa tem vários setores
List<Setor> setoresEmpresaA = empresaA.getSetores(); // A empresa obtém os dados do setor utilizando a sessão e faz o new Setor(sessaoAtual, /* outros parâmetros */)
// Cada setor tem pelo menos um gerente, primeiro setor seria:
List<Gerente> gerentesSetor0EmpresaA = setoresEmpresaA.get(0).getGerentes(); // O setor obtém os dados do gerente utilizando a sessão e faz o new Gerente(sessaoAtual, /* outros parâmetros */)
// Cada gerente coordena vários projetos:
List<Projeto> progetosGerente0Setor0EmpresaA = gerentesSetor0EmpresaA.get(0).getProjetos(); // O gerente obtém os dados do projeto utilizando a sessão e faz o new Projeto(sessaoAtual, /* outros parâmetros */)
// Trabalham vários funcionários em cada projeto:
List<Funcionario> funcionariosProjeto0Gerente0Setor0EmpresaA = progetosGerente0Setor0EmpresaA.get(0).getFuncionarios(); // O projeto obtém os dados do funcionário utilizando a sessão e faz o new Funcionario(sessaoAtual, /* outros parâmetros */)
// E tudo de novo pode acontecer para a sessaoB, pela EmpresaB, cujas instâncias de sessão não podem ser confundidas.
Sendo assim é como se eu tivesse cascateando a instância da sessão atual para todas as demais classes que devem fazer uso do mesmo.
Cara, acho que a grande dificuldade aí é que você está misturando a infra-estrutura da sua aplicação com suas regras de negócio. A única coisa que eu entendi até agora é que você tem um serviço remoto, que pode ser oferecido por vários servidores e que existem diversos objetos que fazem uso desse serviço. No caso, cada Empresa tem uma configuração, e os objetos pertencentes à Empresa tem que usar as configurações da empresa, é isso ?
[quote=rmendes08]Cara, acho que a grande dificuldade aí é que você está misturando a infra-estrutura da sua aplicação com suas regras de negócio. A única coisa que eu entendi até agora é que você tem um serviço remoto, que pode ser oferecido por vários servidores e que existem diversos objetos que fazem uso desse serviço. No caso, cada Empresa tem uma configuração, e os objetos pertencentes à Empresa tem que usar as configurações da empresa, é isso ?[/quote]Isto mesmo, portanto está tudo interligado, os objetos pertencentes aquela empresa vão fazer comunicação pela mesma conexão, se uma outra conexão for estabelecida provavelmente será retornada uma empresa diferente, apesar dos métodos, e relações com outros “membros”, serem os mesmos, deve fazer uso da conexão a qual a empresa foi criada.
O jeito que fiz foi o único que consegui imaginar de forma com que fique simples e um tanto organizado, mas como disse antes, será que não existe alguma maneira, alguma logica que me ajude a deixar este código menos repetitivo? Será que é fácil fazer a reflexão das classes, a “mágica” que você mencionou, será que compensa? Só resta esta alternativa mesmo?
[quote=tiagopimenta][quote=rmendes08]Cara, acho que a grande dificuldade aí é que você está misturando a infra-estrutura da sua aplicação com suas regras de negócio. A única coisa que eu entendi até agora é que você tem um serviço remoto, que pode ser oferecido por vários servidores e que existem diversos objetos que fazem uso desse serviço. No caso, cada Empresa tem uma configuração, e os objetos pertencentes à Empresa tem que usar as configurações da empresa, é isso ?[/quote]Isto mesmo, portanto está tudo interligado, os objetos pertencentes aquela empresa vão fazer comunicação pela mesma conexão, se uma outra conexão for estabelecida provavelmente será retornada uma empresa diferente, apesar dos métodos, e relações com outros “membros”, serem os mesmos, deve fazer uso da conexão a qual a empresa foi criada.
O jeito que fiz foi o único que consegui imaginar de forma com que fique simples e um tanto organizado, mas como disse antes, será que não existe alguma maneira, alguma logica que me ajude a deixar este código menos repetitivo? Será que é fácil fazer a reflexão das classes, a “mágica” que você mencionou, será que compensa? Só resta esta alternativa mesmo?[/quote]
Mas peraí, quem vem primeiro, o ovo ou a galinha ? Por exemplo, é a partir do código da empresa que eu obtenho os dados da conexão ou eu recebo o código da empresa depois que eu estabeleci a conexão ? Seja mais claro por favor, essas configurações de conexão vem de um arquivo ? De uma tabela no banco de dados ? O usuário informa ?
Bom não sei qual o ecossitema da tua aplicação, mas tive um problema semelhante de protocolo próprio, e recomendo utilização de JCA, pois permite que voce tenha um controle melhor sobre esse protocolo e separe isso da aplicação
[quote=rmendes08]Mas peraí, quem vem primeiro, o ovo ou a galinha ? Por exemplo, é a partir do código da empresa que eu obtenho os dados da conexão ou eu recebo o código da empresa depois que eu estabeleci a conexão ? Seja mais claro por favor, essas configurações de conexão vem de um arquivo ? De uma tabela no banco de dados ? O usuário informa ?[/quote]O usuário fornece os dados das conexões, plural pois ele pode fornecer uma ou várias conexões, a partir de uma conexão estabelecida eu obtenho os dados da empresa, e aí começa aquele rolo todo que já mencionei.
[quote=leciusjm]Bom não sei qual o ecossitema da tua aplicação, mas tive um problema semelhante de protocolo próprio, e recomendo utilização de JCA, pois permite que voce tenha um controle melhor sobre esse protocolo e separe isso da aplicação[/quote]Vou dar uma estudada então, não conheço esta ferramenta.
Cara
caindo de paraqueda ake … (n li todos os post …) + pelo que entendi segue sugestões…
sugestão 1:
na consulta usar “select for update”, isso da um lock no banco …
isso funciona para formularios tipo quando alguem consulta um registro e pode efetuar a alteração.
sugestão 2:
a grosso modo "flag"
para quando a execução fique fora de um update adiciona um campo que seja a flag, wlp pode ser pego o nome da instancia que está rodando, acredito que com os demais servidores tambem possa… wlp é um property …
System.getProperty("weblogic.Name")
da um update no registro setando o nome da instancia com isso caso outra instancia tente ler o registro ela ja estará ciente que o registro esta sendo lido
[quote=tiagopimenta][quote=rmendes08]Mas peraí, quem vem primeiro, o ovo ou a galinha ? Por exemplo, é a partir do código da empresa que eu obtenho os dados da conexão ou eu recebo o código da empresa depois que eu estabeleci a conexão ? Seja mais claro por favor, essas configurações de conexão vem de um arquivo ? De uma tabela no banco de dados ? O usuário informa ?[/quote]O usuário fornece os dados das conexões, plural pois ele pode fornecer uma ou várias conexões, a partir de uma conexão estabelecida eu obtenho os dados da empresa, e aí começa aquele rolo todo que já mencionei.
Bom, eu vou escrever um pouco agora, mas acho que a discussão vale a pena …
Como eu havia dito, o problema maior que eu vi é a necessidade de separar as regras de negócio da infra-estrutura da aplicação. Objetos como: Empresa, Funcionario, Projeto, Gerente, etc. estão relacionadas a regras de negócio, e é interessante mantê-las isoladas da infra-estrutura, no caso, as informações sobre conexões, sessões, protocolos de comunicação, etc. Assim, na minha opinião, o que está faltando é um conjunto de classes que tomem a responsabilidade de gerenciar essa infra-estrutura. Como eu faria:
1 - criaria uma interface de serviços para que as classes de domínio/regras de negócio possam acessar os serviços remotos, mas de maneira abstrata, e aí sim, para implementar esse serviço, eu usaria a sua classe de sessão:
public interface Service{
List<Funcionario> listaFuncionarioBy(Projeto p);
List<Funcionario> listaFuncionarioBy(Empresa e);
//aqui você declara os seus métodos de negócio,
//se a interface ficar muito grande, você pode quebrá-la em interfaces menores
}
public class ServiceSessao implements Service{
private Sessao sessao;
public FuncionarioServiceSessao(Sessao s){
this.sessao = s;
}
public List<Funcionario> listFuncionarioBy(Projeto p){
return (List<Funcionario>) sessao.execute(Command.LIST, new Object[]{p}); //isso é só um exemplo, não conheço seu protocolo
}
}
Perceba que as classes que usam o serviço não conhecem como o serviço é implementado. Você pode até implementá-los com outros protocolos que as classes que utilizam o serviço não precisarão ser alteradas. Isso resguarda suas classes de negócio inclusive de mudanças no protocolo.
2 - Precisamos agora achar uma maneira de fornecer o serviço apropriado para cada objeto. Assim, os objetos de negócio não precisam receber a Sessao no construtor, nem o serviço. Ou seja, o fato de usar um serviço externo não ficará explícito na interface pública do objeto, que é o que nos interessa.
public class ServiceLocator{
private static ServiceLocator instance; //esse cara pode ser Singleton
public static getInstance(){ if( instance == null) instance = new ServiceLocator(); return instance }
private Map<Empresa, Sessao> sessoesPorEmpresa; //você disse que é uma sessão por empresa, materemos isso numa Map
public Service locateServiceFor(Empresa e){
Sessao s = sessoesPorEmpresa.get(e);
return new ServiceSessao(s);
}
public Service locateServiceFor(Funcionario func){
Empresa e = func.getGerente().getProjeto().getEmpresa();
return locateServiceFor(e);
}
//precisaremos de um método de localização por tipo de objeto de negócio
//mas isso pode ser melhorado com alguma "mágica"
public void criarSessao(Configuracao c){
//aqui, a partir da Configuracao você cria a sessão e coloca na Map
}
}
class Configuracao{
String ip;
String porta;
String user;
String senha;
//... ou seja, quaisquer dados de conexão do seu protocolo
}
Bom, só a título de referência, isso representa dois padrões em conjunto: o ServiceFacade e o ServiceLocator, mas veja bem, eu não sai “caçando” os padrões. À medida que você me passou informações eu apliquei princípios até chegar nele. E isso não quer dizer que vai resolver seu problema. Espero que ajude.
class Gerente{
public void passarServico(){
Service service = ServiceLocator.getInstace().findServiceFor(this); //supondo que há um método para gerente
List<Funcionario> listaFunc = service.listFuncionarioBy(this.getEmpresa());
for(Funcionario f : listaFunc){ f.trabalhe() );
}
}
No item 1 basicamente você está criando uma camada a mais para separar tudo que tiver que comunicar com o servidor, ok, isto pode ser feito sem problemas.
O grande protagonista do espetáculo está no item 2, se entendi certo basicamente estaremos obtendo o serviço a partir da própria instância do objeto que está requisitando-o, interessante, não tinha pensado nesta possibilidade, embora eu estranhe a idéia já que nunca tinha visto código parecido, talvez isto resolva e de quebra deixe meu código mais organizado, vou experimentar.