Dando continuidade ao assunto, neste artigo pretendo exemplificar o conceito de herança em OO. Neste momento, faremos uma abordagem simples onde nos ateremos somente ao trivial de como fazer isso em Delphi. A minha idéia é tentar chagar ao mais próximo de uma situação real. Acho que dessa forma posso tornar mais acessível o conteúdo desse artigo.
Vou extender a classe que criamos no artigo
Construção de Componentes II, para uma que saiba trabalhar também com uma barra de progressão. Para que seja opcional ao programador optar por usar a barra ou não, implementaremos a associação dela com o TZNDisplayTimerUltimate externamente. Na literatura sobre criação de componentes em Delphi você vai encontrar esses termos, componentes compostos internos e externos:
Componentes Internos: Criados e Gerenciados pelo componente pricipal. Esta opção deixa
o componente encapsulado, obtendo a vantagem de se poder decidir o
que vamos publicar ou esconder do componente. Assim como fizemos na
relação do ZnDisplayTimer com o campo FTimer. A desvantagem é o
acoplamento extremo entre os dois componentes. Em determinadas situações
isso pode ser indesejável.
Componentes Externos: São associados através de uma propriedade do componente principal. Temos
um acoplamento bem mais fraco, isso pode ser bastante vantajoso.
Associações
Craig Larman, em Applying UML and Patterns (Traduzido como: Utilizando UML e Padrões), descreve associações entre classes como um relacionamento entre tipos (ou, mais especificamente, instâncias desses tipos). Não esquecendo que devemos entender “tipos” como classes, e que estes relacionamentos indicam uma conexão com significado e interesse. Na UML, as associações são descritas como: “Um relacionamento semântico entre dois ou mais classificadores envolvendo conexão entre suas instâncias Por que um ProgressBar?
Minha escolha foi justamente por conta da principal funcionalidade do componente que criamos. De forma visual, transmitir ao usuário, uma idéia de quanto tempo falta até que seja disparado um evento. E este evento pode ser usado para se codificar qualquer coisa: A execução de uma consulta, a exibição de um filme, a transmissão de um e-mail, etc...
Toda vez que em seu programa, alguma módulo executar uma ação que demande tempo, você pode usar uma barra de progressão (TProgressBar, da palheta Win32, o TGauge, palheta Samples. Existem ainda vários outros, mais incrementados, como os da JVCL - Jedi) que mostrará quanto da ação ainda resta para ela ser completada. A propriedade “Prosition” do TProgressBar indica o quanto da barra já foi preenchida. As propriedades “Max” e “Mim”, marcam o valor relativo de início e final do preenchimento da barra. Para efetuar o preenchimento gradual da barra faça chamada ao método “StepBy” ou “StepIt”. A Propriedade “Step” determina o valor de incremento que será usado por “StepIt”. Veja um micro-exemplo:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
ProgressBar1.Max := 100000000;
ProgressBar1.Min := 0;
for i := 0 to 100000000 do
begin
ProgressBar1.StepBy(i);
end;
end;
Criando o Componente
No Delphi, No menu File ► Open Project ... , abra a unit “ZnConstruindoComponenetes” da package que criamos e temos trabalhado nela nos artigos anteriores sobre componentes. Em seguida, no menu Component ►New Component, vamos criar mais um componente que herdará do temporizador que construímos anteriormente.
Salve a unit ZNDisplayTimerProgress.pas, pois ela já está adicionada a package.
Antes de qualquer coisa adicione na seção uses da interface a unit “ZNDisplayTimerUltimate”. Onde está definido a classe ancestral.
interface
uses
SysUtils, Classes, ZNDisplayTimerUltimate;
Vamos criar a associação entre a classe TZNDisplayTimerProgress e a classe TProgressBar, a qual implementaremos em forma de uma propriedade. Visto que, nosso objetivo é criarmos um componente composto por um componente externo.
type
TZNDisplayTimerProgress = class(TZNDisplayTimerUltimate)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
property ProgressBar: TProgressBar;
end;
“Ctrl + Shift + C” para implementarmos o método SetProgressBar. Antes de digitar o código abaixo apague o que você ancontar lá.
{ TZNDisplayTimerProgress }
procedure TZNDisplayTimerProgress.SetProgressBar(
const Value: TProgressBar);
begin
if FProgressBar <> Value then
begin
FProgressBar := Value;
if FProgressBar <> nil then
FProgressBar.FreeNotification(Self);
end;
end;
Pretendo associar um componente ProgressBar ao temporizador, adicionando um, a propriedade que acabamos de codificar. Ora adicionaremos, ora, se necessário, removeremos. um TComponente pode notificar que um outro componente está sendo inserido ou está sendo removido de alguma associação. Por exemplo, se um componente possuir campos ou propriedades cujos tipos sejam referências a outros componentes (outras classes, como no nosso caso), ele pode verificar através das notificações uma remoção de componente, por exemplo, permitindo assim invalidar essas referências evitando “access violate”.
O método FreeNotification, insere um componente, através do parâmetro “AComponent”, numa lista que registra os componentes cujo algumas operações sobre eles deverão ser notificadas ao Owner (componente proprietário). Em outras palavras, isso permite que um componente Owner A, seja notificado que um outro B, numa de suas associações, será destruído. Permitindo que seja implementado tratamento para essa situação. Veja um screenshot da definição deste método na classe TComponent.
Agora vamos sobrescrever o método “Notification” na nossa classe.
procedure Notification(AComponent: TComponent; Operation: TOperation);
Observe na imagem a cima o funcionamento do método “Notification”. O tipo de operação é testado. Caso remoção, indicando que um componente foi removido da lista de componentes associados (FComponents), chama “RemoveFreeNotification”, que remove o componente passado pelo parâmetro (“AComponent”) da lista de componentes notificáveis (FFreeNotifies.Remove(AComponent);)
type TOperation = (opInsert, opRemove);
O parâmetro “Operation”, cujo o tipo é TOpration representa os tipos de operações sobre os componentes registrados na lista de notificações (FFreeNotifies) cujo a ocorrência é propagada pelo método Notification. Os valores definidos por esse tipo são:
opInsert: Indica que um objeto (indicado pelo parânetro AComponent) recentemente foi criado.
opRemove: Indica que um objeto foi destruído.
type
TZNDisplayTimerProgress = class(TZNDisplayTimerUltimate)
private
FProgressBar: TProgressBar;
procedure SetProgressBar(const Value: TProgressBar);
{ Private declarations }
Protected
(*Estamos fazendo herança do método “Notification” e sobrescrevendo ele. *)
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
public
{ Public declarations }
published
property ProgressBar: TProgressBar read FProgressBar write SetProgressBar;
end;
Testamos através do parâmetro “AComponent” se a notificação ocorrida se refere ao campos FProgressBar, testamos também a operação, caso seja de remoção do componente, limpamos a referência (ao endereço de memória) do componente atribuindo “nill”.
procedure TZNDisplayTimerProgress.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
//Remove a referência quando o componente externo é destuido
if (AComponent = FProgressBar) and (Operation = opRemove) then
FProgressBar := nil;
end;
Agora vou ter que fazer uma pequena alteração na superclasse (TZNDisplayTimerUltimate), o método privado “SetResetTimeValue (const Value: Integer);” deve ficar na seção “protected”e ser marcado como “virtual”. Dei um mole nesse ponto, mais nada que não possa ser reajustado sem grandes traumas Logo, voltando a unit “TZNDisplayTimerUltimate”, faça a alteração conforme exemplo abaixo:
protected
FTimer: TTimer;
function GetEnabledTimer: Boolean; virtual;
function GetInterval: Integer; virtual;
procedure SetEnabledTimer(const Value: Boolean); virtual;
procedure SetInterval(const Value: Integer); virtual;
procedure UpdateClock(Sender: TObject); virtual;
procedure UpdateCurrentTime(const Multiplicador: Integer); virtual;
(* Este método estava na private, nesnte momento o movi para
a seção "privaate"*).
procedure SetResetTimeValue(const Value: Integer); virtual;
public
constructor Create(AOwner: TComponent); override;
Recompile a package, e salve. Voltemos a unit do “ZNDisplayTimerProgress” para digitarmos a assinatura.
ype
TZNDisplayTimerProgress = class(TZNDisplayTimerUltimate)
private
FProgressBar: TProgressBar;
procedure SetProgressBar(const Value: TProgressBar);
{ Private declarations }
protected
(* Este método estava na private, nesnte momento o movi para
a seção "privaate"*)
procedure SetResetTimeValue(const Value: Integer); override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
public
{ Public declarations }
published
property ProgressBar: TProgressBar read FProgressBar write SetProgressBar;
end;
Agora fazendo o polimorfismo, implementaremos o corpo do método ...
procedure TZNDisplayTimerProgress.SetResetTimeValue(const Value: Integer);
begin
inherited SetResetTimeValue(Value);
if not Assigned(FProgressBar) then Exit;
FProgressBar.Min := 1;
FProgressBar.Max := Value;
end;
Mais um polimorfismo: “UpdateCurrentTime”, neste método vamos incrementar a ProgressBar juntamente com o Label.
type
TZNDisplayTimerProgress = class(TZNDisplayTimerUltimate)
private
FProgressBar: TProgressBar;
procedure SetProgressBar(const Value: TProgressBar);
{ Private declarations }
protected
(* Este método estava na private, nesnte momento o movi para
a seção "privaate"*)
procedure SetResetTimeValue(const Value: Integer); override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure UpdateCurrentTime(const Multiplicador: Integer); override;
public
{ Public declarations }
published
property ProgressBar: TProgressBar read FProgressBar write SetProgressBar;
end;
Codificando o corpo do método ....
procedure TZNDisplayTimerProgress.UpdateCurrentTime(
const Multiplicador: Integer);
begin
inherited UpdateCurrentTime(Multiplicador);
if not Assigned(FProgressBar) then Exit;
FProgressBar.Position := CurrentTimeValue;
end;
Agora re-compile apackage “ZnConstruindoComponenetes” .....
Re-instale, clickando em Instal ...
Para testar o que acabamos de construir, feche o projeto da package, no menu File ►Close All. Em seguida inicie uma nova aplicação no menu File ► New ►Application.
Adicione os seguintes componentes no Form1:
1- Um ProgressBar (Palheta Win32)
Configure as seguintes propriedades:
Left = 8
Top = 80
Width = 350
Font.Color = clNavy
Font.Height = 24
Font.Name = 'MS Sans Serif'
Font.Style = [fsBold]
2- Um componente ZNDisplayTimerProgress (Palheta EstacaoZn).
Configure as seguintes propriedades:
Left = 232
Top = 24
Width = 3
Height = 13
ResetTimeValue = 5
Interval = 1000
ProgressBar = ProgressBar1
As propriedades dos componentes são configuradas no Object Inspector.
3- Um BitBtn (palheta additional).
Configure as seguintes propriedades:
Name = btnAtivar
Left = 16
Top = 16
Width = 75
Height = 25
Caption = '&Ativar'
4- Um SpinEdit (Palheta Samples)
Configure as seguintes propriedades:
Left = 112
Top = 16
Width = 77
Height = 22
5 – Um ListBox (Palheta Standard):
Configure as seguintes propriedades:
Left = 8
Top = 108
Width = 353
Height = 229
Codificando o programa
Primeiro o evento OnCreate do Form1
procedure TForm1.FormCreate(Sender: TObject);
begin
SpinEdit1.Value := 10;
end;
Segundo o evento OnChange do SpinEdit1
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
ZNDisplayTimerProgress1.ResetTimeValue := SpinEdit1.Value;
end;
Terceiro o evento OnClick do btnAtivar
procedure TForm1.btnAtivarClick(Sender: TObject);
begin
ZNDisplayTimerProgress1.EnabledTimer := not ZNDisplayTimerProgress1.EnabledTimer;
end;
Quarto o evento OnExecute do ZNDisplayTimerProgress1
procedure TForm1.ZNDisplayTimerProgress1Execute(Sender: TObject);
begin
ListBox1.Items.Add('Disparada Ação ' + TimeToStr(Now));
end;
Segue todo o código da Unit referente ao programa teste:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, Buttons, ComCtrls, ZNDisplayTimerUltimate,
ZNDisplayTimerProgress;
type
TForm1 = class(TForm)
ZNDisplayTimerProgress1: TZNDisplayTimerProgress;
ProgressBar1: TProgressBar;
btnAtivar: TBitBtn;
SpinEdit1: TSpinEdit;
ListBox1: TListBox;
procedure btnAtivarClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure ZNDisplayTimerProgress1Execute(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnAtivarClick(Sender: TObject);
begin
ZNDisplayTimerProgress1.EnabledTimer := not ZNDisplayTimerProgress1.EnabledTimer;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SpinEdit1.Value := 10;
end;
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
ZNDisplayTimerProgress1.ResetTimeValue := SpinEdit1.Value;
end;
procedure TForm1.ZNDisplayTimerProgress1Execute(Sender: TObject);
begin
ListBox1.Items.Add('Disparada Ação '+ TimeToStr(Now));
end;
end.
Para conferir também, segue a unit onde criamos o componente deste artigo:
unit ZNDisplayTimerProgress;
interface
uses
SysUtils, Classes, ZNDisplayTimerUltimate, ComCtrls;
type
TZNDisplayTimerProgress = class(TZNDisplayTimerUltimate)
private
FProgressBar: TProgressBar;
procedure SetProgressBar(const Value: TProgressBar);
{ Private declarations }
protected
(* Este método estava na private, nesnte momento o movi para
a seção "privaate"*)
procedure SetResetTimeValue(const Value: Integer); override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure UpdateCurrentTime(const Multiplicador: Integer); override;
public
{ Public declarations }
published
property ProgressBar: TProgressBar read FProgressBar write SetProgressBar;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('EstacaoZn', [TZNDisplayTimerProgress]);
end;
{ TZNDisplayTimerProgress }
procedure TZNDisplayTimerProgress.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited;
//Remove a referência quando o componente externo é destuido
if (AComponent = FProgressBar) and (Operation = opRemove) then
FProgressBar := nil;
end;
procedure TZNDisplayTimerProgress.SetProgressBar(
const Value: TProgressBar);
begin
if FProgressBar <> Value then
begin
FProgressBar := Value;
if FProgressBar <> nil then
FProgressBar.FreeNotification(Self);
end;
end;
procedure TZNDisplayTimerProgress.SetResetTimeValue(const Value: Integer);
begin
inherited SetResetTimeValue(Value);
if not Assigned(FProgressBar) then Exit;
FProgressBar.Min := 1;
FProgressBar.Max := Value;
end;
procedure TZNDisplayTimerProgress.UpdateCurrentTime(
const Multiplicador: Integer);
begin
inherited UpdateCurrentTime(Multiplicador);
if not Assigned(FProgressBar) then Exit;
FProgressBar.Position := CurrentTimeValue;
end;
end.
Artigo completo (View Full Post)