Bom esse é apenas um texto traduzido pelo Translate do Google, então não sejam muito críticos com o português, tem por objetivo servir com incentivo ao uso desse recurso em Harbour xBase ( Clipper).
Texto do site : http://www.kresin.ru/en/hrbfaq_3.html#Doc3 o mesmo foi parcialmente traduzido mas já ajuda.
—[Texto parcialmente traduzido ]—
A implementação de OOP (programação orientada a objeto) é talvez a primeira coisa que os desenvolvedores do Harbour adicionaram à linguagem padrão. As versões mais recentes do Clipper continham elementos da OOP. Havia várias classes pré-definidas, você poderia criar seus objetos, mas não era possível criar classes próprias. Houve, no entanto, algumas das possibilidades não documentadas, que permitiram fazê-lo, várias bibliotecas de terceiros que implementam o OOP para Clipper foram baseadas nelas, as mais conhecidas delas são Fivewin (mais precisamente, Objects.lib do Fivewin) e Class (y). Para usar as classes no Harbour você não precisa vincular bibliotecas adicionais, o suporte OOP é parte da linguagem e, como os fundos são parte do kernel, o Harbour oferece mais possibilidades do que o Clipper com todas as bibliotecas de terceiros .
So, to create a new class, use the command CLASS … ENDCLASS:
[CREATE] CLASS <cClassName> [ FROM | INHERIT <cSuperClass1> [, … ,<cSuperClassN>] ]
[ MODULE FRIENDLY ] [ STATIC ] [ FUNCTION <cFuncName> ]
[HIDDEN:]
[ CLASSDATA | CLASSVAR | CLASS VAR <DataName1>]
[ DATA | VAR <DataName1> [,<DataNameN>] [ AS <type> ] [ INIT <uValue> ]
[[EXPORTED | VISIBLE] | [PROTECTED] | [HIDDEN]] [READONLY | RO] ]
…
[ METHOD <MethodName>( [<params,…>] ) [CONSTRUCTOR] ]
[ METHOD <MethodName>( [<params,…>] ) INLINE <Code,…> ]
[ METHOD <MethodName>( [<params,…>] ) BLOCK <CodeBlock> ]
[ METHOD <MethodName>( [<params,…>] ) EXTERN <funcName>([<args,…>]) ]
[ METHOD <MethodName>( [<params,…>] ) SETGET ]
[ METHOD <MethodName>( [<params,…>] ) VIRTUAL ]
[ METHOD <MethodName>( [<params,…>] ) OPERATOR <op> ]
[ ERROR HANDLER <MethodName>( [<params,…>] ) ]
[ ON ERROR <MethodName>( [<params,…>] ) ]
…
[PROTECTED:]
…
[VISIBLE:]
[EXPORTED:]
…
[FRIEND CLASS <ClassName,…>]
[FRIEND FUNCTION <FuncName,…>]
[SYNC METHOD <cSyncMethod>]
ENDCLASS [ LOCK | LOCKED ]
Uma classe pode ser declarada como herdeira de um ou mais pais (especialmente multi-herança – herança múltipla que consideraremos mais tarde) com a ajuda da cláusula FROM ou INHERIT. Isso significa que ele recebe da classe pai todas as variáveis e métodos (exceto aqueles marcados como HIDDEN). Qualquer um dos métodos herdados pode ser substituído.
The parent method can be invoked as Super:<methodName>() or ::<cSuperClass>:<methodName>() (if it is not HIDDEN).
Eu quero mencionar uma peculiaridade interessante relacionada à herança. Se a classe filha tiver uma variável com o mesmo nome que o pai, cada objeto conterá duas variáveis com o mesmo nome. Um deles deve ser endereçado como obj: <varName>, o outro – obj: <cSuperClass>: <varName>..
Uma classe pode ser declarada como STATIC – isto é, não pode ser usada fora do módulo atual.
Uma classe pode ser declarada como “MODULE FRIENDLY” – isto significa que todas as classes e funções, que são declaradas no módulo atual, são seus amigos, ou seja, têm acesso a suas variáveis e métodos HIDDEN e PROTECTED. E já que estamos falando sobre as funções amigáveis, preste atenção nas cláusulas FRIEND CLASS e FRIEND FUNCTION – elas determinam que essas classes e funções sejam amigáveis.
As palavras FUNCTION <cFuncName> na declaração de uma classe a definem como uma classe escalar.
uma definição de variáveis de uma classe. Durante isto pode ser rigorosamente definido o tipo da variável AS <type> (Caractere, Numérico, Data, Logical, Codeblock, Nil), pode ser definido seu valor padrão inicial INIT <uValue> e definido Scope (Hidden, Protected, Exported , Somente leitura). O escopo pode ser definido para cada variável separadamente (na cláusula DATA) e para o grupo de variáveis e métodos:
– HIDDEN – variável ou método só estão disponíveis dentro da classe, onde são identificados e não são herdados;
– PROTECTED – estão disponíveis apenas para a classe, onde são determinados para os herdeiros;
– EXPORTED ( or VISIBLE ) – estão disponíveis por todos os lados;
– READONLY (ou RO) – se a variável é definida como EXPORTED, então a modificação de seu valor só pode ser feita a partir de sua classe e seus herdeiros, se como PROTECTED, então somente de sua classe.
As variáveis podem pertencer ao objeto e a toda a classe – as últimas são declaradas como CLASSDATA, CLASSVAR ou CLASS VAR. Essa variável está disponível a partir de qualquer objeto de uma classe como obj: <DataName>, mas é armazenada não nas áreas de objetos de dados, mas em um lugar, no campo dos dados da classe e, portanto, tem o mesmo valor para todos os objetos de uma classe. Há outra diferença na implementação de CLASSDATA, por um lado, e CLASSVAR ou CLASS VAR, por outro. Essas variáveis podem ser declaradas como COMPARTILHADAS. Isso significa que, se a classe pai tiver essa variável, as classes derivadas usarão a mesma cópia única dela, compartilhe-a. Se o mesmo SHARED não for declarado, as classes derivadas o herdarão da maneira usual, criando uma nova cópia da variável para cada classe do herdeiro. Assim, para o CLASSVAR e o CLASS VAR funciona, como foi descrito acima, e as variáveis CLASSDATA se comportam como SHARED, independentemente de você usar essa palavra no anúncio ou não. Foi pela compatibilidade retroativa com os programas Fivewin para Clipper.
Clause METHOD – esta é a definição dos métodos da classe. Em geral, se o método não for INLINE, não BLOCK e não VIRTUAL, após a definição da classe (após ENDCLASS) você deve colocar a implementação do método:
?
METHOD <methodname>( [<params,…>] ) CLASS <classname>
…
RETURN Self
</classname></params,…></methodname>
The method should return a reference to the object ( Self ), if it is used as a constructor.
A seguir, diferentes maneiras de declarar um método:
– INLINE permite que você coloque a implementação do método na mesma linha, onde é declarado. Este método pode ser usado quando o código é bastante pequeno, tornando o anúncio do método mais simples e demonstrativo.
– BLOCK semelhante ao INLINE, mas ele espera que o método do registro represente na forma de codeblock e permite que você envie um número arbitrário de parâmetros.
– EXTERN <funcName> ([<args, …>]) é aplicado, se a função externa <funcName> () implementar o que este método necessita.
– O SETGET é usado para os dados a serem calculados, ele permite que você use o nome do método como o nome da variável a ser lido / gravado. Por exemplo, se sua classe é uma variável, cujo valor não é desejável para instalar diretamente, uma vez que deve ter um determinado formato ou deve ser apenas em um determinado intervalo. Você define essa variável como HIDDEN para excluir sua mudança direta e define SETGET o método que será usado para definir o valor de uma variável, verificar o valor passado, formatá-lo como deveria ser e assim por diante. Agora, para definir o valor de uma variável, você escreve: obj: <methodName>: = <value>.
– VIRTUAL este método não faz nada. Pode ser usado para classes básicas de pais – neste caso, a realização concreta do método será feita nas classes de herdeiros.
– OPERATOR <op> é usado para sobrecarregar o operador <op> (“+”, “-“, …) – ou seja, quando a ação <op> no objeto no programa está ocorrendo, esta ação é implementada por o método, que é indicado com a palavra OPERATOR. Como parâmetro para o método, o segundo objeto participante da operação <op> é passado.
Outra maneira de declarar um método é configurá-lo com a ajuda de ERROR HANDLER ou ON ERROR. Este método é invocado no caso, se a classe de objeto à qual ele pertence, foi enviada uma mensagem não especificada para esta classe; Em outras palavras, um método ou variável que não está presente nesta classe está sendo chamado para. Você pode, obviamente, usar esse método para o propósito direto – para o tratamento de erros, mas existem maneiras mais interessantes de usá-lo. Assim, ele pode ser usado dinamicamente para simular a disponibilidade das variáveis do objeto, que não foram definidas na declaração de classe, e em diferentes objetos da mesma classe diferentes métodos e as variáveis podem ser simuladas.
SYNC METHOD é usado para sincronizar a execução dos métodos no aplicativo multithread. Se um método estiver marcado como SYNC, ele não executará dois ou mais threads simultaneamente para um objeto.
A opção LOCK ou LOCKED do ENDCLASS impede a modificação subseqüente desta classe, pode ser usada no desenho de grandes projetos do grupo de desenvolvedores, se você não quiser, que alguém hackeie sua classe.
Para criar e testar uma classe simples, basta escrever:
?
#include “hbclass.ch”
FUNCTION Main
Local obj := myFirstClass():New(3)
? obj:x // 3
?
RETURN Nil
CLASS myFirstClass
VAR x
METHOD New( n ) INLINE ( ::x := n, Self )
ENDCLASS
Anunciei aqui o método INLINE (interno) para fazer apenas o design da CLASS … ENDCLASS e não descrevo o método New (n) separadamente. Aqui devemos prestar atenção aos seguintes momentos:
– você deve incluir o hbclass.ch no prg, contendo a declaração da classe;
– variável de objeto dentro de um método deste objeto é precedido por duplo dois pontos :: x;
– o objeto dentro do método é chamado Self;
– o método usado para criar um novo objeto deve retornar uma referência a este objeto: Self;
– como criar um novo objeto: <className> (): <method> () (obj: = myFirstClass (): New (3)).
Now one more complicated example:
?
#include “hbclass.ch”
FUNCTION Main
Local obj1, obj2
obj1 := mySecondClass():New( 10,”Alex” )
? ” mySecondClass:”, mySecondClass():nKolObj
obj2 := mySecondClass():New( 11,”Mike” )
? “mySecondClass:”, mySecondClass():nKolObj
? obj1:x, obj2:x
?
RETURN Nil
CLASS myFirstClass
VAR x
METHOD New( n ) INLINE ( ::x := n, Self )
ENDCLASS
CLASS mySecondClass FROM myFirstClass
HIDDEN:
VAR cStringS
EXPORTED:
CLASS VAR nKolObj INIT 0
VAR cString1 INIT “Sample”
METHOD New( n, s )
ENDCLASS
METHOD New( n, s ) CLASS mySecondClass
Super:New( n )
::nKolObj ++
::cStringS := s
RETURN Self
Aqui apareceram esses novos recursos:
– temos duas classes, myFirstClass e mySecondClass, e o mySecondClass é o herdeiro do myFirstClass;
– duas variáveis, nKolObj e cString1, na declaração de uma classe os valores iniciais são atribuídos, eles permanecerão para qualquer novo objeto da classe;
– o método New da classe mySecondClass é descrito separadamente com a ajuda de METHOD <methodName> CLASS <className>;
– neste método Super: New (n) é usado para chamar um método com nome idêntico da classe pai para inicializar as variáveis do objeto herdadas de um pai.
– mySecondClass variável cStringS tem o atributo (escopo) HIDDEN (ou seja, só está disponível a partir de métodos da classe), e o restante das variáveis e o método de New marcado como EXPORTED (ou seja, acessível de qualquer lugar);
– no mySecondClass o CLASS VAR nKolObj apareceu – variável, que pertence não ao objeto, mas a toda a classe, aqui ela é usada como um contador de objetos dessa classe;
Outro exemplo mostrando as declarações de método como BLOCK e EXTERN:
?
#include “hbclass.ch”
function Main()
Local o1 := HSamp1():New()
Local o2 := HSamp2():New()
? o1:x, o2:x // 10 20
? o1:Calc( 2,3 ), o2:Mul( 2,3 ) // 60 120
? o1:Sum( 5 ) // 15
?
return nil
CLASS HSamp1
DATA x INIT 10
METHOD New() INLINE Self
METHOD Sum( y ) BLOCK {|Self,y|::x += y}
METHOD Calc( y,z ) EXTERN fr1( y,z )
ENDCLASS
CLASS HSamp2
DATA x INIT 20
METHOD New() INLINE Self
METHOD Mul( y,z ) EXTERN fr1( y,z )
ENDCLASS
Function fr1( y, z )
Local o := QSelf()
Return o:x * y * z
Observe a chamada de função QSelf (), ela retorna uma referência ao objeto, no contexto do qual ele está localizado, ou seja, o objeto que é chamado Self nos métodos da classe.
E aqui está um exemplo de uso do ERROR HANDLER:
?
#include “hbclass.ch”
FUNCTION Main
LOCAL oTable
oTable := Table():Create( “sample.dbf”, { {“F1″,”C”,10,0}, {“F2″,”N”,8,0} } )
oTable:AppendRec()
oTable:F1 := “FirstRec”
oTable:F2 := 1
oTable:AppendRec()
? oTable:nRec // 2
oTable:nRec := 1
? oTable:nRec // 1
? oTable:F1, oTable:F2 // “FirstRec” 1
?
RETURN nil
CLASS Table
METHOD Create( cName, aStru )
METHOD AppendRec() INLINE dbAppend()
METHOD nRec( n ) SETGET
ERROR HANDLER OnError( xParam )
ENDCLASS
METHOD Create( cName, aStru ) CLASS Table
dbCreate( cName, aStru )
USE (cName) NEW EXCLUSIVE
RETURN Self
METHOD nRec( n ) CLASS Table
IF n != Nil
dbGoTo( n )
ENDIF
RETURN Recno()
METHOD OnError( xParam ) CLASS Table
Local cMsg := __GetMessage(), cFieldName, nPos
Local xValue
IF Left( cMsg, 1 ) == ‘_’
cFieldName := Substr( cMsg,2 )
ELSE
cFieldName := cMsg
ENDIF
IF ( nPos := FieldPos( cFieldName ) ) == 0
Alert( cFieldName + ” wrong field name!” )
ELSEIF cFieldName == cMsg
RETURN FieldGet( nPos )
ELSE
FieldPut( nPos, xParam )
ENDIF
Return Nil
Isso mostra uma classe Table extremamente simplificada, todo objeto de oTable deve corresponder à tabela recém-criada ou aberta (método Open is omitted here) (arquivo dbf). Queremos nos referir aos campos da tabela quanto às propriedades (variáveis) de um objeto oTable. Mas não podemos inserir os nomes dos campos em uma declaração de classe, porque, em geral, eles não são conhecidos no momento da gravação da classe e são simplesmente diferentes para tabelas diferentes. Aqui ERROR HANDLER ajudará. Quando uma variável não existente (ou métodos) da classe é chamada, um erro é produzido e o método é chamado definido como ERROR HANDLER (ou ON ERROR). A partir desse método, você pode obter o mesmo nome inexistente da variável chamada com a ajuda da função __GetMessage (). Além disso, se houver uma tentativa de escrever algo nessa variável, o nome é precedido pelo símbolo “_” e o método é passado pelo valor registrado como o primeiro parâmetro. Tudo o resto eu acho que é claro a partir do exemplo.
No código acima, também vimos um exemplo do uso do método SETGET. Observe que uma chamada para o método SETGET nRec ocorre como uma variável do objeto. Se tentarmos defini-lo (oTable: nRec: = 1), o método obtém o valor definido como parâmetro e produz o movimento no banco de dados, mas se lemos (? OTable: nRec), ele simplesmente retorna o resultado Recno ()
Uma lista de funções para a manipulação de classes e objetos segue:
lExist := __objHasData( oObject, cName ) Retorna um valor booleano, especifica se o objeto oObject tem um nome de variável cName
lExist := __objHasMethod( oObject, cName ) Retorna um valor booleano, especifica se o objeto oObject tem o método com o nome cName
aNames := __objGetMsgList( oObject, [lData], [nClassType] ) Retorna uma matriz dos nomes de todas as variáveis e métodos de um objeto oObject; lData especifica que os métodos necessários (.F.) ou variáveis (.T., o valor padrão); nClassType especifica quais variáveis devem ser incluídas na lista. 3 valores são possíveis definidos no hboo.ch:
HB_MSGLISTALL 0
HB_MSGLISTCLASS 1 CLASS DATA
HB_MSGLISTPURE 2 е DATA
aNames := __objGetMethodList( oObject ) Retorna uma matriz dos nomes de todos os métodos de um objeto objeto
conveys := __objGetValueList( oObject, [aExcept] ) Retorna um array bidimensional de todas as variáveis do objeto oObject (nome e valor); aExcept – uma matriz dos nomes das variáveis, que não devem ser incluídas no resultado.
oObject := __ObjSetValueList( oObject, conveys ) Define o valor das variáveis do objeto oObject, transmite – array bidimensional de nome / valor
oObject := __objAddMethod( oObject, сMethodName, nFuncPtr ) Adiciona um método na classe já existente à qual o objeto pertence em oObject; сMethodName – o nome do método, nFuncPtr é um ponteiro para uma função que implementa este método (os ponteiros para as funções)
oHappy := HBClass():New( “THappy” )
__objAddMethod( oHappy, “Smile”, @MySmile() )
oHappy:Smile( 1 )
Static Function MySmile( nType )
Return Iif( nType==0,”:)”,”:):)” )
oObject := __objAddInline( oObject, cInlineName, bInline ) Adiciona o método inline na classe já existente à qual o objeto pertence em oObject; сInlineName – o nome do método, bInline – um codeblock que implementa este método.
oObject: = __objAddData (oObject, cDataName) Adiciona uma variável na classe já existente à qual o objeto pertence em oObject; cDataName – o nome da nova variável
oObject: = __objModMethod (oObject, сMethodName, nFuncPtr) Substitui a implementação do método na classe existente à qual o objeto pertence em oObject; сMethodName – o nome do método, nFuncPtr é um ponteiro para uma função que implementa esse método (consulte a descrição das funções __objAddMethod ())
oObject: = __objAddInline (oObject, cInlineName, bInline) Substitui a implementação do método inline em uma classe existente à qual o objeto pertence em oObject; сInlineName – o nome do método, bInline – um codeblock que implementa este método. (veja a descrição das funções __objAddInline ())
oObject: = __objDelMethod (oObject, сMethodName) Exclui o método ou o método sequencial com o nome de сMethodName a classe à qual o objeto pertence em oObject
oObject: = __objDelInline (oObject, сMethodName) Exclui o método ou o método sequencial com o nome de сMethodName a classe à qual o objeto pertence em oObject
oObject: = __objDelData (oObject, ÑDataName) Remove uma variável com o nome de ÑDataName da classe à qual o objeto pertence em oObject
lIsParent: = __objDerivedFrom (oObject, xSuper) Retorna um valor booleano, indica se a classe é xSuper (que pode ser definida como um objeto ou um nome de classe), um pai para a classe à qual o objeto pertence em oObject
oNovo: = __objClone (oSource) Clona um objeto.
xResult: = __objSendMsg (oObject, cName [, xParams …]) Enviar uma mensagem para um objeto oObject: pode ser usado para obter um valor de uma variável de objeto cName ou para definir um novo (neste caso, o nome da variável deve seja prefixado por “_”, ou para chamar um método de objeto.
—[ FIM Texto parcialmente traduzido ]—