A menudo me encuentro escribiendo funciones que se ven así porque me permiten burlarme fácilmente del acceso a los datos y aún así proporcionar una firma que acepta parámetros para determinar a qué datos acceder.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
O
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Entonces lo uso algo como esto:
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);
}
¿Es esta una práctica común? Siento que debería estar haciendo algo más como
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Pero eso no parece funcionar muy bien porque tendría que hacer una nueva función para pasar al método para cada tipo de tasa.
A veces siento que debería estar haciendo
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Pero eso parece eliminar cualquier reutilización de búsqueda y formato. Cada vez que quiero buscar y formatear tengo que escribir dos líneas, una para buscar y otra para formatear.
¿Qué me falta de programación funcional? ¿Es esta la forma correcta de hacerlo, o hay un patrón mejor que sea fácil de mantener y usar?
fuente
GetFormattedRate()
aceptar la velocidad para formatear como parámetro, en lugar de que acepte una función que devuelve la velocidad para formatear como parámetro?closures
donde pasa el parámetro a una función, que a cambio le da una función que se refiere a ese parámetro específico. Esta función "configurada" se pasaría como un parámetro a la función, que la utiliza.Respuestas:
Si hace esto lo suficiente, eventualmente se encontrará escribiendo esta función una y otra vez:
Felicidades, has inventado la composición de funciones .
Las funciones de envoltura como esta no tienen mucho uso cuando están especializadas en un tipo. Sin embargo, si introduce algunas variables de tipo y omite el parámetro de entrada, su definición de GetFormattedRate se verá así:
Tal como está, lo que estás haciendo tiene poco propósito. No es genérico, por lo que debe duplicar ese código por todas partes. Sobrecomplica su código porque ahora su código tiene que ensamblar todo lo que necesita de mil funciones pequeñas por sí solo. Sin embargo, su corazón está en el lugar correcto: solo necesita acostumbrarse a usar este tipo de funciones genéricas de orden superior para armar las cosas. O bien, utilizar una buena lambda vieja manera de convertir
Func<A, B>
yA
enFunc<B>
.No te repitas a ti mismo.
fuente
FormatRate(GetRate(rateKey))
.GetFormattedRate
directamente de ahora en adelante.Func<Func<A, B>, C>
); Esto significa que solo necesita una función de composición que funcione para cualquier función. Sin embargo, puede trabajar con las funciones de C # lo suficientemente bien con solo usar cierres; en lugar de pasarFunc<rateKey, rateType>
, realmente solo necesitaFunc<rateType>
, y al pasar el func, lo construye como() => GetRate(rateKey)
. El punto es que no expones los argumentos que a la función objetivo no le importan.Compose
función en realidad solo es útil si necesita retrasar la ejecución deGetRate
por alguna razón, como si desea pasarCompose(FormatRate, GetRate)
a una función que proporciona una tasa de su propia elección, por ejemplo, para aplicarla a cada elemento en un lista.No hay absolutamente ninguna razón para pasar una función y sus parámetros, solo para luego llamarla con esos parámetros. De hecho, en su caso no tiene ninguna razón para pasar una función en absoluto . La persona que llama también podría llamar a la función en sí y pasar el resultado.
Piénselo, en lugar de usar:
por qué no simplemente usar:
?
Además de reducir el código innecesario, también reduce el acoplamiento: si desea cambiar la forma en que se obtiene la velocidad (por ejemplo, si
getRate
ahora necesita dos argumentos), no tiene que cambiarGetFormattedRate
.Del mismo modo, no hay razón para escribir en
GetFormattedRate(formatRate, getRate, rateKey)
lugar de escribirformatRate(getRate(rateKey))
.No compliques demasiado las cosas.
fuente
formatRate
eso lo que debería asignarse a las tasas que deberían formatearse).Si absolutamente necesita pasar una función a la función porque pasa algún argumento adicional o la llama en un bucle, entonces puede pasar una lambda:
La lambda vinculará los argumentos que la función no conoce y ocultará que incluso existen.
fuente
¿No es esto lo que quieres?
Y luego llámalo así:
Si desea un método que pueda comportarse de múltiples maneras diferentes en un lenguaje orientado a objetos como C #, la forma habitual de hacerlo es hacer que el método llame a un método abstracto. Si no tiene una razón específica para hacerlo de una manera diferente, debe hacerlo de esa manera.
¿Esto parece una buena solución, o hay inconvenientes en los que estás pensando?
fuente
GetFormattedRate
método y simplemente llamarIRateFormatter.FormatRate(rate)
). Sin embargo, el concepto básico es correcto, y creo que también OP debería implementar su código polimórficamente si necesita múltiples métodos de formateo.