Codemountain, Paulo Suzart's Blog

Empréstimos em Scala

with one comment

Continuando meus estudos em Scala, me deparei com um padrão sugerido pelos autores do livro Programming Scala, o loan pattern, ou seja, empréstimos. O objetivo deste padrão é permitir que o desenvolvedor crie estruturas semelhantes a built-in controls da linguagem, leia-se if, while, try, etc. Estas estruturas nativas em Scala tem um comportamento e até aplicações diferentes destas mesmas estruturas de controle em Java. Mas isso é um assunto para um próximo post.
Então onde está a vantagem do uso de loan pattern e a construção de estruturas parecidas com um while?
Tomando como exemplo códigos Java, muito frequentimente usamos um padrão para escrever DAOs:

public String getAdditionalInfo(String uoo) throws Exception {
        Connection conn = null;
        CallableStatement cs = null;
        ResultSet rs = null;
        String info = null;
        try {
            //prepara recursos envolvidos
            ...
            cs.registerOutParameter(1, Types.VARCHAR);
            ...
            info = cs.getString(1);
        } finally {
            if (conn != null)
                conn.close();
            if (rs != null)
                rs.close();
            if (cs != null)
                cs.close();
        }
       return info;
}

Logo, é comum existirem DAOs com todos os métodos com o mesmo finally,  os mesmos testes para verificar se o recurso não é nullo, e a depender do caso outros testes mais complexos.
Além de repetitivo, esse código não tem um visual natural, e pode ser esquecido facilmente de ser anexado ao método do DAO em questão, e até modificações devem ser custosamente reescritas manualmente em cada método. O resultado é um risco de problemas decorrente do não fechamento de conexões, Statements e ResultSets, que incluem impactos na performance da aplicação. Supondo este mesmo DAO em Scala, podemos escrevê-lo assim:

def getAdditionalInfo(uoo: String) = {
    var conn : Connection = null
    var cs : CallableStatement = null
    var rs  : ResultSet = null

    useAndClose(conn, cs, rs) {
        //prepara recursos envolvidos
        ...
        cs.registerOutParameter(1, Types.VARCHAR)
        ...
        cs getString 1;
    }
}

A função useAndClose com cara de while foi criada para permitir a execuçao de algum código e no fim, garantir o fechamento dos recursos passados como argumento para a função.  Esta função irá englobar de maneira centralizada o comportamento do fechamento dos recursos envolvidos. Outro ponto positivo é a eliminação de reatribuição, ou seja, enquanto o código Java demandava uma variável local para servir de retorno para o método, o DAO em Scala usa o próprio resultado da invocação de useAndClose como resultado da função getAdditionalInfo.
Nossa função useAndClose poderia ser escrita assim:


def useAndClose[T](conn: Connection, rs : ResultSet, ps: PreparedStatement)(op: => T) = {
    try { op }
    finally {
        if (conn != null)
                conn.close()
            if (rs != null)
                rs.close()
            if (cs != null)
                cs.close()
        }

Aqui fazemos o uso do padrão loan, porque a função useAndClose empresa seu comportamento padrão a um trecho de código escrito em outra parte. A função continua com cara de Java, além disso, por algum motivo, podemos desejar fechar apenas o ResultSet e preservar a conexão.

Criar outra função sobrecarregada recebendo desta vez apenas o ResultSet e o Statement é possível, mas também soa repetitiva. Se os três objetos ao menos obedecessem uma mesma interface, poderiamos usar sua interface comum e algo como vargars, então, após executar o código em questão, iterariamos este array e fechariamos cada recursos. Mas infelizmente estas três classes não compartilham uma mesma hierarquia.

O que fazer?

Bom, sabemos que as três classes possuem ao menos um método em comum: close(), que é o que nos interessa.  Scala fornece um mecanismo muito interessante que permite que tratemos estes 3 objetos de classes diferentes como algo compatível entre sí por possuírem este mesmo método close().
Escrevemos então nossa função useAndClose assim:

def useAndClose[T](closeable : {def close() : Unit}*)(op: => T) : T = {
try { op }
finally {
   for (c <- closeable if c!= null)
     c close
 }
}

&#91;/sourcecode&#93;

Wow! Estranho? Depende dos olhos de quem vê! O intrigante é que não definimos exatamente quais os tipos dos parâmetros de entrada para a função useAndClose, apenas informamos que quem for enviado, deve conter um método close que não recebe parâmetros e nem retorna valor. Outra coisa importante aqui é o uso de * para indicar que a função pode receber mais de um objeto que contém ao menos um método close().

Você pode estar se perguntando, e como fica o tratamento das exceções destes métodos? Elas podem ser ligeiramente diferentes, não? Sim, podem. Em outra oportunidade podemos falar nisso, por hora, é só confiar.

O código então é executado, invocando-se <strong>op</strong>, que representa o trecho de código passando entre chaves para useAndClose, e depois, no bloco finally, cada "closeable" é fechado, se este for diferente de null.

O código do cliente (o DAO) não precisa de modificações para tirar vantagem do empréstimo de useAndClose, agora modificado. E se formos além, este comportamento de usar e fechar pode ser empregados em muitas situações diferentes, a exemplo do uso de Streams sem nenhuma alteração na nossa função.

Em outro post também pretendo falar sobre Generics em Scala, o uso aqui permite que useAndClose retorne o mesmo valor que o código passado como parâmetro retornaria, permitindo inclusive atribuições desse jeito:



    val myInfo = useAndClose(conn, cs, rs) {
        //prepara recursos envolvidos
        ...
        cs.registerOutParameter(1, Types.VARCHAR)
        ...
        cs getString 1;
    }

Que atribui a myInfo uma String retornada após a invocação do Statement. Podemos ainda aninhar este tipo de estrutura criando uma forma mais natural e  concisa de programar, adicionando comportamento distintos onde nossa imaginação é o limite.

Espero ter despertado a curiosidade dos leitores do blog.
Tenham uma boa diversão com Scala.

Nota Sobre os autores do livro Programing Scala:
Martin Odersky é o criador da linguagem e professor na EPFL. Lex Spoon trabalou com Scala por 2 anos em seu pós-doutorado e Bill Vennes é somente o presidente da Artma, a editora do livro

Written by paulosuzart

fevereiro 22, 2009 às 9:40 pm

Publicado em Java, scala

Tagged with ,

Uma resposta

Subscribe to comments with RSS.

  1. Esse realmente ficou muito bom! Gostei muito desse recurso da linguagem para construção de código “saudavel”😀

    Abraão

    fevereiro 26, 2009 at 11:59 am


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: