segunda-feira, 5 de setembro de 2011

Exemplo de Padrão de Projeto Factory em Delphi

O exemplo será apresentado através de uma necessidade, retornar o nome de uma pessoa que pode ser (Cliente, Agência ou Vendedor).

Minha definição de Factory nesse exemplo seria passar a responsabilidade da criação do objeto TPessoa para outro objeto.

Essa unit é responsável pela classe base TPessoa na qual possui um método Nome no qual será implementado em outro momento.
unit Pessoa;

interface

type
  TPessoa = class
    function Nome: string; virtual; abstract;
  end;

implementation

end.

Essa unit PessoaCliente é responsável pela classe TPessoaCliente que herda da classe base TPessoa na qual implementará o método Nome, observe que ele irá mostrar o nome da classe e o nome da pessoa para facilitar o desenvolvimento.
unit PessoaCliente;

interface

uses
  Pessoa;

type
  TPessoaCliente = class(TPessoa)
    function Nome: string; override;
  end;

implementation

function TPessoaCliente.Nome: string;
begin
  Result := ClassName + ': Microsoft';
end;

end.

Essa unit PessoaVendedor é semelhante a da classe TPessoaCliente
unit PessoaVendedor;

interface

uses
  Pessoa;

type
  TPessoaVendedor = class(TPessoa)
  public
    function Nome: string; override;
  end;

implementation

function TPessoaVendedor.Nome: string;
begin
  Result := ClassName + ': Bruno';
end;

end.

Essa unit PessoaFactory é responsável por criar e devolver a instanciar do objeto de acordo com o parâmetro informado TTipoPessoa.
Observe que o retorno é do tipo base TPessoa, facilitando a ampliação para outras classes como, por exemplo, TUsuario.
unit PessoaFactory;

interface

uses
  Classes,
  Pessoa;

type
  TTipoPessoa = (tpCliente, tpAgencia, tpVendedor);

  // Simple Factory: Retorna apenas a instancia, diferente do Abstract que pode retornar vários métodos
  TPessoaFactory = class
  public
    function CriarPessoa(const ATipoPessoa: TTipoPessoa): TPessoa;
  end;

implementation

uses
  PessoaCliente,
  PessoaAgencia,
  PessoaVendedor;

function TPessoaFactory.CriarPessoa(const ATipoPessoa: TTipoPessoa): TPessoa;
begin
  Result := nil;
  case ATipoPessoa of
    tpCliente:
      Result := TPessoaCliente.Create;
    tpAgencia:
      Result := TPessoaAgencia.Create;
    tpVendedor:
      Result := TPessoaVendedor.Create;
  end;
end;

end.

A unit Principal só tem conhecimento da unit PessoaFactory, que cria e chama o método nome de acordo com o parâmetro informado.
procedure TFormPrincipal.ButtonListarPessoasClick(Sender: TObject);
var
  Pessoa: TPessoaFactory;
begin
  Pessoa := TPessoaFactory.Create;
  try
    Memo.Lines.Add(Pessoa.CriarPessoa(tpCliente).Nome);
    Memo.Lines.Add(Pessoa.CriarPessoa(tpAgencia).Nome);
    Memo.Lines.Add(Pessoa.CriarPessoa(tpVendedor).Nome);
  finally
    Pessoa.Free;
  end;
end;


Mais detalhes no repositório https://bitbucket.org/brunosanson/design-patterns-em-delphi/

segunda-feira, 29 de agosto de 2011

Exemplo de Padrão de Projeto Decorator em Delphi

O exemplo será apresentado através de uma necessidade, adicionar acessórios (Bluetooth, 3G, WiFi) na montagem de celular, calculando o valor final do produto.

Minha definição de Decorator nesse exemplo seria adicionar os acessórios dinamicamente no objeto Celular.

Essa unit é responsável pela classe celular e subclasses para obter diferentes decorações:

unit CelularDecorator;

interface

type
  ICelular = interface
    function Valor: Currency;
  end;

  TCelular = class(TInterfacedObject, ICelular)
  public
    function Valor: Currency; virtual; abstract;
  end;

  TCelularDecorator = class(TCelular)
  private
    FCelular: ICelular;
  public
    function Valor: Currency; override;
    constructor Create(ACelular: ICelular);
  end;

  TTelefonia = class(TCelular)
  public
    function Valor: Currency; override;
  end;

  TBluetooth = class(TCelularDecorator)
  public
    function Valor: Currency; override;
  end;

  T3G = class(TCelularDecorator)
  public
    function Valor: Currency; override;
  end;

  TWiFi = class(TCelularDecorator)
  public
    function Valor: Currency; override;
  end;

implementation

constructor TCelularDecorator.Create(ACelular: ICelular);
begin
  FCelular := ACelular;
end;

function TCelularDecorator.Valor: Currency;
begin
  Result := inherited Valor;
end;

function TBluetooth.Valor: Currency;
begin
  Result := FCelular.Valor + 10.00;
end;

function T3G.Valor: Currency;
begin
  Result := FCelular.Valor + 15.15;
end;

function TWiFi.Valor: Currency;
begin
  Result := FCelular.Valor + 20.20;
