Fonksiyonları parametre olarak diğer fonksiyonlara geçirmek, kötü uygulama?


40

AS3 uygulamamızın arka ucumuzla konuşma şeklini değiştirme sürecindeyiz ve eskisinin yerine geçmek için REST sistemini uygulama sürecindeyiz.

Ne yazık ki, çalışmaya başlayan geliştirici şimdi uzun süreli hastalık izninde ve bana devredildi. Son bir haftadır çalışıyorum ve sistemi anlıyorum, ama beni endişelendiren bir şey var. İşlevlerin işlevlere geçmesi çok görünüyor. Örneğin, sunucularımıza çağrı yapan sınıfımız, işlem tamamlandığında ve hatalar işlendiğinde bir nesneyi arayacak ve geçirecek bir işlev alır.

Bana, kendimi korkunç bir uygulama gibi hissettiğim "kötü hissi" veriyor ve nedenini düşünebiliyorum, ancak sisteme yeniden bir çalışma önermeden önce onay istiyorum. Bu olası sorunla ilgili herhangi bir deneyiminiz olup olmadığını merak ediyor muydum?


22
Neden böyle hissediyorsun? İşlevsel programlama konusunda herhangi bir tecrübeniz var mı? (Kabul etmeyeceğim, ancak bir göz atmalısınız)
Phoshi

8
O zaman bunu bir ders olarak düşün! Akademi’nin öğrettiği ve gerçekte faydalı olan şeylerin çoğu zaman şaşırtıcı derecede az bir çakışması var. Dışarıda, bu tür dogmaya uymayan bütün bir programlama teknikleri dünyası var.
Phoshi

32
İşlevleri diğer işlevlere geçirmek, bir dil onu desteklemediğinde, insanların tek amacı söz konusu işlevi bir stopgap olarak içerecek olan önemsiz "nesneler" yaratma yolundan çıkacaklarıdır.
Doval

36
Benim zamanımda bu geçiş işlevlerini "Geri aramalar" olarak adlandırdık
Mike

Yanıtlar:


84

Bu bir sorun değil.

Bilinen bir tekniktir. Bunlar daha yüksek dereceli fonksiyonlardır (fonksiyonları parametre olarak alan fonksiyonlar).

Bu tür işlevler aynı zamanda fonksiyonel programlamada temel bir yapı taşıdır ve Haskell gibi işlevsel dillerde yaygın olarak kullanılır .

Bu tür işlevler kötü ya da iyi değil - eğer bir nosyon ve tekniğe rastlamadıysanız, ilk başta kavramaları zor olabilir, ancak çok güçlü olabilirler ve alet çantanız için iyi bir araç olabilirler.


Bunun için, işlevsel programlama konusunda gerçek bir deneyimim olmadığından, onu inceleyeceğim. Sadece iyi oturmadı çünkü benim küçük bilgimden, özel bir işlev olarak bir şey ilan ettiğinizde o sınıfa erişebilmeli ve halka nesneye atıfta bulunulmalı. Düzgün bir şekilde inceleyeceğim ve biraz daha takdir edeceğimi umuyorum!
Elliot Blackburn,


8
@BlueHat nasıl kullanıldığını kontrol etmek istiyorsanız özel bir şey olarak ilan edersiniz, eğer bu kullanım belirli fonksiyonlar için bir geri arama ise o zaman iyidir
cırcır ucube

5
Kesinlikle. Özel olarak işaretlemek, başkasının gelmesini ve istediğiniz zaman isimlerine göre erişmelerini istemediğiniz anlamına gelir. Özel bir işlevi bir başkasına geçirmek, özellikle de bu parametre için belgelenen şekilde çağırmasını istediğiniz için kesinlikle iyidir. Özel bir alanın değerini parametre olarak başka bir işleve parametre olarak geçirmekten farklı bir şey olması gerekmiyor, muhtemelen seninle fena oturmuyor :-)
Steve Jessop

2
Kendinizi yapıcı parametreler olarak fonksiyonlarla somut sınıflar yazarken bulursanız, muhtemelen yanlış yaptığınızı söylersiniz.
Gusdor

30

Sadece işlevsel programlama için kullanılmazlar. Geri aramalar olarak da bilinir :

Geri çağırma, başka bir argüman olarak argüman olarak iletilen ve argümanı uygun bir zamanda geri çağırması (yürütmesi) beklenen bir çalıştırılabilir kod parçasıdır. Arama, senkronize bir geri aramadaki gibi hemen olabilir veya senkronize olmayan bir geri aramada olduğu gibi daha sonra yapılabilir.

Bir saniye için asenkron kod düşünün. Örneğin, kullanıcıya veri gönderen bir işleve geçersiniz. Yalnızca kod tamamlandığında, bu işlevi yanıtın sonucuyla çağırırsınız; bu işlev, daha sonra verileri kullanıcıya geri göndermek için kullanır. Bu bir zihniyet değişikliği.

Torrent verilerini tohum kutunuzdan alan bir kütüphane yazdım. Bu kütüphaneyi yürütmek ve veri almak için engelleyici olmayan bir olay döngüsü kullanıyorsunuz, daha sonra kullanıcıya geri gönderiniz (örneğin bir websocket bağlamında). Bu olay döngüsüne bağlı 5 kişiniz olduğunu ve birinin torrent veri sayaçlarını alma isteklerinden birini hayal edin. Bu tüm döngüyü engeller. Bu nedenle, zaman uyumsuz olarak düşünmeniz ve geri aramaları kullanmanız gerekir - döngü çalışmaya devam eder ve "verileri kullanıcıya geri verme" yalnızca işlev yürütmeyi tamamladığında çalışır, bu nedenle beklemeniz gerekmez. Yak ve unut.


