Otimização de Código, parte I: Entendendo a Complexidade de Algoritmos

Escrito em 26 de Março de 2008 em Performance por Leonel Togniolli

A otimização de código é uma tarefa muitas vezes deixada de lado, e, muito pior, feita de formas erradas muitas vezes por inexperiência do programador, tendo como resultado programas que funcionam bem em testes com um volume pequeno de dados e que demoram horas conforme o volume de dados aumenta.

Para entender os passos necessários para otimizar seu código de uma forma que tenha um bom resultado prático sem comprometer a qualidade do código, deve-se entender alguns conceitos importantes:

  1. Quando otimizar.
  2. Como reconhecer qual parte do código toma mais tempo.
  3. E o que fazer para diminuir o tempo tomado por essas partes do código.

Vou elaborar o primeiro item hoje, e deixar os próximos dois para artigos futuros.

Quando Otimizar

Donald Knuth escreveu, em 1974, a já famosa e gasta frase: "Devemos esquecer das pequenas eficiências, cerca de 97% do tempo. Otimização prematura é a raiz de todo o mal". Em seguida, ele elabora que não se deve ignorar os 3% restantes, mas apenas após que eles forem identificados.

Mais que isso, diz que a experiência universal de programadores (incluindo a minha), é que após utilizar ferramentas para medir a performance do código, você percebe que o que as tentativas de adivinhar quais são esses trechos críticos geralmente são falhas. É fácil perceber que tempo gasto otimizando partes do código que não são críticas para a performance é uma perda de tempo, e acabam tendo um impacto negativo quando se considera o tempo que será levado para depurar e manter esse código, agora mais complexo por otimizações desnecessárias.

O retorno da otimização costuma seguir o Principio de Pareto, que postula que apenas 20% do código é responsável por cerca de 80% da performance. Dessa forma, mesmo que se diminua pela metade o tempo levado por um trecho específico de código, se ele só influencia em 1% no tempo total de determinado processo, você só teve 0.5% de ganho de performance. Por outro lado, ao identificar um trecho que leva 30% do tempo, qualquer melhoria de performance nele terá um retorno 30 vezes maior. Na prática, vejo porcentagens ainda maiores que essa, acentuando ainda mais a importancia de localizar corretamente os trechos que levam mais tempo, conhecidos como gargalos, ou bottlenecks, se preferir um informatiquês mais elaborado.

Isso quer dizer, que, na prática, ao escrever código, evite tomar qualquer decisão no impacto de performance que ela vai ter. Decisões como usar uma ou outra função para realizar determinada tarefa devem ser tomadas com base nos méritos de legibilidade e mantenabilidade do código, muito acima de qualquer suposta diferença de performance entre elas. Não deixe a intuição de que algum ponto será um gargalo influenciar essa decisão (lembre-se, intuições geralmente são desbancadas por medições de performance). Qualquer escolha errada nesse ponto é facilmente adequada nas fases de medição e otimização.

O que isso não quer dizer, em contrapartida, é que se deve ignorar a performance na hora de planejar a solução que vai ser implementada, antes da codificação. É importante saber o que está sendo implementado e qual a complexidade dessa solução. Deve-se estimar a quantidade de itens que o código deve ser capaz de processar, e adequar a complexidade do algoritmo e estruturas de dados escolhidas para obter uma performance apropriada a esse número. Uma das soluções na etapa de diminuir o tempo tomado por determinado gargalo é substituir o algoritmo por um mais eficiente, mas tal substituição costuma ser trabalhosa e demanda mais trabalho que um planejamento adequado tomaria.

Complexidade de Algoritmos

A complexidade de algoritmos é uma medida da escalabilidade de um algoritmo. De quanto trabalho ele precisa fazer para resolver o problema conforme o número de itens aumenta. Existe uma forma padrão de analisar e representar a complexidade de algoritmos, que é a notação Big-O (raramente traduzida para Grande-O). Para determinar a complexidade de um algoritmo, conta-se o número de operações que ele faz para cada item, onde operação pode ser comparações, trocas, leituras de disco, etc. Em seguida, é determinada uma expressão que representa essa quantidade. Uma complexidade O(n), ou linear, representa que o número de operações é constante e proporcional ao número de itens na lista. O(n²), ou quadrática, mostra a proporção que o número de operações cresce conforme o número de itens aumenta.

É muito mais fácil de entender essa representação graficamente:

O(n) vs O(n²)

