21 de dezembro de 2009

Adoção do protocolo IPv6 terá ritmo acelerado em 2010

Há algum tempo corre uma previsão dizendo que o crescimento da Internet deve em breve esgotar a disponibilidade de endereços IP, o que impediria a expansão da rede, isto é, seria impossível dar uma identificação única a novos computadores e outros dispositivos ao acessarem a Internet. Essa previsão dá atualmente a data para esgotamento como sendo Julho de 2011 com o modelo atual, chamado de IPv4. Assim, a Internet Engineering Task Force (IETF) desenvolveu um novo modelo - o IPv6 - que comporta uma quantidade muito maior de endereços e deverá substituir o IPv4.

O IPv6 vem sendo implantado gradualmente mas ainda representa uma porcentagem bastante pequena dos endereços existentes. Veja abaixo a reprodução de uma reportagem que saiu no site IDG Now! sobre esse assunto. Para acessar a matéria original, clique aqui.
Após observar uma aceleração nas atividades em torno do novo protocolo de comunicação da internet, o IPV6, provedores de acesso e empresas que fornecem equipamentos de redes apostam em 2010 como o ano da atualização.

A comunidade de engenharia de internet já espera há uma década pela adoção em massa do IPv6 e afirma que a migração é necessária porque o atual protocolo IPv4 está ficando sem endereços.

O IPv4 usa endereços de 32 bits e pode suportar aproximadamente 4,3 bilhões de dispositivos com endereços específicos na internet. Já o IPv6 usa endereços de 128 bits e é capaz de suportar uma quantidade tão grande de dispositivos que somente uma expressão matemática - 2 elevados à 128ª potência - pode medir.

A migração para o IPv6 é um processo contínuo, que já foi iniciado em diversos países, incluindo o Brasil, "mas não há prazo para terminar", explica o diretor de serviços e tecnologia do Registro.br, órgão responsável pelo controle de domínios criados no Brasil, Frederico Neves.

Nos Estados Unidos, provedores de acesso registram uma aceleração no uso de protocolos IPv6 este ano. A Hurricane Electric, da Califórnia, viu sua rede IPv6 dobrar de tamanho este ano, com conexões a outras 600 redes no novo protocolo.

A NTT America também observou 100% de crescimento no tráfego usado por seus clientes com o novo protocolo. "Tivemos 40% de alta no número de portas que vendemos para IPv6", diz o diretor de engenharia de produtos da empresa, Cody Christman.

A Global Crossing também prevê aumento na oferta de serviços via IPv6 em 2010. A empresa informa que o número de pedidos de serviços no novo protocolo triplicou em 2009, sendo mais intenso no segundo semestre.

"O IPv6 representou 6,3% de novos pedidos em nossa linha de produtos de internet - alta de 2,1% em relação a 2008", afirma o diretor de serviços de acesso à internet da Global Crossing, Matt Sewell. Em 2010, o executivo estima um incremento de 10% em novos pedidos ligados ao IPv6. "Muitos clientes perguntam se estamos preparados para o IPv6".

O número de provedores de conteúdo e aplicações que suportam o IPv6 também cresceu em 2009. O movimento mais expressivo foi do Google, que adicionou o IPv6 a diversas aplicações incluindo buscas, Google Docs, Gmail e News, bem como o navegador Chrome e a plataforma móvel Android. Em 2010 será a vez do YouTube, informam engenheiros da empresa de internet.

Entre outras empresas de internet que apostaram no IPv6, nos Estados Unidos, estão o serviço de streaming de vídeo, Limelight Networks, e a distribuidora de filmes online, NetFlix.

Um dos maiores provedores de conteúdo dos Estados Unidos, Comcast, deve alavancar a adoção do IPv6 em 2010, quando a planeja testar o novo protocolo em residências. A empresa também trabalha na migração de sua rede para o IPv6, bem como na oferta de alguns conteúdos próprios adequados ao novo protocolo, o que inclui os sites dos canais E! Entertainment Television, Style Network e Golf Channel.

