G. Programação Orientada a Objetos em Tcl/Tk com o pacote TclOO

G.1. Instalação do pacote TclOO

A POO pode ser usada em Tcl (até a versão 8.5) com o uso de pacotes de extensão tais como: IncrTcl, XOTcl, Snit e TclOO.

A partir da Tcl 8.6 o TclOO vem incorporado ao interpretador. Por isso procurei informações sobre TclOO no Wiki Tcl/Tk e encontrei no link http://wiki.tcl.tk/18152, o qual sugere o uso do Teacup ou o pacote do TclOO do Sourceforge.

Resolvi baixar o pacote 0.6 do TclOO Sourceforge.

Baixei, descompactei com o comando:

tar -xzvf TclOO0.6.tar.gz

Entrei no diretório TclOO0.6 e seguindo as informações do README.txt executei o comando:

$ ./configure
checking for correct TEA configuration... ok (TEA 3.6)
checking for Tcl configuration... configure: WARNING: Can't find Tcl configuration definitions

No site http://cep.xor.aps.anl.gov/specfe/x712.html encontrei a solução para este problema. Bastou localizar o arquivo tclConfig.sh e rodar o comando anterior com a opção --with-tcl=/usr/lib/tcl8.5/

./configure --with-tcl=/usr/lib/tcl8.5/
checking for correct TEA configuration... ok (TEA 3.6)
checking for Tcl configuration... found /usr/lib/tcl8.5/tclConfig.sh
checking for existence of /usr/lib/tcl8.5/tclConfig.sh... loading
...
...
checking for tclsh... /usr/bin/tclsh8.5
checking for sdx... none
checking for sdx.kit... none
configure: WARNING: cannot find sdx; building starkits will fail
configure: building as a normal library still supported
configure: creating ./config.status
config.status: creating Makefile
config.status: creating tclooConfig.sh
config.status: creating config.h

Dei uma olhada nas mensagens exibidas pelo comando e me chamou atenção a falta dos aplicativos para Starkit.

checking for sdx... none
checking for sdx.kit... none
configure: WARNING: cannot find sdx; building starkits will fail

Starkit é uma ferramenta muito útil pois permite empacotar o script principal e bibliotecas em um único arquivo que pode ser executado pelo interpretador da ActiveTcl ou pelo interpretador Tclkit.

O Starkit pode ser combinado com o Tclkit em um único pacote executável chamado Starpack, que pode ser usado sem a necessidade de descompactação ou instalação.

O Tclkit também já oferece suporte nativo para POO com a IncrTcl.

Resolvi continuar a instalação do TclOO e deixar para mais tarde a instalação do Starkit e IncrTcl.

Em seguida executei make -> make test e como root executei make install.

Pela saída do comando make install pode-se verificar que o arquivo pkgIndex.tcl foi instalado no diretório /usr/lib

Para verificar se o pacote está funcionando rodar o tclsh em um terminal e testar:

% package require TclOO
0.6

G.1.1. Definição de uma Classe

Vamos começar criando uma classe simples chamada Contador que permitirá incrementar, retornar o valor atual e zerar (reiniciar). (Fonte: Tcl 8.5 Networking Programming, 2010)

#!/usr/bin/env tclsh
#Exemplo de uma classe

package require TclOO

oo::class create Contador {
    constructor {} {
	my ZerarContador
    }

    method ZerarContador {} {
	#mapeia a variável c como variável local e define o conteúdo como 0
	my variable c
	set c 0
    }

    method incrementar {} {
	#mapeia a variável c como variável local e incrementa
	my variable c
	incr c
    }

    method retornarvalor {} {
	#retornar valor corrente
	my variable c
	return $c
    }

}

#Teste

set c1 [Contador new]
set c2 [Contador new]

puts "c1 = $c1 e c2 = $c2 \n"

$c1 incrementar
$c2 incrementar
$c1 incrementar

puts [$c1 retornarvalor]
puts [$c2 retornarvalor]

O comando package require TclOO carrega o pacote TclOO e em seguida a linha oo::class create Contador cria uma classe chamada Contador e define um construtor que chama o método ZerarContador para zerar o conteúdo.

...
package require TclOO