1
+1 Geri arama terminolojisini kullanmak için. Bu terimle "yüksek dereceli işlevler" den çok daha sık karşılaşıyorum.
Rodney Schuler

Bu cevabı beğendim, çünkü OP, geri aramaları, özellikle genel olarak daha yüksek dereceli fonksiyonları değil, genel olarak geri çağrıları tarif ediyor gibiydi: "Örneğin, sunucularımıza çağrı yapan sınıfımız, daha sonra bir nesneyi çağıracak ve iletecek bir fonksiyon alır. İşlem tamamlandığında ve hataların işlendiği zamanlar vs.
Ajedi32

11

Bu kötü bir şey değil. Aslında, çok iyi bir şey.

Fonksiyonlara fonksiyonlara geçmek o kadar önemlidir ki lambda fonksiyonlarını bir steno olarak icat ettik . Örneğin, bir jenerik algoritmanın arama ve sıralama gibi şeyler yapmak için yerel değişkenleri ve diğer durumları kullanma yeteneğini sağlayan çok kompakt ama etkileyici bir kod yazmak için C ++ algoritmalı lambdaları kullanabilirsiniz.

Nesneye yönelik kitaplıklar , temel olarak az sayıda işlevi belirten arabirimler olan geri aramalara da sahip olabilir (ideal olarak bir tane, ancak her zaman değil). Kişi daha sonra bu arayüzü uygulayan ve o sınıfın nesnesini bir fonksiyona geçiren basit bir sınıf oluşturabilir. Bu, olay tabanlı programlamanın temel taşıdır; burada çerçeve düzeyinde kod (belki başka bir konu bile) bir kullanıcının eylemine cevaben durumu değiştirmek için bir nesneyi çağırması gerekir. Java'nın ActionListener arayüzü buna iyi bir örnektir.

Teknik olarak, bir C ++ functor operator()()aynı şeyi yapmak için, sözdizimsel şekeri kaldıran bir tür geri çağırma nesnesidir .

Son olarak, sadece C'de kullanılması gereken C tarzı fonksiyon işaretçileri var. Ayrıntıya girmeyeceğim, sadece tamlıktan bahsettim. Yukarıda belirtilen diğer soyutlamalar çok üstündür ve kendi dillerinde kullanılmalıdır.

Diğerleri fonksiyonel programlamayı ve bu fonksiyonlarda geçiş işlevlerinin ne kadar doğal olduğunu belirttiler. Lambdas ve geri aramalar prosedürel ve OOP dillerinin bunu taklit ettiği yoldur ve çok güçlü ve kullanışlıdırlar.


Ayrıca, C # delegelerinde ve lambdalarında yoğun olarak kullanılır.
Evil Dog Pie

6

Daha önce de söylediğim gibi kötü bir uygulama değil. Bu sadece sorumluluğu ayrıştırmanın ve ayırmanın bir yoludur. Örneğin, OOP’de şöyle bir şey yaparsınız:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

Genel yöntem, hakkında hiçbir şey bilmediği belirli bir görevi bir arabirim uygulayan başka bir nesneye devretmektir. Genel yöntem yalnızca bu arayüzü bilir. Senin durumunda, bu arayüz çağrılacak bir fonksiyon olurdu.


1
Bu deyim işlevi basitçe bir arabirimde sarmalamaktır, bir işlevin geçmesine çok benzer bir şey yapar.

Evet, sadece bunun alışılmadık bir uygulama olmadığını göstermek istedim.
Philipp Murry,

3

Genel olarak, işlevleri diğer işlevlere geçirmede yanlış bir şey yoktur. Eşzamansız arama yapıyorsanız ve sonuçla ilgili bir şeyler yapmak istiyorsanız, bir tür geri arama mekanizmasına ihtiyacınız olacaktır.

Ancak, basit geri aramaların birkaç olası dezavantajı vardır:

  • Bir dizi görüşme yapmak, geri aramaların derinlere yuvalanmasını gerektirebilir.
  • Hata işleme, bir arama sırasındaki her arama için tekrarlama gerektirebilir.
  • Birden fazla görüşmeyi koordine etmek, aynı anda birden fazla görüşme yapmak ve daha sonra hepsi bittiğinde bir şey yapmak gibi garip.
  • Bir arama grubunu iptal etmenin genel bir yolu yoktur.

Basit web servisleriyle yaptığınız gibi çalışır, ancak daha karmaşık çağrıların sıralanmasına ihtiyacınız olursa garipleşir. Yine de bazı alternatifler var. Örneğin, JavaScript ile vaatlerin kullanımına doğru bir kayma olmuştur ( Javascript vaatlerinde bu kadar harika olan ne ).

Yine de işlevleri başka işlevlere geçirmeyi içerir, ancak zaman uyumsuz çağrılar doğrudan geri çağrı almak yerine geri çağrı alan bir değer döndürür. Bu, bu çağrıları bir araya getirmek için daha fazla esneklik sağlar. Buna benzer bir şey ActionScript'te oldukça kolay bir şekilde uygulanabilir.


Ayrıca gereksiz geri aramaların kullanılmasının, kodu okuyan herkes için yürütme akışını takip etmeyi zorlaştırabileceğini de ekleyeceğim. Açık bir aramanın anlaşılması, kodun uzak bir bölümünde ayarlanmış olan bir geri aramadan daha kolaydır. (Kurtarma için sınır değerler!) Yine de, olayların tarayıcıda ve diğer olaya dayalı sistemlerde ateşlenmesi; Daha sonra, olay işleyicisinin ne zaman ve hangi sırayla olay işleyicilerin aranacağını bilmek stratejisini anlamamız gerekir.
joeytwiddle
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.