"Vamos anunciar um portal para IPv6, em um futuro não muito distante", informa o chefe de arquitetura do projeto de IPv6 da Comcast, John Jason Brzozowski. Na previsão da Comcast, em 2010, consumidores norte-americanos começam a observar os benefícios do IPv6 em equipamentos residenciais de rede e outros componentes eletrônicos adaptados ao novo padrão.

Apesar do crescimento em sua adoção este ano, o IPv6 ainda representa 1% de todo o tráfego mundial da internet, segundo estimativas da indústria. O Google informa que 0,2% de seus usuários possuem acesso via IPv6.

A fabricante de equipamentos de rede, Cisco Systems, acredita que 2010 pode ser um marco para a adoção do IPv6 por conta dos novos serviços de internet que demandarão mais dispositivos conectados à rede.

Entre as aplicações que devem alavancar o uso do IPv6, a empresa aposta no avanço das 'smart grids' para o setor de energia elétrica, bem como nos dispositivos conectados para a área de saúde. Sistemas de monitoramento e administração de redes para redução do consumo de água e energia também devem aderir rapidamente ao novo protocolo.


18 de dezembro de 2009

Comunicação entre processos : Sockets - Client

No último post, mostrei como construir um Serviço usando um Socket servidor para monitorar requisições de Clients que conheçam a sintaxe apropriada para se comunicar com esse Serviço. No caso, apenas uma mensagem foi implementada no Servidor e ela retorna uma lista de Certificados Digitais instalados no computador onde o Serviço está em execução. Neste post, mostro a outra ponta da implementação, isto é, como construir um Client capaz de se comunicar com esse Servidor e obter a tal lista.

Para a aplicação Client poder se conectar com o Serviço, ela tem que configurar uma instância do componente TClientSocket, informando nela tanto o endereço do servidor quanto a porta que ele está monitorando. Depois, enviar uma mensagem com a solicitação desejada e aguardar a resposta.
_StatusMens = 0; /* Status da mensagem */
_MemorySize = 0;
_Memory->Clear ();
_ClientSocket->Port = 1024;
_ClientSocket->Host = "PC_151";

aguarde ("Obtendo certificados");
try {
_ClientSocket->Active = true;

/* Aguarda conectar */
while (!_ClientSocket->Active && _StatusMens == 0)
Application->ProcessMessages ();

if (_ClientSocket->Active)
{
_ClientSocket->Socket->SendText ("#GET_CERTIFICATE");

/* Aguarda retorno */
while (_StatusMens == 0)
Application->ProcessMessages ();
}

if (_Status < 0)
col_erro ("Não foi possível obter certificados.");

_ClientSocket->Active = false;
}
__finally {
fim_aguarde ();
}

Note no trecho reproduzido acima que é possível especificar o Host pelo nome, o que é recomendado principalmente se o IP do Servidor for variável. Note também que após a conexão há um laço para aguardar o retorno. Isso é necessário porque a resposta do Servidor não é síncrona, isto é, a função que envia mensagens não recebe o retorno enviado pelo Servidor. Aqui, o Client envia o texto "#GET_CERTIFICATE". Estes caracteres representam para o meu Serviço o nome do comando que deve retornar os certificados existentes no Servidor. Obviamente, esse nome foi estabelecido por mim quando o Serviço foi projetado e o Client deve saber dessa informação de antemão para poder submeter corretamente a requisição.

Para o Client receber o retorno, é preciso responder ao evento OnRead do TClientSocket e ir lendo os bytes enviados em blocos pelo Servidor. Portanto, o OnRead pode ser executado mais de uma vez antes que a resposta toda seja recebida:
void __fastcall TFrmCert::ClientSocketRead(TObject *Sender, TCustomWinSocket *Socket)
{
char *lBuffer = NULL;
int lLen = Socket->ReceiveLength();

if (lLen > 0)
{
lBuffer = new char [lLen];

try {
/* Guarda no buffer interno os bytes recebidos */
Socket->ReceiveBuf ((void *)lBuffer, lLen);

if (memcmp (lBuffer, "#CERTIFICATE_ERROR", 18) == 0)
_StatusMens = -1;
else
{
/* Primeiro bloco recebido ? Então recupera o tamanho total do retorno enviado pelo servidor */
if (_MemorySize == 0)
memmove (&_MemorySize, lBuffer, sizeof (_MemorySize));

{ Guarda no Memory stream todo o buffer recuperado. Cada bloco lido é acrescentado ao fim do último bloco até receber a mensagem toda. }
_Memory->Write (lBuffer, lLen);

{ Recebeu todos os blocos ? }
if (_Memory->Size == (_MemorySize + (int) sizeof (_MemorySize)))
_StatusMens = 1;
}
}
__finally {
delete [] lBuffer;
}
}
}

