A kontroll megfordítása (angolul inversion of control, röviden IoC, kiejtése: /ˌaɪ.əʊˈsiː/ (UK), /ˌaɪ.oʊˈsiː/ (US), ájouszí (magyaros)) főleg objektumorientált programozási nyelvekben használt technika a komponensek összeillesztésére, konfigurálására és kezelésére.

A technika lényege, hogy a komponenskezelést (pl. létrehozást, példányosítást, paraméterezést, megszüntetést, metódus hívás) kiemeljük a programkódból, és általában egy külső keretrendszerre bízzuk, mint pl. a Spring.

Célja a modularitás növelése és bővíthetővé tétele.[1] Az objektumorientáltság nem feltétel. A kifejezést Robert C. Martin és Martin Fowler népszerűsítette.

Egy rokon minta a függőség befecskendezése, ami a függőségeket megosztott absztrakcióval kezeli a felső és az alsó rétegek között. Kapcsolódik az eseményvezérelt programozáshoz is, mert azt gyakran a kontroll megfordításával valósítják meg. A felhasználó kódja csak az eseményeket dolgozza fel, míg az eseményciklus és az események, üzenetek közvetítése a keretrendszerre vagy a futtató környezetre van bízva.

Áttekintés szerkesztés

Hagyományosan a főprogram (main függvény) végzi a hívásokat egy menü könyvtárba, ami megmutatja a választható parancsokat, és megkéri a felhasználót, hogy válasszon.[2] A könyvtár a kiválasztott opciót visszatérési értékként adja vissza, majd a main függvény meghívja a megfelelő parancsot. Egy e-mail kliens esetén a parancsok lehetnek az új levelek betöltése, új levél írása, válasz a megnyitott levélre stb. Kilépni egy bizonyos gomb lenyomásával vagy szintén egy paranccsal lehet.

A vezérlés megfordításával a programot egy keretrendszer felhasználásával szerkesztik. Ez ismeri az általános viselkedési és grafikus elemeket, mint az ablakozás, menük, az egér kezelése. A fejlesztő kódja a keretrendszer által nyújtott keretet tölti ki. Így például megadja a menüpontokat, és regisztrál hozzájuk egy-egy szubrutint, de a keretrendszer figyeli a felhasználó cselekedeteit, és hívja meg a szubrutint. Az e-mail kliens példában a keretrendszer figyeli az egeret és a billentyűzetet, és meghívja a hozzájuk tartozó parancsot, míg egy másik szálon a hálózati interfészen várja az új üzeneteket, és frissíti a listát, ha szükséges. Ugyanez a keretrendszer felhasználható táblázatkezelő vagy szövegszerkesztő megírásához is.

A vezérlés megfordítása szétválasztja a kód általános és speciális részeit, amivel támogatja az újrafelhasználhatóságot. Egyes tervminták követik a vezérlés megfordításának elvét, mint szoftverkeretrendszerek, callback függvények, időzítők, eseményciklusok és függőség befecskendezések.

A vezérlés megfordítása a következő tervezési célokat szolgálja:

  • Egy feladat kiszervezése az implementációból
  • Arra a modulra fókuszálni, amire a feladatnak szüksége van
  • A moduloknak más modulok viselkedésére vonatkozó feltételezéseinek szerződésekbe foglalása
  • A modulcsere mellékhatásainak megelőzése

A vezérlés megfordításának elve viccesen megfogalmazva: Hollywood-elv: Ne hívj, hanem mi hívunk.

Háttere szerkesztés

A vezérlés megfordítása nem egy új elv. Martin Fowler egészen 1988-ig vezette vissza.[3] A függőség befecskendezés ennek egy speciális változata a környezetben való kereséssel kiegészítve. A szolgáltatáslokátor is hasonlóan működik, lásd a Java JNDI-it. Loek Bergman cikkében tervezési elvként szerepel.[4]

Robert C. Martin cikkében a függőség befecskendezés elve és a rétegződés absztrakciója összekapcsolódik.[2] Az inverzió szót a hagyományos fejlesztéssel szemben használta. A szolgáltatások rétegekre bontásáról ír, miközben a függőség befecskendezést tárgyalja. Az elv a rétegek határainak meghúzásához is hasznos.

Leírása szerkesztés