A figura representa o número de operações efetuadas por dois algoritmos hipotéticos, um O(n) e o segundo O(n²). Algumas características importantes:

  • É fácil de observar a falta de escalabilidade do segundo algoritmo. Conforme o número de itens aumenta, o número de operações aumenta muito rapidamente.
  • Não é um gráfico de f(x) = n e g(x)=x², apesar que esse seria um exemplo igualmente válido (e mais simples). A expressão mostrada pela notação O não é exatamente o número de operações que vão ser executadas, mas sim uma representação de como esse número muda em relação à quantidade de itens processados. Não existe O(n + 10) nem O(10n). Ambos seriam representados por O(n). Dois algoritmos podem ter a mesma complexidade mas um efetuar mais operações, ou o mesmo número de operações e levar um tempo maior que o outro (se a operação for mais demorada).
  • O algoritmo mais complexo pode levar menos tempo que o menos complexo. O tempo extra gasto no caso mais simples pode ser a geração de uma tabela de pesquisa, por exemplo, que pode levar mais tempo do que a busca por força em um número pequeno de itens. Supondo que no gráfico acima a operação efetuada pelos dois algoritmos seja equivalente, o mais complexo vai ser mais rápido até um pouco mais de 150 itens. Se o número de itens vai ser baixo (por exemplo, processar as janelas abertas em um editor de imagens), a segunda opção pode ser a mais adequada. Se o número for alto (ou desconhecido), deve-se tomar uma extrema atenção na complexidade do algoritmo que será usado.

Sabendo desses detalhes, fica mais fácil determinar a complexidade de um algoritmo qualquer. Um exemplo seria a busca de um item em uma lista desordenada. Em pseudocódigo:

resultado = -1
de i := 1 até Lista.Count
  se Lista[i] = Item
    resultado = i
retorne resultado

A operação que está sendo contada é a comparação. O número de operações é sempre igual ao número de itens na lista, claramente O(n). Uma otimização óbvia reduz o número de comparações:

de i := 1 até Lista.Count
  se Lista[i] = Item
    retorne i
retorne -1

Nesse novo exemplo, no melhor caso, o item procurado é o primeiro da lista, sendo feita apenas uma comparação. No pior caso, não é encontrado, sendo feitas n comparações. O complexidade analisa o pior caso, ainda mantendo esse algoritmo como O(n). Perceba que se forem feitas diversas buscas em uma lista, o número médio de comparações será entre 1 e N, e cresce proporcionalmente a N, mantendo a característica de complexidade.  A média seria 3n/4 para uma lista não ordenada e n/2 em uma lista ordenada, caso tenha curiosidade.

Um algoritmo para encontrar todos os itens duplicados em uma lista poderia ser escrito assim:

de i := 1 até Lista.Count
  de j := 1 até Lista.Count
    se (i <> j) e (Lista[i] = Lista[j])
      Duplicados.Adiciona(i, j)

Este seria um exemplo de O(n²). Para cada item, todos os outros itens são percorridos. Otimizações simples, como no exemplo anterior, podem ser limitar a iteração no loop interno de 1 até N-1, e no loop interno para a faixa i + 1 até N, que diminuem o número de comparações, mas não alteram a complexidade. O número de iterações ainda é proporcional ao quadrado do número de itens.

Algumas medidas Big-O comuns são as seguintes, em ordem crescente de complexidade:

  • O(1), ou constante - O tempo do algoritmo independe do número de itens. Claramente a melhor opção quando N pode crescer muito. É a complexidade em inserção e busca em Tabelas Hash
  • O(log n), ou logarítmico - É proporcional ao logaritmo de n, que cresce em uma taxa muito mais lenta que n. É uma opção bem adequada quando não é possível utilizar O(1). A busca binária em uma lista ordenada tem essa complexidade.
  • O(n), ou linear - É o exemplo que já vimos de uma busca de força bruta em uma lista qualquer. Começa a ser um problema quando seu código tem que tratar um número indeterminado de itens.
  • O(n²), ou quadrática - O número de operações é proporcional ao quadrado do número de itens. O algoritmo intuitivo de ordenação, a ordenação por inserção, tem essa complexidade. Deve ser evitada praticamente para qualquer número de itens que não estejam sobre estrito controle.
  • O(n!), ou fatorial - Proporcional ao fatorial de n, ou seja, cresce extremamente rápido. Mesmo que as operações sejam rápidas, rapidamente com o crescimento de n o tempo tomado pelo algoritmo passará a ser excessivamente grande (horas, dias, ou mais). A força bruta de uma senha tem essa essa complexidade, ou a solução do problema do caixeiro viajante.

Conclusão