A leitura do retorno mostrada acima é automaticamente executada numa thread separada e é por isso que no trecho anterior há o laço de aguarde logo após o envio da requisição. Aquele código monitora a variável que controla o status da recepção. Mudanças no valor do status sinalizam que a recepção foi concluída - ou, se for o caso, que um erro ocorreu. após o término do laço e se não houve erro, a variável stream _Memory conterá todos os bytes enviados pelo Servidor. O Client, então, poderá tratar esse conteúdo da forma que julgar necessário, baseado no conhecimento prévio sobre o significado dos bytes retornados.

Se você estabelecer que o seu Servidor receberá solicitações em um formato XML bem definido, contendo tags com o nome de função a ser chamada e os parâmetros a serem supridos, você terá uma versão sua simplificada do conceito de WebServices.

17 de dezembro de 2009

Comunicação entre processos : Sockets - Servidor

Em junho de 2009, eu postei um texto introduzindo o assunto "Comunicação entre Processos". Naquela ocasião, dei um exemplo em C++ Builder usando a API do Windows para fazer duas aplicações no mesmo computador compartilhar parte da memória para trocar informações usando File Mapping. Agora, vou complementar o assunto mostrando uma forma básica de fazer com que dois programas na mesma rede possam trocar informações. Para isso, vou usar Sockets.

Um Socket é um componente de software disponibilizado pelo Sistema Operacional para permitir que um programa receba corretamente pacotes de dados direcionados a ele. O Socket não faz qualquer inferência sobre o conteúdo do pacote de dados, podendo ser qualquer sequência de bytes que se faça necessária. A ideia é a seguinte: um programa cria um Socket Server e o ativa para poder receber solicitações. Este Socket Server é caracterizado por um endereço (o IP do próprio computador) e um número de porta de modo que outros programas que conheçam esse par endereço/porta podem se comunicar com o programa servidor.

Mas, se o Socket não estipula um formato para os dados trocados, como é que o Servidor e o Client conseguem se entender ? A interpretação dos dados é de responsabilidade do programa Servidor e é ele quem dita as sequências de bytes que devem ser enviadas para a mensagem fazer sentido. Qualquer Client que queira se comunicar com ele deve saber como montar as sequências de bytes corretamente para ter suas solicitações atendidas. Ou seja, o Servidor estabelece seu próprio protocolo e os Clients devem segui-lo para poderem se comunicar.

Na ABC71, nós construimos um serviço que, entre outras coisas, lê os certificados digitais válidos instalados no Servidor e envia a lista à aplicação Client para que o usuário possa selecionar qual dos certificados deve ser usado para assinar documentos digitalmente. Vou usar esse cenário para exemplificar o uso dos Sockets.

O C++ Builder e o Delphi têm um componente chamado TServerSocket (na minha versão, ele fica na guia de componentes Internet). O trecho abaixo foi montado no C++ Builder como resposta ao evento OnExecute de um projeto para construção de um Serviço:
class TWServiceOmegaNFe : public TService
{
__published: // IDE-managed Components
TServerSocket *ssCert;
void __fastcall ServiceExecute(TService *Sender);
/* ... */
};

/* ... */
void __fastcall TWServiceOmegaNFe::ServiceExecute(TService *Sender)
{
ssCert->Port = 1024;
ssCert->Active = true;
try {
while (!Terminated)
ServiceThread->ProcessRequests (true);
}
__finally {
/* finalizando o serviço ... */
ssCert->Active = false;
}
}

