Sempre que possível, irei tratar aqui, neste blog, de assuntos relacionados à melhor organização e reutilização de código, das boas práticas de programação, da Programação Orientada a Objetos e assim por diante. Sendo assim, a partir do momento que passamos a prestar mais atenção nos padrões de projeto (Design Patterns), fica fácil perceber que eles nos ajudam e muito nesta direção.

Neste post, apresentarei o padrão Singleton.

Cada design pattern tem um propósito, um objetivo. No caso do Singleton, o padrão garante a existência de apenas uma instância de uma classe, mantendo um ponto global de acesso ao seu objeto. Para facilitar o entendimento, vamos a um exemplo prático.

Construindo o exemplo

Em nossos projetos, muitas vezes temos algumas tarefas repetitivas, como por exemplo, o acesso a dados de um usuário logado ou o acesso a um banco de dados. Ok, eu sei que não precisamos ficar nos conectando ao banco a todo momento. Para isso, é mais comum a utilização de um componente de acesso ao banco num Datamodule e a conexão à base de dados se fazendo necessário somente na abertura do programa. Mas por ser mais simples, irei utilizar esta ideia, ou seja, a de obter a conexão de um banco de dados via código.

Para o exemplo, irei utilizar o bom e velho IBX (mas poderia ser qualquer outro conjunto de componentes de acesso, como o dbExpress e ADO).

No Delphi (estou utilizando agora o XE2), crie uma nova aplicação. No formulário em branco coloque três botões, dois edits e um memo, deixando o formulário parecido com este:

Crie uma nova unit com o nome de uConexao. Salve.

Nesta nova unit, vamos criar uma classe chamada TConexao:

[sourcecode language=”delphi”]
unit uConexao;

interface

uses IBDatabase, System.Classes, System.SysUtils;

type
TConexao = class(TIBDatabase)
private
class var FInstancia: TConexao;
class function getInstancia: TConexao; static;
public
class property Banco: TConexao read getInstancia;
end;

implementation

{ TConexao }

uses forms;

class function TConexao.getInstancia: TConexao;
begin
If not Assigned(FInstancia) Then
FInstancia := TConexao.Create(Application);

Result := FInstancia;
end;

[/sourcecode]

No código acima, criamos uma classe chamada TConexao derivada de TIBDatabase. Ela tem uma proriedade (Banco) que, através do método getInstancia, permitirá a criação de apenas um objeto de conexão. Caso já exista, ele simplesmente utiliza o objeto já criado através da variável FInstancia. Perceba que utilizamos propriedade e método de classe, ou seja, não será necessário instanciar o objeto previamente através do método Create.

Pronto, agora que temos a nossa conexão, vamos voltar para o formulário e inserir o seguinte código no primeiro botão:

[sourcecode language=”delphi”]
TConexao.Banco.DatabaseName := ‘c:\meubanco.fdb’;
ShowMessage(‘Banco de dados: ‘+TConexao.Banco.DatabaseName);
[/sourcecode]

Adicione ao uses a unit uConexao.

No código, através da propriedade Banco, que nos retorna a conexão, definimos o nome do banco de dados. Feito isso, mostramos uma mensagem.

Note que, toda vez que acessamos a propriedade Banco, seja na hora de definir o nome do banco de dados seja na hora de mostrar a mensagem, o método getInstancia é acionado.

Neste caso, na primeira vez o objeto é criado através do método Create. Já na segunda, como já existe o objeto, simplesmente retornamos a variável FInstancia.

Vamos para o segundo botão:

[sourcecode language=”delphi”]
TConexao.Banco.Params.Add(‘user: usuario’);
TConexao.Banco.Params.Add(‘password: senha’);
TConexao.Banco.LoginPrompt := False;
ShowMessage(‘Configurações adicionais inseridas na conexão!’);
[/sourcecode]

Aqui, simplesmente adicionamos parâmetros à conexão e definimos para não mostrar a tela de login.

E por fim, no terceiro botão mostramos os dados da conexão:

[sourcecode language=”delphi”]
edNomeBd.Text := TConexao.Banco.DatabaseName;
if TConexao.Banco.LoginPrompt then
edPrompt.Text := ‘Sim’
else
edPrompt.Text := ‘Não’;
edParametros.Lines.Assign(TConexao.Banco.Params);
[/sourcecode]

Ao executar a aplicação, o objeto será criado no primeiro clique. Já nos demais, apenas será retornado a instância do objeto já criado. E ao finalizar a aplicação, ele será destruído.

Este é um exemplo simples, mas a ideia é a mesma, mesmo para usos mais complexos. Eu não cheguei a me conectar efetivamente no banco de dados, mesmo porque este banco não existe na realidade. Vale lembrar também, que os códigos mostrados neste post serviram apenas para exemplificar o uso do padrão, como por exemplo, os códigos dos botões 1 e 2. O motivo de estarem em locais diferentes é para simular as diversas tentativas de conexões que podem ocorrer num determinado espaço de tempo.

Veja, no início eu citei o acesso de usuários. Você poderá criar um singleton do usuário e controlar desde permissões a logs (operações efetuados pelo usuário no sistema). Tudo isso, mantendo apenas um objeto instanciado.

Fico por aqui, obrigado e espero que este artigo seja útil à comunidade.

One thought on “Design Pattern: Singleton”

  1. Gostaria de lembrar que, para ambientes multi-thread, você poderá usar uma seção crítica. Para isso, será necessário criar uma instância global de System.SyncObjs.TCriticalSection. Como por exemplo:

    [sourcecode language=”delphi”]
    var
    MyLock : TCriticalSection;

    implementation

    uses forms;

    class function TConexao.getInstancia: TConexao;
    begin
    If not Assigned(FInstancia) Then
    begin
    MyLock.Acquire;
    try
    If not Assigned(FInstancia) Then
    FInstancia := TConexao.Create(Application);
    finally
    MyLock.Release;
    end;
    end;
    Result := FInstancia;
    end;
    [/sourcecode]

    Você deve inicializar e destruir a instância:

    [sourcecode language=”delphi”]
    initialization
    MyLock := TCriticalSection.Create;
    finalization
    MyLock.Free;
    [/sourcecode]

    Assim, o seu singleton se torna thread safe.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *