Interfész ( angol interfész ) - olyan program/szintaktikai struktúra, amely kapcsolatot definiál olyan objektumokkal, amelyeket csak valamilyen viselkedés egyesít. Osztályok tervezésekor az interfész tervezése megegyezik a specifikáció tervezésével (a metódusok halmaza, amelyet minden interfészt használó osztálynak meg kell valósítania).
Az interfészek az absztrakt osztályokkal és protokollokkal együtt kölcsönös kötelezettségeket hoznak létre a szoftverrendszer elemei között, ami a szerződéses programozás ( Eng. design by contract , DbC) koncepciójának alapja. Az interfész egy interakciós határt határoz meg az osztályok vagy komponensek között egy bizonyos absztrakció megadásával, amelyet az implementátor megvalósít.
Az OOP felülete egy objektumorientált nyelv szigorúan formalizált eleme, és széles körben használják a programok forráskódjában.
Az interfészek lehetővé teszik az objektumok többszörös öröklését , és ugyanakkor megoldják a rombusz alakú öröklődés problémáját . A C++ nyelvben ez osztályörökléssel oldható meg a virtual.
Egy OOP interfész leírása az egyes nyelvek szintaxisának részletein kívül két részből áll: az interfész nevéből és metódusaiból .
Az interfészek kétféleképpen használhatók:
Általános szabály, hogy az objektum-orientált programozási nyelvekben az interfészek, akárcsak az osztályok, örökölhetők egymástól. Ebben az esetben a gyermek interfész tartalmazza az ősfelület összes metódusát, és opcionálisan saját metódusokat ad hozzájuk.
Így egyrészt az interfész egy „szerződés”, amelynek teljesítésére az azt megvalósító osztály vállalja, másrészt egy interfész adattípus, mert leírása kellően egyértelműen meghatározza az objektumok tulajdonságait a típushoz változókat az osztállyal egyenlő alapon. Hangsúlyozni kell azonban, hogy az interfész nem egy teljes adattípus, hiszen csak az objektumok külső viselkedését határozza meg. Az interfész által meghatározott viselkedés belső felépítését és megvalósítását az interfészt megvalósító osztály biztosítja; ezért nincsenek tiszta formában "interfész példányok", és minden "interfész" típusú változó konkrét osztályok példányait tartalmazza.
Az interfészek használata az egyik lehetőség az objektumnyelvek és keretrendszerek polimorfizmusának biztosítására. Minden osztály, amely ugyanazt a felületet valósítja meg, az általuk meghatározott viselkedés szempontjából, ugyanúgy viselkedik kívülről. Ez lehetővé teszi, hogy általánosított adatfeldolgozó algoritmusokat írjon, amelyek típusként interfész paramétereket használnak, és alkalmazzák azokat különböző típusú objektumokra, minden alkalommal a kívánt eredményt kapva.
Például a „ ” interfész Cloneableleírhatja az objektumok klónozásának (pontos másolatok létrehozásának) absztrakcióját egy „ Clone” metódus megadásával, amelynek át kell másolnia egy objektum tartalmát egy másik, azonos típusú objektumba. Ezután minden osztálynak, amelynek objektumait másolni kell, meg kell valósítania az interfészt Cloneable, és biztosítania kell egy metódust Clone, és a programban bárhol, ahol objektum klónozásra van szükség, a metódus meghívódik az objektumra erre a célra Clone. Sőt, az ezt a metódust használó kódnak csak az interfész leírásával kell rendelkeznie, előfordulhat, hogy semmit sem tud arról az osztályról, amelynek az objektumait másolják. Így az interfészek lehetővé teszik a szoftverrendszer modulokra bontását kölcsönös kódfüggés nélkül.
Látható, hogy egy interfész formális szempontból csak egy tiszta absztrakt osztály , vagyis egy olyan osztály, amelyben az absztrakt metódusokon kívül semmi nincs definiálva . Ha egy programozási nyelv több öröklődést és absztrakt metódust támogat (mint például a C++ ), akkor nincs szükség külön „interfész” fogalmának bevezetésére a nyelv szintaxisába. Ezeket az entitásokat absztrakt osztályok segítségével írják le, és az osztályok öröklik őket az absztrakt módszerek megvalósításához.
A többszörös öröklődés teljes körű támogatása azonban meglehetősen bonyolult és sok problémát okoz, mind a nyelvi megvalósítás szintjén, mind az alkalmazásarchitektúra szintjén. Az interfészek koncepciójának bevezetése egy kompromisszum, amely lehetővé teszi a többszörös öröklődés számos előnyének kihasználását (különösen a logikailag kapcsolódó metóduskészletek kényelmes definiálását osztályszerű entitásokként, amelyek lehetővé teszik az öröklődést és a megvalósítást), megvalósítás nélkül. teljes egészében, és így anélkül, hogy a legtöbb azzal kapcsolatos nehézséggel szembesülne.
Végrehajtási szinten a többszörös öröklődés klasszikus sémája további kellemetlenségeket okoz:
A séma interfészekkel (többszörös öröklődés helyett) elkerüli ezeket a problémákat, kivéve az interfész metódusok hívásának problémáját (vagyis a virtuális metódushívások többszörös öröklődésben, lásd fent). A klasszikus megoldás az (például a JVM for Java vagy a CLR for C#), hogy az interfész metódusok kevésbé hatékony módon, virtuális tábla segítsége nélkül hívódnak: minden hívásnál először egy adott objektumosztály kerül meghatározásra, majd megkeresik benne a kívánt metódust (természetesen számtalan optimalizálással).
A programozási nyelvek általában lehetővé teszik egy interfész öröklését több ősfelületről. Az ősfelületeken deklarált összes metódus a gyermek interfész deklarációjának részévé válik. Az osztályörökléssel ellentétben az interfészek többszörös öröklése sokkal könnyebben megvalósítható, és nem okoz jelentős nehézségeket.
Egy ütközés azonban továbbra is lehetséges az interfészek többszörös öröklésével és több interfész egy osztály általi megvalósításával. Ez akkor fordul elő, ha két vagy több interfész, amelyet egy új interfész örökölt vagy egy osztály implementált, azonos aláírású metódusokkal rendelkezik. A programozási nyelvek fejlesztői ilyen esetekre kénytelenek bizonyos ellentmondások feloldásának módszereit választani. Itt több lehetőség is van: a megvalósítás tilalma, egy konkrét konkrét megjelölése és az alap interfész vagy osztály megvalósítása.
Az interfészek megvalósítását nagymértékben meghatározzák a nyelv kezdeti képességei és az interfészek bevezetésének célja. A Java , Object Pascal , Delphi és C++ felületek használatának jellemzői nagyon jelzésértékűek , mivel három alapvetően eltérő helyzetet mutatnak be: a nyelv kezdeti orientációját az interfész fogalmának használatához, a kompatibilitási felhasználást és az osztályok szerinti emulációt.
A Delphiben interfészeket vezettek be a Microsoft COM technológiájának támogatására . Amikor azonban a Kylix megjelent , az interfészek, mint a nyelv elemei, leválasztásra kerültek a COM technológiától. Minden interfész az IInterface [1] interfésztől öröklődik, amely a win32 platformon megegyezik az azonos IUnknownnevű szabványos COM interfésszel, ahogyan az összes benne lévő osztály az osztály leszármazottja TObject. Az IUnknown ősként való kifejezett használata a COM technológiát használó kód számára van fenntartva.
Interfész deklarációs példa:
IMyInterface = interfész eljárás DoSomething ; vége ;Az interfészek megvalósításának deklarálásához az osztályleírásban meg kell adni a nevüket zárójelben a kulcsszó classután, az ősosztály neve után. Mivel "az interfész egy szerződés, amelyet teljesíteni kell", a program nem fordítja le, amíg a megvalósítási osztályban meg nem valósul.procedure DoSomething;
A Delphi interfészek fent említett, a COM technológiára való összpontosítása némi kellemetlenséghez vezetett. A helyzet az, hogy az interfész IInterface(ahonnan az összes többi interfész öröklődik) már tartalmaz három, a COM interfészeknél kötelező metódusokat: QueryInterface, _AddRef, _Release. Ezért minden interfészt megvalósító osztálynak meg kell valósítania ezeket a metódusokat, még akkor is, ha a program logikája szerint az interfésznek és az osztálynak semmi köze a COM-hoz. Megjegyzendő, hogy ezt a három módszert egy objektum élettartamának szabályozására is használják, és az interfész kérési mechanizmust a „ as” operátoron keresztül hajtják végre.
Példa egy interfészt megvalósító osztályra:
TMyClass = osztály ( TMyParentClass , IMyInterface ) eljárás DoSomething ; function QueryInterface ( const IID : TGUID ; out Obj ) : HResult ; stdcall ; function _AddRef : Integer ; stdcall ; function _Release : Integer ; stdcall ; vége ; végrehajtásA programozónak megfelelően végre kell hajtania a QueryInterface, _AddRef, metódusokat _Release. A szabványos metódusok írásának szükségességétől való megszabadulás érdekében egy könyvtári osztályt biztosítunk TInterfacedObject - ez a fenti három metódust valósítja meg, és bármely osztály, amely tőle és leszármazottaitól örökli, megkapja ezt a megvalósítást. Ezeknek a metódusoknak a megvalósítása TInterfacedObjectaz objektum élettartama feletti automatikus vezérlést feltételezi a _AddRefés metódusokon keresztüli hivatkozások számlálásával _Release, amelyek automatikusan meghívódnak a hatókörbe való belépéskor és kilépéskor.
Példa egy osztályörökösre TInterfacedObject:
TMyClass = osztály ( TInterfacedObject , IMyInterface ) eljárás DoSomething ; vége ;Interfészt megvalósító osztály interfészek nélküli osztálytól való öröklésekor a programozónak kézzel kell megvalósítania az említett metódusokat, meghatározva a referenciaszámláló vezérlés meglétét vagy hiányát, valamint az interfészt a -ban kell megszereznie QueryInterface.
Példa tetszőleges osztályra hivatkozásszámlálás nélkül:
TMyClass = osztály ( TObject , IIInterface , IMyInterface ) //IIinterfész függvény QueryInterface ( const IID : TGUID ; out Obj ) : HResult ; stdcall ; function _AddRef : Integer ; stdcall ; function _Release : Integer ; stdcall ; //IMyInterface eljárás DoSomething ; vége ; { TMyClass } funkció TMyClass . QueryInterface ( const IID : TGUID ; out Obj ) : HResult ; begin if GetInterface ( IID , Obj ) then Eredmény := 0 else Eredmény := E_NOINTERFACE ; vége ; funkció TMyClass . _AddRef : Integer ; kezdődik Eredmény := - 1 ; vége ; funkció TMyClass . _Release : Integer ; kezdődik Eredmény := - 1 ; vége ; eljárás TMyClass . DoSomething ; begin //Csinálj valamit vége ;A C++ több öröklődési és absztrakt osztályt támogat , ezért, mint fentebb említettük, ezen a nyelven nincs szükség külön szintaktikai konstrukcióra az interfészekhez . Az interfészek absztrakt osztályokkal vannak meghatározva , és az interfész megvalósítása ezekből az osztályokból való örökléssel történik.
Példa az interfész meghatározására :
/** * interface.Openable.h * */ #ifndef INTERFACE_OPENABLE_HPP #define INTERFACE_OPENABLE_HPP // iMegnyitható interfész osztály. Meghatározza, hogy valamit lehet-e nyitni/zárni. osztályú iNyitható { nyilvános : virtuális ~ iOpenable (){} virtuális üresség nyitva () = 0 ; virtuális üres bezárás () = 0 ; }; #endifEgy interfész örökléssel valósul meg ( a többszörös öröklődés jelenléte miatt szükség esetén több interfész is megvalósítható egy osztályban ; az alábbi példában az öröklődés nem többszörös):
/** * osztály.Ajtó.h * */ #include "interfész.nyitható.h" #include <iostream> osztály Ajtó : nyilvános iNyitható { nyilvános : Ajtó (){ std :: cout << "Ajtóobjektum létrehozva" << std :: endl ;} virtuális ~ ajtó (){} //Az iOpenable interfész metódusainak növelése a Door osztály virtuális void open (){ std :: cout << "Ajtó nyitva" << std :: endl ;} virtual void close (){ std :: cout << "Ajtó zárva" << std :: endl ;} //Ajtóosztály-specifikus tulajdonságok és módszerek std :: string mMaterial ; std :: string mColor ; //... }; /** * class.Book.h * */ #include "interfész.nyitható.h" #include <iostream> osztály Könyv : nyilvános iMegnyitható { nyilvános : Book (){ std :: cout << "Könyvobjektum létrehozva" << std :: endl ;} virtuális ~ könyv (){} //Az iOpenable felület metódusainak növelése a Book osztály virtuális void open (){ std :: cout << "Könyv megnyitva" << std :: endl ;} virtual void close (){ std :: cout << "Könyv zárva" << std :: endl ;} //Könyvspecifikus tulajdonságok és módszerek std :: string mTitle ; std :: string mAuthor ; //... };Teszteljünk mindent együtt:
/** * test.openable.cpp * */ #include "interfész.nyitható.h" #include "class.Door.h" #include "class.book.h" //Az iOpenable felületet megvalósító heterogén objektumok megnyitásának/bezárásának funkciója void openAndCloseSomething ( iOpenable & smth ) { vmit . nyitott (); vmit . bezár (); } int main () { Ajtó myDoor ; BookmyBook ; _ openAndCloseSomething ( myDoor ); openAndCloseSomething ( myBook ); system ( "szünet" ); return 0 ; }A C++-tól eltérően a Java nem teszi lehetővé egynél több osztály öröklését. A többszörös öröklődés alternatívájaként interfészek vannak. A Java minden osztálya bármilyen interfészkészletet megvalósíthat. Java interfészekből nem lehet objektumokat származtatni .
Interfész deklarációkAz interfész deklaráció nagyon hasonlít az egyszerűsített osztálydeklarációra.
Címmel kezdődik. A módosítók elsőként vannak felsorolva . Egy interfész deklarálható -ként public, amely esetben nyilvános használatra elérhető, vagy egy hozzáférés-módosító elhagyható, ebben az esetben a felület csak a -ban lévő típusok számára érhető el . Interfész módosító abstractnem szükséges, mert minden interfész absztrakt osztály . Megadható, de nem ajánlatos megtenni, hogy ne zsúfolja össze a .
Ezután a kulcsszó interfaceés az interfész neve beírásra kerül.
Ezt követheti egy kulcsszó extendsés azon interfészek listája, amelyekről a deklarált interfész örökölni fog. Sok szülőtípus (osztály és/vagy interfész) lehet – a lényeg, hogy ne legyenek ismétlődések, és az öröklődési kapcsolat ne alkosson ciklikus függőséget.
Az interfész öröklése valóban nagyon rugalmas. Tehát, ha van két interfész, Aés B, és Böröklődik a -tól A, akkor az új interfész Cmindkettőtől örökölhető. Nyilvánvaló azonban, hogy a -tól való örökléskor a -tól Bvaló öröklés jelzése Aredundáns, mivel ennek az interfésznek minden eleme már a B interfészen keresztül öröklődik.
Ezután az interfész törzsét zárójelbe kell írni.
Példa az interfész deklarációjára (Hiba a Színezhető és Átméretezhető osztályoknál: A Színezhető és Átméretezhető típus nem lehet a Drawable szuperinterfésze; a szuperinterfésznek interfésznek kell lennie):
nyilvános felület Rajzolható kiterjesztések Színezhető , Átméretezhető { }Az interfész törzse elemek deklarációjából áll , azaz mezőkből - konstansokból és absztrakt metódusokból . Minden interfészmező automatikusan public final static, így ezek a módosítók opcionálisak, sőt nemkívánatosak, hogy ne zsúfolják össze a kódot. Mivel a mezők véglegesek, azonnal inicializálni kell őket .
public interface Irányok { int JOBBRA = 1 ; int BAL = 2 ; int FEL = 3 ; int LE = 4 ; }Minden interfész metódus public abstract, és ezek a módosítók is opcionálisak.
public interface Moveable { void moveRight (); érvénytelen mozgásBal (); void moveUp (); void moveDown (); }Mint látható, az interfész leírása sokkal egyszerűbb, mint az osztálydeklaráció.
Interfész megvalósításEgy interfész megvalósításához meg kell adni az osztálydeklarációban a implements. Példa:
interfész I { void interfaceMethod (); } public class ImplementingInterface implements I { void interfaceMethod () { System . ki . println ( "Ez a módszer az I interfészről valósul meg" ); } } public static void main ( String [] args ) { ImplementingInterface temp = new ImplementingInterface (); hőm . interfészMódszer (); }Minden osztály bármilyen elérhető interfészt megvalósíthat. Ugyanakkor minden absztrakt metódust, amely interfészekből vagy szülőosztályból való örökléskor megjelent , az osztályban implementálni kell , hogy az új osztályt nem absztraktnak lehessen nyilvánítani.
Ha az azonos aláírású metódusokat különböző forrásokból örököljük , akkor elég egyszer leírni a megvalósítást, és az összes metódusra érvényes lesz. Ha azonban eltérő visszatérési értékkel rendelkeznek, akkor ütközés lép fel. Példa:
interface A { int getValue (); } interfész B { double getValue (); } interfész C { int getValue (); } public class Az A , C helyes megvalósítása // az osztály helyesen örökli a metódusokat azonos aláírással { int getValue () { return 5 ; } } class Rossz implementálja A , B // class fordítási idejű hibát dob { int getValue () { return 5 ; } double getValue () { return 5,5 ; } }A C# nyelvben az interfészek örökölhetnek egy vagy több másik interfésztől. Az interfész tagjai lehetnek metódusok, tulajdonságok, események és indexelők:
interfész I1 { void Method1 (); } interfész I2 { void Method2 (); } interfész I : I1 , I2 { void Method (); int Szám { get ; } event EventHandler SomeEvent ; string this [ int index ] { get ; készlet ; } }Interfész implementálásakor az osztálynak magának az interfésznek és az alap interfészeinek metódusait is meg kell valósítania:
nyilvános C osztály : I { public void Method () { } public int Szám { get { throw new NotImplementedException (); } } nyilvános rendezvény EventHandler SomeEvent ; public string this [ int index ] { get { throw new NotImplementedException (); } set { throw new NotImplementedException (); } } public void 1. módszer () { } public void 2. módszer () { } }Az UML interfészei a rendszer alkotóelemei közötti UML dokkoló csomópontok megjelenítésére, meghatározására, létrehozására és dokumentálására szolgálnak. A típusok és az UML-szerepek mechanizmust biztosítanak egy absztrakció interfészhez való statikus és dinamikus leképezésének modellezésére egy adott környezetben.
Az UML-ben az interfészeket „interfész” sztereotípiájú osztályokként vagy körökként ábrázolják (ebben az esetben az interfészben található UML műveletek nem jelennek meg).
Adattípusok | |
---|---|
Értelmezhetetlen | |
Numerikus | |
Szöveg | |
Referencia | |
Összetett | |
absztrakt | |
Egyéb | |
Kapcsolódó témák |