O código acima apenas configura uma porta no Socket e o ativa para iniciar o monitoramento de requisições dos Clients. No exemplo, escolhi e fixei o número 1024 mas pode ser qualquer porta livre. O comando ServiceThread->ProcessRequests (true) instrui o serviço a receber e tratar quaisquer mensagens enviadas diretamente para a thread principal - por exemplo, quando o usuário solicita a interrupção do Serviço através do console de Serviços do Windows.

Quando um socket Client envia uma mensagem para este Servidor, o socket server dispara o evento OnClientRead, ocasião em que o Servidor interpreta a mensagem e devolve as informações solicitadas - ou uma mensagem de erro, se for apropriado:
void __fastcall TWServiceOmegaNFe::ssCertClientRead( TObject *Sender, TCustomWinSocket *Socket)
{
int lLen = Socket->ReceiveLength();
char *lBuffer = NULL;
TMemoryStream *lMemoryStream, *lStream;

if (lLen > 0)
{
lBuffer = new char [lLen + 1];
lMemoryStream = new TMemoryStream ();

/* lStream não pode ser destruído, pois ele será destruído pelo socket */
lStream = new TMemoryStream ();

try {
memset (lBuffer, 0, lLen + 1);
Socket->ReceiveBuf ((void *)lBuffer, lLen);

/* Este server só reconhece a mensagem #GET_CERTIFICATE mas outras podeam ser incluidas aqui*/
if (memcmp (lBuffer, "#GET_CERTIFICATE", 16) == 0)
{
/* Monta em lMemoryStream um XML com os certificados */
BuscaCertificadoDigital (lMemoryStream);
lLen = lMemoryStream->Size;
if (lLen > 0)
{
/* Para que um Client saiba quantos bytes devem ser lidos, inclui essa informação no início */
lStream->Write (&lLen, sizeof (lLen));

/* Acrescenta o Stream com os certificados */
lStream->Write (lMemoryStream->Memory, lLen);

/* Reposiciona o Stream para que o Socket envie todo o conteúdo */
lStream->Seek (0, soFromBeginning);

/* Envia o Stream para o Client */
Socket->SendStream (lStream);
}
else /* Erro ! */
Socket->SendText ("#CERTIFICATE_ERROR");
}
}
__finally {
delete lMemoryStream;
delete [] lBuffer;
}
}
}

Veja que o Socket servidor recebe a mensagem num buffer - um texto que em princípio é interpretado como um nome de função, indicando qual é o tipo de informação solicitada pelo Client - e depois prepara o stream de retorno e o envia usando o próprio Socket servidor. Para o Client, entretanto, a resposta virá em momento diferente, assincronamente, como veremos.

Mostro no próximo post como montar uma aplicação Client para se comunicar com esse Servidor e obter a lista de Certificados.

Mais Informações
Criação de Serviços (parte 1 e parte 2)

10 de dezembro de 2009

Design Patterns com Delphi : FlyWeight - parte 2

Há alguns, falei aqui no blog sobre o conceito e terminiologia associados ao Design Pattern estrutural FlyWeight. Faltou mostrar como organizar e implementar as classes usando Delphi. Recordando o conceito, esse design pattern tem por objetivo minimizar o uso de memória (e outros recursos) quando a criação de uma grande quantidade de instâncias de classe é necessária para uma determinada aplicação.

Para facilitar a compreensão, reproduzo novamente abaixo o diagrama UML que exemplifica o uso do FlyWeight:
Diagrama UML para FlyWeight

Baseado no diagrama, teremos que declarar a classe TWFonte que atua como uma interface, isto é, sua característica principal é introduzir o comportamento (as funções) que permite compartilhar dados intrínsecos da classe e aplicar temporariamente a esses dados outras informações externas (os dados extrínsecos) particulares a cada contexto.

É bastante comum que esse tipo de classe seja abstrata pura, isto é, que não possua variáveis internamente - esta característica é reservada às versões concretas da classe - mas isso não é uma obrigação. No exemplo do diagrama, há informações comuns aos tipos de Fonte que fazem sentido ser colocados nessa classe:
TWFonte = class
private
{ Nome do Fonte }
_Nome : String;
{ Lista de imagens disponíveis para o Fonte }
_Imagens : TList;

