29 de setembro de 2011

Design Patterns com Delphi: Strategy - Parte I

Certas tarefas em um sistema computacional podem ser implementadas de diferentes maneiras sem que isso afete o resultado final esperado. Isto é, partindo de um determinado contexto, podemos selecionar um algoritmo entre vários possíveis e atingir o mesmo objetivo com qualquer um deles. Por exemplo, dada uma lista que precisa ser ordenada, há diversos algoritmos de ordenação que podem realizar a tarefa, como o método da bolha, quick sort ou busca binária. Independentemente do algoritmo escolhido, ao final do processo teremos a lista classificada. É claro que há diferenças de performance de um método pra outro mas isso é parte do processo de escolha daquele que melhor se adapta a uma situação. Cada situação encontrada pode levar a uma escolha distinta, sem que uma seja necessariamente preferida em relação às outras.

O programa pode, então, ter que intercambiar dinamicamente o algoritmo, seja por opção explícita do usuário ou por causa de outras questões circunstanciais encontradas durante a execução. Em projetos orientados a objetos, podemos desenhar uma solução para esse tipo de cenário usando o Design Pattern comportamental Strategy.

O objetivo do Strategy é permitir que o programa utilize de forma transparente qualquer algoritmo capaz de realizar uma determinada tarefa, construindo para isso uma estrutura de classes com baixo acoplamento. Com isso, fica fácil adicionar novos algoritmos, enquanto os códigos que os utilizarão continuam independentes da escolha feita.

Num sistema real, essa solução pode ser aplicada, por exemplo, no pagamento de títulos. Considere que um título pode ser pago levando o respectivo boleto ao banco, cadastrando a conta como débito automático ou registrando o pagamento no Internet Banking. Qualquer que seja a forma selecionada, o resultado é o título pago. O diagrama UML abaixo mostra as classes e suas relações para o Strategy aplicado a este cenário:
Diagrama UML para o padrão Strategy
A nomenclatura formal para as classes que participam da solução com o pattern Strategy é a seguinte:
A classe Strategy é uma interface onde é definido o comportamento comum a ser respeitado por todos os algoritmos. Em outras palavras, devemos incluir nesta classe funções virtuais (e provavelmente abstratas) que fixarão a forma com que a tarefa estará acessível às outras partes do programa. A classe TWPagamento do diagrama acima exerce esse papel, definindo que pagamentos deverão ser feitos através da função efetuaPgto.

São chamadas de Concrete Strategy as classes que atendem a definição introduzida no Strategy e que, portanto, implementam um algoritmo que efetivamente realize a tarefa proposta. No diagrama, esse papel cabe a três classes distintas : TWPgtoBoleto, TWPgtoDebitoAuto e TWPgtoInternete. Cada uma delas executará a tarefa a sua maneira, providenciando uma versão própria da função efetuaPgto.

O Context é a classe que usará o comportamento definido pela Strategy, invocando as funções disponibilizadas por ela. Portanto, o Context deverá armazenar internamente uma referência ao Strategy. Em geral, ele também terá que expor formas de interagir com o Strategy, publicando propriedade e funções. No nosso exemplo, o Context é a classe TWTitulo; a interação com o Strategy é conseguida passando-se para a tarefa uma instância do próprio título a ser pago.

Um Client é qualquer parte do sistema que solicite uma operação à classe Context. Esse tipo de classe foi omitido do diagrama.
No próximo post, mostro uma sugestão de como implementar na prática essa solução usando o Delphi.

Mais Informações
Posts sobre Design Patterns

23 de setembro de 2011

Trabalhando com a tag Canvas do HTML5 - parte III

Dando continuidade aos posts sobre uso da tag Canvas (veja parte I e parte II), falo aqui sobre o uso de textos no canvas e a manipulação de imagens.

As propriedades básicas disponíveis para formatarmos a exibição de textos no Canvas são o tipo de fonte e a cor para o desenho. O tipo do fonte pode ser modificado através da propriedade font. Como a interpretação do valor contido nessa propriedade é idêntica ao de folhas de estilo CSS, podemos configurar com ela a família do fonte (Arial, Tahoma, Courier, etc.), o tamanho das letras e estilos especiais (negrito, itálico, etc.).

