Escolar Documentos
Profissional Documentos
Cultura Documentos
Plataforma
DevMedia
Saiba mais
28
Artigo
Técnicas Avançadas de
Este Artigo faz parte
do Guia
ClubeDelphi 140
O artigo trata de apresentar alguns estudos de caso e em cima deles demonstrar a aplicação de
boas práticas de Programação Orientada a Objetos com Delphi, incluindo o uso de Fábricas de
Construtores, Design Patterns e Interfaces.
A primeira parte deste artigo vai mostrar como criar objetos de forma correta com o
Delphi, usando por exemplo Métodos Estáticos de Fábrica, Singletons e Fábricas,
usando tipos levemente acoplados. A seguir, vamos examinar um recurso do Delphi
presente desde suas primeiras versões, que também ajuda a criar so wares mais
exíveis e reduz código, facilitando a manutenção, é a técnica de re exão. Outro tema
intimamente relacionado com Boas Práticas, também abordado neste artigo, é a
refatoração, que permite modi car a estrutura (forma) interna do aplicativo, seja
para melhorar sua legibilidade ou design, mantendo seu comportamento (behavior)
externo. Técnicas avançadas de POO também são aplicadas na prática nos estudos de
caso deste artigo, como o uso de Interfaces, Abstração, Encapsulamento,
Polimor smo, Delegates e Métodos Anônimos.
Assim como qualquer código, um código orientado a objetos mal escrito é tão ruim
quanto um código procedural também mal escrito, talvez até pior. A orientação a
objetos vai muito além de simplesmente criar uma classe, declarar suas propriedades,
alguns métodos e então utilizar um objeto por todo o sistema. É preciso organizar
como esses objetos se comunicam, ter o cuidado para que uma classe não faça mais
do que ela precisa fazer, e se as camadas lógicas (camada de apresentação, lógica e
acesso a dados) não estão se misturando de forma errada, que crie uma dependência
entre elas. Existem técnicas, boa práticas e estudos que podem ser aplicados para
garantir a boa qualidade de um sistema que faz uso da orientação a objetos. Podemos
citar padrões de projeto, uso de interface, abstrações, métodos anônimos e muito
mais. Vamos abordar essas boas práticas com exemplos que facilitam o
entendimento.
Uma boa prática é nunca utilizar diretamente os operadores new (Prism) ou Create
(Win32) para criar objetos a partir de suas classes concretas. Isso pode causar um
forte acoplamento entre elas, o que diminui o reaproveitamento de código e causa
dependência que mais tarde pode ocasionar problemas. Uma saída é considerar o uso
de fábricas ou métodos estáticos de fábrica. Um exemplo está mostrado na Listagem
1.
1 program Exemplo1;
2
3 {$APPTYPE CONSOLE}
4
5 {$R *.res}
6
7 uses
8 System.SysUtils;
9
10 type
11 // Classe PessoaFisica com atributos simples
12 PessoaFisica = class
13 strict private
14 FNome: string;
15 FCPF: string;
16 public
17 property Nome: string read FNome write FNome;
18 property CPF: string read FCPF write FCPF;
19 end;
20
21 // Fábrica que constrói pessoas físicas
22 Fabrica = class
23 public
24 class function CriaPessoa: PessoaFisica;
25 end;
26
27 { Fábrica }
28
29 class function Fabrica.CriaPessoa: PessoaFisica;
30 begin
31 result := PessoaFisica.Create();
32 end;
33
34 var
35 GP: PessoaFisica;
36 begin
37 // Uso da fábrica ao invés de chamar create diretamente
38 GP := Fabrica.CriaPessoa();
39 GP.Nome := 'Guinther Pauli';
40 GP.CPF := '123456789-00';
41 Write(GP.Nome,' - ',GP.CPF);
42 end.
A aplicação de Console consome uma classe PessoaFisica através de uma fábrica, não
possuindo um relacionamento direto de dependência. A fábrica possui um método
estático através do uso da palavra reservada class do Delphi, logo ela não precisa ser
instanciada. O Create está encapsulado dentro da fábrica, não sendo exposto. Este
exemplo pode ser melhorado, aplicando-se uma interface para reduzir ainda mais o
acoplamento (isso é feito em outro exemplo adiante).
Nota do DevMan
Singletons
Uma boa técnica seria esconder a instância do objeto dentro da própria classe,
juntamente com o construtor, expondo apenas um método público que devolve esta
instância interna. Esta técnica é muitas vezes chamada também de Singleton, que
possui várias formas de apresentação, dependendo da linguagem e recursos
utilizados. Um exemplo está na Listagem 2.
program Exemplo2;
1
2
{$APPTYPE CONSOLE}
3
4
{$R *.res}
5
6
7
uses
8
System.SysUtils;
9
type
10
// Classe PessoaSocial com atributos simples
11
PessoaSocial = class
12
strict private
13
FNome: string;
14
FBlog: string;
15
FFacebook: string;
16
FTwitter: string;
17
// escondendo o construtor
18
constructor Create();
19
class var
20
_INSTANCE: PessoaSocial;
21
public
22
property Nome: string read FNome write FNome;
23
property Blog: string read FBlog write FBlog;
24
property Facebook: string read FFacebook write FFacebook;
25
property Twitter: string read FTwitter write FTwitter;
26
27
// Método estático
28
class function GetInstance: PessoaSocial;
29
end;
30
var
31
GP: PessoaSocial;
32
33
{ PessoaSocial }
34
35
constructor PessoaSocial.Create;
36
begin
37
// sem implementação
38
end;
39
40
class function PessoaSocial.GetInstance: PessoaSocial;
41
begin
42
if _INSTANCE = nil then
43
_INSTANCE := PessoaSocial.Create();
44
result := _INSTANCE;
45
end;
46
47
var
48
49
GP : PessoaSocial;
50
begin
// Chamando GetInstance ao invés de Create diretamente
51
GP := PessoaSocial.GetInstance();
52
GP.Nome := 'Guinther Pauli';
53
54 GP.Blog := 'http://gpauli.com';
55
GP.Facebook := '/guintherpauli';
56
GP.Twitter := '@guintherpauli';
57
write(GP.Nome,' - ',GP.Blog,' - ',GP.Facebook,GP.Twitter);
58
end.
Fábricas
Uma outra técnica bem interessante é criar uma fábrica que devolve múltiplos tipos
de objetos de dadas classes concretas que possuem um ancestral comum, de forma
que possam se aplicar técnicas como herança, polimor smo e interfaces. A ideia
consiste em solicitar a uma fábrica um tipo genérico passando um indicador de baixo
acoplamento para ela (“leve”), por exemplo uma ag ou um integer, ou ainda um
enum, que não vão causar problemas de dependência. Internamente, a fábrica faz um
teste no que foi solicitado e instancia a classe apropriada, devolvendo um objeto de
um tipo concreto em uma referência de um tipo mais abstrato.
Nota do DevMan
1 ...
2 // Classe Genérica Abstrata
3 MeioTransporte = class abstract
4 public
5 function Ligar(): string; virtual; abstract;
6 end;
7
8 // Classe Concreta derivada
9 Carro = class(MeioTransporte)
10 function Ligar(): string; override;
11 end;
12
13 // Classe Concreta derivada
14 Aviao = class(MeioTransporte)
15 function Ligar(): string; override;
16 end;
17
18 // Classe Concreta derivada
19 Bus = class(MeioTransporte)
20 function Ligar(): string; override;
21 end;
22
23 implementation
24
25 { Carro }
26
27 function Carro.Ligar(): string;
28 begin
29 result := 'Método Ligar de Carro chamado';
30 end;
31
32 { Aviao }
33
34 function Aviao.Ligar(): string;
35 begin
36 result := 'Método Ligar de Aviao chamado';
37 end;
38
39 { Bus }
40
41 function Bus.Ligar(): string;
42 begin
43 result := 'Método Ligar de Bus chamado';
44 end;
1 ...
2 type
3 // Enum simples para reduzir o acoplamento
4 TipoMeioTransporte = (mtCarro, mtAviao, mtBus);
5
6 // Fábrica
7 MeioTransporteFactory = class
8 public
9 // método estático de fábrica
10 class function
11 CreateTransporte (Tipo: TipoMeioTransporte): MeioTransporte;
12 end;
13
14 { MeioTransporteFactory }
15
16 class function MeioTransporteFactory.CreateTransporte(
17 Tipo: TipoMeioTransporte): MeioTransporte;
18 begin
19 case Tipo of
20 mtCarro: result := Carro.Create();
21 mtAviao: result := Aviao.Create;
22 mtBus: result := Bus.Create;
23 end;
24 end;
Quase todas as técnicas para ser criar um bom código (“cheiroso”) se resumem na
criação de estruturas com baixo acoplamento, seja aplicando Design Patterns,
Refatoração, Re exão e fazendo-se uso intenso de interfaces, todos temas deste
artigo.
Re exão
A ideia chave neste exemplo é que o usuário possa escolher o meio de transporte
desejado no combobox, que é preenchido dinamicamente. A seguir, ao clicar em um
botão, o método polimór co “Ligar” é chamado a partir da classe abstrata, e através
de polimor smo, seja identi cado em runtime qual o método de mesmo nome de
uma classe derivada deve ser realmente invocado, porém, sem causar uma forte
dependência (como se fôssemos utilizar por exemplo RTTI com vários if’s, “is” e “as”,
um para cada tipo). O formulário da aplicação é apresentado adiante, na Figura 1.
1 ...
2 TFrmMain = class(TForm)
3 ...
4 private
5 FMeioTransporte: MeioTransporte;
6 public
7 ...
8 property Transporte: IMeioTransporte
9 read FMeioTransporte write FMeioTransporte;
10 ...
Agora vamos examinar bem o código da Listagem 7. Primeiro, nós obtemos o valor do
tipo enumerado a partir de um integer, fazendo um cast para ver o que o usuário
selecionou no combobox. A seguir, solicitamos à fábrica que devolva um tipo
concreto baseado nessa “ ag”. O método Ligar, polimór co e comum a todos os meios
de transporte é então invocado. Note que neste momento, o compilador do Delphi
não sabe o que está dentro da referência Transporte, pois está declarado como um
tipo abstrato no formulário (MeioTransporte). É o polimor smo que garante que o
método correto seja chamado em runtime. Basicamente, ele está declarado como
abstract apenas para poder ser chamado pelo compilador e reduzir o acoplamento no
framework, caso contrário, para cada novo meio de transporte que fosse criado
(lembre-se, mudanças ocorrem), mais um teste precisaria ser feito e novo código
injetado. Ao nal, a mensagem de debug é colocada em um memo para ns de
pro ling.
Identi car “Bad Smells” no código para criar oportunidades de refatoração é uma
técnica bastante interessante e difundida atualmente. Podemos por exemplo usar
métricas de so ware para, por exemplo, identi car falhas no design de um so ware
que apresenta classes de um framework com um alto grau de acoplamento,
reduzindo assim o reaproveitamento. Neste caso, poderia se usar refatoração para
padrões de projeto, a m de melhorar o design. Testes ou grá cos de dependência
podem ajudar a medir a e ciência dos refactorings. Outro ponto importante a ser
considerado são as pré e pós condições das atividades de refatoração. Por exemplo,
uma pré-condição de um sistema de tempo real, como uma aplicação Web de E-
Commerce, é o tempo de resposta ao usuário. De nada adianta melhorar a legibilidade
do código e design se ao nal tivermos uma queda no tempo de resposta, pois nesse
caso, performance é uma pré-condição. O mesmo vale para sistemas embarcados,
por exemplo, aplicações que rodam em Smartphones, elas precisam consumir pouca
memória e bateria, outra pré-condição.
1 IMeioTransporte = interface
2 function Ligar(): string;
3 end;
1 // Fábrica
2 MeioTransporteFactory = class
3 public
4 // método de fábrica
5 class function CreateTransporte
6 (Tipo: TipoMeioTransporte) : IMeioTransporte; ß Repate a Interface
7 end;
Para aplicar mais algumas Best Practices, vamos considera agora um outro estudo de
caso. A ideia deste exemplo é modelar um sistema de noti cação de clientes de uma
empresa, por exemplo, uma operadora de telefonia ou um banco. Tais noti cações
podem ser enviadas por SMS, Email e VoiceMail. Um requisito da aplicação é que ela
esteja preparada para futuras formas de noti cação que a empresa possa vir a adotar,
ou seja, precisamos criar o design de um so ware bem preparado para futuras
mudanças.
A modelagem inicial do framework contém “bad smells”, como pode ser visto na
Listagem 11. Note que a classe de Noti cacao possui uma dependência rígida e direta
com a classe Email, podendo enviar mensagens ao cliente somente através deste
meio. Porém existe um design um tanto elegante, escondemos a forma de envio
encapsulando a referência que está interna na classe Noti cacao, na sessão private,
favorecendo um bom princípio da OO, o encapsulamento.
1 unit uNotificao;
2
3 interface
4
5 uses
6 uFormasNotificacao;
7
8 type
9 Email = class
10 procedure Avisar(Texto: string);
11 end;
12
13 Notificacao = class
14 private
15 FEMail: Email;
16 public
17 constructor Create;
18 procedure Send(Texto: string);
19 end;
20
21 implementation
22
23 { Notificacao }
24
25 procedure Notificacao.Send(Texto: String);
26 begin
27 FEmail.Avisar(Texto);
28 end;
29
30 constructor Notificacao.Create;
31 begin
32 inherited;
33 FEMail := Email.Create();
34 end;
35
36 { Email }
37
38 procedure Email.Avisar(Texto: string);
39 begin
40 Writeln('Email enviado: ' + Texto);
41 end;
42
43 end.
A Listagem 12 mostra uma aplicação de Console que consome a classe de Noti cacao.
Um aviso é enviado ao cliente informado a data de vencimento da fatura. A saída nal
da aplicação é algo do tipo:
1 program Exemplo4;
2
3 {$APPTYPE CONSOLE}
4
5 {$R *.res}
6
7 uses
8 System.SysUtils,
9 uNotificao in 'uNotificao.pas';
10
11 var
12 Aviso: Notificacao;
13 begin
14 Aviso := Notificacao.Create();
15 Aviso.Send('Sua fatura vence dia 29/04');
16 end.
1 unit uFormasNotificacao;
2
3 interface
4
5 uses
6 Dialogs;
7
8 type
9 IFormaNotificacao = interface
10 procedure Avisar(Texto: String);
11 end;
12
13 Email = class(TInterfacedObject,IFormaNotificacao)
14 public
15 procedure Avisar(Texto: String);
16 end;
17
18 SMS = class(TInterfacedObject,IFormaNotificacao)
19 public
20 procedure Avisar(Texto: String);
21 end;
22
23 VoiceMail = class(TInterfacedObject,IFormaNotificacao)
24 public
25 procedure Avisar(Texto: String);
26 end;
27
28 implementation
29
30 { Email }
31
32 procedure Email.Avisar(Texto: String);
33 begin
34 ShowMessage('Email enviado: ' + Texto);
35 end;
36
37 { SMS }
38
39 procedure SMS.Avisar(Texto: String);
40 begin
41 ShowMessage('SMS enviado: ' + Texto);
42 end;
43
44 { VoiceMail }
45
46 procedure VoiceMail.Avisar(Texto: String);
47 begin
48 ShowMessage('Voice mail gravado: ' + Texto);
49 end;
50
51 end.
Listagem 14. Classe de Noti cacao, nova versão, ainda dependente de classe concreta
1 unit uNotificacao;
2
3 interface
4
5 uses
6 uFormasNotificacao;
7
8 type
9
10 Notificacao = class
11 private
12 FEMail: Email;
13 public
14 constructor Create;
15 procedure Send(Texto: string);
16 end;
17
18 implementation
19
20 { Notificacao }
21
22 procedure Notificacao.Send(Texto: String);
23 begin
24 FEmail.Avisar(Texto);
25 end;
26
27 constructor Notificacao.Create();
28 begin
29 inherited;
30 FEMail := Email.Create();
31 end;
32
33 end.
Listagem 15. Formulário usa a classe Noti cacao para despachar mensagens
1 unit uFrmMain;
2
3 interface
4
5 uses
6 Winapi.Windows, Winapi.Messages,
7 System.SysUtils, System.Variants, System.Classes,
8 Vcl.Graphics, Vcl.Controls, Vcl.Forms,
9 Vcl.Dialogs, Vcl.StdCtrls, uNotificacao;
10
11 type
12 TFrMain = class(TForm)
13 edtTexto: TEdit;
14 Label1: TLabel;
15 BtnNotificar: TButton;
16 procedure BtnNotificarClick(Sender: TObject);
17 private
18 FNotificacao: Notificacao;
19 { Private declarations }
20 public
21 { Public declarations }
22 property Aviso: Notificacao read FNotificacao write FNotificacao;
23 end;
24
25 var
26 FrMain: TFrMain;
27
28 implementation
29
30 {$R *.dfm}
31
32 procedure TFrMain.BtnNotificarClick(Sender: TObject);
33 begin
34 Aviso := Notificacao.Create();
35 try
36 Aviso.Send(edtTexto.Text);
37 finally
38 Aviso.Free;
39 end;
40 end;
41
42 end.
formas de envio de noti cação, vamos adaptar a classe Noti cacao para que não faça
referência mais a uma classe concreta de Email, mas sim a uma estrutura mais
exível e plugável, a interface (Listagem 16). Além disso, o objeto concreto que
enviará a noti cação não será mais criado internamente pela classe de Noti cacao,
ele virá prontinho de fora, da própria classe do formulário (ou seja, a preferência é do
cliente). Acabamos de inverter o controle da situação (IoC). A referência é recebida no
construtor e guardada internamente, para que possa ser chamada em momento
oportuno, mais precisamente, quando o método Send de Noti cacao for invocado
chamamos então o método de interface Avisar que resolverá qual classe concreta
será usada (como o polimor smo).
1 unit uNotificacao;
2
3 interface
4
5 uses
6 uFormasNotificacao;
7
8 type
9
10 Notificacao = class
11 private
12 FFormaNotificao: IFormaNotificacao;
13 public
14 constructor Create(AFormaNotificacao: IFormaNotificacao);
15 procedure Send(Texto: string);
16 end;
17
18 implementation
19
20 { Notificacao }
21
22 procedure Notificacao.Send(Texto: String);
23 begin
24 FFormaNotificao.Avisar(Texto);
25 end;
26
27 constructor Notificacao.Create(AFormaNotificacao: IFormaNotificacao);
28 begin
29 inherited Create();
30 // repassa instânncia do create para referência interna
31 FFormaNotificao := AFormaNotificacao;
32 end;
33
34 end.
1 unit uFrmMain;
2
3 interface
4
5 uses
6 Winapi.Windows, Winapi.Messages, System.SysUtils,
7 System.Variants, System.Classes, Vcl.Graphics,
8 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
9 uNotificacao, Vcl.ExtCtrls, uFormasNotificacao;
10
11 type
12 TFrMain = class(TForm)
13 edtTexto: TEdit;
14 Label1: TLabel;
15 BtnNotificar: TButton;
16 rdForma: TRadioGroup;
17 procedure BtnNotificarClick(Sender: TObject);
18 private
19 FNotificacao: Notificacao;
20 FFormaNotificacao: IFormaNotificacao;
21 function CriaFormaNotificacao: IFormaNotificacao;
22 { Private declarations }
23 public
24 { Public declarations }
25 property Aviso: Notificacao
26 read FNotificacao write FNotificacao;
27 property FormaNotificacao: IFormaNotificacao
28 read FFormaNotificacao write FFormaNotificacao;
29 end;
30
31 var
32 FrMain: TFrMain;
33
34 implementation
35
36 {$R *.dfm}
37
38 procedure TFrMain.BtnNotificarClick(Sender: TObject);
39 begin
40 FormaNotificacao := CriaFormaNotificacao();
41 Aviso := Notificacao.Create(FormaNotificacao);
42 try
43 Aviso.Send(edtTexto.Text);
44 finally
45 Aviso.Free;
46 end;
47 end;
48
49 function TFrMain.CriaFormaNotificacao: IFormaNotificacao;
50 begin
51 // poderiaíamos usar uma fábrica aqui
52 case rdForma.ItemIndex of
53 0: result := Email.Create();
54 1: result := SMS.Create();
55 2: result := VoiceMail.Create();
56 end;
57 end;
58
59 end.
Bom, a partir daqui pediria que o amigo leitor esquece-se um pouco das técnicas que
aplicamos até aqui, até porque muitos condenam a prática que irei apresentar a
seguir. Meu objetivo aqui é abrir uma discussão / paralelo entre Delegates vs.
Polimor smo, e também entre Métodos Anônimos vs. Abstrações. E também
alavancar o uso de delegates e métodos anônimos em Delphi, visto que na plataforma
.NET, suportado pelo Prism, o recurso vem sendo amplamente utilizado,
principalmente com LINQ e Expressões Lamda, para tornar o código mais simples e
uente.
Delegates
Em nosso estudo de caso, precisamos rever alguns conceitos. Por que criamos um
framework que comporta n formas de noti cação? Para suportar futuras formas que
A Listagem 18 mostra como cou nosso novo framework de Noti cacao. Repare que
extinguimos toda a família de classes concretas em prol de um único ponteiro,
chamado aqui de FFormaNoti cacao, que ao invés de referenciar uma interface que
mais tarde apontaria para uma classe concreta que invocaria um método concreto,
aponta para o próprio método concreto que contém a lógica da noti cação. O Create
da classe não recebe mais um tipo concreto compatível com IFormaNoti cacao, mas
um ponteiro para método, que é guardado internamente para ser chamado quando o
usuário for enviar a Noti cacao (método Send).
1 unit uNotificacao;
2
3 interface
4
5 uses
6 uFormasNotificacao;
7
8 type
9 // procedure of object em Delphi equivale a um "delegate"
10 FormaNotificacaoHandler = procedure (Text: string) of object;
11
12 Notificacao = class
13 private
14 // isso não é mais uma interface,
15 // é um ponteiro para um método
16 FFormaNotificao: FormaNotificacaoHandler;
17 public
18 constructor Create(AFormaNotificacao: FormaNotificacaoHandler);
19 procedure Send(Texto: string);
20 end;
21
22 implementation
23
24 { Notificacao }
25
26 procedure Notificacao.Send(Texto: String);
27 begin
28 FFormaNotificao(Texto);
29 end;
30
31 constructor Notificacao.Create(AFormaNotificacao: FormaNotificacaoHandler);
32 begin
33 inherited Create();
34 // repassa a referência do método para a variável interna
35 FFormaNotificao := AFormaNotificacao;
36 end;
37
38 end.
Listagem 19. Injetando código usando delegates ao invés de uma classe concreta
1 unit uFrmMain;
2
3 interface
4
5 uses
6 Winapi.Windows, Winapi.Messages, System.SysUtils,
7 System.Variants, System.Classes, Vcl.Graphics,
8 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
9 uNotificacao, Vcl.ExtCtrls, uFormasNotificacao;
10
11 type
12 TFrMain = class(TForm)
13 edtTexto: TEdit;
14 Label1: TLabel;
15 BtnNotificar: TButton;
16 rdForma: TRadioGroup;
17 procedure BtnNotificarClick(Sender: TObject);
18 private
19 { Private declarations }
20 FNotificacao: Notificacao;
21 // Código a ser injetado no "delegate"
22 procedure Avisar(Texto: string);
23 public
24 { Public declarations }
25 property Aviso: Notificacao
26 read FNotificacao write FNotificacao;
27 end;
28
29 var
30 FrMain: TFrMain;
31
32 implementation
33
34 {$R *.dfm}
35
36 // A implementação variante, antes obtida por polimorfismo,
37 // é agora passada por parâmetro usando delegates,
38 // sendo injetada de fora, sem herança
39 procedure TFrMain.Avisar(Texto: string);
40 begin
41 ShowMessage(Texto + ' notificado via ' + rdForma.Items[rdForma.ItemIndex]);
42 end;
43
44 procedure TFrMain.BtnNotificarClick(Sender: TObject);
45 begin
46 // Abaixo é passado um ponteiro para o método no construtor
47 Aviso := Notificacao.Create(Avisar);
48 try
49 Aviso.Send(edtTexto.Text);
50 finally
51 Aviso.Free;
52 end;
53 end;
54
55 end.
Métodos Anônimos
1 Button1.OnClick := Aqui_Vai_O_Nome_Do_Seu_Método;
2
3 Em um cenário de M.A., poderíamos fazer:
4
5 Button1.OnClick := procedure (Sender: TOBject)
6 begin
7 // implementação
8 end
Para usar o recurso no exemplo, é necessário modi car assinatura do “delegate” para:
A Listagem 20 mostra como cou a versão nal da aplicação, note que não
precisamos criar um método na classe do formulário e passar uma referência a ele no
construtor. Nós passamos a própria implementação do método como parâmetro do
Create. Eu sei que isso vai parecer muito estranho a primeira vista para quem nunca
usou o recurso, porém note que não foi necessário parar a codi cação para incluir
um novo método na classe com um dado nome, implementá-lo e voltar ao Create para
referenciá-lo, tudo é feito de forma mais uente, injetando diretamente a
funcionalidade, sem um nome.
Listagem 20. Passando um “pedaço” de código sem nome que contém a lógica de
noti cação, para ser chamado internamente na classe Noti cacao
1 unit uFrmMain;
2
3 interface
4
5 uses
6 Winapi.Windows, Winapi.Messages, System.SysUtils,
7 System.Variants, System.Classes, Vcl.Graphics,
8 Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
9 uNotificacao, Vcl.ExtCtrls, uFormasNotificacao;
10
11 type
12 TFrMain = class(TForm)
13 edtTexto: TEdit;
14 Label1: TLabel;
15 BtnNotificar: TButton;
16 rdForma: TRadioGroup;
17 procedure BtnNotificarClick(Sender: TObject);
18 private
19 { Private declarations }
20 FNotificacao: Notificacao;
21 public
22 { Public declarations }
23 property Aviso: Notificacao
24 read FNotificacao write FNotificacao;
25 end;
26
27 var
28 FrMain: TFrMain;
29
30 implementation
31
32 {$R *.dfm}
33
34 procedure TFrMain.BtnNotificarClick(Sender: TObject);
35 begin
36 // "Pedaço" de código que contém a lógica de notificação
37 // é injetado em Notificacao e chamado internamente,
38 // porém passado externamente, como método anônimo
39 Aviso := Notificacao.Create(
40 procedure (Texto: string)
41 begin
42 ShowMessage(Texto + ' notificado via ' + rdForma.Items[rdForma.ItemIndex
43 end
44 );
45 try
46 // Send internamente vai chamar o bloco de código acima
47 Aviso.Send(edtTexto.Text);
48 finally
49 Aviso.Free();
50 end;
51 end;
52
53 end.
E nalmente, vou mostrar um recurso que existe já no Delphi Prism e no C#, e que
provavelmente poderemos enxergar em futuras implementações do compilador do
Delphi Win32 / 64, são as Lambda Expressions. É simplesmente uma forma mais
enxuta de escrever um método anônimo, isso quer dizer que no futuro poderemos
escrever este mesmo exemplo assim (esta é uma previsão):
Conclusão
A palavra-chave é criar sistemas mais exíveis, de baixo acoplamento, mas para isso,
vimos que é necessário um estudo profundo das boas práticas e dos recursos que a
linguagem e do IDE nos oferece para aplicá-los. Vimos que uma Boa Prática não deve
levar só em consideração aspectos como legibilidade e design, mas também
performance, segurança e outros requisitos. Pensar em um bom design de so ware,
prevenir “bad smells”, escrever código elegante e refatoração constante, dominando e
usufruindo o que há de melhor no Delphi, foram os pontos fortes aqui discutidos.
Bom proveito e bom código.
Links
Blogs do Autor
http://gpauli.comhttp:
//twitter.com/guintherpauli
http://www.facebook.com/guinther.pauli
Tecnologias:
Por Guinther
Em 2012
Menu Tecnologias
Av. Ayrton Senna 3000, Shopping Via Parque, grupo 3087 - Barra da Tijuca - Rio de Janeiro - RJ