constructor Create(ANome : String); overload;
public
constructor Create;reintroduce; overload;
procedure Desenha(AContext: TWContext; AStyle: TWStyle) = 0;
end;
{ ... }
constructor TWFonte.Create(ANome : String);
begin
_Nome := ANome;
{ ... }
end;

constructor TWFonte.Create;
begin
Raise Exception.Create('Classe deve ser construída a partir da Factory');
end;

Veja que há dois construtores com o nome de Create, um privado e outro público. A intenção por trás disso é forçar o uso da classe de Factory quando for necessário obter instâncias do TWFonte já que é o Factory quem garante que apenas uma instância de cada herança concreta existe e, em última análise, garante o sucesso do compartilhamento dos dados intrínsecos de cada fonte. Para maiores detalhes sobre essa técnica de esconder um construtor, veja nesse endereço a seção Hiding a Constructor.

Como dito no parágrafo anterior, a classe Factory para o pattern Flyweight funciona de forma ligeiramente diferente daquela estabelecida pelo pattern Factory Method. Aqui será preciso ter uma instância da classe Factory (provavelmente um Singleton) para gerenciar as instâncias criadas:
TWFonteFactory = class
private
_Fontes : TStringList;
{ ... }
public
function GetFonte (ANome: String) : TWFonte;
end;
{ ... }
function TWFonteFactory.GetFonte (ANome: String) : TWFonte;
var idx : integer;
begin
Result := nil;
idx := _Fontes.IndexOf(ANome);
if (idx >= 0) then
Result := _Fontes.Objects[idx] As TWFonte
else begin
if ANome = 'Arial' then
Result := TWArial.Create
else
if ANome = 'TimesNewRoman' then
Result := TWTimesNewRoman.Create;
end;
end;

Talvez a diferença mais importante nesse Factory é que ele mantem internamente as instâncias criadas e, quando solicitado, retornam um ponteiro para uma instância para uso externo. Então, embora não apareça no trecho de código acima, o destructor da classe TWFonteFactory deve destruir todas as instâncias de TWFonte criadas e armazenadas por ela. Com isso, deve-se tomar cuidado para não usar uma instância do TWFonte após o destrutor da Factory ter sido chamado.

As últimas classes relevantes para o FlyWeight são as que implementam concretamente a interface de compartilhamento. No exemplo, as ConcreteFlyWeight são a TWArial e a TWTimesNewRoman, que são codificadas como heranças simples:
TWArial = class(TWFonte)
public
procedure Desenha(AContext: TWContext; AStyle: TWStyle);
end;
{ ... }
procedure TWArial.Desenha(AContext: TWContext; AStyle: TWStyle);
var lImagem : TImagem;
begin
lImagem := GeraImagem (_Imagens, AStyle, AContext.Texto);
DesenhaImagem (AContext, lImagem);
end;

A destacar nesse trecho é a forma como a função Desenha faz uso das propriedades intrínsecas e dos parâmetros para obter o resultado desejado. Os parâmetros são propriedades extrínsecas - isto é, de fora da classe - e, portanto, não são compartilhados. A função interna GeraImagem cria uma imagem para cada caractere do texto e concatena todas elas para gerar a imagem correspondente ao texto informado no Contexto. A imagem de cada caractere é escolhida na lista interna de acordo com o estilo e tamanho requisitado pelos parâmetros. Para finalizar, a função interna DesenhaImagem toma a imagem gerada e a desenha usando as informações estipulada pelo contexto - handle do canvas, localização, etc..

8 de dezembro de 2009

ABC71 disponibiliza gratuitamente e-book sobre SPED

A Escrituração Digital - SPED - é o projeto do Governo Federal que tem como objetivo receber digitalmente dos contribuintes informações fiscais exigidas pela legislação. Do ponto de vista estritamente técnico, o projeto tem alguns desafios para as Empresas, como o uso de assinatura digital nos documentos e a forma de comunicação (Web Services) com a Receita Federal. O grande nó, entretanto, é desvendar as informações que devem constar nos arquivos digitais a serem gerados e enviados para o Governo. Portanto, entender o escopo do projeto de SPED e o impacto que sua implantação tem para a Empresa passa a ser mandatório.

