A programação orientada a objetos (POO) no Harbour é feita usando comandos próprios como CLASS e ENDCLASS para criar moldes, definir dados e criar funções que pertencem aos objetos.
A biblioteca nativa que gerencia a POO no Harbour é a HBClass. Ela permite criar sistemas modulares, reutilizáveis e fáceis de manter.
1. Estrutura Básica de uma Classe
Para definir uma classe, usamos os comandos CLASS e ENDCLASS. Dentro dela, definimos as características (DATA) e os comportamentos (METHOD).
Exemplo de Sintaxe:
Código: Selecionar todos
#include "hbclass.ch"
CLASS Cliente
// Atributos (Variáveis da classe)
DATA ID INIT 0
DATA Nome INIT ""
DATA Saldo INIT 0.00
// Métodos (Funções da classe)
METHOD New( nId, cNome ) CONSTRUCTOR
METHOD Depositar( nValor )
METHOD VerSaldo()
ENDCLASS
2. Desenvolvendo os Métodos
Os métodos declarados dentro da classe precisam ser desenvolvidos fora dela. Usamos a sintaxe METHOD NomeDoMetodo() CLASS NomeDaClasse.

Nota Importante:
Dentro do método, usamos a palavra-chave :: (que significa self:) para acessar as variáveis e métodos do próprio objeto.
Código: Selecionar todos
// Método Construtor (Instancia o objeto)
METHOD New( nId, cNome ) CLASS Cliente
::ID := nId
::Nome := cNome
return Self
// Método para alterar o saldo
METHOD Depositar( nValor ) CLASS Cliente
if nValor > 0
::Saldo += nValor
endif
return NIL
// Método para exibir informações
METHOD VerSaldo() CLASS Cliente
? "Cliente:", ::Nome
? "Saldo Atual: R$", Transform( ::Saldo, "@E 999,999.99" )
return NIL
3. Instanciando e Usando o Objeto
Para criar e utilizar o objeto no seu programa principal, usamos o operador : (dois pontos).
Código: Selecionar todos
PROCEDURE Main()
LOCAL oCliente
// Criando uma nova instância da classe Cliente
oCliente := Cliente():New( 101, "Carlos Andrade" )
// Executando os métodos
oCliente:Depositar( 1500.50 )
oCliente:VerSaldo()
// Acessando um dado diretamente (se for público)
? "ID do Cliente consultado diretamente:", oCliente:ID
return
4. Encapsulamento (Controle de Acesso)
Por padrão, as variáveis DATA são públicas. Para proteger seus dados contra modificações diretas e indevidas, use as diretivas de visibilidade:
EXPORTED: (Padrão) Acessível por qualquer parte do programa.
PROTECTED: Acessível apenas pela própria classe e por suas subclasses (herança).
HIDDEN: Acessível estritamente apenas pela própria classe onde foi criada.
Código: Selecionar todos
CLASS ContaBancaria
PROTECTED:
DATA NumeroConta
DATA Saldo INIT 0
EXPORTED:
METHOD New( cNumero ) CONSTRUCTOR
METHOD ObterSaldo()
ENDCLASS
METHOD New( cNumero ) CLASS ContaBancaria
::NumeroConta := cNumero
return Self
METHOD ObterSaldo() CLASS ContaBancaria
return ::Saldo // Programa externo
5. Herança (Reutilização de Código)
A herança permite que uma classe filha herde todas as características e métodos de uma classe pai (FROM).
Código: Selecionar todos
// Classe Pai
CLASS Pessoa
DATA Nome INIT ""
DATA Email INIT ""
ENDCLASS
// Classe Filha que herda de Pessoa
CLASS PessoaFisica FROM Pessoa
DATA CPF INIT ""
ENDCLASS
PROCEDURE TesteHeranca()
LOCAL oPF := PessoaFisica():New()
// Atributos herdados da classe Pessoa
oPF:Nome := "Maria Souza"
oPF:Email := "maria@email.com"
// Atributo exclusivo da classe PessoaFisica
oPF:CPF := "123.456.789-00"
return
6. Métodos Inline (Atalhos rápidos)
Se o seu método executa apenas uma linha de comando simples, você não precisa desenvolvê-lo fora da classe. Pode criá-lo diretamente como INLINE.
Código: Selecionar todos
CLASS Calculadora
// Método inline sem necessidade de bloco externo
METHOD Somar( a, b ) INLINE ( a + b )
METHOD Subtrair( a, b ) INLINE ( a - b )
ENDCLASS
7. Polimorfismo (Sobrescrita de Métodos)
O polimorfismo acontece quando uma classe filha herda um método da classe pai, mas altera o comportamento dele para se adequar à sua própria realidade. No Harbour, basta redefinir o METHOD na classe filha com o mesmo nome e criar a nova lógica.
Exemplo Prático:
Sistema de Folha de Pagamento
Código: Selecionar todos
#include "hbclass.ch"
// -----------------------------------------------------------
// CLASSE PAI: Funcionario
// -----------------------------------------------------------
CLASS Funcionario
DATA Nome INIT ""
DATA SaldoBase INIT 0.00
METHOD New( cNome, nSal ) CONSTRUCTOR
METHOD CalcularSalario()
ENDCLASS
METHOD New( cNome, nSal ) CLASS Funcionario
::Nome := cNome
::SaldoBase := nSal
return Self
METHOD CalcularSalario() CLASS Funcionario
return ::SaldoBase
// -----------------------------------------------------------
// CLASSE FILHA 1: Gerente (Ganha o salário base + bônus de 20%)
// -----------------------------------------------------------
CLASS Gerente FROM Funcionario
// Sobrescreve o método do Pai para aplicar o bônus
METHOD CalcularSalario()
ENDCLASS
METHOD CalcularSalario() CLASS Gerente
return ::SaldoBase * 1.20
// -----------------------------------------------------------
// CLASSE FILHA 2: Vendedor (Ganha salário base + comissão fixa)
// -----------------------------------------------------------
CLASS Vendedor FROM Funcionario
DATA Comissao INIT 500.00
// Sobrescreve o método do Pai para somar a comissão
METHOD CalcularSalario()
ENDCLASS
METHOD CalcularSalario() CLASS Vendedor
return ::SaldoBase + ::Comissao
Executando o Polimorfismo:
Código: Selecionar todos
PROCEDURE Main()
LOCAL oFunc1, oFunc2
oFunc1 := Gerente():New( "Alice Souza", 5000.00 )
oFunc2 := Vendedor():New( "Bruno Lima", 2000.00 )
// O mesmo método "CalcularSalario()" age de formas diferentes (Polimorfismo)
? "Salario da Gerente " + oFunc1:Nome + ": R$", oFunc1:CalcularSalario() // Retorna 6000.00
? "Salario do Vendedor " + oFunc2:Nome + ": R$", oFunc2:CalcularSalario() // Retorna 2500.00
return
8. Exemplo Prático:
POO integrada com DBF (Banco de Dados)
Mapear tabelas de banco de dados (neste caso, arquivos .DBF) para objetos é um padrão de projeto muito eficiente conhecido como Data Mapper ou Active Record.
A classe abaixo gerencia a tabela PRODUTOS.DBF. Ela é responsável por abrir o arquivo, carregar os dados para a memória dentro do objeto, salvar alterações e fechar o arquivo de forma limpa.
Código: Selecionar todos
#include "hbclass.ch"
// -----------------------------------------------------------
// CLASSE: ProdutoDBF
// -----------------------------------------------------------
CLASS ProdutoDBF
// Atributos que espelham os campos do DBF
DATA Codigo INIT 0
DATA Descricao INIT ""
DATA Preco INIT 0.00
// Controle interno do arquivo
PROTECTED:
DATA AreaTrabalho INIT 0
EXPORTED:
METHOD New() CONSTRUCTOR
METHOD Localizar( nCodigo )
METHOD Salvar()
METHOD Fechar()
ENDCLASS
// Construtor: Abre o banco de dados e guarda a área de trabalho
METHOD New() CLASS ProdutoDBF
// Se o DBF não existir, cria a estrutura básica
if !File( "PRODUTOS.DBF" )
DbCreate( "PRODUTOS.DBF", { ;
{ "CODIGO" , "N", 6, 0 }, ;
{ "DESCRICAO", "C", 40, 0 }, ;
{ "PRECO" , "N", 10, 2 } ;
} )
endif
// Abre o arquivo de forma compartilhada
USE PRODUTOS SHARED NEW
::AreaTrabalho := Select() // Guarda o ponteiro desta tabela
return Self
// Busca um produto pelo código e carrega para as variáveis do objeto
METHOD Localizar( nCodigo ) CLASS ProdutoDBF
LOCAL lAchou := .F.
Select( ::AreaTrabalho )
LOCATE FOR field->CODIGO == nCodigo
if Found()
::Codigo := field->CODIGO
::Descricao := field->DESCRICAO
::Preco := field->PRECO
lAchou := .T.
else
::Codigo := nCodigo
::Descricao := "PRODUTO NAO ENCONTRADO"
::Preco := 0.00
endif
return lAchou
// Salva os dados atuais do objeto de volta no arquivo DBF
METHOD Salvar() CLASS ProdutoDBF
Select( ::AreaTrabalho )
// Tenta localizar para atualizar, se não achar, cria um registro novo
LOCATE FOR field->CODIGO == ::Codigo
if !Found()
APPEND BLANK
endif
// Bloqueia o registro para gravação segura (padrão de rede)
if RLock()
field->CODIGO := ::Codigo
field->DESCRICAO := ::Descricao
field->PRECO := ::Preco
DbUnlock()
? "Dados gravados com sucesso no DBF!"
else
? "Erro: Registro travado por outro usuario."
endif
return NIL
// Fecha o arquivo DBF de forma segura ao encerrar o objeto
METHOD Fechar() CLASS ProdutoDBF
Select( ::AreaTrabalho )
USE
return NIL
9. Usando a Classe de Banco de Dados no Programa Principal:
Código: Selecionar todos
PROCEDURE Main()
LOCAL oProd
// 1. Inicializa o objeto (abre o banco de dados por baixo dos panos)
oProd := ProdutoDBF():New()
// 2. Criando e salvando um novo produto
oProd:Codigo := 10
oProd:Descricao := "Teclado Mecanico RGB"
oProd:Preco := 249.90
oProd:Salvar()
// 3. Modificando os dados em memória e salvando a alteração
oProd:Preco := 229.00 // Aplicando um desconto
oProd:Salvar()
// 4. Consultando outro produto para testar a busca
? "--- Testando Busca ---"
if oProd:Localizar( 10 )
? "Codigo encontrado:", oProd:Codigo
? "Nome do Produto: ", oProd:Descricao
? "Preco de Venda: R$", oProd:Preco
endif
// 5. Fecha os arquivos de dados abertos pelo objeto
oProd:Fechar()
return