A kontroll megfordítása

A Wikipédiából, a szabad enciklopédiából

A kontroll megfordítása (angolul inversion of control, röviden IoC) 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 megfordítása, 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, satöbbi. 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 dependency injectionök.

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 dependency injection 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 dependency injection elve és a rétegződés absztrakciója összekapcsolódik.[2] Az inverzió 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 dependency injectiont 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 dependency injection, 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 dependency injection 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:

  • Konstruktor injekció
  • Paraméter injekció
  • Szetter injekció
  • Interfész injekció

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ú
  • konstruktor-alapú
  • intruzív
  • Annotált

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.

Jegyzetek[szerkesztés]

  1. Ralph E. Johnson & Brian Foote: Designing Reusable Classes. Journal of Object-Oriented Programming, Volume 1, Number 2 pp. 22–35. Department of Computer Science University of Illinois at Urbana-Champaign, Hiba: Érvénytelen idő. (Hozzáférés: 2014. április 29.)
  2. ^ a b Dependency Injection.
  3. Inversion of Control on Martin Fowler's Bliki
  4. Archived index at the Wayback Machine. 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

További információk[szerkesztés]