Quando trabalhamos com figuras geométricas no outro post, vimos que há duas formas de comandar um desenho : traçando apenas a borda ou preenchendo também o interior da figura, sendo que as cores para um ou outro são configuradas em propriedades separadas. O mesmo vale para o desenho de textos.

Assim, a propriedade fillStyle conterá a cor para preencher o texto quando este for desenhado usando a função fillText. Por outro lado, a propriedade strokeStyle define a cor das bordas do texto vazado a ser desenhado usando a função strokeText. Ambas as propriedades aceitam valores que estejam em conformidade com as regras de definição de cores estabelecidas pelo padrão CSS. Veja um exemplo :
var myCanvas = document.getElementById("cnvText2");
var ctx = myCanvas.getContext("2d");

ctx.fillText ("Fonte e cores padrões", 5, 20);

ctx.font = "bold 16px Verdana";
ctx.fillStyle = "blue";
ctx.fillText ("Verdana 16px negrito", 5, 40);

ctx.font = "20px Courier";
ctx.strokeStyle = "#0FAF0F";
ctx.strokeText ("Courier 20px stroke", 5, 65);

ctx.font = "36px Arial";
ctx.strokeStyle = "maroon";
ctx.strokeText ("Arial 36px stroke", 5, 100);
Seu navegador (ou leitor de RSS) não suporta canvas ...

Usando fontes pequenas, o efeito de desenhar somente a borda acaba diluído, dando a impressão que o texto está todo preenchido. Portanto, ele funciona melhor com fontes maiores.

Conforme vemos no quadro anterior, as funções de desenho de texto aceitam 3 parâmetros : o próprio texto que será escrito e as coordenadas da posição no canvas onde ele deve ser renderizado. Normalmente, a posição indica o canto superior esquerdo de um retângulo virtual que conterá o texto. O significado dessa posição, no entanto, pode variar de acordo com o valor das propriedades textAlign e textBaseline. A primeira altera a interpretação do posicionamento horizontal do texto enquanto a segunda controla a coordenada vertical. Não é muito comum ter que alterar esses valores pois, no geral, os valores originais dão conta do recado.

Poderíamos ainda ter passado como 4o parâmetro o comprimento máximo que o texto deve ocupar - caso o tamanho real exceda esse valor, o tamanho do fonte é alterado automaticamente para que o texto caiba.

Um outro aspecto para incrementar a renderização de textos é a criação do efeito de sombra. Essa característica é controlada pelas propriedades shadowColor - que corresponde à cor da sombra - e o par shadowOffsetX e shadowOffsetY - respectivamente, o deslocamento horizontal e vertical da sombra a ser projetada em relação ao texto. É possível também esfumaçar a sombra, deixando o efeito mais sutil. Consegue-se isso usando a propriedade shadowBlur, cujo valor indica o quão borrada deve ser a sombra projetada. Seguem alguns exemplos:
ctx.font = "18pt Arial";
ctx.fillStyle = "#0F0FA0";
ctx.shadowColor = "gray";
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fillText ("Texto com sombra comum", 5, 40);

ctx.fillStyle = "#7F0FA0";
ctx.shadowColor = "black";
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.fillText ("Texto com sombra borrada", 5, 70);

ctx.fillStyle = "green";
ctx.shadowColor = "black";
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 8;
ctx.fillText ("Sombra muito borrada", 5, 100);
Seu navegador (ou leitor de RSS) não suporta canvas ...

De acordo com a padronização da W3C, para desenharmos uma imagem no canvas devemos usar a função drawImage. Com ela, podemos transferir para o canvas uma imagem inteira ou uma parte retangular dela. Também podemos modificar a proporção da imagem renderizada no canvas, indicando se ela é cópia exata da imagem original ou se será ajustada para um novo comprimento e altura. Neste link publicado pela W3C há uma explicação e um gráfico que ilustram o funcionamento dos parâmetros envolvidos na cópia da imagem original para o Canvas.