Para ajudar a desatar esse nó, a ABC71 apoiou a publicação de um livro sobre o assunto e agora disponibiliza gratuitamente uma versão digital dele. Veja abaixo a nota, que foi produzida originalmente pela SOMA Agência de Comunicação Sustentável, que dá mais detalhes:
Com mais de 60 projetos implementados em SPED – Sistema Público de Escrituração Fiscal -, a ABC71 a fim de auxiliar ainda mais as empresas que precisam se adequar aos novos procedimentos da Receita Federal oferece ao público a versão eletrônica do livro "Big Brother Fiscal III: o Brasil na Era do Conhecimento", de Roberto Dias Duarte, uma das grandes referências no tema SPED.

Voltado aos empresários, diretores e gerentes das áreas de negócios, esta terceira edição do livro conta com informações atualizadas sobre certificação digital, Nota Fiscal Eletrônica (NF-e), Nota Fiscal Eletrônica de Serviços (NFS-e), escrituração contábil digital, escrituração fiscal digital, sistemas integrados de gestão (ERP), entre outros assuntos. O livro foca a gestão e sua respectiva melhoria de processos por conta da nova legislação e dos novos padrões contábeis e fiscais que as instituições deverão atender.

"A ABC71 adquiriu muita experiência na implantação de projetos de SPED em 2009 e tem hoje aproximadamente 700 clientes. Para nós é um prazer apoiar esta terceira edição, pois a importância deste livro para as áreas gerenciais e decisórias da empresa é essencial. Saber como irá funcionar a nova legislação, observar quais serão os pontos-chave para atender ao SPED, a importância de um sistema de gestão seguro na empresa são algumas das principais dúvidas do mercado quanto ao que fazer e como fazer", explica Selene Sidney, gerente de marketing da ABC71.
As empresas e suas respectivas áreas de TI e administrativo-fiscal são geralmente as responsáveis pelo sucesso e/ou fracasso da nova abordagem legal perante o governo. "Os profissionais das áreas contábeis, fiscais, tributárias e também de tecnologia precisam necessariamente entender os impactos dessa nova forma de envio de informações ao Fisco, além de compreender melhor como toda a cadeia produtiva está intrinsecamente ligada a essa nova modalidade", analisa Selene.

Além disso, um sistema de gestão amplia as possibilidades de melhoria nos processos: desde um controle de estoques mais efetivo, recebimento de mercadorias, produção até a saída do produto final e entrega no cliente. Sabendo que no SPED a qualidade das informações é imprescindível para que não ocorram problemas junto à Receita, um sistema de gestão onde os usuários do sistema possam inserir dados fidedignos é de extrema importância para que não só sejam atendidas as demandas fiscais e contábeis, mas que a empresa possa extinguir riscos, erros e retrabalhos.

O e-book "Big Brother Fiscal III: o Brasil na Era do Conhecimento" possui também depoimentos de empresas que já estão adequadas ao SPED e que já contam com os benefícios de uma gestão integrada proporcionada pelo ERP Omega, solução de gestão criada pela ABC71.

Para os interessados em receber a versão eletrônica do livro, basta preencher um cadastro no site www.abc71.com.br. O e-book estará disponível a partir do dia 08/12.

Mais informações sobre o livro "Big Brother Fiscal III: o Brasil na Era do Conhecimento" acesse: www.robertodiasduarte.com.br.


4 de dezembro de 2009

Design Patterns com Delphi : FlyWeight - parte 1

Mesmo com o barateamento dos chips de memória e o consequente aumento da disponibilidade desse recurso nos computadores mais modernos é uma boa ideia que, na construção de programas, se use com inteligência esse recurso. Programas que usam menos memória são mais leves para o Sistema Operacional gerenciar e acabam sendo mais responsivos ao usuário, isto é, atendem mais prontamente qualquer ação requisitada pelo usuário.