A complexidade de algoritmos é um ponto importante para ser considerado na escolha do design ou das estruturas usadas na codificação, evitando introduzir problemas de escalabilidade. Para isso, é importante saber estimar a complexidade de um código e reconhecer a complexidade de inserções e buscas em estruturas de dados comuns. Apesar disso, deve-se evitar a tentação de intuitivamente deduzir quais serão os gargalos no momento da codificação e tomar decisão baseadas nessa intuição, prezando a legibilidade acima de tudo. É muito mais fácil localizar e corrigir gargalos depois de ter o código escrito do que localizar e corrigir problemas de estilo.

Se você é novo por aqui, não deixe de assinar o feed RSS ou notificações por email. Não perca novos artigos!

Introdução ao BlackfishSQL

Escrito em 28 de Novembro de 2007 em BlackfishSQL por Leonel Togniolli

No final de outubro apresentei o BlackfishSQL na sétima Borland Conference, em São Paulo. Os slides desta apresentação e das outras já estão disponíveis para download.

Ontem apresentei uma palestra semelhante na CodeRage II, em português e inglês. A CodeRage ainda está acontecendo (e é grátis!), e algum tempo depois do seu término o replay será disponibilizado na CodeGearTV.

Enquanto isso, se você estiver interessando em mais uma oportunidade de conhecer  o BlackfishSQL, aprender a escrever Stored Procedures e Triggers em Delphi, e fazer perguntas ao vivo, não perca o CodeGear Delphi Day em Português, dia 7 de dezembro. Além da minha apresentação do BlackfishSQL, existem seis outras palestras técnicas, gratuitas, em português.

Se você ainda não conheçe o BlackfishSQL, vou mostrar rapidamente porque você deve assistir minha apresentação.

BlackfishSQL

BlackfishSQL é um banco 100% gerenciado, rodando completamente sobre uma máquina virtual, sem dependências de nenhum sistema operacional. Existem versões de BlackfishSQL para a plataforma Java e para a plataforma .Net. Apesar de ser o primeiro lançamento para plataforma .Net, e a primeira vez que é chamado de BlackfishSQL, é a oitava versão do banco de dados, que existia com nome JDataStore anteriormente, acompanhando o desenvolvimento do JBuilder por um bom tempo. Apesar de ainda termos poucas escolhas de plataforma com .Net, Java existe em praticamente todo lugar e o BlackfishSQL o acompanha em Linux, Solaris, ou no seu celular, por exemplo.

BlackfishSQL suporta o padrão SQL92, conhecido de todos que já trabalham com SQL, tendo uma boa compatibilidade, sem muitas extensões fora do padrão, preferindo prover os recursos adicionais através de stored procedures já embutidas.

Outras caracteristicas importantes são a fácil distribuição de um servidor BlackfishSQL, de uma aplicação que acessa tal servidor, ou de um aplicação que utiliza o BlackfishSQL de forma embutida no processo - não é necessário instalação, registrar DLLs, ou nada disso - é copiar alguns arquivos e rodar.

A caracteristica do BlackfishSQL que vai interessar mais desenvolvedores Delphi é a possibilidade de escrever Stored Procedures, UDFs e triggers em código Delphi (ou reaproveitar seu código Delphi existente como Stored Procedures), e usar o editor e debugger do Delphi para esse desenvolvimento, de uma forma bem fácil.

Se isso parece interessante, não perca o Delphi Day para conhecer o BlackfishSQL de verdade e ver as outras apresentações.

Conhecendo a API ZDBC

Escrito em 03 de Novembro de 2007 em Bancos de Dados por CyberVegeta

A API ZDBC (Zeos DataBase Conectivity) é um port do JDBC (Java DataBase Conectivity) 2.0 utilizada pelos componentes do Zeos DBO para prover acesso uniforme a diferentes bancos de dados. Inclusive, você pode recorrer à documentação do JDBC para sanar dúvidas quanto às interfaces e métodos da ZDBC.

Quanto aos componentes TZConnection, TZQuery, etc, eles apenas encapsulam as interfaces da ZDBC para permitir integração com o mecanismo padrão de acesso a banco de dados do Delphi (TDataSource, TDataSet e etc).

Algumas das vantagens da utilização desta API são:

  • Velocidade
  • Mais simples de utilizar
  • Baseado em interfaces

Como nem tudo é um mar de rosas, temos também algumas desvantagens:

  • Totalmente incompatível com TDataSet. Então se você um dia precisar trocar o Zeos por DBX, IBX, etc, terá uma dose extra de dor de cabeça por isso.
  • Não permite ligação com componentes data-aware.

