Bağımlılık inversiyonunun üst düzey fonksiyonlarla ilişkisi nedir?


41

Bugün, F # gelişiminde SOLID ilkesinin uygunluğunu tanımlayan bu makaleyi daha yeni gördüm.

F # ve Tasarım ilkeleri - SOLID

Ve sonuncuyu ele alırken - "Bağımlılık inversiyon ilkesi", yazar şöyle dedi:

İşlevsel bir bakış açısına göre, bu kaplar ve enjeksiyon kavramları, basit bir yüksek dereceli fonksiyonla veya dilin içine yerleştirilmiş ortadaki delikli tip desenle çözülebilir.

Ama daha fazla açıklamadı. Öyleyse benim sorum şu, yüksek mertebe fonksiyonlarla ilgili bağımlılık inversiyonu nasıl?

Yanıtlar:


38

OOP’daki Bağımlılık İnversiyonu, daha sonra bir nesnedeki bir uygulama tarafından sağlanan bir arayüze karşı kod yazdığınız anlamına gelir.

Yüksek dil işlevlerini destekleyen diller, davranışları OO anlamında bir arayüz uygulayan bir nesne yerine bir işlev olarak geçirerek basit bağımlılık inversiyon problemlerini çözebilirler.

Bu gibi dillerde, fonksiyonun imzası arayüz haline gelebilir ve istenen davranışı sağlamak için geleneksel bir nesne yerine bir fonksiyonun içine geçilir. Orta düzendeki delik bunun için iyi bir örnektir.

Arayan için istenen davranışı sağlamak için bir (OOP) arabirimine uyan bir sınıfı uygulamanız gerekmediğinden, aynı sonucu daha az kod ve daha etkileyici şekilde elde etmenizi sağlar. Bunun yerine, basit bir fonksiyon tanımını iletebilirsiniz. Kısacası: Kod, daha yüksek dereceli işlevler kullandığında bakımı daha kolay, daha etkileyici ve daha esnektir.

C # örneğinde

Geleneksel yaklaşım:

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);

Daha yüksek dereceli işlevlerle:

public IEnumerable<Customer> FilterCustomers(Func<Customer, bool> filter, IEnumerable<Customers> customers)
{
    foreach(var customer in customers)
    {
        if(filter(customer))
        {
            yield return customer;
        }
    }
}

Şimdi uygulama ve çağrı daha az hantal hale geldi. Artık bir IFilter uygulaması tedarik etmemize gerek yok. Artık filtreler için sınıflar uygulamamız gerekmiyor.

var filteredDataByName = FilterCustomers(x => x.Name.Equals("CustomerName"), customers);
var filteredDataByBirthday = FilterCustomers(x => x.Birthday == SomeDateTime, customers);

Tabii ki, bu zaten C # ile LinQ tarafından yapılabilir. Bu örneği, bir arayüz uygulayan nesneler yerine daha yüksek dereceli işlevleri kullanmanın daha kolay ve daha esnek olduğunu göstermek için kullandım.


3
Güzel örnek Bununla birlikte, Gulshan gibi, işlevsel programlama hakkında daha fazla şey öğrenmeye çalışıyorum ve bu "işlevsel DI" türünün "nesne yönelimli DI" ile karşılaştırıldığında biraz titizlik ve önem vermeyeceğini merak ediyorum. Daha yüksek sipariş imzası yalnızca iletilen işlevin bir Müşteriyi parametre olarak alması ve bir bool döndürmesi gerektiğini belirtirken, OO sürümü nesnenin iletilen bir filtre olduğu gerçeğini uygular (IFilter <Müşteri> uygular). Ayrıca, Etki Alanı'nın temel bir kavramıysa, iyi bir şey olabilecek filtre fikrini açıkça ortaya koyar (bkz. DDD). Ne düşünüyorsun ?
guillaume31

2
@ Ian31: Bu gerçekten ilginç bir konudur! FilterCustomer'a iletilen herhangi bir şey dolaylı olarak bir tür filtre gibi davranır. Filtre kavramı, etki alanının önemli bir parçası olduğunda ve sistemde birden çok kez kullanılan karmaşık filtre kurallarına ihtiyacınız olduğunda, bunları kapsüllemek daha iyidir. Çok düşük derecelerde olmasa da, teknik sadeliği ve pragmatizmi hedeflerim.
Şahin,

5
@ Ian31: Tamamen katılmıyorum. Uygulama IFilter<Customer>hiç zorlama değildir. Daha üst düzey işlev büyük ölçüde daha esnektir, bu büyük bir faydadır ve satır içi yazabilmek büyük bir yarardır. Lambdaların yerel değişkenleri yakalaması da çok kolaydır.
DeadMG

