Saya sering menemukan diri saya menulis fungsi yang terlihat seperti ini karena memungkinkan saya untuk dengan mudah mengejek akses data, dan masih memberikan tanda tangan yang menerima parameter untuk menentukan data apa yang akan diakses.
public static string GetFormattedRate(
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = rate.DollarsPerMonth.ToString("C0");
return formattedRate;
}
Atau
public static string GetFormattedRate(
Func<RateType, string> formatRate,
Func<string, RateType>> getRate,
string rateKey)
{
var rate = getRate(rateKey);
var formattedRate = formatRate(rate);
return formattedRate;
}
Maka saya menggunakannya sesuatu seperti ini:
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);
}
Apakah ini praktik umum? Saya merasa harus melakukan sesuatu yang lebih seperti
public static string GetFormattedRate(
Func<RateType> getRate())
{
var rate = getRate();
return rate.DollarsPerMonth.ToString("C0");
}
Tapi itu sepertinya tidak berfungsi dengan baik karena saya harus membuat fungsi baru untuk masuk ke metode untuk setiap jenis tingkat.
Terkadang saya merasa harus melakukan sesuatu
public static string GetFormattedRate(RateType rate)
{
return rate.DollarsPerMonth.ToString("C0");
}
Tapi itu tampaknya mengambil pengambilan dan memformat penggunaan kembali. Setiap kali saya ingin mengambil dan memformat saya harus menulis dua baris, satu untuk mengambil dan satu untuk memformat.
Apa yang saya lewatkan tentang pemrograman fungsional? Apakah ini cara yang tepat untuk melakukannya, atau apakah ada pola yang lebih baik yang mudah dipelihara dan digunakan?
sumber
GetFormattedRate()
menerima laju untuk memformat sebagai parameter, sebagai lawan dari itu menerima fungsi yang mengembalikan laju untuk memformat sebagai parameter?closures
tempat Anda melewatkan parameter itu sendiri ke suatu fungsi, yang sebaliknya memberi Anda fungsi yang merujuk ke parameter khusus itu. Fungsi "yang dikonfigurasi" ini akan diteruskan sebagai parameter ke fungsi, yang menggunakannya.Jawaban:
Jika Anda melakukan ini cukup lama, pada akhirnya Anda akan menemukan diri Anda menulis fungsi ini berulang-ulang:
Selamat, Anda telah menemukan komposisi fungsi .
Fungsi wrapper seperti ini tidak banyak digunakan ketika mereka dikhususkan untuk satu jenis. Namun, jika Anda memperkenalkan beberapa variabel tipe dan menghilangkan parameter input, maka definisi GetFormattedRate Anda terlihat seperti ini:
Seperti apa adanya, apa yang Anda lakukan hanya memiliki sedikit tujuan. Itu tidak umum, jadi Anda perlu menduplikasi kode itu di semua tempat. Itu terlalu rumit kode Anda karena sekarang kode Anda harus mengumpulkan semua yang dibutuhkan dari seribu fungsi kecil sendiri. Hati Anda berada di tempat yang tepat: Anda hanya perlu membiasakan diri menggunakan fungsi - fungsi tingkat tinggi yang umum ini untuk menyatukan semuanya. Atau, gunakan lambda mode lama yang bagus untuk berubah
Func<A, B>
danA
menjadiFunc<B>
.Jangan ulangi dirimu sendiri.
sumber
FormatRate(GetRate(rateKey))
.GetFormattedRate
langsung mulai sekarang.Func<Func<A, B>, C>
); ini berarti Anda hanya perlu satu fungsi Tulis yang berfungsi untuk fungsi apa pun. Namun, Anda dapat bekerja dengan fungsi C # cukup baik hanya menggunakan penutupan - alih-alih lewatFunc<rateKey, rateType>
, Anda hanya benar-benar membutuhkanFunc<rateType>
, dan ketika melewati fungsi, Anda membangunnya seperti() => GetRate(rateKey)
. Intinya adalah Anda tidak mengekspos argumen yang tidak diperhatikan oleh fungsi target.Compose
fungsi ini benar-benar hanya berguna jika Anda perlu menunda eksekusiGetRate
karena beberapa alasan, seperti jika Anda ingin meneruskanCompose(FormatRate, GetRate)
ke fungsi yang memberikan tingkat pilihan sendiri, misalnya untuk menerapkannya ke setiap elemen dalam suatu daftar.Sama sekali tidak ada alasan untuk melewatkan fungsi, dan parameternya, hanya untuk kemudian memanggilnya dengan parameter tersebut. Bahkan, dalam kasus Anda, Anda tidak punya alasan untuk melewatkan fungsi sama sekali . Penelepon mungkin juga hanya memanggil fungsi itu sendiri dan meneruskan hasilnya.
Pikirkan - alih-alih menggunakan:
mengapa tidak menggunakan saja:
?
Selain mengurangi kode yang tidak perlu, juga mengurangi kopling - jika Anda ingin mengubah cara pengambilannya (katakanlah, jika
getRate
sekarang membutuhkan dua argumen) Anda tidak perlu mengubahGetFormattedRate
.Demikian juga, tidak ada alasan untuk menulis
GetFormattedRate(formatRate, getRate, rateKey)
daripada menulisformatRate(getRate(rateKey))
.Jangan terlalu rumit.
sumber
formatRate
yang harus dipetakan atas tarif yang harus diformat).Jika Anda benar-benar harus melewatkan fungsi ke dalam fungsi karena ia melewati beberapa argumen tambahan atau memanggilnya dalam satu lingkaran maka Anda bisa meneruskan lambda:
Lambda akan mengikat argumen yang tidak diketahui fungsi dan menyembunyikan bahwa mereka ada.
sumber
Bukankah ini yang kamu inginkan?
Dan menyebutnya seperti ini:
Jika Anda menginginkan metode yang dapat berperilaku dalam berbagai cara berbeda dalam bahasa berorientasi objek seperti C #, cara yang biasa dilakukan adalah meminta metode memanggil metode abstrak. Jika Anda tidak memiliki alasan khusus untuk melakukannya dengan cara yang berbeda, Anda harus melakukannya dengan cara itu.
Apakah ini terlihat seperti solusi yang baik, atau apakah ada kelemahan yang Anda pikirkan?
sumber
GetFormattedRate
metode dan panggil sajaIRateFormatter.FormatRate(rate)
). Namun konsep dasarnya sudah benar, dan saya pikir OP juga harus mengimplementasikan kode polimorfisnya jika ia memerlukan beberapa metode format.