end;

function TTelefonia.Valor: Currency;
begin
  Result := 100;
end;

end.

Esse método instância a classe TCelular e suas subclasse que adiciona os acessórios:

procedure TFormPrincipal.ButtonCalcularValorCelularClick(Sender: TObject);
var
  Celular: ICelular;
begin
  Celular := TTelefonia.Create;
  Memo.Lines.Add('Valor original do celular: R$ ' + FormatCurr('###,##0.00', Celular.Valor));

  Celular := TBluetooth.Create(Celular);
  Memo.Lines.Add(' + Bluetooth: R$ ' + FormatCurr('###,##0.00', Celular.Valor));

  Celular := T3G.Create(Celular);
  Memo.Lines.Add(' + 3G: R$ ' + FormatCurr('###,##0.00', Celular.Valor));

  Celular := TWiFi.Create(Celular);
  Memo.Lines.Add(' + WiFi: R$ ' + FormatCurr('###,##0.00', Celular.Valor));
end;

Mais detalhes no repositório https://bitbucket.org/brunosanson/design-patterns-em-delphi/

terça-feira, 16 de agosto de 2011

Exemplo de Padrão de Projeto Strategy em Delphi

O exemplo será apresentado através de uma necessidade, exportar uma lista de áudio para diferentes player (iTunes, Winamp, Windows Media Player) com seus respectivos formatos (xml, csv, txt).

Minha definição de Strategy nesse exemplo seria um conjunto de algorítimos independentes e encapsulado de acordo com o player / formato sendo consumido por um módulo comum de exportação.

Essa unit é responsável pela organização da exportação:

unit Exportacao;

interface

uses
  Classes,
  SysUtils;

type
  EExportcaoErro = class(Exception);

  TExportacao = class
  private
    FPlaylist: TStrings;
  public
    function GetPlayer: string; virtual; abstract;
    procedure Exportar; virtual; abstract;
    property Playlist: TStrings read FPlaylist write FPlaylist;
  end;

const
  PlayerNaoIntegrado = 'Não há integração com o player %s';

implementation

end.

Essa unit é responsável pela exportação para o iTunes:

unit ExportacaoItunes;

interface

uses
  Exportacao;

type
 TiTunes = class(TExportacao)
 private
   function ExportarXML: Boolean;
  public
    function GetPlayer: string; override;
    procedure Exportar; override;
 end;

implementation

procedure TiTunes.Exportar;
begin
  ExportarXML;
end;

function TiTunes.ExportarXML: Boolean;
begin
  // Exportar o conteúdo do Playlist no formamto XML conforme espeficicações do iTunes;
  raise EExportcaoErro.Create(PlayerNaoIntegrado);
end;

function TiTunes.GetPlayer: string;
begin
  Result := 'iTunes';
end;

end.

Essa unit é responsável pela exportação para o Windows Media Player:

unit ExportacaoWinamp;

interface

uses
  Classes,
  Exportacao;

type
  TWinamp = class(TExportacao)
  private
    procedure ExportarM3U;
  public
    function GetPlayer: string; override;
    procedure Exportar; override;
  end;

implementation

procedure TWinamp.Exportar;
begin
  ExportarM3U;
end;

procedure TWinamp.ExportarM3U;
begin
  // Exportar o conteúdo do Playlist no formamto M3U conforme espeficicações do Winamp;
  raise EExportcaoErro.Create(PlayerNaoIntegrado);
end;

function TWinamp.GetPlayer: string;
begin
  Result := 'Winamp';
end;

end.

Essa unit é responsável pelo exportação:

unit Principal;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, 
  Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Exportacao;

type
  TFormExportacao = class(TForm)
    ButtonExportar: TButton;
    RadioGroupPlayer: TRadioGroup;
    MemoPlaylis: TMemo;
    procedure ButtonExportarClick(Sender: TObject);
  private
    procedure Exportar;
  end;

var
  FormExportacao: TFormExportacao;

implementation

uses
  ExportacaoWindowsMidiaPlayer,
  ExportacaoItunes,
  ExportacaoWinamp;

{$R *.dfm}

procedure TFormExportacao.ButtonExportarClick(Sender: TObject);
begin
  Exportar;
end;

procedure TFormExportacao.Exportar;
var
  Exportacao: TExportacao;
begin
  Exportacao := nil;
  try
    case RadioGroupPlayer.ItemIndex of
      0: Exportacao := TiTunes.Create;
      1: Exportacao := TWinamp.Create;
      2: Exportacao := TWindowsMidiaPlayer.Create;
      else
        ShowMessage('Não foi possível realizar a exportação');
    end;
    Exportacao.Playlist := MemoPlaylis.Lines;
    try
      Exportacao.Exportar;
      ShowMessage('Exportação realizada com sucesso no ' + Exportacao.GetPlayer);
    except
      on E: EExportcaoErro do
        ShowMessage(Format(E.Message, [Exportacao.GetPlayer]));
    end;
  finally
    Exportacao.Free;
  end;
end;

end.

Mais detalhes no repositório https://bitbucket.org/brunosanson/design-patterns-em-delphi/