Hagyományosan az üzleti logika folyamatát statikusan kötött objektumok határozzák meg. A vezérlés megfordításával a folyamat a program végrehajtása közben felépülő objektumgráftól függ. Egy ilyen dinamikus folyamatot olyan objektuminterakciók tesznek lehetővé, amik absztrakciókkal vannak definiálva. Ezt a futás idejű kötés olyan mechanizmusokkal éri el, mint a függőség befecskendezés, vagy a névszolgáltatás. Ezek lehetővé teszik, hogy a program továbbra is statikusan linkelhető maradjon. Végrehajtáskor azonban a gép inkább a külső konfiguráció olvasásával találja meg a végrehajtandó kódszakaszokat.

A függőség befecskendezés esetén a függő objektumot vagy modult futásidőben tölti be a rendszer. Ez fordítási időben nem határozható meg statikus elemzéssel. Az objektuminterakciókkal leírva az elv más paradigmák mentén is alkalmazható.

Ahhoz, hogy futás közben az objektumok összekapcsolhatók legyenek, kompatibilis interfészeket kell megvalósítaniuk. Például az A osztály viselkedést delegál az I interfésznek, amit a B osztály valósít meg. A program példányosítja A-t és B-t, és B példányát A példányába injektálja.

A megvalósítás módszerei szerkesztés

Az objektumorientált programozásban számos módszer létezik a vezérlés megfordításának megvalósítására. Ezek:

Martin Fowler cikkében az első három technikát tárgyalja.[5] Egy leírás, ami a vezérlési típusok megfordításával foglalkozik, említi az utolsót.[6] A kontextus alapú keresést gyakran névtérszolgáltatással kapcsolják össze.

Különböző változatok szerkesztés

  • getter/setter-alapú, amikor a függőségeket getteren keresztül kapja meg az objektum
  • konstruktor-alapú, amikor a függőségeket a konstruktorban kapja meg az objektum
  • intruzív
  • Annotált, azaz annotációval jelölten kapja meg az objektum

Példák szerkesztés

A legtöbb keretrendszer, például a .NET vagy az Enterprise JavaBeans a következő mintát mutatja:

public class ServerFacade {
   public <K, V> V respondToRequest(K request) {
      if (businessLayer.validateRequest(request)) {
         DAO.getData(request);
         return Aspect.convertData(request);
      }
      return null;
   }
}

Ez a vázlatos Java példa mutatja a kontroll megfordításának alapszerkezetét. Fontos megjegyezni, hogy itt máris sok mindent feltételez a ServerFacade arról, hogy milyen objektumot kap a DAO (data access object) objektumtól. Ez azonban összekapcsolja a ServerFacade és a DAO objektumokat, amely kapcsolat megnehezíti a karbantartást. A vezérlés átadása elvén maga a kapcsolat egyszerűsíthető, ha teljesen a DAO objektumra bízzuk a vezérlést. Ezzel a ServerFacade sokat egyszerűsíthető:

public class ServerFacade {
   public <K, V> V respondToRequest(K request, DAO dao) {
      return dao.getData(request);
   }
}

Most már a paraméterek határozzák meg a vezérlés átadását; ez emlékeztet arra, hogy az objektumok egyes programozási nyelvekben üzenetekkel kommunikálnak.

Implementációk szerkesztés

C++:

Java:

C#

  • StructureMap
  • NInject
  • Castle Windsor
  • Spring.NET
  • Unity

PHP

  • Pimple
  • PHP-DI

Fordítás szerkesztés

Ez a szócikk részben vagy egészben az Inversion of control című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.

Jegyzetek szerkesztés

  1. Ralph E. Johnson & Brian Foote: Designing Reusable Classes. Journal of Object-Oriented Programming, Volume 1, Number 2 (June–July) pp. 22–35. Department of Computer Science University of Illinois at Urbana-Champaign, 1988. (Hozzáférés: 2014. április 29.)
  2. a b Függőség vefecskendezés.
  3. Inversion of Control on Martin Fowler's Bliki
  4. Archívumok indexe a Wayback Machine-ben. Inside Architecture: write once, run anywhere by Loek Bergman
  5. Inversion of Control Containers and the Dependency Injection Pattern by Martin Fowler
  6. IoC Types. [2009. június 15-i dátummal az eredetiből archiválva]. (Hozzáférés: 2009. május 13.)

További információk szerkesztés