Em sua versão mais simples, o drawImage aceita como parâmetros a imagem em si e a coordenada do canto superior esquerdo no canvas a partir de onde a imagem será renderizada. Neste contexto, uma imagem pode ser tanto uma tag IMG quanto um outro canvas ou até mesmo a tag VIDEO, introduzida no HTML5. No caso da IMG, podemos recuperar uma referência à imagem usando a função getElementById do documento HTML (se a imagem já estiver embutida na página HTML) ou criar uma nova referência do zero, instanciando o objeto Image:
var img = new Image();
img.onload = function () {
ctx.drawImage (img, 10, 10);
ctx.drawImage (img, 110, 10, 70, 110);
ctx.drawImage (img, 10, 15, 45, 60, 220, 20, 45, 60);
}
img.src = '40anos.gif';

ctx.font = "10pt Tahoma";
ctx.fillText ("Normal", 30, 130);
ctx.fillText ("Novo tamanho", 110, 130);
ctx.fillText ("Cópia parcial",210, 130);
Seu navegador (ou leitor de RSS) não suporta canvas ...
É importante ressaltar que a carga da imagem é disparada de modo assíncrono, logo após a URL para a imagem ter sido informada. Isto significa que o script continua a executar enquanto o navegador localiza e carrega a imagem. Por isso, no script acima a imagem é desenhada no canvas dentro do evento onload da tag da imagem. Ou seja, o desenho só é feito quando efetivamente a imagem está disponível.

As propriedades para sombreamento de texto discutidas neste post também são aplicáveis ao desenho de imagens. Assim, podemos incrementar o resultado adicionando esse efeito conforme a necessidade:
var img = new Image();
img.onload = function () {
/* Remove o recurso de sombreamento para a 1a imagem */
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.drawImage (img, 10, 10);

/* Aplica o recurso de sombreamento para as demais imagens */
ctx.shadowColor = "#2D90E0";
ctx.shadowOffsetX = 7;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 8;

/* Copia a imagem completa para um local no canvas */
ctx.drawImage (img, 110, 10);
/* Copia parte da imagem para um local no canvas */
ctx.drawImage (img, 10, 15, 45, 60, 220, 20, 45, 60);
}
img.src = '40anos.gif';

/* Para os textos, remove os efeitos de sombreamento */
ctx.font = "10pt Tahoma";
ctx.shadowColor = "black";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.fillText ("Normal", 30, 130);
ctx.fillText ("Com Sombra", 110, 130);
ctx.fillText ("Cópia parcial", 210, 123);
ctx.fillText ("com sombra", 210, 137);
Seu navegador (ou leitor de RSS) não suporta canvas ...
Note pelo exemplo anterior que mesmo na cópia de apenas parte de uma imagem o efeito de sombreamento se aplica.

Em outra oportunidade mostrarei mais recursos do canvas associado a cores e imagens (como rotação e criação de gradientes) e como misturar isso tudo.

15 de setembro de 2011

Funcionamento do Certificate Store do Windows

Tenho recebido com frequência dúvidas a respeito do acesso a certificados digitais importados no Certificate Store do Windows. Pela forma com que as dúvidas têm sido colocadas, percebi que faltou nos posts sobre esse assunto dar uma ideia global do que é o Certificate Store e como ele funciona.

Como disse no post sobre acesso ao Certificate Store do Windows com C#, o Certificate Store é um repositório centralizado onde são armazenados os certificados digitais disponíveis em um computador. É possível acessar esse repositório visualmente, acessando o console de gerenciamento pela opção Executar do menu Iniciar do Windows. Na caixa de edição que se abre, digite o comando abaixo:
mmc \windows\system32\certmgr.msc
Normalmente, a tela que aparece é similar à que está reproduzida abaixo:
Certificados Digitais
Com essa aplicação, é possível gerenciar os certificados instalados no computador, fazendo a importando de novos certificados e eliminando aqueles que não são mais desejados. Observe que o painel à esquerda mostra uma organização hierárquica, com uma raiz e diversas pastas embaixo dela. O nó raiz nesse exemplo apresenta o nome Certificates - Current User. Isso significa que todas as pastas sob ele e os certificados listados no painel à direita estão disponíveis apenas para o usuário que atualmente está logado no Windows.

