Często zdarza mi się pisać funkcje, które wyglądają tak, ponieważ pozwalają mi łatwo kpić z dostępu do danych i nadal dostarczają podpis, który akceptuje parametry określające, do których danych mam dostęp.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
Lub
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Potem używam czegoś takiego:
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);
}
Czy to powszechna praktyka? Czuję, że powinienem robić coś więcej
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Ale to nie wydaje się działać zbyt dobrze, ponieważ musiałbym stworzyć nową funkcję, aby przejść do metody dla każdego rodzaju stawki.
Czasami czuję, że powinienem to robić
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Ale wydaje się, że to zabiera ponowne pobieranie i formatowanie. Ilekroć chcę pobrać i sformatować, muszę napisać dwa wiersze, jeden do pobrania, a drugi do sformatowania.
Czego brakuje mi w programowaniu funkcjonalnym? Czy to właściwy sposób, aby to zrobić, czy jest lepszy wzór, który jest jednocześnie łatwy w utrzymaniu i użyciu?
źródło
GetFormattedRate()
jest zaakceptować szybkość do sformatowania jako parametru, w przeciwieństwie do akceptowania funkcji, która zwraca stawkę do sformatowania jako parametru?closures
gdzie przekazujesz sam parametr do funkcji, co w zamian daje ci funkcję odnoszącą się do tego konkretnego parametru. Ta „skonfigurowana” funkcja byłaby przekazywana jako parametr do funkcji, która jej używa.Odpowiedzi:
Jeśli zrobisz to wystarczająco długo, w końcu będziesz musiał ciągle pisać tę funkcję:
Gratulacje, wymyśliłeś kompozycję funkcji .
Takie funkcje otoki nie mają większego zastosowania, gdy są wyspecjalizowane w jednym typie. Jeśli jednak wprowadzisz niektóre zmienne typu i pominiesz parametr wejściowy, twoja definicja GetFormattedRate wygląda następująco:
W tej chwili to, co robisz, ma niewielki cel. Nie jest ogólny, więc musisz powielić ten kod w dowolnym miejscu. Nadmiernie komplikuje twój kod, ponieważ teraz twój kod musi zebrać wszystko, czego potrzebuje, z tysiąca małych funkcji. Twoje serce jest jednak we właściwym miejscu: wystarczy przyzwyczaić się do używania tego rodzaju ogólnych funkcji wyższego rzędu, aby złożyć wszystko w całość. Lub skorzystać z dobrej starej mody lambda, aby włączyć
Func<A, B>
iA
doFunc<B>
.Nie powtarzaj się.
źródło
FormatRate(GetRate(rateKey))
.GetFormattedRate
od tej pory będzie mógł korzystać bezpośrednio.Func<Func<A, B>, C>
); oznacza to, że potrzebujesz tylko jednej funkcji tworzenia, która działa dla dowolnej funkcji. Możesz jednak pracować z funkcjami C # wystarczająco dobrze, używając tylko zamknięć - zamiast przekazywaćFunc<rateKey, rateType>
, naprawdę potrzebujeszFunc<rateType>
, a kiedy przekazujesz func, budujesz go tak() => GetRate(rateKey)
. Chodzi o to, że nie ujawniasz argumentów, o które funkcja docelowa nie dba.Compose
funkcja jest naprawdę przydatna tylko wtedy, gdy zGetRate
jakiegoś powodu musisz opóźnić wykonanie , na przykład jeśli chcesz przejśćCompose(FormatRate, GetRate)
do funkcji, która zapewnia tempo według własnego wyboru, np. aby zastosować ją do każdego elementu w lista.Nie ma absolutnie żadnego powodu, aby przekazywać funkcję i jej parametry, a jedynie wywoływać ją z tymi parametrami. W rzeczywistości, w przypadku, gdy nie ma powodu, aby przekazać funkcję w ogóle . Osoba wywołująca może równie dobrze wywołać samą funkcję i przekazać wynik.
Pomyśl o tym - zamiast używać:
dlaczego nie użyć po prostu:
?
Oprócz zmniejszenia niepotrzebnego kodu zmniejsza również sprzężenie - jeśli chcesz zmienić sposób pobierania stawki (powiedz, jeśli
getRate
teraz potrzebujesz dwóch argumentów), nie musisz zmieniaćGetFormattedRate
.Podobnie nie ma powodu, aby pisać
GetFormattedRate(formatRate, getRate, rateKey)
zamiast pisaćformatRate(getRate(rateKey))
.Nie komplikuj rzeczy.
źródło
formatRate
być odwzorowane na stawkach, które należy sformatować).Jeśli absolutnie potrzebujesz przekazać funkcję do funkcji, ponieważ przekazuje ona dodatkowy argument lub wywołuje ją w pętli, możesz zamiast tego przekazać lambda:
Lambda powiąże argumenty, o których funkcja nie wie, i ukryje, że one nawet istnieją.
źródło
Czy nie tego chcesz?
A potem nazwij to tak:
Jeśli potrzebujesz metody, która może zachowywać się na wiele różnych sposobów w języku obiektowym, takim jak C #, zwykłym sposobem na to jest wywołanie metody abstrakcyjnej. Jeśli nie masz konkretnego powodu, aby zrobić to inaczej, powinieneś to zrobić w ten sposób.
Czy to wygląda na dobre rozwiązanie, czy może są jakieś wady?
źródło
GetFormattedRate
metodę i po prostu wywołaćIRateFormatter.FormatRate(rate)
). Podstawowa koncepcja jest jednak poprawna i myślę, że również OP powinien zaimplementować polimorficznie swój kod, jeśli potrzebuje wielu metod formatowania.