Imagine que você está criando um editor de textos que permite a formatação de trechos com fontes com características diferentes - negrito ou uma cor, por exemplo - em qualquer ponto do texto. O primeiro impulso é o de criar uma classe que tenha como membros o pedaço de texto em questão e a formatação aplicada - inclusive uma instância da Fonte. Num texto muito longo e ricamente formatado, pode haver centenas de ocorrências. Então, o consumo de memória com a solução apresentada pode tornar a navegação no texto bastante lenta, prejudicando a experiência do usuário.

Por isso, racionalizar a criação de instâncias de Fontes é uma boa saída. Para cenários como o descrito acima, foi criado o Design Pattern estrutural FlyWeight - ou Peso Mosca, numa referência à categoria de lutadores de boxe mais leves. Nesse pattern, a racionalização do uso da memória se dá através do compartilhamento das partes imutáveis das instâncias das classes. No caso dos fontes, isso significa criar uma parte fixa com a representação gráfica de cada caractere (e outras informações relevantes para desenhá-los) enquanto as variações de formatação permitidas, como negrito, itálico e a cor da letra, podem ser armazenadas da forma tradicional, dentro da própria instância da classe aonde a formatação está aplicada.

O conjunto dos valores imutáveis que compõem a instância da Fonte é chamado de Propriedades Intrínsicas, porque são elas que definem quem a Fonte é, independente do contexto onde ela será usada. É esse conjunto que será compartilhado para poupar o uso de memória. Em contrapartida, os valores que poderão variar de acordo com o contexto são denominados Propriedades Extrínsicas. Elas são livres para assumir qualquer valor que seja necessário para o contexto em que são usadas e, por isso, não podem ser compartilhadas, sendo que cada classe que fará uso delas deverá ter sua própria cópia.

Segue um esquema UML representando as classes envolvidas no pattern FlyWeight e suas associações:
Diagrama UML para FlyWeight

Conforme representado no diagrama acima, a nomenclatura para as classes envolvidas na implementação do FlyWeight é a seguinte :
FlyWeight contem as propriedades intrínsecas comuns do objeto, isto é, todos os dados que serão compartilhados. Ela também declara como interfaces as operações que poderão ser aplicadas com dados extrínsecos, dados esses que são passados como parâmetros na operação. A operação, então, usa as informações fornecidas como parâmetro para obter o resultado desejado, preservando no entanto o estado original do objeto. No exemplo, esse papel é exercido pela classe TWFonte, que introduz todas as informações que permitem desenhar os caracteres no padrão do fonte. A operação Desenha publicada pela classe aceita como parâmetros o local para desenho (contexto) e características temporárias que configuram cada operação de desenho. Por exemplo, esta operação pode desenhar um texto em negrito e vermelho num momento e em outro instante desenhar em itálico e verde - tudo calculado localmente com base nos dados globais de desenho e nas configurações estipuladas pelos parâmetros.
Os ConcreteFlyWeight são classes concretas que implementam as operações exigidas pela interface FlyWeight. Eles podem também introduzir novos dados intrínsecos, isto é, que serão usados pelas operações mas que são preservados - e, consequentemente, compartilhados por todas as instâncias. No exemplo, as classes TWArial e TWNewTimesRoman exercem esse papel.
O FlyweightFactory é uma espécie de Factory Method que mantem uma lista com as instâncias compartilhadas dos FlyWeights. A ideia é que sempre que for necessário obter uma instância de classe FlyWeight, aciona-se o Factory. Se uma instância já existir, ela é retornada; caso contrário, o Factory cria uma nova instância e adiciona-a à sua lista interna. Neste cenário, o Factory é reponsável por liberar a memória consumida pelas instâncias criadas por ele, quando for apropriado. No diagrama mostrado acima, a classe TWFontFactory desempenha essa tarefa.
O Client é toda classe que faz uso das operações publicadas pelo FlyWeight. Ela deve armazenar os dados extrínsecos para poder informá-los quando tiver que comandar uma das operações. Ela também pode manter referências à instâncias das classes FlyWeight. A classe TWTextoFormatado representada no diagrama é um exemplo desse papel.



1 de dezembro de 2009

Algumas dicas para usar o TStringGrid