Agora imagino que você esteja se perguntando: Espera aí, como assim “não permite ligação com os componentes data-aware”? Vou ter que codificar toda a ligação entre a GUI e os dados na mão?

Calma, não é bem assim. Bom, de fato, você teria que fazer isso caso quisesse utilizar a ZDBC para criar telas. Eu recomendo utilizar esta API apenas em rotinas que precisem se comunicar com o banco de dados mas sem qualquer interação entre dados e GUI, e deixar os TDataSets para a interface com o usuário. E não se preocupe, não existe qualquer problema em misturar ambas as abordagens em um sistema.

Como disse anteriormente, a API é baseada em interfaces, e as principais são:

  • IZResultSet – Representa os resultados de uma consulta. É basicamente o equivalente ao TDataSet.
  • IZStatement – Um comando a ser executado no banco de dados. Pode ser um insert, um select, chamada a um stored procedure, um comando DML, etc. Não suporta parâmetros. Quando uma query é executada, um IZResultSet é retornado.
  • IZPreparedStatement – Como o IZStatement, mas este permite executar comandos parametrizados.
  • IZConnection – É a conexão com o banco de dados. É quem cria os statements, controla a transação(Sim, no singular, múltiplas transações não são suportadas), etc.
  • IZDriver – Implementação de um driver específico para um determinado servidor de banco de dados. É quem cria as conexões, mas você normalmente não lida com ele.
  • IZDriverManager – Gerencia os drivers registrados. Normalmente, é quem acessa os drivers para criar conexões para você. Só deve haver um objeto IZDriverManager na memória, o qual é acessado pela variável global DriverManager na unit ZDbcIntfs.pas

Cada driver deve prover implementações das interfaces acima (bem como de outras não citadas) específicas para o banco de dados em questão.

Agora vamos pôr as mãos na massa.

Todas as interfaces ZDBC estão na unit ZDbcIntfs.pas, então precisamos incluir esta unit aonde quisermos utilizar tais interfaces (Não nos interessa aonde estão as implementações delas).

Agora, precisamos obter uma conexão. Isso pode ser feito de duas maneiras:

Através de um TZConnection. Sim, o componente de conexão utilizado pelo código baseado em TDataSet. Como eu disse anteriormente, os componentes como TZConnection, TZQuery e etc são simplesmente uma camada adicionada sobre a ZDBC, que é quem efetivamente faz o trabalho de acesso aos dados.

Esse é o método mais utilizado por mim, pois assim meu aplicativo só precisará de uma conexão com o banco de dados tanto para os componentes baseados em TDataSet quanto para a ZDBC.

Para obter tal conexão basta conectar o TZConnection e depois acessar a sua propriedade chamada DbcConnection (caso o TZConnection esteja desconectado, esta propriedade tem o valor nil).

Outra forma de obter a conexão é requisitando-a diretamente ao DriverManager. Para isso precisamos montar uma connection string, sua sintaxe é a seguinte:
zdbc:<protocolo>://<host>[:<porta>]/<banco>[?<param1>[=<valor1>]…]

Onde “protocolo” é o nome do driver a ser utilizado. Por exemplo:

firebird-2.0

ado

mssql

postgresql

Quanto aos parâmetros, existem alguns comuns a todos os drivers, como username e password, e outros específicos de cada um. Para maiores detalhes sobre isso, consulte a documentação do Zeos.

Aqui vai um exemplo de connection string para firebird 2.0:

zdbc:firebird-2.0://localhost/MeuBanco?username=SYSDBA;password=masterkey

De posse da connection string, basta passa-la para o método GetConnection do DriverManager para obter uma implementação apropriada de IZConnection:

var
 
