web-dev-qa-db-fra.com

Les fonctions qui prennent des fonctions comme paramètres devraient-elles également prendre des paramètres pour ces fonctions comme paramètres?

Je me retrouve souvent à écrire des fonctions qui ressemblent à ceci car elles me permettent de simuler facilement l'accès aux données, tout en fournissant une signature qui accepte des paramètres pour déterminer les données auxquelles accéder.

public static string GetFormattedRate(
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

Ou

public static string GetFormattedRate(
        Func<RateType, string> formatRate,
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = formatRate(rate);
    return formattedRate;
}

Ensuite, je l'utilise quelque chose comme ceci:

using FormatterModule;

public static Main()
{
    var getRate = GetRateFunc(connectionStr);
    var formattedRate = GetFormattedRate(getRate, rateType);
    // or alternatively
    var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);

    System.PrintLn(formattedRate);
}

Est-ce une pratique courante? Je sens que je devrais faire quelque chose de plus

public static string GetFormattedRate(
        Func<RateType> getRate())
{
    var rate = getRate();
    return rate.DollarsPerMonth.ToString("C0");
}

Mais cela ne semble pas très bien fonctionner car je devrais créer une nouvelle fonction pour passer dans la méthode pour chaque type de taux.

Parfois, je sens que je devrais faire

public static string GetFormattedRate(RateType rate)
{
   return rate.DollarsPerMonth.ToString("C0");
}

Mais cela semble supprimer toute possibilité de récupération et de réutilisation de format. Chaque fois que je veux récupérer et formater, je dois écrire deux lignes, une à récupérer et une à formater.

Qu'est-ce qui me manque dans la programmation fonctionnelle? Est-ce la bonne façon de le faire ou existe-t-il un meilleur modèle à la fois facile à entretenir et à utiliser?

20
rushinge

Si vous faites cela assez longtemps, vous finirez par vous retrouver à écrire cette fonction encore et encore:

public static Type3 CombineFunc1AndFunc2(
    Func<Type1, Type2> func1,
    Func<Type2, Type3>> func2,
    Type1 input)
{
    return func2(func1(input))
}

Félicitations, vous avez inventé composition de la fonction .

Les fonctions d'encapsuleur comme celle-ci n'ont pas beaucoup d'utilité lorsqu'elles sont spécialisées dans un type. Cependant, si vous introduisez des variables de type et omettez le paramètre d'entrée, votre définition GetFormattedRate ressemble à ceci:

public static Func<A, C> Compose(
    Func<B, C> outer, Func<A, B>> inner)
{
    return (input) => outer(inner(input))
}

var GetFormattedRate = Compose(FormatRate, GetRate);
var formattedRate = GetFormattedRate(rateKey);

Dans l'état actuel des choses, ce que vous faites a peu d'utilité. Ce n'est pas générique, vous devez donc dupliquer ce code partout. Il sur-complique votre code car maintenant votre code doit assembler tout ce dont il a besoin à partir de mille petites fonctions. Cependant, votre cœur est au bon endroit: il vous suffit de vous habituer à utiliser ces sortes de fonctions génériques d'ordre supérieur pour assembler les choses. Ou, utilisez un bon lambda à l'ancienne pour tourner Func<A, B> et A dans Func<B>.

Ne te répète pas.

38
Jack

Il n'y a absolument aucune raison de passer une fonction et ses paramètres, puis de l'appeler avec ces paramètres. En fait, dans votre cas, vous n'avez aucune raison de passer une fonction pas du tout. L'appelant pourrait tout aussi bien appeler la fonction elle-même et transmettre le résultat.

Pensez-y - au lieu d'utiliser:

var formattedRate = GetFormattedRate(getRate, rateType);

pourquoi ne pas simplement utiliser:

var formattedRate = GetFormattedRate(getRate(rateType));

?

En plus de réduire le code inutile, il réduit également le couplage - si vous souhaitez modifier la façon dont le taux est récupéré (par exemple, si getRate a maintenant besoin de deux arguments), vous n'avez pas à modifier GetFormattedRate.

De même, il n'y a aucune raison d'écrire GetFormattedRate(formatRate, getRate, rateKey) au lieu d'écrire formatRate(getRate(rateKey)).

Ne compliquez pas les choses.

106
user253751

Si vous devez absolument passer une fonction dans la fonction car elle passe un argument supplémentaire ou l'appelle dans une boucle, vous pouvez plutôt passer un lambda:

public static string GetFormattedRate(
        Func<string> getRate)
{
    var rate = getRate();
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

var formattedRate = GetFormattedRate(()=>getRate(rateKey));

Le lambda liera les arguments que la fonction ne connaît pas et cachera qu'ils existent même.

15
ratchet freak