Dependency Inversion dalam OOP berarti Anda mengkodekan terhadap antarmuka yang kemudian disediakan oleh implementasi dalam suatu objek.
Bahasa yang mendukung fungsi bahasa yang lebih tinggi sering dapat memecahkan masalah inversi ketergantungan sederhana dengan meneruskan perilaku sebagai fungsi alih-alih objek yang mengimplementasikan antarmuka dalam OO-sense.
Dalam bahasa seperti itu, tanda tangan fungsi dapat menjadi antarmuka dan fungsi dilewatkan sebagai ganti objek tradisional untuk memberikan perilaku yang diinginkan. Lubang di pola tengah adalah contoh yang baik untuk ini.
Ini memungkinkan Anda mencapai hasil yang sama dengan kode lebih sedikit dan lebih ekspresif, karena Anda tidak perlu mengimplementasikan seluruh kelas yang sesuai dengan antarmuka (OOP) untuk memberikan perilaku yang diinginkan untuk penelepon. Sebagai gantinya, Anda hanya bisa melewatkan definisi fungsi sederhana. Singkatnya: Kode seringkali lebih mudah dipelihara, lebih ekspresif, dan lebih fleksibel ketika seseorang menggunakan fungsi urutan yang lebih tinggi.
Contoh dalam C #
Pendekatan tradisional:
public IEnumerable<Customer> FilterCustomers(IFilter<Customer> filter, IEnumerable<Customers> customers)
{
foreach(var customer in customers)
{
if(filter.Matches(customer))
{
yield return customer;
}
}
}
//now you've got to implement all these filters
class CustomerNameFilter : IFilter<Customer> /*...*/
class CustomerBirthdayFilter : IFilter<Customer> /*...*/
//the invocation looks like this
var filteredDataByName = FilterCustomers(new CustomerNameFilter("SomeName"), customers);
var filteredDataBybirthDay = FilterCustomers(new CustomerBirthdayFilter(SomeDate), customers);
Dengan fungsi urutan yang lebih tinggi:
public IEnumerable<Customer> FilterCustomers(Func<Customer, bool> filter, IEnumerable<Customers> customers)
{
foreach(var customer in customers)
{
if(filter(customer))
{
yield return customer;
}
}
}
Sekarang implementasi dan doa menjadi kurang rumit. Kami tidak perlu menyediakan implementasi IFilter lagi. Kami tidak perlu menerapkan kelas untuk filter lagi.
var filteredDataByName = FilterCustomers(x => x.Name.Equals("CustomerName"), customers);
var filteredDataByBirthday = FilterCustomers(x => x.Birthday == SomeDateTime, customers);
Tentu saja, ini sudah bisa dilakukan oleh LinQ di C #. Saya hanya menggunakan contoh ini untuk menggambarkan bahwa lebih mudah dan lebih fleksibel untuk menggunakan fungsi urutan yang lebih tinggi daripada objek yang mengimplementasikan antarmuka.
IFilter<Customer>
tidak ada penegakan sama sekali. Fungsi tingkat tinggi jauh lebih fleksibel, yang merupakan manfaat besar, dan mampu menuliskannya sebaris adalah manfaat besar lainnya. Lambdas juga lebih mudah menangkap variabel lokal.public delegate bool CustomerFilter(Customer customer)
. dalam bahasa fungsional murni seperti haskell, jenis aliasing sepele:type customerFilter = Customer -> Bool
Jika Anda ingin mengubah perilaku suatu fungsi
Anda bisa melewati fungsi lain
yang mengimplementasikan perilaku yang Anda ingin berbeda.
"doThisWith" adalah fungsi tingkat tinggi karena mengambil fungsi lain sebagai argumen.
Misalnya Anda bisa
sumber
Jawaban singkat:
Injeksi Ketergantungan Klasik / Inversi Kontrol menggunakan antarmuka kelas sebagai pengganti untuk fungsionalitas dependen. Antarmuka ini diimplementasikan oleh kelas.
Alih-alih Interface / ClassImplementation banyak dependensi dapat lebih mudah diimplementasikan dengan fungsi delegasi.
Anda menemukan contoh untuk keduanya di c # di ioc-factory-pro-dan-contras-untuk-antarmuka-versus-delegasi .
sumber
Bandingkan ini:
dengan:
Versi kedua adalah cara Java 8 untuk mengurangi kode boilerplate (perulangan dll) dengan menyediakan fungsi tingkat tinggi seperti
filter
yang memungkinkan Anda untuk melewati batas minimum (yaitu ketergantungan yang akan disuntikkan - ekspresi lambda).sumber
Piggy-backing off contoh LennyProgrammers ...
Salah satu hal yang dilewatkan oleh contoh lain adalah Anda dapat menggunakan fungsi urutan lebih tinggi bersama dengan aplikasi fungsi parsial (PFA) untuk mengikat (atau "menyuntikkan") dependensi ke dalam suatu fungsi (melalui daftar argumennya) untuk membuat fungsi baru.
Jika bukannya:
kami (untuk menjadi konvensional dalam cara PFA biasanya dilakukan) memiliki fungsi pekerja tingkat rendah sebagai (swapping arg order):
Kami kemudian dapat menerapkan doThisWith sebagian seperti:
Yang memungkinkan kita nanti untuk menggunakan fungsi baru seperti:
Atau bahkan:
Lihat juga: https://ramdajs.com/docs/#partial
... dan, ya, adders / multipliers adalah contoh yang tidak imajinatif. Contoh yang lebih baik adalah fungsi yang mengambil pesan, dan mencatatnya atau mengirim surel tergantung pada apa yang dilewati oleh fungsi "konsumen" sebagai ketergantungan.
Memperluas gagasan ini, daftar argumen yang lebih panjang dapat semakin dipersempit ke fungsi-fungsi yang semakin khusus dengan daftar argumen yang lebih pendek dan lebih pendek, dan tentu saja salah satu dari fungsi-fungsi tersebut dapat diteruskan ke fungsi-fungsi lain sebagai dependensi untuk diterapkan sebagian.
OOP bagus jika Anda membutuhkan sekumpulan hal dengan beberapa operasi yang terkait erat, tetapi itu berubah menjadi pekerjaan untuk membuat sekelompok kelas masing-masing dengan metode "melakukannya" publik tunggal, ala "The Kingdom of Nouns".
sumber