Conn: IZConnection;
begin
Conn := DriverManager.GetConnection(‘zdbc:firebird-2.0://localhost/MeuBanco?username=SYSDBA;password=masterkey’);

Agora que temos a conexão, já podemos executar algum comando no banco de dados.

Qualquer comando que queremos executar é representado por um objeto que implementa IZStatement, ou uma subinterface.

Um IZStatement representa uma instrução que é executada sem envolver parâmetros, algo como:

SELECT A, B FROM TABELA
UPDATE TABELA SET A = B + 1
INSERT INTO TABELA(A, B) VALUES (1, 2)

Uma instancia de IZStatement é obtida através do método CreateStatement de IZConnection. Desta forma:

var
 
Stmt: IZStatement;
begin
 
Stmt := Connection.CreateStatement;

Em IZStatement iremos usar, basicamente, dois métodos:

  • ExecuteQuery, para executar selects, este método retorna um IZResultSet representando o conjunto de dados retornado. Irei escrever mais sobre esta interface mais adiante.
  • ExecuteUpdate, para executar comandos que não retornem dados, como instruções DDL, insert, update e delete. Este método retorna um inteiro que corresponde ao número de registros afetados pelo comando executado.

Nota: Ambos os métodos acima citados recebem como parâmetro a instrução SQL a ser executada.

Para casos aonde precisamos executar uma instrução parametrizada, utilizamos o IZPreparedStatement. Ele é criado pelo método PrepareStatement de IZConnection e recebe como parâmetro a instrução a ser executada. Exemplo:

var
 
Stmt: IZPreparedStatement;
begin
  Stmt := Connection.PrepareStatement(‘select a, b from tabela where c=?’);

Após obter o statement, precisamos alimentar os parâmetros. Isto é feito com os métodos SetXXX, onde XXX indica o tipo de dado que irá para o parâmetro. Estes métodos recebem dois parâmetros: O primeiro, um inteiro, indica o índice do parâmetro que receberá o valor, sendo que este índice é baseado em 1, ou seja, o primeiro parâmetro tem índice 1, o segundo 2 e assim por diante. Na verdade, todos os índices na ZDBC possuem 1 como primeiro elemento. O segundo parâmetro dos métodos SetXXX é o valor que será gravado no parâmetro, seu tipo corresponde ao método que você está utilizando, por exemplo, SetInt recebe um Integer neste parâmetro, SetString recebe uma String, SetFloat recebe um Double, e assim por diante. Temos também o SetNull que indica que um parâmetro tem valor indefinido, isto é, null. Ele recebe o índice do parâmetro e uma constante de TZSQLType que indica o tipo de dados do parâmetro. Exemplo:

var
 
Stmt: IZPreparedStatement; begin
 
Stmt := Connection.PrepareStatement(‘select a, b from tabela where c=? and d=?’);
  Stmt.SetInt(1, 10);
  Stmt.SetNull(2, stInteger);

Tendo o statement com os parâmetros definidos, podemos executa-lo. Para instruções select, devemos utilizar o método ExecuteQueryPrepared, que retorna um IZResultSet e ExecuteUpdatePrepared para as demais instruções, e retorna um inteiro indicando o número de registros afetados.

Como prometido, irei explicar um pouco sobre o IZResultSet.

Esta interface representa um conjunto de registros retornados por uma consulta. Como eu disse antes, pode ser comparado ao TDataSet.

Para navegar pelos registros, o processo é um pouco diferente do utilizado em TDataSet. Primeiramente, devemos saber que o resultset é inicialmente posicionado ANTES do primeiro registro, então você irá obter uma exceção caso tentar ler alguma coluna antes posicioná-lo em um registro (Enquanto que um TDataSet é aberto e posicionado sobre o primeiro registro). Para ir para o registro seguinte, temos o método Next. Diferentemente do seu homônimo em TDataSet, este retorna um boolean que indica se existe algum próximo registro, ou seja, em um laço que deve iterar sobre todos os registros, Next pode ser utilizado tanto para a navegação quanto para controlar a condição do laço. Seria assim:

var
 
RS: IZResultSet;
begin
 
RS := Connection.CreateStatement.ExecuteQuery(‘select * from tabela’);
 
while RS.Next do
   
// Aqui você manipula o registro.

Essa forma de navegação é bem mais interessante que a utilizada em TDataSet, na minha opinião. Achei tão melhor que até implementei uma interface que me permite navegar em um TDataSet como se fosse um IZResultSet, mas isso foge ao tema deste texto.

Para ler o conteúdo dos campos, temos os métodos IsNull, que recebe o índice do campo e retorna true ou false indicando se o campo está ou não nulo, e GetXXX, que recebe o índice do parâmetro e retorna o seu valor lido como um determinado tipo de dado (Ex, GetString para ler como String, GetInt para ler como Integer, e por aí vai).

O método GetMetaData de IZResultSet retorna um IZResultSetMetaData, que possui metadados sobre o resultset. São informações como número, nome e tipo das colunas.

Estes são os pontos básicos da utilização da API ZDBC, o necessário para conseguirmos tirar proveito dela em nossos sistemas. É claro que existem outros pontos a serem explorados, mas preferi não entrar em maiores detalhes.

Ruby on Rails - Livro gratuito

Escrito em 05 de Outubro de 2007 em Orientação a Objetos, Programação, Rails, Ruby por Zote

Recebi hoje por email esse link e achei interessante compartilhar.
É um link de download de um livro completo e gratuito de Ruby on Rails.
Quem tiver interesse, deve ser rápido pois esse download estará disponível somente por mais alguns dias (contador no site acima).

Abraço

Apresentando imagens aleatórias

Escrito em 04 de Outubro de 2007 em PHP, Programação por Feijo

Um amigo estava usando um código php que ele encontrou, para apresentar sempre uma imagem diferente no topo do blog.

Não estava funcionando muito bem, pedi o código e além de complexo e longo, tinha algum problema.

Criei rapidamente um código super enxuto, e mais simples de entender.

 

Segue o mesmo, espero que seja útil para muitos.

 

Crie um arquivo .php (pode ser index.php se ele ficar na pasta das imagens que serão randomizadas)

E copie este código:

<?
srand(time());
$aimg = glob(’*.jpg’);
header(’Location: /img/headers/’. $aimg[rand(0, count($aimg)-1)]);
?>

Pronto, só isso :)

