Design Patterns em Delphi (Padrões de Projeto)

Design patterns (ou padrões de projeto) são estruturas e relacionamentos que utilizamos repetidas vezes em projetos orientados a objeto. Para conhecê-los pode ajudar você a projetar melhor (e com isso melhorar o desenvolvimento do seu software), tornar mais reutilizáveis de código e também ajudá-lo a aprender para a concepção de sistemas mais complexos.

Neste artigo, vamos falar especificamente de design patterns em Delphi, usando código para entender e mostrar os conceitos por trás de cada padrão.

O que é Padrão de Projeto?

A aplicação de padrão de projeto pode melhorar a qualidade dos sistemas desenvolvidos, porque são, na prática, soluções testadas e aprovadas para problemas comuns dentro da programação.

Com o uso de padrões, você pode reduzir acoplamento, melhorar a legibilidade de seu código e aumentar o grau de reuso do mesmo. E também ajuda outros desenvolvedores a entender mais rápido o seu código.

Tipos de Padrão de Projeto

Segundo o GOF, os Design Patterns são divididos em três categorias:

  • Criacionais – Abstract Factory, Factory Method, Builder, Prototype, Singleton;
  • Estruturais – Composite, Adapter, Bridge, Decorator, Facade, Flyweight, Proxy;
  • Comportamentais – Chain of Responsability, Strategy, Interpreter, Template Method, , Command, Iterator, Mediator, Memento, Observer, State, Visitor.

Como o Delphi te ajuda com padrões

O Delphi implementa totalmente linguagem orientada a objeto com a prática muitos refinamentos que simplificam o desenvolvimento.

A mais importante classe de atributos a partir de um padrão de perspectiva são os básicos de herança de classes; virtual e métodos abstratos; e a utilização da protegidas e âmbito público. Estas dar-lhe as ferramentas para criar padrões que podem ser reutilizados e estendidos, e permitem isolar funcionalidades variadas a partir da base de dados de atributos que são imutáveis.

Dica Extra: Treinamento exclusivo de Design Pattern para Delphi

Se você quer um método para aprender Design Pattern para Delphi, de um jeito prático, eu sugiro olhar este treinamento.

Exemplos de padrões de projeto

Aqui, nós vamos dar as definições sobre os principais padrões de projeto, junto com um exemplo.

Singleton

Garante que uma classe possuirá apenas uma instância, e proverá um único ponto de acesso global a mesma. Talvez seja um dos design patterns mais simples de serem implementados.

unit Singletn;

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

Forms, Dialogs;

type

TCSingleton = class(TComponent)

public

constructor Create(AOwner: TComponent); override;

destructor Destroy; override;

end;

TOSingleton = class(TObject)

public

constructor Create;

destructor Destroy; override;

end;

var

Global_CSingleton: TCSingleton;

Global_OSingleton: TOSingleton;

procedure Register;

implementation

procedure Register;

begin

RegisterComponents('Design Patterns', [TCSingleton]);

end;

{ TCSingleton }

constructor TCSingleton.Create(AOwner: TComponent);

begin

if Global_CSingleton <> nil then

{NB could show a message or raise a different exception here}

Abort

else begin

inherited Create(AOwner);

Global_CSingleton := Self;

end;

end;

destructor TCSingleton.Destroy;

begin

if Global_CSingleton = Self then

Global_CSingleton := nil;

inherited Destroy;

end;

{ TOSingleton }

constructor TOSingleton.Create;

begin

if Global_OSingleton <> nil then

{NB could show a message or raise a different exception here}

Abort

else

Global_OSingleton := Self;

end;

destructor TOSingleton.Destroy;

begin

if Global_OSingleton = Self then

Global_OSingleton := nil;

inherited Destroy;

end;

procedure FreeGlobalObjects; far;

begin

if Global_CSingleton <> nil then

Global_CSingleton.Free;

if Global_OSingleton <> nil then

Global_OSingleton.Free;

end;

begin

AddExitProc(FreeGlobalObjects);

end.

Adapter

Converte uma interface de uma classe em outra interface, de acordo com a exigência do cliente. Um adapter torna mais simples o uso do trabalho em conjunto e evita incompatibilidades.

unit Adapter;

interface

uses SysUtils, Classes;

type

{ The new class }

TNewCustomer = class

private

FCustomerID: Longint;

FFirstName: string;

FLastName: string;

FDOB: TDateTime;

protected

function GetCustomerID: Longint; virtual;

function GetFirstName: string; virtual;

function GetLastName: string; virtual;

function GetDOB: TDateTime; virtual;

public

constructor Create(CustID: Longint); virtual;

property CustomerID: Longint read GetCustomerID;

property FirstName: string read GetFirstName;

property LastName: string read GetLastName;

