Mikor és mivel jobb interface-ből példányosítani? [C#][OOP]
Tehát van egy interfacem
interface IKiir{
void Kiir();
}
class Pelda : IKiir{
void Kiir(){Console.WriteLine("Pelda");}
}
Példányosítás:
IKiir _kiir = new Pelda();
vagy
Pelda _pelda = new Pelda();
Melyik formula, mikor jó és miért jó? Sok helyen azt látom, hogy Interfaceből példányosítanak. Miért van ez?
Én azért helyesbítek: Nem interfész objektumot hozunk létre, és nem interfészt példányosítunk, hanem a megfelelő osuztált példányosítjuk, és az utána kasztolódik interfész-objektummá.
Elsődlegesen heterogén kollekcióban lehet érdemes használni, ha olyan funkcionalitást akarunk használni belőle, ami mindegyikben implementálva kell legyen.
Egy példa. IGurul interfészt hozunk létre, és vannak osztályaink, mint Bicikli, Auto, Vonat. Ezek különbözőképpen mozognak, azonban közös bennük, hogy van mozog() függvényük. (Sok esetben persze érdemesebb leszármazni egy Jarmu osztályból, de csak egyszeres öröklés lehetséges c#-ban, így ha másból is akarunk leszármazni, akkor interfész kell)
Ekkor Listában eltárolhatjuk például így:
List<IGurul> gurulo_dolgok;
És ha for ciklussal végigmegyünk, akkor
for(var v in gurulo_dolgok){
v.gurul();
}
És akkor mindegy nekünk, hogy amúgy mit tud még, vagy hogyan csinálja, csak az a fontos, hogy mindegyiknek van gurul függvénye, amiben bármit csinálhat, akár egy kutya ugathat is, nem kell (de ajánlott) reprezentálnia a valós működést.
Vannak olyan rendszerek (pl. a Java-ban a Spring) ami konfigurációt kód szinten is belehet állítani. Ez esetben mivel több implementációja is lehet egy adott funkciónak, így interface-t kér, hogy erre implementáld le amit szeretnél. Ezzel szabad kezet ad, hogy hogyan valósítod meg más-más funkciót egy adott interfészre.
De kimondottan akkor szoktam interfészt létrehozni, és most korrekt példával jövök, mert erre pont kellet ilyen:
- Van egy Cassandra adatbázis modellem (olyan mint az ORM model)
- és van egy Redis adatbázis modellem
Mivel ezek nem kompatibilisek egymással, de az egyik csak memóriában gyórsító tárazza a Cassandra adatát. Tehát közel azonosak az adatok. Így mind2 rendelkezik azzal ami nekem kell. Szóval csináltam rá egy interfészt, ami visszadja a számomra fontos információt.
Ezek után, most hogy Cassandra modeljét adom át vagy a Redis modeljét, mivel mindkettőben benne van azok a metódusok amiket az interfész megkövetel, így nincs problémám vele, hogy melyiket adom át. A hívandó metódusban, meg már számomra lényegtelen mi jött át, csak a metódusokat érjem el. :)
IKiir _kiir = new Pelda();
Pelda _pelda = new Pelda();
Ha ez így szerepel, akkor a kettő közül mindig a másodikat használd. Az első változat egy rossz megoldás, ugyanis a '_kiir' változó statikus típusa miatt elveszted azokat az információkat (függvények, propertyk, stb), amik csak a 'Pelda' típuson vannak rajta.
Olyat láthatsz gyakran, hogy egy függvény paramétere az interface. Ilyen esetekben egy öröklési hierarchiánál célszerű azt a legmagasabb szinten lévőt választani (tehát amelyiknek a legkevesebb őse van), amelyikkel az adott függvény vagy osztály még működni képes. Így több helyen és módon fogod tudni felhasználni az osztályodat / függvényedet, és jobb lesz a kód bővíthetősége.
Konyhanyelven fogalmazva az interfész azt írja le, hogy hogyan kell valamilyen szolgáltatást nyújtani, illetve hogyan lehet egy adott szolgáltatást igénybe venni.
Vegyünk egy egyszerű (és egyben lesarkított) példát. A mikrohullámú sütőnek áram kell, hogy működjön. Honnan lehet áramod? Előállíthatja azt egy aggregátor, egy atomerőmű, napelem, stb.
Így első nekifutásra ez egy teljesen rendben lévő kódnak tűnik:
class Áram {...}
class Kaja {...}
class Aggregátor {
.. public Áram GetÁram() {...}
}
class Mikró {
.. public void Melegít(Kaja k, Aggregátor áramforrás) {...}
}
Aggregátor aggregátor = new Aggregátor();
Mikró m = new Mikró();
m.Melegít(new Kaja(), aggregátor);
Teljesen oké, működik a mikró. Mi van akkor, ha nekem nem aggregátorom van, hanem mondjuk napelemem?
class Napelem {
.. public Áram GetÁram();
}
Napelem napelem = new Napelem();
Mikró m = new Mikró();
m.Melegít(new Kaja(), napelem);
Na de állj... Miért nem működik ez?! A mikrónk konkrétan csak aggregátor által előállított árammal hajlandó működni? Hát, itt valamit elrontottunk...
Elvileg mindegy kellene lennie, hogy honnan van áramom, nem? Amíg van valamim, ami áramot ad, addig a mikrónak mennie kellene... Tehát a mikrónknak nem aggregátorra van szüksége, hanem valamire, ami áramot ad neki. És itt jön a képbe az interfész:
interface IÁramforrás {
.. Áram GetÁram();
}
//Van két osztályom, ami az IÁramforrás által előírt dolgokat szolgáltatja (áramot ad)
class Aggregátor : IÁramforrás {...}
class Napelem : IÁramforrás {...}
//És van a mikróm, ami azt a szolgáltatást igénybe veszi (tehát nem konkrét megvalósítástól függ, hanem egy szolgáltatás kell neki)
class Mikró {
.. public void Melegít(Kaja k, IÁramforrás áramforrás) {...}
}
Innentől kezdve a mikrómnak már tökmindegy, honnan van áram:
Aggregátor aggregátor = new Aggregátor();
Napelem napelem = new Napelem();
Mikró m = new Mikró();
m.Melegít(new Kaja(), napelem);
m.Melegít(new Kaja(), aggregátor);
Nyilván ez bagatel példa volt, és a programozás világában ettől vannak absztraktabb élethelyzetek is. De vegyünk például egy olyan esetet, hogy regisztrálsz egy oldalra, és kiválaszthatod, hogy e-mailben vagy SMS-ben kérsz kódot a fiókod megerősítéséhez. Az kinézhet mondjuk így:
interface IVerificationCodeSenderService {
.. void SendVerificationCode(string code, string address);
}
class SmsVerificationCodeSenderService : IVerificationCodeSenderService {...}
class EmailVerificationCodeSenderService : IVerificationCodeSenderService {...}
class UserRegistrationService {
.. private readonly IVerificationCodeSenderService _codeSenderService;
.. public UserRegistrationService(IVerificationCodeSenderService codeSender) {
.. .. _codeSenderService = codeSender;
.. }
.. public void Register(string user_address) {
.. .. //...
.. .. _codeSenderService.SendVerificationCode("test", user_address);
.. .. //...
.. }
}
Ha megfigyeled, a UserRegistrationService-ben egy sort sem kell átírnom, ha e-mailről át akarok térni mondjuk SMS-re, vagy netán egy teljesen új módszert akarok bevezetni, ami eddig nem volt.
Vagy egy másik példa lehet az, hogy valamilyen adatot el kell menteni. Ott is mindegy, hogy fájlba mented, hálózaton küldöd át, feltöltöd Google Drive-ba, Bluetooth-on küldöd el telefonra...
Kapcsolódó kérdések:
Minden jog fenntartva © 2025, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!