Se a pasta com as imagens estiver em /img/fotos, use assim: <img src=/img/fotos>

Se o arquivo php tiver outro nome que não index.php, use assim: <img src=/img/fotos/rand.php>

 

Naturalmente pequenos ajustes podem ser necessários para adequar ao teu uso.

Qualquer dúvida, deixa um comentário.

Pegando a Data da Última Leitura de um arquivo

Escrito em 02 de Outubro de 2007 em Delphi, Linguagem Delphi por Feijo

Função pequena e útil, descobre quando aquele arquivo foi acessado pela última vez.

 

function FileAccessTime(FileName: String): TDateTime;
var
  FileHandle: Integer;
  LTime,
  FTimeA: TFileTime;
  STime: TSystemTime;
begin
  FileHandle := FileOpen(FileName,fmShareDenyNone);

  if FileHandle >= 0 then
  begin
    // Accessado
    GetFileTime(FileHandle,nil, @FTimeA, nil);
    FileClose(FileHandle);
    FileTimeToLocalFileTime(FTimeA,LTime);

    if FileTimeToSystemTime(LTime,STime) then
    begin
      Result := EncodeDate(STime.wYear,STime.wMonth,STime.wDay);
      Result := Result + EncodeTime(STime.wHour,STime.wMinute,STime.wSecond,STime.wMilliSeconds);
    end;
  end;
end;

 

procedure TForm1.Button1Click(Self);

