Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Este um assunto bastante explorado, contudo, o mercado sempre se renova, novos profissionais surgem vidos por informao. Portanto neste artigo, abordarei os meandros da orientao por objetos na programao em Delphi. Costumo dizer que a orientao a objetos no a soluo para todos os problemas. H um ditado que retrata muito bem isto: "quando a nica ferramenta que temos na mo um martelo, tudo vira prego". A idia da OOP agregar a modelos de programao estruturados e modular paradigmas que trazem benefcios objetivos.
Pilares da OOP.
Classes e Objetos
Eu poderia aqui fazer uma grande dissertao acadmica falando de classes e objetos, os comparando a bolos, carros, pessoas etc.. Acredito que depois de mais de 30 anos ningum agenta mais tais analogias. Classes representam o prottipo para objetos em memria (instncia). Classes determinam todo o comportamento de um objeto, em outras palavras, um objeto uma representao fsica de uma classe, assim como uma edificao a representao de uma planta. A principal motivao que impulsiona a orientao por objetos a criao de classes que representam uma funcionalidade especfica, uma tarefa determinada, ou seja, para cada classe um objeto, para cada objeto uma responsabilidade, com isto, alcanamos o que chamamos de coeso. Uma classe deve possuir uma classe ancestral obrigatoriamente, contudo no Delphi no necessrio declarar esta herana explicitamente, pois qualquer classe quando declarada sem sua classe ancestral automaticamente herda da classe TObject, que a classe progenitora de todas as classes que compem a VCL (Visual Component Library).
Um prottipo de objeto (classe) pode ser composto de campos (Fields), propriedades (Property) e mtodos (procedures e functions). Toda classe deve possuir ao menos dois mtodos, um mtodo construtor e outro destrutor. Os mtodos construtores de uma classe so declarados atravs da palavra reservada constructor e geralmente so chamados de Create. Mtodos construtores so mtodos especializados, pois alocam toda a memria necessria criao (instanciao) de um objeto, retorna a referncia para este objeto, e o local recomendado para inicializao de campos, onde todos os membros so inicializados com nil ou string vazia. Dica: caso algo de errado acontea durante a construo do objeto constructor automaticamente seu mtodo destrutor ser invocado. Mas ao ser observado a estrutura do construtor na classe base TObject, ser constatado que o mesmo no possui instruo alguma:
constructor TObject.Create; begin end;
Todo construtor automaticamente chama o mtodo NewInstance da classe Tobject que invoca o mtodo InitInstance. Por sua vez, InitInstance usa a funo InstanceSize do objeto para determinar quanto em bytes necessrio para alocar memria para o objeto. Nunca o mtodo NewInstance ou InitInstance devero ser invocados diretamente.
class function NewInstance: TObject; virtual; class function InitInstance(Instance: Pointer): TObject; class function InstanceSize: Longint;
Observe que o mtodo NewInstance virtual, portanto pode ser sobreposto em situaes onde houver a necessidade de modificao da forma com os recursos (memria) so alocados. Uma vez que o NewInstance seja sobreposto para customizao da alocao de memria, o FreeInstance tambm dever ser sobreposto, pois os recursos alocados precisam ser dispensados. Contudo, no frigir dos ovos objetivamente, no o mtodo Create quem aloca memria diretamente, ele apenas um estimulador automtico dos mtodos NewInstance e InitInstance. O mtodo destrutor de uma classe tem um papel inverso ao construtor, sua tarefa desalocar os recursos que o construtor demandou. Todo mtodo destrutor de um objeto chama automaticamente o mtodo FreeInstance usa tambm o InstanceSize para dispensar a memria alocada pelo NewInstance para o objeto.
Contudo isto, se justifica que os mtodos construtor e destrutor de Tobject estejam vazios, pois so apenas encadeadores dos verdadeiros mtodos que realizam as tarefas de alocar e desalocar memria para os objetos.
Declaraes Forward
Freqentemente quando trabalhamos com classes comum nos depararmos com a situaes de mtua dependncia, ou seja, duas classes que fazem referncia uma as outras:
TClassA = class B: TclassB; End; TclassB = class A: TclassA; End; //erro aqui!
Um erro em tempo de desenvolvimento seria exibido, pois a classe TclassA usa (associao) a classe TclassB que est declara aps a classe, ou seja, o compilador ainda no tomou cincia da existncia da classe TclassB, pois a compilao top-down (de cima para baixo). Para resolver esta mtua dependncia, usamos uma declarao forward acima da classe TclassA:
TclassB = class; //declaracao forward TclassA = class B: TclassB; End; TclassB = class A: TclassA; End;
muito comum confundir declaraes forward com a de classes do tipo Tsample = class end. So coisas totalmente distintas.sdfsdfsdfsdfsdfsdfsdf.
Meta Classes
Meta Classes so estruturas referncia para classes, que podem ser utilizadas no cdigo para aumentar a flexibilidade. A declarao de um tipo referencia de classe feita atravs da diretiva class of.
TMetaCliente = class of TCliente;
Com isto, agora temos em TMetaCliente, uma referncia a classe TCliente, ou seja, TMetaCliente poder ser exigido em operaes que exijam o tipo da classe e no sua instancia. Observe este exemplo prtico:
type TBaseClass = class end; TMetaBaseClass = class of TBaseClass; TCliente = class(TBaseClass) Nome: string; end; TFuncionario = class(TBaseClass) Matricula: string; end;
Observe que o construtor pode ser invocado atravs de uma varivel referncia de classe.
function TForm1.GetObjectInstance(Meta: TMetaBaseClass): TBaseClass; begin result := Meta.Create; end;
Com isso podemos criar instancias de objetos genricos sem conhecer suas estruturas originais.
var Cliente: TCliente; Funcionario: TFuncionario; begin Cliente := GetObjectInstance(TCliente) as TCliente; Funcionario := GetObjectInstance(TFuncionario) as TFuncionario; end;
Herana
Herana realmente um mecanismo poderoso da orientao a objetos mas, objetivamente no a panacia tecnolgica. Neste observaremos que valorizam essa prtica e outras as quais ela trs prejuzos. Herana agrega evidente reaproveitamento de cdigo, onde verdadeiras estruturas codificadas podem se especializadas em uma nova classe. Quando falamos em herana dois termos precisam ser esclarecidos: Generalizao e Especializao.
Generalizao um grau de abstrao de alto nvel, representada atravs de classes que servem de base polimrfica e/ou de implementao para classes descendentes. No Delphi isto est presente em toda a arquitetura da VCL. TAnimal generalizao de todas as classes que descendem de TAnimal. A Generalizao pode ser classificada em trs tipos: disjunta, sobreposta, completa ou incompleta. Especializao a herana propriamente dita, quando reaproveitamos estruturas j declaradas por uma classe ancestral. TDog uma especializao de TAninal; A especializao de uma classe sempre total, ou seja, ao herdar de uma determinada classe, voc assumir todos os seus membros, sem exceo. Classes especializadas podem ganhar flexibilidade adicional quando usada associada a mtodos no estticos, que veremos em seguida. A arquitetura da linguagem Delphi no foi projetada de forma a permitir especializar mais de uma classe simultaneamente herana mltipla. Acredito que esta regra esteja em total conformidade com os paradigmas da orientao a objetos.
Efeitos colaterais so caracterizados por mudanas no cdigo que afetam o funcionamento de outras classes que utilizam desta implementao. Um exemplo tpico seria uma classe que usa um servio de uma outra classe que calcula um determinado imposto. Na evoluo do projeto, muito comum ocorrem mudanas, e uma destas mudanas afetou a forma de como o imposto calculado para uma nica situao especfica. Ao altera este comportamento, voc atenderia a demanda especfica, mas faria com que todas as outras classes que utilizam deste servio deixem de funcionar. Para minimizar estes reflexos no cdigo, devemos reduzir o acoplamento entre as classes usando associaes em vez de herana. Associao entre classes estabelecem uma relao lgica que classificada de duas formas: Agregao ou Composio.
Interfaces
Interface uma ferramenta da orientao a objetos extremamente poderosa. Grande novidade no Delphi 3, Interfaces propem-se a estabelecer uma camada abstrata que determina uma estrutura ou contrato para um objeto. Isto significa que, analogamente a um exemplo real como a de construo de moradias, as necessidades expostas por um processo licitatrio seria nossa interface, ou seja, toda empresa que desejasse participar do processo dever estar em conformidade com as necessidades determinadas no documento de licitao. Com isto temos uma abstrao total de qual empresa objeto efetuar a tarefa.
Ilicitacao = interface procedure DocumentarProjetos; procedure ElicitarRequisitos; procedure ModelacaoUML; end; TEmpresaA = procedure procedure procedure end; TEmpresaB = procedure procedure procedure end; class(TinterfacedObject, Ilicitacao) DocumentarProjetos; ElicitarRequisitos; ModelacaoUML;
Neste exemplo, usamos uma interface que declara o mtodo ExecuteCalc, que ser implementado por vrias classes, cada qual a sua necessidade.
type ICalculator = interface function ExecuteCalc(a,b: Double): Double; end; TSum = class(TInterfacedObject, ICalculator)
public function ExecuteCalc(a, b: Double): Double; end; TDivide = class(TInterfacedObject, ICalculator) public function ExecuteCalc(a, b: Double): Double; end; TMultiply = class(TInterfacedObject, ICalculator) public function ExecuteCalc(a, b: Double): Double; end; TSubtraction = class(TInterfacedObject, ICalculator) public function ExecuteCalc(a, b: Double): Double; end; implementation uses SysUtils; function TSum.ExecuteCalc(a, b: Double): Double; begin result := a +b ; end; function TDivide.ExecuteCalc(a, b: Double): Double; begin if b = 0 then begin result := 0; raise Exception.Create('Diviso por Zero !!!!'); end else result := a / b; end; function TMultiply.ExecuteCalc(a, b: Double): Double; begin result := a * b; end; function TSubtraction.ExecuteCalc(a, b: Double): Double; begin result := a - b; end;
Cliente:
var a, b, rSum, rMult: Double; CalcSum, CalcMult: ICalculator; begin
a := 10; b := 20; CalcSum := TSum.Create; rSum := CalcSum.ExecuteCalc(a, b); //resultado da soma rMult := CalcMult.ExecuteCalc(a, b); //resultado da multiplicacao end;
Costumo usar este exemplo com meus alunos, onde eu digo: ...Eu quero um carro. Com esta afirmativa eu coloquei uma necessidade num contexto geral, no me preocupando com os detalhes envolvidos no que eu pedia. Isto uma interface, ou seja, um veculo pra ser entendido como carro precisa de requisitos mnimos, como pneus, motor, volante e como diz minha me: carro pro seu pai basta ter a ignio !. A afirmativa no detalhou qual carro se deseja, isso quer dizer que qualquer objeto concreto (TMercedez) que tenha as caracterstica de um carro (ICarro) solucionaria nossa questo.
type ICarro = interface procedure SetModelo(const Value: string); function GetModelo: string; procedure Ligar; property Modelo: string read GetModelo write SetModelo; end; TMercedez = class(TInterfacedObject, ICarro) private FModelo: string; public procedure SetModelo(const Value: string); function GetModelo: string; constructor Create; published procedure Ligar; property Modelo: string read GetModelo write SetModelo; end; implementation uses Dialogs; constructor TMercedez.Create; begin inherited; FModelo := 'Mercedez'; end; function TMercedez.GetModelo: string; begin result := FModelo; end; procedure TMercedez.Ligar; begin ShowMessage('Motor ligado!'); end;
Interfaces so componentes indispensveis a uma boa abstrao e reduo de acoplamento. Interfaces no Delphi obedecem a uma hierarquia que tem como base a interface Iinterface. Toda interface escrita em Delphi herda direta ou indiretamente de IInterface, portanto, para uma interface que ter um papel de automao (COM Component Object Model) dever ter IUnknown como interface base. Esta diviso hierrquica atende apenas para fins de organizao, visto que Iunknown explicitamente equivalente a IInterface
IUnknown = IInterface;
Interfaces possuem um mecanismo de contagem de referncia, o que permite o autogerenciamento no que diz respeito aos recursos de memria. Isto elimina a necessidade de liberaes explicitas de objetos atravs de Free, Destroy ou FreeAndNil. {codigo IInterface e mtodos} Interfaces so similares a classes abstratas, que possuem mtodos abstratos no implementados. Uma interface no implementa mtodo algum, ela apenas os declara, os publica, deixando a cargo das classes concretas que os implementam.
Implements
Use a diretiva implements para delegar property da classe a implementao dos mtodos de uma interface.
type IInterfaceA = interface procedure Sample; end; TMyClass = class(TInterfacedObject, IInterfaceA) FMyInterface: IInterfaceA; property MyInterface: IInterfaceA read FMyInterface implements IInterfaceA; end; TConcrete = class(TInterfacedObject, IInterfaceA) public procedure Sample; end; procedure TForm1.Button1Click(Sender: TObject); var MyClass: TMyClass;
InterfaceA: IInterfaceA; begin MyClass := TMyClass.Create; MyClass.FMyInterface := TConcrete.Create; InterfaceA := MyClass; InterfaceA.Sample; end;
Polimorfismo
Polimorfismo o principal recurso que justifica o uso de herana no seu cdigo. Polimorfismo vrias formas uma tcnica que permite o aumento da abstrao na chamada a mtodos de um objeto. Atravs de uma cadeia de heranas, mtodos so herdados da classe progenitora, ocasionando que todas as classes possuam o mesmo mtodo, mas possuam efeitos diferenciados.
TAnimal procedure end;
O polimorfismo caracterizado no cdigo quando usamos as diretivas virtual ou dynamic e override nos membros da classe. Isto indica que o mtodo sinalizado como virtual ou dynamic um membro que pertence a uma cadeia polimrfica, ou seja, as classes que herdarem mtodos com estas diretivas podem adicionar novas funcionalidades a eles.
TAnimal procedure end;
Walk;virtual;
THuman = class(TAnimal) procedure Walk;override; end; TDog = class(TAnimal) procedure Walk;override; end;
Uma vez criada esta cadeia polimrfica para o mtodo Walk ganhamos uma maior flexibilidade ao codificar nossas rotinas, uma vez que podemos abstrair o verdadeiro mtodo a ser invocado.
procedure DoWalk(Animal: Tanimal); begin Animal.Walk; end; // begin DoWalk(Tdog.Create); end;
Com isto, em tempo de execuo, o Delphi ir decidir qual mtodo ir invocar, este recurso chamado de ligao tardia ou late binding, pois em tempo de desenvolvimento no possvel se determinar qual instncia ser passada no mtodo DoWalk pode ser qualquer classe que pertena a hierarquia de Tanimal;
A palavra reservada inherited pode ser usada em qualquer situao onde seja necessrio identificar que o mtodo a ser invocado no o da classe atual, mas sim, o da classe ancestral. Mas neste exemplo no foi utilizado para no confundir nosso foco central. No cdigo seguinte, feita uma quebra intencional na classe TFourthClass:
type TFirstClass = class procedure Sample;virtual;
end; TSecondClass = class(TFirstClass) procedure Sample;override; end; TThirdClass = class(TSecondClass) procedure Sample;override; end; TFourthClass = class(TThirdClass) procedure Sample;virtual; end;
O mtodo sample na classe TfourthClass estabeleceu um novo teto polimorfico. E a chamada teria um efeito inusitado:
var FirstClass: TFirstClass; begin FirstClass := TFourthClass.Create; FirstClass.Sample; end; Resultado: Third!
O que aconteceu que o mtodo sample da classe TfirstClass no pertence mais a cadeia, pois foi declarado como virtual e quebrou a seqncia hierrquica. Isto fez com que em tempo de execuo fosse escolhido para execuo o mtodo da classe hierarquicamente acima. Se sample fosse override o mtodo de TfourthClass seria executado.
Sobrecarga de Mtodos
Uma das regras na declarao de classes que no podem haver dois ou mais mtodos com o mesmo nome, ainda que com parmetros diferentes.
type TSample = class procedure Calc(Value: integer); function Calc(a,b: integer): integer; end; implementation uses Dialogs, SysUtils; procedure TSample.Calc(Value: integer); begin ShowMessage(IntToStr(Value)); end; function TSample.Calc(a,b: integer): integer; begin result := a + b; end;
//erro aqui!!
Para permitir essa flexibilidade, utilizada a diretiva overload nos mtodos envolvidos.
type TSample = class procedure Calc(Value: integer); overload; function Calc(a,b: integer):integer; overload; end;
O compilador usar os critrios de nmero de parmetros, tipo dos parmetros para determinar qual mtodo dever ser invocado. No caso acima, o fator que determinou o mtodo a ser usado em cada situao foi o nmero de parmetros, uma vez que ambos requerem parmetros de tipos inteiros. O overload tambm pode ser usado em casos de mtodos herdados, para evitar seu ocultamento.
TSample = class procedure Calc(Value: integer);overload; function Calc(a,b: integer): integer;overload; procedure Test;
Com isto, para a classe Tsample1 agora tempos disponvel o mtodo Test herdado da classe ancestral e Test(s: string), novo mtodo sobrecarregado em Tsample1. Na situao a seguir, declarado dois mtodos sobrecarregados, ambos requerem parmetros inteiros, porm de tipos diferentes:
TCalculator = class procedure Calc(si: ShortInt);overload; procedure Calc(si: SmallInt);overload; end;
At aqui nenhum problema que possa nos impedir compilar, mas o que faria o compilador caso eu invoque Calc com o seguinte parmetro:
var Calculator: TCalculator; begin Calculator := TCalculator.Create; Calculator.Calc(29); end;
O compilador ir avaliar o tamanho (range) suportado por cada tipo em ambos parmetros. Shortint Smallint -128..127 signed 8-bit -32768..32767 signed 16-bit
Como o valor passado no parmetro potencialmente poderia ser suportado por ambos os tipos, ser escolhido aquele cujo tipo do parmetro possui o menor intervalo.
Mtodos Abstratos
Mtodos declarados como abstratos usam a diretiva abstract. Este tipo de mtodo no possui implementao na classe onde foi declarado, deixando isto a cargo das classes descendentes. O uso da diretiva abstract sempre vir acompanhada de um virtual ou dynamic, para permitir sobrescrever o mtodo. Classes que especializam classes com mtodos abstratos no so obrigadas a implement-los.
procedure Sample;virtual;abstract;
As classes descendentes podem sobrepor override o mtodo abstrato e implementlo. Ao invocar um mtodo abstrato que no tenha sido sobreposto a seguinte exceo ser erguida:
| Abstract error |
Mtodos abstratos so usados em situaes onde importante que todas as classes de uma cadeia hierrquica tenha um determinado mtodo, como por exemplo um conjunto de classes que modelam o mundo animal, teramos a classe TMamifero com o mtodo abstrato Walk, cuja implementao estaria a cargo das classes descendentes.
Type Casts
Type Cast um mecanismo do Delphi Language que permite a converso pontual de um determinado tipo. Em conjunto com polimorfismo, type casts tornam-se indispensveis na codificao de rotinas genricas. Type Casts so classificados em dois tipos: Value Type Casts e Variable Type Casts. Observe neste exemplo a aplicao de um Variable Type Cast:
TAnimal = class Age: byte; end; TDog = class(TAnimal) Pedigree: string; end; TCat = class(TAnimal) Name: string; end; var Animal: TAnimal; begin Animal := TCat.Create; TCat(Animal).Name := 'Lissi'; end;
//type cast
Observe que foi feita a converso de uma varivel do tipo Tanimal para o tipo Tcat. Isso s foi possivel porque Tanimal ancestral comum a classe TCAt, em outras palavras, elas pertencem a mesma cadeia hierrquica. Value Type Casts so mais incomuns, no aparecem com tanta freqncia. Veja este exemplo:
var ascii: integer; begin
ascii := Integer('Z');
A varivel ascii receber o valor da tabela ASCII correspondente a letra Z. Neste outro exemplo
var obj: TObject; P: Integer; begin Obj := TObject.Create(nil); P := Integer(Obj); //pega o endereo de Obj ShowMessage(TObject(P).ClassName); end;
Encapsulamento
Todo objeto dever ser responsvel por seus prprios comportamentos, com isto garantir a integridade daquilo que o faz funcionar de maneira regular. Encapsulamento uma tcnica OO para ocultar determinados membros de um objeto. Conhecido como Data Hidding o encapsulamento utilizado como mecanismo regulador de acesso aos dados de um objetos. O encapsulamento torna as mudanas no comportamento interno de um objeto transparentes para outros objetos e auxilia na preveno de problemas de efeitos colaterais no cdigo. Pegue o que varia no seu cdigo e encapsule! No necessrio se preocupar em saber como uma tarefa realizada, portanto que seja feita! Quando usamos o mtodo Ligar do objetos carro uma srie de outros mtodos sero invocados internamente no objeto. Estes detalhes do funcionamento no No Delphi existem clusulas que so utilizadas para demarcar regies de uma classe, que determinam qual o grau de visibilidade de cada membro dentro destas regies, so elas Public, Protected, Private, Published, Strict Private, Strict Protected. Pblico A propriedade ou mtodo da classe pode ser acessado por todas as demais entidades do sistema. Privado O acesso aos membros da classe s permitido aos mtodos da prpria classe. Protegido O acesso aos membros da classe s permitido a classes da mesma hierarquia. Published
Regras de visibilidade idnticas ao public, sendo que alm disso; aparece no object inspector (no caso de componentes do delphi ). Um dos questionamentos que caberiam seria: O que encapsular? Quando encapsular? Pra que encapsular? Evidentemente todas estas perguntas precisam e devem ser respondidas e esclarecidas. A linguagem Delphi fornece um recurso muito utilizado quando trabalhamos com encapsulamento, as Propertys, assim com os campos de uma classe, as propriedades criam um mecanismo de leitura e escrita para um campo privado classe. Muito mais eficiente que a declarao simples de um campo, propriedades permitem uma forma concreta de controle do que pode ser escrito e lido de um determinado campo. Quem nunca teve um erro em sua frente Invalid property value quando entrou com um valor incompatvel a uma propriedade ainda que acidentalmente atravs do object inspector. Propriedades so declaradas geralmente nas sees public ou published de uma classe e obedece a seguinte semntica:
property NoDaPropriedade: Tipo read FNoDaPropriedade write FnoDaPropriedade;
Get e Sets
Uma vez que as propriedades promovem uma forma de encapsulamento e controle de leitura e escrita, podemos substituir a leitura e escrita direto do campo por mtodos Get e Set.
Property Nome: string read GetNome write SetNome;
Os mtodos GetNome e SetNome sero usados pela property para as tarefas de leitura e escrita na varivel encapsulada. Isto nos permite criar um estgio de verificao preliminar a escrita ou leitura do campo.
O Operador Index
Os mtodos Get e Set so instrumentos eficientes no controle da leitura e escrita por parte de uma propriedade, mas pode ser tornar um elemento perturbador no que diz respeito a legibilidade de uma classe, por isso, possvel compartilhar um nico par de mtodos Get e Set entre diversas propriedades indexadas. Para declarar esta funcionalidade adicione a diretiva index seguido de um inteiro entre -2147483647 e 2147483647 em todas as propriedades que iro usar os mtodos compartilhados:
type TAluno = class private
FNome: string; FEmail: string; FTelefone: string; function GetData(const Index: Integer): string; procedure SetData(const Index: Integer; const Value: string); public property Nome: string index 0 read GetData write SetData; property Email: string index 1 read GetData write SetData; property Telefone: string index 2 read GetData write SetData; end; implementation function TAluno.GetData(const Index: Integer): string; begin case Index of 0: result := FNome; 1: result := FEmail; 2: result := FTelefone; end; end; procedure TAluno.SetData(const Index: Integer; const Value: string); begin case Index of 0: FNome := Value; 1: FEmail := Value; 2: FTelefone := Value; end; end;
Dica: Na declarao de propriedades, digite a penas at o tipo, encerrando em seguida com ponto e virgula. Logo aps pressione ctrl+shift+c e o restante da estrutura da propriedade ser criada automaticamente.
Propriedades Indexadas
Propriedades podem ser declaradas de forma a permitir a leitura seqencial de uma estrutura ordinal como um array. A declarao bem simples, basta informa a lista de parmetros ao lado do nome da propriedade:
interface uses Classes; type TCliente = class private FTelsList: TStringList; function GetTelefones(Index: integer): string; procedure SetTelefones(Index: integer; const Value: string); public constructor Create; destructor Destroy; override;
property Telefones[Index: integer]: string read GetTelefones write SetTelefones; end; implementation uses SysUtils; constructor TCliente.Create; begin inherited; FTelsList := TStringList.Create; end; destructor TCliente.Destroy; begin FreeAndNil(FTelsList); inherited; end; function TCliente.GetTelefones(Index: integer): string; begin result := FTelsList.Strings[Index]; end; procedure TCliente.SetTelefones(Index: integer; const Value: string); begin FTelsList.Strings[Index] := Value; end;
Para Ter acesso a leitura e escrita na propriedade Telefones informe o indice desejado:
var Cliente: TCliente; begin Cliente := TCliente.Create; Cliente.Telefones[0] := '21 2223-1234'; end;