property DOB: TDateTime read GetDOB;

end;

{ An interface method }

{ Lets us hide details of TOldCustomer from the client }

function GetCustomer(CustomerID: Longint): TNewCustomer;

implementation

const

Last_OldCustomer_At_Year_2000 = 15722;

Last_OldCustomer_In_Database = 30000;

{ The new class }

constructor TNewCustomer.Create(CustID: Longint);

begin

FCustomerID := CustID;

FFirstName := 'A';

FLastName := 'New_Customer';

FDOB := Now;

end;

function TNewCustomer.GetCustomerID: Longint;

begin

Result := FCustomerID;

end;

function TNewCustomer.GetFirstName: string;

begin

Result := FFirstName;

end;

function TNewCustomer.GetLastName: string;

begin

Result := FLastName;

end;

function TNewCustomer.GetDOB: TDateTime;

begin

Result := FDOB;

end;

type

{ The old class }

TOldDOB = record

Day: 0..31;

Month: 1..12;

Year: 0..99;

end;

TOldCustomer = class

FCustomerID: Integer;

FName: string;

FDOB: TOldDOB;

public

constructor Create(CustID: Integer);

property CustomerID: Integer read FCustomerID;

property Name: string read FName;

property DOB: TOldDOB read FDOB;

end;

constructor TOldCustomer.Create(CustID: Integer);

begin

FCustomerID := CustomerID;

FName := 'An Old_Customer';

with FDOB do begin

Day := 1;

Month := 1;

Year := 1;

end;

end;

type

{ The Adapter class }

TAdaptedCustomer = class(TNewCustomer)

private

FOldCustomer: TOldCustomer;

protected

function GetCustomerID: Longint; override;

function GetFirstName: string; override;

function GetLastName: string; override;

function GetDOB: TDateTime; override;

public

constructor Create(CustID: Longint); override;

destructor Destroy; override;

end;

{ The Adapter class }

constructor TAdaptedCustomer.Create(CustID: Longint);

begin

inherited Create(CustID);

FOldCustomer := TOldCustomer.Create(CustID);

end;

destructor TAdaptedCustomer.Destroy;

begin

FOldCustomer.Free;

inherited Destroy;

end;

function TAdaptedCustomer.GetCustomerID: Longint;

begin

Result := FOldCustomer.CustomerID;

end;

function TAdaptedCustomer.GetFirstName: string;

var

SpacePos: integer;

begin

SpacePos := Pos(' ', FOldCustomer.Name);

if SpacePos = 0 then

Result := ''

else

Result := Copy(FOldCustomer.Name,1,SpacePos-1);

end;

function TAdaptedCustomer.GetLastName: string;

var

SpacePos: integer;

begin

SpacePos := Pos(' ', FOldCustomer.Name);

if SpacePos = 0 then

Result := FOldCustomer.Name

else

Result := Copy(FOldCustomer.Name,SpacePos+1,255);

end;

function TAdaptedCustomer.GetDOB: TDateTime;

var

FullYear: Word;

begin

if CustomerID > Last_OldCustomer_At_Year_2000 then

FullYear := 2000 + FOldCustomer.DOB.Year

else

FullYear := 1900 + FOldCustomer.DOB.Year;

Result := EncodeDate(FullYear, FOldCustomer.DOB.Month, FOldCustomer.DOB.Day);

end;

function GetCustomer(CustomerID: Longint): TNewCustomer;

begin

if CustomerID > Last_OldCustomer_In_Database then

Result := TNewCustomer.Create(CustomerID)

else

Result := TAdaptedCustomer.Create(CustomerID) as TNewCustomer;

end;

end.

 

Template Method

Template Method na prática, é algoritmo cuja obrigatoriedade é seguir os passos internos.

Define um passo a passo de uma operação, delegando alguns passos para subclasses. Este design pattern permite que subclasses possam redefinir certos passos de um algoritmo, sem alterar sua estrutura.

Abstração é implementado em Delphi através de métodos virtuais.

Métodos diferentes de métodos virtuais pela classe-base, e permite não ter uma implementação. A classe filha é completamente responsável para a implementação de um método abstrato. Chamar um método abstrato que não tenha sido substituído irá resultar em um erro de tempo de execução.

Este exemplo mostra algumas muito simples, mas ilustra o princípio de adiar a implementação de uma subclasse.

unit Tpl_meth;

interface

type

TAbstractTemplateClass = class(TObject)

protected

function Algorithm_StepA: Integer; virtual; abstract;

function Algorithm_StepB: Integer; virtual; abstract;

function Algorithm_StepC: Integer; virtual; abstract;

public

function Algorithm: Integer;

end;

TConcreteClassA = class(TAbstractTemplateClass)

protected

function Algorithm_StepA: Integer; override;