oo::class create Contador {
    constructor {} {
	my ZerarContador
    }
...

O comando my, usado na criação do método ZerarContadordefine o método ZerarContador como um método privado. E o comando my variable é usado para mapear a variável de um objeto específico como uma variável local, e portanto my variable c faz com que a variável c (específica de um objeto) se torne disponível para um método específico.

O método incrementar aumenta o valor do Contador e o método retornarvalor retorna o valor do Contador.

Para testar o uso da classe foram criados dois objetos da classe Contador com o comando

[ Nome_da_Classe ] new

o qual retorna um identificador único que é armazenado na variável c1 e c2 respectivamente.

set c1 [Contador new]
set c2 [Contador new]

Os conteúdos são exibidos com o comando puts [$c1 retornarvalor], em seguida são incrementados ($c1 incrementar) e novamente exibidos.

puts [$c1 retornarvalor]
puts [$c2 retornarvalor]

$c1 incrementar
$c2 incrementar
$c1 incrementar

puts [$c1 retornarvalor]
puts [$c2 retornarvalor]

No final os objetos são removidos da memória com o comando destroy.

Os objetos também podem ser criados com a sintaxe

[ Nome_da_Classe ] create [ Nome_do_Objeto ]

, e o teste do programa anterior poderia ser escrito como:

...
Contador create c1
Contador create c2

puts [c1 retornarvalor]
puts [c2 retornarvalor]

c1 incrementar
c2 incrementar

puts [c1 retornarvalor]
puts [c2 retornarvalor]

c1 destroy
c2 destroy

Um outro método poderia ser definido para objetos da classe Contador com os comandos:

oo::define Contador {
    method defvalor {valor} {
        my variable c
        set c $valor
    }
}

ou

oo::define Contador method defvalor {valor} {
    my variable c
    set c $valor
}

G.1.2. Herança

Podemos criar classes que herdam as características de outras classes.

Por exemplo, podemos criar uma classe Contador_zero (classe filho)que herda as características da classe Contador (classe pai) mas com a diferença de poder zerar o Contador após cada leitura.

oo::class create Contador_zero {
    #Declara a classe Contador como superclasse
    superclass Contador
    method retornarvalor {} {
        #obtem o resultado da classe Contador
        set result [next]
        my ZerarContador
        return $result
    }
}

E para testar o código com os comandos:

Contador_zero create c0
puts [c0 retornarvalor]
c0 incrementar
puts [c0 retornarvalor]
puts [c0 retornarvalor]

Em TclOO o comando superclass Contador declara a classe Contador como a superclasse da nova classe Contador_zero.

Com a linha method retornarvalor {} { estamos sobrescrevendo o método retornarvalor que passa a ter um novo comportamento quando for chamado pelos objetos da classe Contador_zero.

A linha set result [next] utiliza o comando next o qual executa o método retornarvalor da classe Contador e atribui o resultado à variável result.

E em seguida é executado o método ZerarContador com o comando my pois se trata de um método privado.

Nota

Métodos Privados realizam tarefas ou processam informações que dizem respeito apenas ao funcionamento interno da classe em questão.

Por exemplo, se você tenter acessar (executar) o método ZerarContador com o comando c1 ZerarContador receberá uma mensagem de erro:

unknown method "ZerarContador": must be defvalor, destroy, incrementar or retornarvalor
    while executing
"c1 ZerarContador"

O pacote TclOO permite que os métodos sejam chamados de dentro do objeto ( my [nome_do_método]) e de fora do objeto ( c1 [nome_do_método] ). Mas esse comportamento pode ser modificado com o uso dos comandos export e unexport na definição da classe.

Por exemplo, podemos criar uma classe (Ex: Contador_tempo que não permite o método incrementar seja chamado de fora do objeto.

oo::class create Contador_tempo {

  superclass Contador

  constructor {} {
      #Implementar alguma rotina para incrementar o contador
      #em intervalos periódicos
  }

  unexport incrementar

}  

G.1.2.1. Herança Múltipla

Também é possível uma classe herdar as características de várias outras classes, especificando mais de uma superclasse.

Neste caso os métodos chamados são procurados em todas as superclasses, e se um método está implementado em mais de uma classe, a escolha do método a ser executado será feita com base na ordem em que as classes foram especificadas.

oo::class create Classe_teste {
    method retornarvalor {} {
        puts "Método retornarvalor da Classe_teste"
        next
    }
}
oo::class create Contador_teste {
    superclass Classe_teste Contador
}

Por exemplo, quando o método retornarvalor for chamado por um objeto da classe Contador_teste, será executado primeiramente o método da classe Classe_teste que exibe no terminal a mensagem Método retornarvalor da Classe_teste e em seguida o comando next executa o método retornarvalor da classe Contador.

O comando next também pode ser usado com parâmetros quando o método chamado exigir argumentos.

G.1.3. Redefinindo Objetos

O comando oo::objdefine permite editar o código de um método para um objeto específico.

oo::objdefine c1 {
    method retornarvalor {} {
        puts "Executando o método retornarvalor para o objeto c1"
        next
    }
}

No exemplo anterior foi acrescentado o comando puts "Executando..." ao método retornarvalor apenas para o objeto c1. E o comando next chama a execução do método retornarvalor original (da classe Contador).