3
@ ian31: İşlev derleme zamanında da doğrulanabilir. Ayrıca, bir işlev yazabilir, adlandırabilir ve sonra bariz sözleşmeyi yerine getirdiği sürece argüman olarak iletebilirsiniz (müşteriyi alır, bool döndürür). Bir lambda ifadesini geçmeniz gerekmez. Yani, bu ifade eksikliğini belirli bir dereceye kadar karşılayabilirsin. Ancak, sözleşme ve amacı açıkça ifade edilmemiştir. Bu bazen büyük bir dezavantaj. Sonuçta bu bir ifade, dil ve enkapsülasyon meselesidir. Bence her davayı kendi başına yargılamak zorundasın.
Falcon,

2
Enjekte edilen bir fonksiyonun semantik anlamını netleştirmek konusunda güçlü hissediyorsanız, delegeleri kullanarak C # name fonksiyon imzalarını kullanabilirsiniz public delegate bool CustomerFilter(Customer customer). haskell gibi saf işlevsel dillerde, takma adlar önemsizdir:type customerFilter = Customer -> Bool
sara

8

Bir fonksiyonun davranışını değiştirmek istiyorsanız

doThis(Foo)

başka bir işlevi geçebilirsin

doThisWith(Foo, anotherFunction)

farklı olmak istediğiniz davranışı uygular.

"doThisWith", üst düzey bir işlevdir, çünkü başka bir işlevi bağımsız değişken olarak alır.

Örneğin sahip olabilirdin

storeValues(Foo, writeToDatabase)
storeValues(Foo, imitateDatabase)

5

Kısa cevap:

Klasik Bağımlılık Enjeksiyonu / Kontrolün Ters Çevirilmesi, bağımlı işlevsellik için yer tutucu olarak bir sınıf arayüzü kullanır. Bu arayüz bir sınıf tarafından uygulanır.

Interface / ClassImplementation yerine, bir delege işleviyle birçok bağımlılık daha kolay uygulanabilir.

Arayüz için delegelerin ioc-factory-pros-pros-ve-contras için ikisine de bir örnek bulabilirsiniz .


0

Bunu karşılaştırın:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = new LinkedList<String>();
for (String name : names) {
    if (name.startsWith("S")) {
        namesBeginningWithS.add(name);
    }
}

ile:

String[] names = {"Fred", "Susan"};
List<String> namesBeginningWithS = names.stream().filter(n <- n.startsWith("S")).collect();

İkinci versiyon, Java 8'in filter, minimum değeri (örneğin enjekte edilmeye olan bağımlılık - lambda ifadesi) geçirmenize izin veren daha yüksek dereceli işlevler sağlayarak kazan kodu kodunu azaltma yöntemidir (döngü vb .).


0

LennyProgrammers örneğinin piggy desteği ...

Diğer örneklerin kaçırdığı şeylerden biri, yeni bir işlev oluşturmak için bir fonksiyona bağımlılıkları bağlamak (veya "enjekte etmek") ile birlikte kısmi işlev uygulaması (PFA) ile birlikte yüksek dereceli işlevleri kullanabilmenizdir.

Eğer yerine:

doThisWith(Foo, anotherFunction)

biz (PFA'nın genellikle nasıl yapıldığının konvansiyonel olması için) alt seviyedeki işçi işlevini şu şekilde değiştiriyoruz:

doThisWith( anotherFunction, Foo )

Daha sonra bunun gibi kısmen doThis uygulayabiliriz :

doThis = doThisWith( anotherFunction )  // note that "Foo" is still missing, argument list is partial

Bu da daha sonra yeni işlevi şu şekilde kullanmamıza izin verir:

doThis(Foo)

Ya da:

doThat = doThisWith( yetAnotherDependencyFunction )
...
doThat( Bar )

Ayrıca bakınız: https://ramdajs.com/docs/#partial

... ve, evet, toplayıcılar / çarpanlar yaratıcı olmayan örneklerdir. Daha iyi bir örnek, "tüketici" işlevinin bağımlılık olarak geçirdiği şeye bağlı olarak mesajları alan ve onları günlüğe kaydeden veya e-postayla gönderen bir işlev olabilir.

Bu düşüncenin genişletilmesi, daha uzun ve uzun tartışma listeleri, giderek daha kısa ve daha kısa tartışma listeleri ile gittikçe uzmanlaşmış fonksiyonlara giderek daraltılabilir ve elbette, bu fonksiyonlardan herhangi biri kısmen uygulanacak bağımlılıklar olarak diğer fonksiyonlara geçirilebilir.

OOP, birbiriyle yakından ilişkili birden fazla işlemi olan bir şeyler paketine ihtiyaç duyduğunuzda iyidir, ancak her birinin tek bir kamu “yap” yöntemiyle la la “İsimlerin Krallığı” olduğu bir grup sınıf yapmak için çalışmaya dönüşür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.