MultiMethods


É muito comum criarmos métodos em nossas classes que possuam “versões” diferentes. Por “versão”, entenda como overload ou sobrecarga do método. Isso quer dizer que podemos ter um método com o mesmo nome, mas que possuem quantidade ou tipos diferentes.

Em linguagens como o C# e o VB.NET, a resolução de qual dos overloads será invocado é feita em tempo de compilação, baseando no tipo que está sendo passado para o método, o compilador determinará qual das versões invocar. Suponhamos que temos a seguinte estrutura de classes:

public class Pessoa
{
    public string Nome;
}

public class PessoaJuridica : Pessoa
{
    public string CNPJ;
}

public class PessoaFisica : Pessoa
{
    public string CPF;
}

Agora, temos uma classe que tem a finalidade de gerenciar instâncias da classe Pessoa. Essa classe possui dois overloads, onde cada um deles espera uma instância concreta da classe Pessoa:

public class GestorDePessoas
{
    public void AdicionarPessoa(PessoaFisica pf)
    {
        Console.WriteLine("PF");
    }

    public void AdicionarPessoa(PessoaJuridica pj)
    {
        Console.WriteLine("PJ");
    }
}

Nossa aplicação trabalhará de forma independente de qual tipo de Pessoa esteja utilizando. Haverá um método chamado ConstruirPessoa que, dado o nome e o número do documento, retornará uma instância da classe PessoaFisica ou PessoaJuridica que, no nosso caso, deveremos passá-la para a classe GestorDePessoas. Se traduzirmos este parágrafo para código, então teríamos algo como:

Pessoa p = ConstruirPessoa(“Israel Aece”, “123456789”);
GestorDePessoas gp = new GestorDePessoas();
gp.AdicionarPessoa(p);

Se tentarmos compilar, receberemos um erro avisando que não foi possível encontrar um método com essa assinatura. As duas versões do método AdicionarPessoa esperam instâncias das classes concretas (PessoaFisica e PessoaJuridica). Mesmo que “p” armazene internamente a instância de uma classe concreta, ele não conseguirá determinar qual dos métodos invocar, pois somente saberemos o que “p” representa durante a execução da aplicação.

MultiMethods é um conceito que existe para escolher qual dos métodos invocar em tempo de execução, ao contrário do que acontece com o overloading, que fará essa escolha estaticamente, ainda em tempo de compilação. Há várias implementações de MultiMethods para .NET, e uma delas é o MultiMethods.NET. Com essa library, podemos criar apenas uma versão pública do método, que por sua vez, receberá a versão mais básica do tipo, que no nosso caso é a classe Pessoa. Internamente, a library determinará qual dos overloads invocar de acordo com o tipo que está sendo passado. O código abaixo ilustra a classe GestorDePessoas ligeiramente alterada, utilizando a library em questão:

public class GestorDePessoas
{
    private MultiMethod.Action<Pessoa> _adicionar;

    public GestorDePessoas()
    {
        this._adicionar = Dispatcher.Action<Pessoa>(this.AdicionarPessoa);
    }

    public void AdicionarPessoa(Pessoa p)
    {
        this._adicionar(p);
    }

    protected virtual void AdicionarPessoa(PessoaFisica pf)
    {
        Console.WriteLine("PF");
    }

    protected virtual void AdicionarPessoa(PessoaJuridica pj)
    {
        Console.WriteLine("PJ");
    }
}

O código de consumo desta classe que antes não funcionava, passa a funcionar, e em tempo de execução, o método AdicionarPessoa será invocado de acordo com o tipo retornado pelo método ConstruirPessoa. Publicamente temos apenas o método AdicionarPessoa que aceita o tipo base, e dentro dele, utiliza o delegate fornecido pela library para efetuar a escolha do método.

É importante dizer que estas libraries de MultiMethods recorrem a Reflection para efetuar todo o trabalho, e como sabemos, isso possui um overhead extra. Se de um lado ganhamos em flexibilidade, do outro perdemos em performance. Já com o .NET 4.0, o “tipo” dynamic evitará o uso dos MultiMethods. Esta palavra chave informará ao compilador que o método somente deverá ser conhecido em tempo de compilação, também se baseando nos tipos que forem passados à ele.

Ao contrário de grande parte dos tipos do .NET, a keyword dynamic não esta mapeada para algo como System.Dynamic; ao detectar uma variável definida com esta keyword, o compilador efetivamente criará uma variável do tipo System.Object mas, internamente, deverá recorrer à alguma API de DLR para invocar o membro, que talvez possa ser mais performático que Reflection.

Anúncios

Um comentário sobre “MultiMethods

  1. Realmente, muito interessante, torna nossa vida mais facil, e deixa as decisões para cargo do processo. penso que pode-se ganhar em desempenho evitando "ifs" desnecessários!

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