As pastas servem para agrupar os certificados por semelhança na finalidade de uso. Na imagem, a pasta Personal está selecionada, permitindo-nos visualizar à direita os certificados catalogados para uso pessoal do usuário Windows atual. Outro exemplo de pasta é a que contém certificados identificando as Autoridades Certificadoras (CA), que são as entidades responsáveis pela emissão dos certificados.

Do ponto de vista do programador que vai acessar o Store usando a classe X509Store (ou o CAPICOM), essas duas informações - nó raiz e pasta - são imprescindíveis. O construtor da X509Store aceita como parâmetros o Store Name e o Store Location, sendo que o name corresponde à "pasta" onde está o certificado e location é o nó raiz ao qual a pasta está atrelada. A pasta personal é mapeada com o nome "My" para essa função.

Agora, imagine que você construiu um serviço que acessa o Certificate Store. No Windows, cada serviço é executado por um usuário específico que é configurado na guia Log On das propriedades do serviço. Com isso, ele poderá entrar em execução mesmo que ninguém esteja logado no Windows pois o login será feito automaticamente para o usuário que foi associado ao serviço. Portanto, se você importou um certificado para seu usuário mas está executando o serviço com um usuário diferente, esse outro usuário não enxergará o seu certificado.

Isso vale também para aplicações ISAPI - sites construídos em ASP ou ASP.NET se encaixam nessa categoria - uma vez que elas rodam como parte do serviço do IIS (Internet Information Services). O IIS é atrelado normalmente a um usuário padrão do Windows chamado Local System, significando que as credencias desse usuário é que serão utilizadas para acessar os recursos do sistema, tais como o registry e a hierarquia de certificados.

Para resolver esse impasse, podemos importar o certificado num Store Location acessível por todos os usuários. Como a aplicação disponível carrega apenas originalmente o Location "Current User", teremos que adicionar na mão um plugin para o Location com a característica que precisamos. Esse Location é o "Local Computer".

Para isso, execute o MMC.exe sem parâmetros no Iniciar -> Executar do Windows. No menu File da aplicação, clique na opção Add/Remove snap-in e então o botão "Add" para ativar a tela de adição dos plugins. Na lista de plugins que aparece, selecione certificates e novamente clique em "Add". A imagem abaixo mostra a tela de adição do plugin de gerenciamento de certificados com a opção "Computer Account" (o computer local) selecionada.
Add snap-in

Após a adição, o console mostrará uma hierarquia similar à da imagem no início deste post, com a exceção de que o nó raiz será "Local Computer". Agora podemos importar certificados na pasta "Personal" e torná-los disponíveis para os programas, independentemente das credenciais do usuário que está executando esse programa ou serviço.

Certificados importados na pasta "Personal" do "Local Computer" podem, então, ser acessados abrindo-se o Certificate Store com os parâmetros mostrados abaixo:
X509Store lStore = new X509Store (StoreName.My, StoreLocation.LocalComputer);
/* ... */


6 de setembro de 2011

Trabalhando com a tag Canvas do HTML5 - parte II

No último post, apresentei o funcionamento básico da tag Canvas do HMTL 5, sua inserção numa página HTML e código JavaScript simples para traçar linhas na área do Canvas. Avançando nesse assunto, gostaria de apresentar aqui outras funções de desenho e como lidar com cores.

Além de linhas, a lista de funções para traçar figuras geométricas básicas inclui também funções para criar retângulos e arcos. No caso dos retângulos, há 2 funções : uma para desenhar apenas a borda, sem preencher o interior - strokeRect - e outra para desenhá-lo preenchido - fillRect.
/* ... */
ctx.beginPath();
ctx.fillRect (10,10,60,100);
ctx.strokeRect (75,10,60,100);
ctx.clearRect (30, 50, 25, 25);
ctx.closePath();

var grausFin = 360;
var radFin = (Math.PI/180) * grausFin;

ctx.beginPath();
ctx.arc(180, myCanvas.height / 2, 25, 0, radFin, false);
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.arc(230, myCanvas.height / 2, 25, 0, radFin, false);
ctx.fill();
ctx.closePath();
Seu navegador não suporta canvas ...

