Tip:
Highlight text to annotate it
X
Olá. Este vídeo vai apresentá-lo ao padrão de projeto Object Pool.
Para começar, vamos refletir sobre o que o padrão descreve:
como a aquisição e liberação de recursos, custosa, pode ser evitada através da reciclagem de recursos.
Se não ficou claro ainda, imagine uma aplicação onde muitos usuários acessam diariamente.
Cada requisição de usuário abre uma conexão num banco de dados, realiza uma operação, e a conexão é fechada.
Em aplicações triviais isso é o comum.
Pense, agora, na mesma aplicação com 100 usuários, ao mesmo tempo.
Agora, com 1000 usuários diferentes acessando simultâneamente.
Por mais que o desenvolvedor imagine que a abertura dessas conexões é um esforço mínimo,
ao potencializarmos a quantidade de requisições concorrentes, ficará evidente o quanto é custoso abrir,
fechar e simplesmente descartar esses objetos, esses recursos.
O Pooling feito neste padrão é destinado a aplicações onde existam um alto custo de instanciação
em uma classe, e muito uso dessas instanciações.
Vamos nos ater a esse exemplo das instanciações de conexões com um banco de dados.
Normalmente, se utiliza uma classe, ou um conjunto delas, para realizar as operações na base de dados.
Classes desse tipo, que somente realizam atividades comuns e simples, são fortes candidatas a serem reutilizadas.
O reaproveitamento dessas instâncias economiza os custos de criação, e pode auxiliar no controle desses recursos, ou instâncias.
O Object Pool tem uma arquitetura simples.
Uma classe abstrata ReusablePool, que concentrará as requisições à outra classe,
ou interface, apelidada de Reusable.
Uma classe Cliente (Client) representa a parte da aplicação que fará as requisições ao Pool.
Este, por sua vez, controla os recursos, instâncias de Reusable, disponíveis e em uso.
Para facilitar o entendimento, usemos uma abstração simples de "empréstimo" de objetos:
Um armário contém canetas, e você, e outras várias pessoas querem usá-las.
O Pool (armário) cede as canetas conforme são solicitadas
e os usuários, assim que terminarem o uso, devem devolvê-las ao armário
Assim podemos ter uma idéia do mecanismo.
Uma caneta emprestada, está indisponível para os outros além daquele que a solicitou.
O padrão exprime a definição de políticas, como:
O que deve acontecer quando um Reusable ficar sem uso por muito tempo?
Há limite para a quantidade de instâncias que o Pool conterá?
O Pool deve ser inicializado com alguma quantidade de instâncias?
Estas políticas variam com a aplicabilidade do padrão à situação-problema do projeto.
Ou seja, você, desenvolvedor, deve ter em mente questões como essas para cada Pool criado.
Para que haja mais coerência no uso do Padrão, é sugerido que o Pool seja um Singleton.
Ou seja, que somente haja, para todo o sistema, uma única instancia da classe.
Pode ser aplicado um outro padrão, o Factory,
para adiconar controle às instanciações de recursos de classes diferentes.
Para praticarmos, vamos nos inserir numa situação problema:
Teremos alguns leitores de arquivos.
Para exibir o conteúdo de texto desses arquivos na tela.
Alguns clientes solicitarão esses leitores a um Pool, para que façam aquilo que lhes é necessário.
Minimizando e controlando a quantidade de instanciações.
Devemos lembrar que o Pool só entrega as instâncias, e não há controle sobre o que as classes entregues fazem.
Esse tipo de definição, do comportamento da classe, visibilidade de métodos e operadores, deve ser feito como se o Pool não existisse.
Primeiro, vejamos nossa Entidade reusável, o Reader.
Para o nosso exemplo, o Reader possui um identificador e é composto de um leitor BufferedReader, para a leitura dos arquivos.
Não há métodos especiais nesta classe além dos definidos neste diagrama, que supomos serem autoexplicativos.
Passemos ao cliente.
Vamos verificar, agora, que a classe cliente, do nosso exemplo, não possui nada além do main, para fazer o uso do Pool e dos Reusable.
Vamos ver o Pool, agora.
Observe que no ReaderPool, há o uso do padrão singleton, através do atributo instance, do construtor privado e do método estático getInstance();
Para o padrão Object Pool, utilizamos: Duas estruturas para armazenar os Readers disponíveis e em utilização.
Respectivamente, unlocked, e locked.
E um último atributo, para controlar a quantidade de máxima de Readers nas estruturas de armazenamento.
Quanto aos métodos que o Padrão implementa estão:
O check In, que faz a devolução de um objeto emprestado à estrutura dos Readers dsponíveis;
O check Out, que avalia se existem Readers, cria instâncias, ou as empresta a quem solicitar;
O create reader, que faz o esperado.
Seria aqui uma boa intervenção para o padrão Factory, se houvesse mais de um tipo de Reader.
Por fim, os métodos de listagem das estruturas de armazenamento dos Readers.
No main do Cliente, há a chamada à instancia do Pool, inicializando-o.
O cliente então chama o check Out, ou o "empreste-me", para receber um leitor, Reader.
E em seguida lê o arquivo definido no Reader;
Depois solicita outro Reader, um segundo.
Em seguida, devolve o segundo Reader e o primeiro, na sequência.
Mais uma vez solicita um reader ao Pool.
Perceba aqui que as três instanciações ocorreram.
Fazemos a leitura do arquivo; Solicitamos o empréstimo. Pela rotação na estrutura, estamos com o READER2 à frente.
Fazemos novamente a leitura do arquivo;
O Reader é mais uma vez, devolvido.
Feita a devolução do Reader, listamos as estruturas que contém os Readers indispoíveis (BLOQUEADOS) e disponíveis (DESBLOQUEADOS);
Depois efetuamos a devolução do último Reader indisponível (bloqueado).
Olá. vamos apresentar a solução de Object Pool utilizada para o exercício.
A solução contém somente as classes da arquitetura básica do padrão: o Cliente, o Reader e o Reader Pool.
Vou apresentar pra vocês as duas últimas classes. A classe Cliente, com o main dela já foi apresentada anteriormente.
E não é nada diferente. Então vamos apresentar pra vocês o Reader e o Reader Pool.
há um arquivo saída.txt que eu já oriento como fazer com ele (copie-o para a raiz do "C:\").
Na classe Reader, que é uma classe bem simples, existe um método, lerArquivo, que vai fazer a leitura do arquivo e jogar na tela.
Percebam que ela é uma classe bem simples e ela não tem nada de especial
Já a classe ReaderPool, que tem toda a lógica, como dissemos, o padrão empregado nela é o padrão Singleton,
como é possivel ver pelo atributo privado, instance, o método estático getInstance e o construtor privado,
Ela tem duas unidades de armazenamento, dois ArrayList, um chamado locked e o outro unlocked,
Para que possamos gerenciar os Readers que foram emprestados e os que estão disponíveis.
Aí vamos entrar na questão da política: por exemplo, nós temos um atributo, que é o maxReaders,
que vai controlar a quantidade máxima de Readers no meu Pool de unlocked (disponíveis).
Ou seja, eu vou controlar a quantidade de Readers disponíveis para serem emprestados.
Eu poderia utilizar isso para controlar a quantidade total de Readers no nosso Sistema. É uma questão de política (diretiva).
Eu poderia também não ter esse atributo,
Então, vamos entender aqui os dois métodos, basicamente, que tratam do padrão.
Que seriam: O checkOut, que vai permitir que alguém adquira um Reader
E ele faz uma pergunta simples: se o Pool de disponíveis estiver vazio, ele cria mais 3,
faz um for aqui, eu poderia colocar para criar somente um, não tem problema,
Depois ele pergunta se o Pool tem uma quantidade de elementos disponíveis
Se tiver, ele vai pegar o primeiro elemento - a partir do iterator dele - vai tirar da lista de disponíveis,
vai passar ele para a lista de indisponíveis, vai imprimir o que aconteceu e retornar o "carinha",
É basicamente isso, para o método da devolução de empreástimo,
É bem simples: você passa o Reader, ele vai tirar o Reader da lista de bloqueados e passar para a lista de desbloqueados
Tornando o Reader de novo disponível para ser utilizado.
Depois disso não tem segredo: o createReader faz o que foi mandado pra ele, cria um Reader novo,
E aqui seria nosso ponto de inserção do padrão Factory, por exemplo, seria aqui que eu controlaria: eu passaria um tipo pra ele...
"vamo rodar" aqui, a classe Cliente, e observar a saída que isso vai produzir.
Percebam que, conforme ocorreram os empréstimos ele foi informando: "o READER1 acaba de ser emprestado"...
Quando eu fiz a aquisição do segundo, e depois eu fiz a devolução dos dois, ele informa
que ocorreu essa devolução.
É possível acompanhar "bem tranquilo", é só acompanhar os "System.out.println()" na classe Reader e na ReaderPool
Na classe Reader, eu só tenho a informação do início da leitura,
na classe ReaderPool, nós colocamos, as informações de empréstimo.
"Ele acabou de ser emprestado", "acabou de ser devolvido", é bem simples,
Mas o padrão é bem legal, bem fácil de implantar,
A questão com ele, é só ter o cuidado para implementar bem essas políticas, e não causar um impacto negativo no seu sistema.
Este padrão causa impactos mais sensíveis em aplicações onde é crítica a velocidade de instanciação de objetos, cuja criação é custosa.
Vide casos de dependência e composição multipla.
Podem haver problemas sérios quanto ao controle das políticas.
É necessário que quem implante um Pool de objetos tenha claramente a necessidade de estabelecer,
bem, o que acontece com os objetos, durante a criação,
se expiram com o tempo, o que ocorre na falta de objetos disponíveis, etc.
Bem, ficamos por aqui. Muito obrigado.