|
|
|
|
|
Dicas
|
|
Visual Basic (Miscelâneas)
|
|
|
Título da Dica: Serialização no Visual Basic
|
|
|
|
Postada em 30/9/2003 por ~Ð@®£@Ñ
Serialização é uma técnica normalmente usada para gravar objetos em disco, transmití-los via rede, armazená-los no registry ou num banco de dados, mas também pode ser usada para outros propósitos onde uma forma simples e flexível de armazenar informações se fizer necessária. Este artigo lhe mostrará como usar o objeto PropertyBag para fazer com que seus objetos possam se transformar numa matriz de bytes que você poderá gravar em disco, transportar na rede, encriptar, enfim, manipular como bem quiser.
O que caracteriza a serialização já está expresso no próprio nome da operação. A serialização consiste em armazenar informações em série. As informações são normalmente armazenadas no formato "Nome da Propriedade/Valor". Antes do VB6, isto era feito criando-se uma string contendo os pares "Nome da Propriedade/Valor" numa sequência. Com novos recursos adicionados às classes mais a possibilidade de declarar variáveis do tipo PropertyBag, tornou-se mais fácil criar objetos que serializam a si próprios.
Usando o Objeto PropertyBag
Um objeto do tipo PropertyBag pode ser usado para armazenar, propriedade por propriedade, todo o conteúdo de um objeto ou mesmo de uma hierarquia de objetos e recuperar posteriormente todas ou apenas uma destas propriedades. O objeto PropertyBag possui os métodos "WriteProperty", "ReadProperty" e a propriedade "Contents". O método "WriteProperty" é usado para gravar um valor no PropertyBag e associar a ele um nome, enquanto o método "ReadProperty" é usado para recuperar-se um valor gravado com determinado nome. A propriedade "Contents" é do tipo Variant contendo a matriz de bytes dos dados armazenados no objeto PropertyBag. O formato do método "WriteProperty" é :
WriteProperty ("Nome da Propriedade", Valor, Default)
onde, "Nome da Propriedade" é um nome que se quer associar ao valor que está sendo gravado, "Valor" é o valor propriamente dito e "Default" é um valor que, em havendo coincidência com o argumento "Valor", indica que "Valor" não necessita de ser gravado, pois, ao usar o método "ReadProperty" para recuperá-lo, ficará subentendido o default no caso de não ser encontrado nenhum valor gravado para a propriedade em questão. O mesmo default deverá ser especificado no método ReadProperty para que haja coerência no uso deste parâmetro não obrigatório. Veja abaixo um exemplo de uso do método "WriteProperty".
Dim pb as PropertyBag
Dim int_Idade as Integer
int_Idade= 28
Set pb = New PropertyBag
pb.WriteProperty "Idade", int_Idade, 0%
Após o uso do método "WriteProperty" acima, a propriedade "Contents" do objeto pb terá as informações de int_Idade associadas ao nome "Idade". Estas informações poderão ser recuperadas mediante o uso do método "ReadProperty" do objeto PropertyBag. O método "ReadProperty" possui o seguinte formato:
ReadProperty ("Nome da Propriedade", Default)
onde, "Nome da Propriedade" é o nome associado a um valor que, supõe-se, tenha sido gravado mediante o uso do método "WriteProperty" e "Default" é um valor que deve ser retornado caso nada seja encontrado associado a "Nome da Propriedade".
Veja abaixo como usar o método "ReadProperty" para recuperar, na mesma variável int_Idade, o valor gravado no exemplo anterior do método "WriteProperty".
int_Idade = pb.ReadProperty ("Idade", 0%)
Repare que o valor 0% foi usado como default tanto no método "WriteProperty" como no "ReadProperty". Isto significa dizer que, caso o valor contido na variável int_Idade fosse igual ao default - no caso, 0% - , nada seria gravado, porque ficaria subentendido que o método "ReadProperty" usaria o mesmo default e retornaria o valor 0% quando não encontrasse nenhum valor gravado para a propriedade "Idade". Isto ajuda a poupar espaço na propriedade "Contents" do objeto PropertyBag. Observe que, para garantir isto, fica a cargo do programador especificar o mesmo default tanto no método "WriteProperty" quanto no método "ReadProperty" da propriedade. A não especificação de nenhum valor default no método "ReadProperty" causaria um erro em tempo de execução caso nenhum valor fosse encontrado associado a "Nome da Propriedade".
Sugestão: defina constantes privativas da classe para os valores a serem usados como default para cada propriedade nos métodos "WriteProperty" e "ReadProperty".
Uma vez que tenhamos gravado todas as propriedades de um objeto dentro de um PropertyBag, podemos obter os dados desta gravação na forma de um Variant extraído da propriedade "Contents". Veja abaixo como isto é feito.
Dim varDados as Variant
varDados = pb.Contents
Desta forma podemos fazer o que quisermos com este Variant: passá-lo como argumento para um método de um objeto remoto e transportá-lo na rede, encriptá-lo, gravá-lo num arquivo em disco - após atribuí-lo a uma variável do tipo matriz de bytes -, gravá-lo no banco de dados de registro, etc.
Veremos agora dois tipos de possibilidade de se fazer serialização: nas classes de projetos do tipo ActiveX, que passaram a ter a propriedade "Persistable" a partir do VB 6, o que facilita a serialização, e no caso de projetos onde as classes não têm esta propriedade.
Classes de Projetos Padrão
No caso de estarmos trabalhando com classes de projetos que não sejam do tipo ActiveX, o que fazemos é criar para a classe uma propriedade com o nome, por exemplo, "Serializacao". No procedimento Get da propriedade "Serializacao", instanciamos um objeto do tipo PropertyBag e o utilizamos para gravar todas as propriedades do objeto. Ao final retornamos sua propriedade "Contents" na forma de um Variant. Veja como isto é feito.
Public Property Get Serializacao() as Variant
Dim pb as PropertyBag
Set pb = New PropertyBag
With pb
.WriteProperty "NomePropriedade1", Propriedade1, DefaultPropriedade1
.WriteProperty "NomePropriedade2", Propriedade2, DefaultPropriedade2
End With
Serializacao = pb.Contents
End Property
Para desserializar o objeto, escrevemos o código do procedimento Let da propriedade "Serializacao". Neste caso, recebemos como argumento um Variant que contem todos os dados do objeto gravado anteriormente por uma chamada á propriedade "Serializacao". Veja como isto é feito.
Public Property Let Serializacao ( ByVal varDados as Variant)
Dim bDados() as Byte, pb as PropertyBag
bDados = varDados
Set pb = New PropertyBag
pb.Contents = bDados
With pb
Propriedade1 = .ReadProperty ("NomePropriedade1", DefaultPropriedade1)
Propriedade2 = .ReadProperty ("NomePropriedade2", DefaultPropriedade2)
End With
End Property
Para o caso de uma hierarquia de objetos onde um objeto de mais alto nível deva ser serializado e dentro da sua serialização também se deva ter os objetos nele contidos, deve-se implementar a serialização nas classes dos objetos contidos. Desta forma, pode-se chamar a propriedade "Serializacao" de cada objeto contido e gravar o seu retorno como uma propriedade do objeto de mais alto nível. Veja um exemplo em que o objeto da classe Cliente possui uma propriedade "Pedidos" que se trata de uma coleção de objetos da classe "Pedido".
Public Property Get Serializacao ( ) as Variant
Dim pb as PropertyBag
Set pb = New PropertyBag
With pb
'grava as propriedades comuns
.WriteProperty "NomePropriedade1", Propriedade1, DefaultPropriedade1
.WriteProperty "NomePropriedade2", Propriedade2, DefaultPropriedade2
'grava a coleção de pedidos
Dim c as Long, i as Long, objPedido as Pedido
c = Pedidos.Count
pb.WriteProperty "QuantidadeDePedidos", c, 0
For i = 1 to c
Set objPedido = Pedidos(i)
pb.WriteProperty "Pedido" & CStr(i), objPedido.Serializacao
Next i
End With
Serializacao = pb.Contents
End Property
Para desserializar o objeto, veja a seguir:
Public Property Let Serializacao ( ByVal varDados as Variant)
Dim pb as PropertyBag, bDados() as Byte
bDados = varDados
Set pb = New PropertyBag
pb.Contents = bDados
With pb
'lê as propriedades comuns
Propriedade1 = .ReadProperty ("NomePropriedade1", DefaultPropriedade1)
Propriedade2 = .ReadProperty ("NomePropriedade2", DefaultPropriedade2)
End With
'lê a coleção de pedidos
Dim c as Long, i as Long, objPedido as Pedido
c = pb.ReadProperty ("QuantidadeDePedidos", 0)
For i = 1 To c
Set objPedido = New Pedido
objPedido.Serializacao = pb.ReadProperty ("Pedido" & CStr(i))
Pedidos.Add objPedido
Next i
End Property
Classes de Projetos do Tipo ActiveX
A partir do VB 6, as classes de projetos do tipo ActiveX DLL e ActiveX Exe passaram a ter mais uma propriedade: "Persistable". Ao escolher o valor desta propriedade como "1-Persistable", você estará possibilitando que a classe dispare três novos eventos: "InitProperties", "ReadProperties" e "WriteProperties". Estes novos eventos somados à possibilidade de você declarar e instanciar variáveis do tipo "PropertyBag" possibilitam o uso do objeto "PropertyBag" para simplificar o processo de serialização de objetos.
Configurando a propriedade "Persistable" com o valor "1-Persistable", fica fácil estruturar o código para fazer a serialização do objeto. Supondo que queiramos serializar um objeto objCliente da classe Cliente. Podemos declarar uma variável como sendo do tipo PropertyBag e chamar o método "WriteProperty" do objeto PropertyBag para gravar de uma só vez o objeto inteiro. Veja como isto é feito.
Dim pb as PropertyBag
Set pb = New PropertyBag
pb.WriteProperty "MeuCliente", objCliente
O que acontece aqui não é nenhuma mágica, porque na verdade ainda teremos que escrever todo o código necessário para gravar o valor de cada uma das propriedades do objeto dentro do PropertyBag. Mas, você deve estar se perguntando: onde diabos eu vou escrever este código? Lembra que falei do evento "WriteProperties" que as classes com a propriedade "Persistable" podem disparar. Pois bem, no exemplo acima, a consequência de você escrever a linha "pb.WriteProperty "MeuCliente", objCliente" é o disparo do evento "WriteProperties" do objeto objCliente. Este evento tem o argumento de nome "PropBag" que é um objeto do tipo PropertyBag. Sabe qual vai ser o valor deste argumento quando o evento for disparado? Pasme: o objeto pb que está sendo usado para gravar o objeto objCliente na linha: "pb.WriteProperty 'MeuCliente', objCliente".
Dentro do tratamento do evento "WriteProperty", você poderá usar o método "WriteProperty" do objeto PropBag recebido como argumento do evento para gravar cada propriedade do objeto objCliente. Veja abaixo:
Private Sub Class_WriteProperties(PropBag As PropertyBag)
With PropBag
.WriteProperties "NomePropriedade1", valorPropriedade1, DefaultPropriedade1
.WriteProperties "NomePropriedade2", valorPropriedade2, DefaultPropriedade2
.
.
.
End With
End Sub
Após terminar de tratar o evento "WriteProperties", o objeto pb usado para gravar objCliente conterá na sua propriedade "Contents" todos os dados das propriedades do objeto.
Suponhamos que o valor da propriedade "Contents" de pb é passado para o método "SalvarCliente" de um objeto remoto e tenhamos que desserializar o objeto Cliente e reconstituí-lo na aplicação deste objeto remoto. Vejamos como poderia ficar o método "SalvarCliente":
Public Sub SalvarCliente (ByVal varDados as Variant)
Dim objCliente as Cliente, bDados() as Byte
Dim pb as PropertyBag
Set pb = New PropertyBag
bDados = varDados
pb.Contents = bDados
Set objCliente = pb.ReadProperty ("MeuCliente")
.
.
.
End Sub
O que acontece aqui, mais uma vez, é que a mágica não está completa. Desta vez o que ainda nos resta a fazer é codificar todas as instruções para a leitura de propriedade por propriedade do objeto objCliente contidas em pb. Você talvez já tenha adivinhado onde iremos escrever este código. Claro, dentro do tratamento do evento "ReadProperties" do objeto objCliente. Ao escrever "Set objCliente = pb.ReadProperty ("MeuCliente")" o que você faz é disparar o evento "ReadProperties" do objeto objCliente. Este evento recebe um argumento "PropBag" que nada mais é do que uma referência ao objeto pb. Veja como fica então o tratamento do evento "ReadProperties":
Private Sub Class_ReadProperties(PropBag As PropertyBag)
With PropBag
Propriedade1 = .ReadProperty ("NomePropriedade1", DefaultPropriedade1)
Propriedade2 = .ReadProperty ("NomePropriedade2", DefaultPropriedade2)
.
.
.
End With
End Sub
Elegante, não? Isto é tudo.
|
|
|
|
|