Visszahívás (programozás)

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2021. január 21-én felülvizsgált verziótól ; az ellenőrzések 3 szerkesztést igényelnek .

Visszahívás ( angol  hívás  - hívás, angol  vissza  - vissza) vagy visszahívási funkció a programozásban  - a végrehajtható kód átadása egy másik kód paramétereként . A visszahívás lehetővé teszi a függvény számára, hogy a meghívásakor végrehajtsa az argumentumokban megadott kódot . Ez a kód más kódkontextusban is definiálható, és nem hívható közvetlenül ebből a függvényből. Néhány algoritmikus feladat bemenete nem csak számok vagy objektumok, hanem műveletek (algoritmusok) is, amelyek természetesen visszahívásként vannak megadva.

Alkalmazás

A visszahívási koncepciónak számos alkalmazása van. Például egyes algoritmusok (függvények) részfeladataként hash értéket számítanak ki egy karakterláncból. Az algoritmus (függvény) indításakor az argumentumokban célszerű megadni, hogy melyik függvényt használjuk a hash értékek kiszámításához.

Egy másik példa olyan algoritmusra, amelynél természetes egy függvényt argumentumként átadni, egy olyan algoritmus, amely egy objektumtárat bejár, és minden objektumra valamilyen műveletet alkalmaz. A visszahívás működhet ilyen műveletként (algoritmusként).

A visszahívási programozási technika olyan programozási nyelvekben, mint a C , egyszerű. A fő függvény meghívásakor egyszerűen átad egy mutatót a visszahívási függvénynek. A klasszikus példa egy függvény qsortaz stdlib könyvtárból . Ez a függvény azonos hosszúságú bájtblokkok tömbjét rendezi. Argumentumként megkapja a tömb első elemének címét, a tömb blokkjainak számát, a bájtblokk méretét, valamint egy függvényre mutató mutatót két bájtblokk összehasonlításához. Ez az összehasonlító függvény a visszahívási függvény ebben a példában:

#include <stdlib.h> // függvény egész számok összehasonlítására modulo int összehasonlító_abs ( const void * a , const void * b ) { int a1 = * ( int * ) a ; int b1 = * ( int * ) b ; return abs ( a1 ) - abs ( b1 ); } int main () { int méret = 10 ; int m [ méret ] = { 1 , -3 , 5 , -100 , 7 , 33 , 44 , 67 , -4 , 0 }; // az m tömb rendezése növekvő modulokban qsort ( m , size , sizeof ( int ), összehasonlítás_absz ); return 0 ; }

A visszahívást úgy is felfoghatja, mint valamely fő eljárás argumentumaként átadott műveletet. És ez a művelet így tekinthető:

  • részfeladat, és adatfeldolgozásra használható fel ezen eljárás keretében;
  • A „telefonkapcsolat” arra szolgál, hogy „kapcsolatba lépjen” azzal, aki az eljárást hívta, amikor valamilyen esemény bekövetkezik (az angol  visszahívás szó szerint „visszahívás”).

A fent látható példa pontosan megfelel az első esetnek. Az az eset, amikor a visszahívást "telefonkapcsolatként" használjuk, azt a kódot tükrözi, ahol az adott jel kezeléséhez szükséges funkciót adják:

#include <stdio.h> #include <signal.h> illékony szig_atomi_t br = 1 ; érvénytelen szig ( int signum ) { br = 0 ; } int main ( int argc , char * argv []) { jel ( SIGINT , sig ); printf ( "A program leállításához nyomja meg a törésbillentyűkombinációt \n " ); míg ( br ); printf ( "SIGINT fogadva, kilépés \n " ); return 0 ; }

Egyes programozási nyelvekben, mint például a Common Lisp , Erlang , Scheme , Clojure , PHP , JavaScript , Perl , Python , Ruby és mások, lehetőség van névtelen (névtelen) függvények és lezáró függvények létrehozására közvetlenül a fő függvényhívási kifejezésben, és ezt a lehetőséget széles körben használják ki.

Az AJAX technológiában , amikor aszinkron kérést küld a szervernek, meg kell adnia egy visszahívási függvényt, amely azonnal meghívásra kerül, amint a kérésre adott válasz megérkezik. Ezt a függvényt gyakran "helyben" határozzák meg anélkül, hogy konkrét nevet adnának:

új Ajax . Request ( 'http://example.com/do_it' , { method : 'post' , onSuccess : function ( szállítás ) { // a window által meghívott függvény . alert ( "Kész!" ); // ha a kérés sikeres volt } , // onFailure : function () { // az ablak által meghívott függvény . figyelmeztetés ( "Hiba!" ); // kérésre végrehajtási hiba } });

A visszahívás funkciót az Observer tervezési mintában is használják . Így például a Prototype könyvtár használatával létrehozhat egy „megfigyelőt”, amely egy azonosítóval rendelkező elemre történő kattintásokat figyeli, és ha esemény érkezik, üzenetet ír az elem belsejébe : "my_button""message_box"

esemény . megfigyel ( $ ( "my_button" ), 'kattintás' , function () { $ ( "üzenetdoboz" ). innerHTML = "Rákattintott a gombra!" });

A visszahívási függvény a függvénypolimorfizmus alternatívája , azaz lehetővé teszi általánosabb célú függvények létrehozását, ahelyett, hogy olyan függvénysorozatokat hozzunk létre, amelyek szerkezetükben megegyeznek, de a végrehajtható részfeladatokban csak bizonyos helyeken különböznek egymástól. Azokat a függvényeket, amelyek más függvényeket argumentumként vesznek fel, vagy eredményként visszaadnak függvényeket, magasabb rendű függvényeknek nevezzük . A visszahívási technika fontos szerepet játszik a kód újrafelhasználásának elérésében .

Miért használjunk visszahívásokat

A visszahívás használatának okainak jobb megértése érdekében fontolja meg a következő műveletek egyszerű feladatát egy számlistán: minden szám kinyomtatása, minden szám négyzetre helyezése, minden szám növelése 1-gyel, minden elem nullára állítása. Nyilvánvaló, hogy e négy művelet végrehajtására szolgáló algoritmusok hasonlóak - ez egy ciklus, amely megkerüli a lista összes elemét, és a ciklustörzsben valamilyen műveletet alkalmaz minden elemre. Ez egy egyszerű kód, és elvileg 4-szer leírhatod. De nézzünk egy bonyolultabb esetet, amikor a lista nem a memóriában, hanem a lemezen van tárolva, és a listával egyszerre több folyamat is tud dolgozni, és meg kell oldani az elemekhez való hozzáférés szinkronizálásának problémáit (több folyamat is lehetséges különböző feladatok elvégzése - egyes elemek eltávolítása a listából, újak hozzáadása, meglévő elemek módosítása a listában). Ebben az esetben a lista összes elemének bejárása meglehetősen összetett kód lesz, amelyet nem szeretne többször átmásolni. Helyesebb lenne egy általános célú függvényt létrehozni a lista elemeinek bejárására, és lehetővé tenni a programozóknak, hogy elvonatkoztassanak a bejárási algoritmus működésétől, és csak egy visszahívási függvényt írjanak a lista egyetlen elemének feldolgozására.

Szoftverstrukturálás

A szoftverek visszahívási függvényekkel történő strukturálása nagyon kényelmes és széles körben alkalmazott megközelítés, hiszen egy változatlan (beleértve a zárt) kódú program viselkedése igen széles tartományban változtatható. Ez kétféleképpen valósítható meg – vagy egy függvény „alternatív megvalósításával”, vagy „egy másik funkció hozzáadásával a hívási lánchoz”.

Általános szabály, hogy a fejlesztő nem valósítja meg a program összes funkcióját visszahívásokkal, hanem csak azokat, amelyeket bővíteni vagy módosítani kell a beépülő modulokkal . A bővítmények csatlakoztatásához egy speciális eljárást biztosítanak, amely a fejlesztőtől származó "standard" inverz függvényeket helyettesíti a bővítmény alternatív funkcióival.

Ennek a megközelítésnek a leghíresebb példája a Microsoft Windows operációs rendszer , ahol a visszahívási funkciókat "handler"-nek ("kezelőnek") nevezik, és lehetőség van egy további eljárás beillesztésére bármely két szabvány közé. Ezt a megközelítést "esemény-elfogásnak" nevezik, és például: víruskeresők használják az éppen elért fájlok ellenőrzésére; vírusok a billentyűzetről bevitt karakterek olvasásához; hálózati szűrők statisztikák gyűjtésére és csomagok blokkolására.

A modern Unix és Linux rendszerekben lehetőség van a kernelmodulok dinamikus be- és kiürítésére, amelyek szintén visszahívási függvényeken alapulnak. Ugyanakkor van egy modul (kernelkiterjesztés) FUSE , amely viszont lehetővé teszi a hétköznapi felhasználói programok számára a virtuális fájlrendszerek kérésének kiszolgálását (elfogását).

A szoftverekben néha előfordul, hogy a programok teljesen a visszahívási funkciókra épülnek, ami kissé rontja a kód olvashatóságát, de maximális lehetőségeket biztosít a bővítmények számára. Ilyen termék például a DokuWiki .

Előnyök és hátrányok

Előnyök:

  • A funkcionalitás dinamikus változtatásának lehetősége (pluginok/modulok csatlakoztatása és leválasztása a program futása közben).
  • A meghívott függvény korlátlan számú változatának lehetősége az alap (ebben az összefüggésben) kód megváltoztatása nélkül.
  • Lehetőség a meghívott függvény beszúrására nemcsak alternatív viselkedéshez, hanem egy másik (köztes) szubrutinként is - általában a műveletek nyomon követésére vagy a következő (meghívott) függvény paramétereinek megváltoztatására. A hívásláncban tetszőleges számú ilyen független "további láncszem" lehet.
  • Visszahívási funkciók támogatása a legtöbb modern általános célú programozási nyelven.

Hibák:

  • A további "inverz függvény" hívásokhoz kapcsolódó teljesítménybüntetés egyenesen arányos a "függvényhívás költségével" futásidőben és a további hívások számával a program futása közben.
  • A forráskód olvashatóságának romlása - a programalgoritmus megértéséhez a hívások teljes láncát követni kell.

Lásd még

Linkek