Já a função clearRect que aparece no código acima serve para limpar uma área retangular do canvas, tornando-a transparente. Isto é, a área é totalmente preenchida com a cor de fundo da tag onde o Canvas está inserido, substituindo os pixels contidos na área. A utilidade desse recurso está em preparar o canvas para receber novos desenhos sem sofrer interferência daqueles que existiam anteriormente.

Os parâmetros das funções de retângulo são sempre o ponto superior esquerdo (x, y) seu comprimento e sua altura.

Como mostra o exemplo anterior, o desenho de um círculo é feito através da função arc. O que determina se ele será preenchido ou não é a função chamada depois : use fill para preenchê-lo ou stroke para desenhar apenas o seu contorno.

Os parâmetros da função para desenhar arco são o ponto central (x, y), seu raio, o ângulo na circunferência onde o desenho será iniciado, o ângulo final e um booleano indicando se o desenho será feito no sentido anti-horário. Como diz o nome da função, ela traça um arco entre o ângulo inicial e final, formando parte de um círculo com centro em (x, y) e o raio dado. Isso implica que temos que informar os ângulos 0 (zero) e 2pi se quisermos desenhar um círculo completo. Observe que os ângulos são informados em radianos e não em graus. A fórmula (PI * 180 / graus) que aparece no exemplo faz a conversão de graus para radianos.

As cores aplicadas em linhas e nos preenchimentos são controladas de forma independente entre si. Ao traçar linhas – aí incluindo a função para desenhar somente as bordas de um retângulo ou arco – a cor é ditada pela propriedade strokeStyle do canvas, enquanto para fazer qualquer preenchimento, a cor usada é aquela atribuída ao fillStyle.
/* ... */
ctx.beginPath();
ctx.fillStyle = "rgb(45,80,150)";
ctx.strokeStyle = "maroon";
ctx.fillRect (10,10,60,100);
ctx.strokeRect (75,10,60,100);
ctx.closePath();

var grausFin = 360;
var radFin = (Math.PI/180)* grausFin;

ctx.fillStyle = "#FFA500";
ctx.strokeStyle = "green";

ctx.beginPath();

ctx.arc(180, myCanvas.height / 2, 25, 0, radFin, false);
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.arc(230, myCanvas.height / 2, 25, 0, radFin, false);
ctx.fill();
ctx.closePath();
Seu navegador não suporta canvas ...
No exemplo acima, utilizei cores especificando diretamente seu nome (com em “green”) ou sua composição RGB (como em "rgb(45,80,150)"). Também poderia ter usado um código hexadecimal (como em "#FFA500"). Ou seja: são os mesmos mecanismos aceitos pelo CSS3.

Um outro aspecto importante para traçar linhas e desenhar bordas é a espessura que a linha deve ter. Podemos modificar o valor contido na propriedade lineWidth do canvas para obter linhas mais grossas. O valor padrão dela que corresponde a 1 unidade, sendo que ela aceita valores positivos.
/* ... */
var angIni = 3 * Math.PI / 2;
var angFin = Math.PI / 2, x, i;

/* Linhas e arcos */
x = 5;
for (i = 0; i < 10; i ++)
{
ctx.lineWidth = i + 1;
ctx.lineCap = "round";

ctx.beginPath();
ctx.moveTo (x, 5);
ctx.lineTo (x, 35);
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.arc(x, 75, 20, angIni, angFin, false);
ctx.stroke();
ctx.closePath();

x = x + 15;
};

/* Bordas de retângulos */
ctx.beginPath();
ctx.lineWidth = 8;
ctx.strokeRect(180, 5, 100, 100);
ctx.lineWidth = 6;
ctx.strokeRect(190, 15, 80, 80);
ctx.lineWidth = 4;
ctx.strokeRect(200, 25, 60, 60);
ctx.lineWidth = 2;
ctx.strokeRect(210, 35, 40, 40);
ctx.closePath();
Seu navegador não suporta canvas ...

No exemplo acima, modifiquei também a propriedade lineCap, que determina como o desenho de linhas será arrematado. Ao usar o valor "round", instruo o canvas a arredondar as pontas das linhas ao invés de deixá-las retas, como no comportamento padrão.

No próximo post, falo sobre uso de textos, imagens e como modificar o modo que os desenhos aplicados ao canvas interagem entre si, criando composições entre eles.