Esses dias tenho trabalhado num novo projeto para a ABC71 e no ponto atual tive que mexer bastante com String Grids. No começo do mês, publiquei sobre a customização da aparência deles usando o Canvas associado ao grid e o evento OnDrawCell. O que vou mostrar aqui é um pouco mais próximo do funcionamento básico do String Grid.

A proposta mais básica do String Grid é exibir textos e, para tanto, temos que colocar os textos desejados na propriedade Cells. Essa é uma propriedade indexada pela linha e coluna da célula de modo que é possível associar um valor para cada combinação de linha/coluna.
sg.ColCount := 2;
sg.RowCount := 5;
{ Primeiro é a coluna, depois a linha }
sg.Cells [0,0] := 'Módulo';
sg.Cells [1,0] := 'Responsável';

{ comprimento em pixels da coluna 'Módulo' }
sg.ColWidths [0] := 130;
{ comprimento em pixels da coluna 'Responsável' }
sg.ColWidths [1] := 240;
{ Altura de cada célula }
sg.DefaultRowHeight := 18;
{ configuração do Grid - inclui permissão para que o usuário edite valores diretamente no Grid }
sg.Options := [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goEditing];

Também é possível associar todo um objeto a cada célula e trabalhar com ele no evento OnDrawCell. Esse recurso permite enriquecer a forma com que os dados são apresentados, incluindo imagens, cores, textos com formatações diferenciadas na mesma célula, etc.A propriedade que permite criar esse tipo de associação é a Objects. Da mesma maneira que o Cells, o Objects também é acessível por linha e coluna, garantindo que cada célula possa ser associada a seu próprio conjunto de informações.

Como o Objects é do tipo TObject e todas as classes em Delphi são heranças dela, podemos associar nesta propriedade a instância de qualquer classe. Em C++ Builder, a herança deve ser explicitamente declarada. No trecho de código abaixo, a instância diferente de uma classe de informações é associada a cada uma das células da coluna 0 (zero).
var i : Integer;
Obj : TWInfoModulo;
begin
for i := 0 to Grupo.Count - 1 do begin
Obj := Grupo.ModuloAt(i);
sg.Objects [0,i+1] := Obj;
end;
end;

A associação poderia ter sido feita em cada uma das células do Grid se fosse necessário. No exemplo, entretanto, cada linha é relativa a apenas um objeto e por isso associei-o a apenas uma coluna e usei a informação compartilhada com as demais colunas da linha. Essa informação pode, então, ser usada no OnDrawCell para customizar a aparência do Grid.

Na verdade, usar o Objects é útil em qualquer um dos demais eventos. Por exemplo, para obter tanto a formatação da entrada de dados quanto os próprios valores para a edição do texto em cada uma das células, além da validação dos valores entrados pelo usuário:
procedure TWCenaRespons.DoOnSgCellChange (Sender: TObject; ACol, ARow: Longint; const Value: string);
var sg : TStringGrid;
lValue : String;
Obj : TWInfoModulo;
begin
sg := Sender As TStringGrid;

{ Só faz a verificação quando a célula sai do modo de edição }
if not sg.EditorMode then begin
lValue := UpperCase (Trim (Value));
Obj := sg.Objects [0,ARow] As TWInfoModulo;

{ Se não é um valor válido, retorna ao modo de edição na mesma linha/coluna }
if not Obj.ResponsavelValido (lValue) then begin
sg.Row := ARow;
sg.Col := ACol;
sg.EditorMode := true;
end;
end;
end;

O evento OnCellChange é acionado a cada novo caractere introduzido ou removido pelo usuário quando editando o valor de uma célula. Isso exige alguns cuidados para construir a resposta ao evento. Na resposta reproduzida acima, por exemplo, eu testo antes se o Grid está em modo de edição e só faço a validação do texto entrado pelo usuário quando a edição termina.

Ou seja, o evento também é acionado quando o editor perde o foco por qualquer razão e, nesta situação, a propriedade EditorMode do Grid é ajustada para falso, indicando que a célula não está mais em modo de edição. Caso o novo valor digitado pelo usuário seja inválido, uso essa mesma propriedade para forçar que uma nova entrada seja digitada para corrigir o problema.