begin

  ShowMessage(DateTimeToStr(FileAccessTime(’c:\windows\areia.bmp’));

end;

Duas Ferramentas Úteis para Windows: SuperCopier e FolderSize

Escrito em 14 de Setembro de 2007 em Utilitários por Leonel Togniolli

SuperCopier

Um dos recursos do Windows que praticamente nunca foram atualizados é a cópia de arquivos. Um diálogo modal que mostra um vago progresso da cópia, qualquer erro cancela o processo e, em geral, muito pouco controle sobre a cópia que está sendo feita. SuperCopier é uma aplicação que substitui o sistema de cópia de arquivos do Windows Explorer. Automaticamente, ao copiar um arquivo, você tem esse diálogo:

SuperCopierScreenshot

É possível acompanhar o progresso da cópia de cada arquivo, do total, pausar a cópia, e até alterar a lista de arquivos conforme a cópia é feita. Baixe o código fonte em Delphi na página do projeto, ou o instalador.

FolderSize

Cada dia discos rígidos vem com mais capacidade, e de alguma forma é cada vez mais fácil encher todas essas centenas de gigabytes. Existem várias formas de analizar o que está usando todo o espaço em um HD, mas nada como ter uma coluna mostrando o tamanho ocupado pelo conteúdo de uma pasta, direto no Windows Explorer. É isso que o FolderSize faz:

FolderSizeScreenshot

Um fato interessante que encontrei no FAQ é que o serviço que determina o tamanho do conteúdo de cada pasta monitora a utilização do disco rígido e pausa a sua operação caso a fila de requisições de disco esteja alta, para não atrapalhar a performance do seu computador. Vale a pena dar uma olhada.

Video Aula ASP.Net 2.0 com Delphi 2007 e BlackfishSQL

Escrito em 13 de Setembro de 2007 em Asp.Net, Video Aulas por Leonel Togniolli

Essa é uma introdução que mostra como criar uma simples aplicação em ASP.Net 2.0 com o Delphi 2007 mostrando dados de uma tabela em um servidor de banco de dados BlackfishSQL. Também mostra os novos layouts que estão incluidos on Delphi 2007, usando CSS e MasterPages.

Veja o vídeo aqui.

Novidades em Banco de Dados no Delphi 2007 para .Net e RAD Studio 2007

Escrito em 12 de Setembro de 2007 em Bancos de Dados, Delphi por Leonel Togniolli

Já falei sobre as novidades na linguagem, em ASP.Net e no ECO IV no Delphi 2007 para .Net e RAD Studio 2007. O próximo tópico é novidades na área de Banco de Dados.

BlackfishSQL para .Net

BlackfishSQL é um novo banco de dados, para .Net, escrito 100% em código gerenciado, sem qualquer dependência de DLLs do windows. Você já pode ter ouvido falar sobre o BlackfishSQL com os nomes NDataStore e SqlDataStore.

Ele possui um código unificado com o BlackfishSQL para Java, anteriormente JDataStore, sendo completamente compatível em protocolo e formato do arquivo de banco de dados. Na prática, ao usar BlackfishSQL, você pode ter um servidor em qualquer plataforma que possua uma VM .Net ou Java. Outro ponto importante é que, apesar de BlackfishSQL para .Net ser um novo produto, é na prática versão 8.0 (seguindo o JDataStore 7), tendo um núcleo maduro, testado e já bastante utilizado.

Um dos recursos mais interessantes do BlackfishSQL é poder escrever Stored Procedures e triggers em Delphi, dentro da IDE, como você sempre fez com suas aplicações. Isso permite também depurar as stored procedures usando o debugger do Delphi, como se você estivesse depurando sua própria aplicação. Basta colocar os breakpoints e iniciar a sessão de depuração. É sem dúvida uma forma muito mais eficiente e produtiva de colocar sua regra de negócio no banco de dados do que aprender um dialeto SQL específico dos outros bancos de dados.

Existem três drivers para o BlackfishSQL. Os dois primeiros são para aplicações .Net, sendo um Local, onde o servidor roda no próprio processo e acessa o banco de dados diretamente, e um Remoto, que utiliza TCP/IP para acessar um servidor Blackfish rodando na mesma máquina ou remotamente. É interessante notar que a distribuição de uma aplicação que utiliza um driver Ado.Net Local para Blackfish é copiar e rodar - não é necessária nenhuma configuração ou instalação.

Isso torna BlackfishSQL para .Net uma escolha bastante interessante para distribuir aplicações Asp.Net - o seu servidor de hospedagem não precisa ter um servidor de banco de dados instalado, e você não precisa pagar a mais por isso - basta copiar a versão Local do driver .Net, o arquivo de banco de dados (ou deixar sua aplicação criá-lo com os novos recursos de metadados que vou mostrar mais pra frente), e sua aplicação web está pronta pra funcionar.

O Blackfish SQL Developer Guide está disponível para download no CodeCentral - 205 páginas de documentação, exemplos e bastante informação pra quem está interessado em ver o que o BlackfishSQL oferece.

DbxClient

A terceira forma de acessar um servidor BlackfishSQL é um protocolo JSON/RPC implementado por um novo driver dbExpress, chamado DbxClient, através de aplicações VCL para Win32 ou .Net. Isso permite você acessar um servidor BlackfishSQL, com suas regras de negócio escritas em Delphi, através de clientes Win32, sem precisar depender do framework .Net em cada estação.

Isso torna BlackfishSQL o nono banco de dados que dbExpress suporta diretamente nessa nova versão, para .Net e Win32. DbxClient é mais um driver escrito em Delphi e que possui todo o código fonte incluído no produto.

Por enquanto BlackfishSQL é o unico servidor que é acessível pelo protocolo DbxClient, mas aguarde notícias sobre esse assunto - na minha opinião, é um tópico bastante promissor.

DbxReadOnlyMetadata e DbxMetadata

DBX4 continua a evoluir, e desta vez é criando uma base sólida de metadados bastante ricos, completamente escrita em Delphi e com o código fonte disponível.

DbxReadOnlyMetadata é uma extensão do dbExpress permitindo ler metadados, de uma forma fácil, prática e comum, de qualquer um dos nove banco de dados suportados. Está disponível em Win32 e .Net. Além de informações sobre a conexão (se ela diferencia maisculas e minusculas, se possui schemas, quais os caracteres de inicio e fim de aspas, etc), é possível obter as seguintes informações sobre o banco:

  1. Tipos de Dados
  2. Catalogs
  3. Schemas
  4. Tabelas
  5. Views
  6. Synonms
  7. Colunas (de tabelas, views, etc)
  8. Indices
  9. Campos que compõe cada indice
  10. Chaves Extrangeiras
  11. Campos que compõe cada chave extrangeira
  12. Stored Procedures
  13. Código Fonte de Stored Procedures
  14. Parametros de Stored Procedures
  15. Package Stored Procedures
  16. Código Fonte de Package Stored Procedures
  17. Parametros de Package Stored Procedures
  18. Usuários
  19. Roles
  20. Palavras Reservadas

Os itens em negrito são comandos novos de metadatados introduzidos nessa nova versão. Todos eles podem ser acessados utilizando uma query com o CommandType Metadata, e o resultado vem como um resultado comum de query, podem ser manipulado com os componentes que já estamos acostumados, ou exibido em grid e assim por diante.

DbxMetadata é a versão de escrita de metadados. Ela gera commandos SQL de Create, Alter e Drop de acordo com coleções que são passadas, gerando tabelas, indices, e vários outros objetos. Extremamente útil para quem quer escrever um aplicação independende de banco de dados, não precisando se preocupar com o sintaxe especifica de cada banco. Criar uma tabela é mais ou menos assim:

begin
  MetaDataTable := TDBXMetaDataTable.Create;
  MetaDataTable.TableName := 'TABELA';
  MetaDataTable.AddColumn(TDBXInt32Column.Create(’Coluna1′));
  MetaDataTable.AddColumn(TDBXDecimalColumn.Create(’Coluna2′, 10, 2));
  MetaDataTable.AddColumn(TDBXUnicodeCharColumn.Create(’Coluna3′, 32));
  MetaDataProvider.CreateTable(MetaDataTable);
end;

A parte de escrita de DbxMetadata só está disponível para a plataforma .Net nessa versão, mas nada impede que seja disponibilizado para Win32 no futuro.

Certified Test Suite e Geradores de Dados

Tendo como objetivo a melhor qualidade possível dos drivers dbExpress incluidos no produto e dos de terceiros, foi criada uma suite de testes automatizados de certificação para dbExpress. Completamente escrita em Delphi e com todo o código fonte disponível. Não é só quem escreve drivers que vai se interessar em aproveitar essa suite - as extensões para o DUnit e as rotinas para trabalhar com dados podem ser usadas por qualquer um que efetue testes automatizados em aplicações de bancos de dados - ou quem está pensando em começar.

Entre o conteúdo dessa suíte, vale um destaque para o TDbxDataGenerator e classes relacionadas. Ela auxilia o controle de qualidade gerando valores para popular tabelas para testes, depois que foram criadas com as rotinas de DbxMetadata mostradas acima, de acordo com o tipo de cada coluna.

AdoDbxClient

Para quem escreve aplicações Asp.Net ou ECO, está disponível o AdoDbxClient. Ele é um provider Ado.Net 2.0 nativo que permite a utilização de drivers dbExpress diretamente em Ado.Net 2.0. Não é mais necessário utilizar classes ou componentes diferentes como era com o BDP - basta programar utilizando DbConnection, DbCommand e afims, de uma forma independente de banco de dados, podendo determinar em tempo de execução se será utilizado os providers do framework .net (SqlClient, OracleClient), os do BlackfishSQL para ADO.Net (Local e Remote), ou qualquer um dos nove drivers suportados em dbExpress.

E Tudo o Mais

Além de tudo isso existem diversas pequenas melhorias e correções de problemas, parte de um processo constante de aperfeiçoamento.

Essa versão, apesar de ser um produto .Net completando o RAD Studio com o Delphi para Win32 e C++Builder já existentes, possui recursos que podem interessar a vários tipos de desenvolvedores, inclusive os que só trabalham com código nativo. Não deixe de conferir a nova versão!

CodeGear Rad Studio 2007, Delphi 2007 para .Net e Delphi 2007 para Win32 Update 3 já disponíveis para Download

Escrito em 11 de Setembro de 2007 em Delphi por Leonel Togniolli

O CodeGear Rad Studio 2007 acaba de ser lançado e já está disponível imediatamente para download para quem comprar pela internet. Isso não é novidade. O interessante é que desta vez a versão trial não demorou e foi disponibilizada simultaneamente - baixe já o CodeGear Rad Studio 2007 Trial.

Outro ponto interessante é que as correções feitas para o Rad Studio 2007 estão disponíveis como Update 3 para o Delphi 2007 para Win32. É possível fazer o download manualmente ou ser notificado ao abrir a IDE e baixar a atualização automaticamente, se tiver habilitado esta opção durante a instalação. Cerca de 100 correções incluidas nessa atualização são resultado de bug reports feitos diretamente no QualityCentral.

Próxima Página »