function Algorithm_StepB: Integer; override;

function Algorithm_StepC: Integer; override;

end;

TConcreteClassB = class(TAbstractTemplateClass)

protected

function Algorithm_StepA: Integer; override;

function Algorithm_StepB: Integer; override;

function Algorithm_StepC: Integer; override;

end;

Builder

Separar a construção de um objeto complexo da sua representação, de modo que o mesmo processo de construção possa criar diferentes representações.

Um Builder é semelhante ao Abstract Factory.

A diferença que o construtor se refere a único objeto de diferentes classes concretas, mas contendo várias partes, considerando que o abstract factory permite criar todo famílias de classes concretas. Por exemplo, um construtor pode construir uma casa, casa ou escritório.

Você pode empregar diferentes construtor de uma casa de tijolo ou de madeira, mas a casa que você gostaria de dar-lhes instruções semelhantes sobre o tamanho e a forma da casa.

type

TAbstractFormBuilder = class

private

FForm: TForm;

procedure BuilderFormClose(Sender: TObject; var Action: TCloseAction);

protected

function GetForm: TForm; virtual;

public

procedure CreateForm(AOwner: TComponent); virtual;

procedure CreateSpeedButton; virtual; abstract;

procedure CreateEdit; virtual; abstract;

procedure CreateLabel; virtual; abstract;

property Form: TForm read GetForm;

end;

type

TRedFormBuilder = class(TAbstractFormBuilder)

private

FNextLeft, FNextTop: Integer;

public

procedure CreateForm(AOwner: TComponent); override;

procedure CreateSpeedButton; override;

procedure CreateEdit; override;

procedure CreateLabel; override;

end;

type

TBlueFormBuilder = class(TAbstractFormBuilder)

private

FNextLeft, FNextTop: Integer;

public

procedure CreateForm(AOwner: TComponent); override;

procedure CreateSpeedButton; override;

procedure CreateEdit; override;

procedure CreateLabel; override;

end;

At runtime the client application instructs one of the concrete classes to create parts using the public part creation procedures. The concrete builder instance is passed to the folliwing procedure:

procedure TForm1.Create3ComponentFormUsingBuilder(ABuilder: TAbstractFormBuilder);

var

NewForm: TForm;

begin

with ABuilder do begin

CreateForm(Application);

CreateEdit;

CreateSpeedButton;

CreateLabel;

NewForm := Form;

if NewForm <> nil then NewForm.Show;

end;

end;

Abstract Factory

Fornece uma interface para criação de famílias de ou relacionadas com objetos dependentes sem especificar suas classes concretas.

Em tempo de execução, a nossa aplicação de cliente instancia o resumo de fábrica com um betão de classe e, em seguida, usa o resumo interface. Partes do aplicativo cliente que usa a fábrica não precisa saber qual a classe do betão é realmente em uso.

TOAbstractFactory = class(TObject)

public

constructor Create;

destructor Destroy; override;

{ abstract widget constructors }

function CreateSpeedButton(AOwner: TComponent): TSpeedButton; virtual; abstract;

function CreateEdit(AOwner: TComponent): TEdit; virtual; abstract;

function CreateLabel(AOwner: TComponent): TLabel; virtual; abstract;

end;

TORedFactory and TOBlueFactory override the abstract interface to support different widget styles.

TORedFactory = class(TOAbstractFactory)

public

{ concrete widget constructors }

function CreateSpeedButton(AOwner: TComponent): TSpeedButton; override;

function CreateEdit(AOwner: TComponent): TEdit; override;

function CreateLabel(AOwner: TComponent): TLabel; override;

end;

TOBlueFactory = class(TOAbstractFactory)

public

{ concrete widget constructors }

function CreateSpeedButton(AOwner: TComponent): TSpeedButton; override;

function CreateEdit(AOwner: TComponent): TEdit; override;

function CreateLabel(AOwner: TComponent): TLabel; override;

end;

Factory Method

Factory Method define uma interface para criar um objeto, mas deixar que as subclasses decidam qual classe instanciar. O padrão de projeto permite que uma classe delegue a instanciação para subclasses.

Desta forma, permite você instanciar objetos por meio de um método parametrizado, o que reduz sua complexidade.

Este modelo é útil quando você deseja encapsular a construção de uma classe e isolar o conhecimento da classe concreta da aplicação do cliente através de uma interface abstrata.

TRedSpeedButton = class(TSpeedButton)

public

constructor Create(AOwner: TComponent); override;

end;

constructor TRedSpeedButton.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

Font.Color := clRed;

end;

function TORedFactory.CreateSpeedButton(AOwner: TComponent): TSpeedButton;

begin

Result := TRedSpeedButton.Create(AOwner);

end;

 

Referências bibliográficas: