Como mover da composição do objeto OOP para a composição da função FP em C #

5

Eu tenho trabalhado por algumas semanas em um novo projeto web e estou percebendo que tudo que eu faço é basicamente cálculos e transformações em dados, e que a maioria das minhas aulas não contém nenhum estado e pode perfeitamente ser transformada em classes estáticas com métodos estáticos. Portanto, estou pensando que esta poderia ser uma boa oportunidade para arquitetar o aplicativo seguindo o paradigma de programação funcional. Eu fiz primeiro algumas pesquisas na Internet para ler sobre as experiências de outras pessoas tentando algo semelhante e eu encontrei um bom entrada de blog com algumas ideias interessantes.

No entanto, estou querendo saber como lidar com a situação em que você tem vários níveis de chamadas de função aninhadas. Vou tentar dar um exemplo simples para mostrar o que quero dizer, primeiro em OOP e depois em FP.

No OOP, eu geralmente tento criar classes simples que constroem funcionalidades em camadas usando composição. Algo parecido com isto:

public class ClassLevel1
{
    private readonly IClassLevel2 _objLevel2;

    public ClassLevel1(IClassLevel2 objLevel2)
    {
        _objLevel2 = objLevel2;
    }

    public int FuncLevel1(int param1, int param2)
    {
        // Some operations here
        var res = _objLevel2.FuncLevel2(param1) + param2;
        // Some more operations here with the 'res'
        return result;
    }  
}

public class ClassLevel2
{
    private readonly IClassLevel3 _objLevel3;

    public ClassLevel2(IClassLevel3 objLevel3)
    {
        _objLevel3 = objLevel3;
    }

    public int FuncLevel2(int param1)
    {
        // Some operations here
        var res = _objLevel3.FuncLevel3(param1);
        // Some more operations here with the 'res'
        return result;
    }  
}

Então eu posso confiar na injeção de dependência para construir as instâncias e eu posso tirar proveito de qualquer estrutura de simulação para facilmente testar cada classe.

Agora vamos imaginar que eu gostaria de mudar para FP e digamos que temos uma função FuncLevel1 que chama internamente FuncLevel2 , que internamente usa uma terceira função FuncLevel3 . Um dos meus principais objetivos seria estruturar o código de tal forma que eu possa facilmente zombar de cada função em meus testes de unidade. Por exemplo, o código gostaria disso:

public static int FuncLevel1(
    Func<Func<int, int>, int, int> FuncLevel2,
    Func<int, int> FuncLevel3,
    int param1,
    int param2)
{
    // Some operations here
    var res = FuncLevel2(FuncLevel3, param1) + param2;
    // Some more operations here with 'res'
    return result;  
}

public static int FuncLevel2(
    Func<int, int> FuncLevel3,
    int param1)
{
    // Some operations here
    var res = FuncLevel3(param1);
    // Some more operations here with 'res'
    return result;  
}

Então a função FuncLevel1 é chamada assim:

var res = FuncLevel1(FuncLevel2, FuncLevel3, 5, 10);

A vantagem desta estrutura é que eu posso zombar das funções FuncLevel2 e FuncLevel3 se eu quiser testar a unidade FuncLevel1 e FuncLevel2 respectivamente.

Em um cenário simples, percebo que você poderia escrever o código como:

var res3 = FuncLevel3(5);
var res2 = FuncLevel2(res3) + 10;
var res1 = FuncLevel1(res2);

Mas é fácil imaginar que possa haver outras situações (mais complexas) em que não é possível resolver o problema desta forma.

Caso você tenha chamadas de função profundamente aninhadas, passar todas as funções necessárias nos níveis inferiores até o topo pode gerar algum código complicado. Por isso, estou muito interessado em ler sobre alternativas para lidar melhor com um cenário como este.

    
por Carlos Rodriguez 23.06.2016 / 21:38
fonte

1 resposta

0

Em uma linguagem funcional como o F #, você pode aproveitar o aplicativo parcial . Ou seja, FuncLevel1 tem apenas um parâmetro de função, FuncLevel2 e já contém FuncLevel3 dentro dele:

let funcLevel1 (funcLevel2 : int -> int) (param1 : int) (param2 : int) : int =
    let result = funcLevel2 param1 + param2;
    result

let funcLevel2 (funcLevel3 : int -> int) (param1 : int) : int =
    let result = funcLevel3 param1
    result

Uso:

let result = funcLevel1 (funcLevel2 funcLevel3) 5 10

Em C #, você poderia modelar as funções da mesma maneira:

public static int FuncLevel1(
    Func<int, int> FuncLevel2,
    int param1,
    int param2)
{
    var result = FuncLevel2(param1) + param2;
    return result;  
}

A questão é como executar a aplicação parcial. C # não suporta diretamente, mas você pode emulá-lo usando lambdas:

var res = FuncLevel1(i => FuncLevel2(FuncLevel3, i), 5, 10);

Outra opção seria escrever um método auxiliar que faça o aplicativo parcial:

public static Func<T2, T3> PApply<T1, T2, T3>(Func<T1, T2, T3> func, T1 arg1)
{
    return arg2 => func(arg1, arg2);
}

Mas quando você tenta usá-lo:

var res = FuncLevel1(PApply(FuncLevel2, FuncLevel3), 5, 10);

você recebe um erro de tempo de compilação sobre não ser capaz de inferir argumentos de tipo. Esse erro é solucionável, mas resulta em um código pior do que a versão lambda, então eu ficaria com isso.

    
por 24.06.2016 / 15:27
fonte