Quando assunto e manipulacao de bancos de dados, ha poucas ferramentas tao praticas e convenientes quanto os DataSets do Delphi. Instacie um dataset qualquer, informe uma consulta sql, chame o metodo open e bingo! temos uma copia dos registros do banco (e uma infinidade de metadados) em memoria, prontos para serem manipulados.procedure Foo;var qry: TSqlQuery;begin qry := TSqlQuery.Create; try qry.CommandText := 'select some fields from some table'; qry.Open; while not qry.Eof do begin //processa o registro atual.. //e avanca para o proximo qry.Next; end; finally qry.Close; FreeAndNil(qry); end;end;Pratico e simples, nao e?Pois bem! Image dar manutencao em um sistema onde ha milhares de funcoes exatamente iguais a esta, espalhadas por centenas de units e voce comecara sentir calafrios.Ha algo muito errado aqui! - Dont' repeat yourself, don't repeat yourself, don't repeat yourself - eu repetia em minha mente cada vez que esbarrava em mais numa copia deste codigo.Normalmente, num caso destes, a saida e bem simples: criar uma classe descentente de TSqlQuery e implementar um metodo que encapsule a logica de criacao,abetura, navegacao, fechamento e liberacao da query. Mas, para funcionar, e preciso alterar todas as ocorrencias de TSqlQuery para a nova classe criada e isso implicaria mais trabalho chato, repetitivo e sujeito a erros.Class InterceptorsInterceptor Design PatternA sintaxe do object pascal (ou delphi language, como foi rebatizada nas versoes mais recentes), permite declarar classes com o mesmo nome se elas estiverem em units diferentes. Quando isto ocorrer, a segunda classe ira interceptar a primeira e ira extende-la, no escopo de sua unit, combinando as duas classes em uma so.Com isto em mente, resolver o problema do codigo repetido acima fica mais facil. Basta interceptar a classe TSqlQuery nas units em que eu estou trabalhando e extende-las com uma funcao mais inteligente para iterar sobre seus dados.Veja o codigo abaixo:unit SQLExprInterceptor;interfaceuses SqlExpr;type TSqlQueryRowCallback = reference to procedure; TSqlQuery = class( SqlExpr.TSqlQuery ) public procedure ForEachRow( const sql : string; callback: TSqlQueryRowCallback ); overload; end;implementationprocedure TSqlQuery.ForEachRow(const sql: string; callback: TSqlQueryRowCallback);begin self.Close; self.CommandText := sql; try self.Open; while not self.Eof do begin callback; self.Next; end; finally self.Close; end;end;end.A classe TSqlQuery foi declarada como um referencia para a classe original ( que esta dentro da unit SqlExpr )TSqlQuery = class( SqlExpr.TSqlQuery )e extendemos sua declaracao com um metodo que recebe um sql e uma referencia para nosso callback, cuja assintatura e de um metodo simples, sem parametros.TSqlQueryRowCallback = reference to procedure;Agora, com a ajuda dos metodos anonimos, podemos eliminar todo o codigo repetitivo do inicio do texto e substitui-lo pelo codigo abaxo:procedure Foo;begin qry.ForEachRow('select some fields from some table', procedure begin //processa o registro atual... end);end;Agora sim! Esta muito melhor. Note que o unico requisito para que os class interceptors funcionem, e que a referencia a classe original apareca antes da unit que contem as extensoes para a classe.Abracos. Inimigos, pontuacao e colisoes!Bem vindo a terceira parte do nosso mini-curso Criando um Game Completo, onde criamos uma versao do classico Space Invaders compativel com Windows e Linux, utilizando aceleracao de hardware para graficos 2D, suporte a joystics e uma tabela de scores online!No ultimo artigo,