Duck typing - Runtime interface

 ( Hevi | 2016. szeptember 30., péntek - 0:15 )

"If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck"

Ha a wikipedian megfogalmazottakbol indulunk ki:

"With normal typing, suitability is assumed to be determined by an object's type only. In duck typing, an object's suitability is determined by the presence of certain methods and properties (with appropriate meaning), rather than the actual type of the object."

Duck typing-gal azt mondjuk, hogy futasidoben ez a fuggveny ilyen es olyan metodusokkal es/vagy propertykkel kell rendelkezzen. JavaScriptben gyakorlatilag ez az egyetlen lehetoseg arra, hogy egy fuggveny eldonthesse, hogy megfelelo objektumot kapta-e.

Erosen tipusos OOP nyelvekben (pl. Java) ezt a szerepet az interface latja el. Az interface meghatarozza hogy az adott osztalynak milyen metodust muszaj implementalnia, szoval mar forditas idoben el tudjuk donteni, hogy az adott objektum megfelelo-e, vagy sem. Annyi tortenik, hogy ahelyett, hogy kezzel kellene Assertalni es leellenorizni minden bejovo parametert, segitsegul tudjuk hivni a forditot, ezzel sok terhet veve le a vallunkrol. Igazabol csak annyi tortent, hogy a fuggveny torzseben levo, vagy automagikusan mukodo runtime checket ott hagytuk a fuggveny szignaturajaban, egy egysegbe zarva. Egy osztaly (figyeljuk meg, hogy Duck typingnal az osztaly nem szamit, csak az, hogy mit lehet rajta hivni) barmennyi interface-t implementalhat, tehat itt se szamit az objektum tipusa, csak az, hogy mit lehet rajta hivni.

---

Iras kozben elgondolkoztam, hogy ez nem-e tul trivialis, de engem most varatlanul ert a felismeres.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

JavaScriptben lehet még használni az instanceof vagy a typeof* operátort. Egyébként ha pl. egy függvény a paraméter, akkor kb. esélyed sincs megállapítani, hogy adott paraméterezéssel van-e értelme meghívni a függvényt (még a length propertyje sem mond semmit, a rendes paraméterezés megkerülhető pl. az arguments használatával).

Egyébként én még megemlíteném a type classokat, ami szintén typesafe, vagyis fordítási időben ellenőrizhető, ugyanakkor nem kell módosítani/wrappelni az eredeti típust, ha egy új type classhoz akarjuk rendelni.

*: szvsz erről 2016-ban már leszokhatna a JS community, az instanceof majdnem minden esetben kiválthatná a typeofot

Na de varj. Az instanceof/typeof az miben kulonbozik az objektum type checkjetol?

Duck typing-gal azt mondjuk, hogy futasidoben ez a fuggveny ilyen es olyan metodusokkal es/vagy propertykkel kell rendelkezzen. JavaScriptben gyakorlatilag ez az egyetlen lehetoseg arra, hogy egy fuggveny eldonthesse, hogy megfelelo objektumot kapta-e.

instanceof-fal is el lehet dönteni, hogy megfelelő objektumot kapott vagy sem. Tehát a duck typing mellett ez a másik lehetőség Javascriptben.

Az addig oke, engem az erdekel, hogy ez miben kulonbozik attol, hogy van egy

Vigyazat, pszeudo kod!

public class Foo extends Bar {}

public void doSomeStuff(Bar stuff) {}

doSomeStuff(new Foo());

Azon kivul, hogy az egyik forditasi idoben, a masik runtime dol el?

(extend helyere nyugodtan be lehet helyettesiteni implements-t is, az objektum API-ja ugyanaz marad)

Nem sokban, Java alatt sem az Interface az egyetlen megoldás annak eldöntésére, hogy megfelelő objektumot kapott-e.

(Hogy jön ez ide?)

Röviden összefoglalva:
A nyitó topikban azt írtad, hogy JavaScriptben gyakorlatilag ez az egyetlen lehetoseg arra, hogy egy fuggveny eldonthesse, hogy megfelelo objektumot kapta-e..
BaT fórum társunk írt egy másik lehetőséget is.
Ennyi csak a történet.

Az egyetlen lehetoseget a runtime check-re ertettem (azt hiszem), de jogos, koszi!

"szvsz erről 2016-ban már leszokhatna a JS community, az instanceof majdnem minden esetben kiválthatná a typeofot"

A typeof-nak van egy behozhatatlan elonye: tudnillik, rovidebb.
--
Blog | @hron84
Üzemeltető macik

A kettő között azért elég sok különbség van.

Az egyik, hogy mikor/hol dől el a felhasználhatóságra vonatkozó szándék. Javascriptben az objektumot használó (kliens) kód készítőjének a szándéka, hogy egy objektumot "kacsaként" használ, attól függetlenül, hogy az objektum fejlesztője szánta-e ilyen felhasználásra vagy sem. Javában viszont az objektum (pontosabban az ő osztálya) készítője jelezheti (pl. az interface-ekkel), hogy milyen felhasználás(ok)ra szánja az objektumot. Ha nem szánta "kacsának", akkor a kliens nem tudja "kacsaként" kezelni. Hiába "tudja" ugyanis a kliens kód, hogy az objektum interfésze egyébként megfelelne adott elváráshalmaznak (amiket interface-ekkel definiáltunk), magát az objektum típusát a JVM nem engedi olyan interface-re castolni (ClassCastExceptiont eredményez), ami nem volt definiálva az objektum osztályában implements után (beleértve az ősosztályokat is).

Nyilvánvalóan a Reflection API felhasználásával megoldható a tetszőleges metódusok hívogatása, de ez a megoldás ebben az esetben inkább hack. :)

Abban igazad van, hogy persze, egy Java-osztály bármennyi interface-et implementálhat, ezért lehetne olyat csinálni, hogy az osztály fejlesztője minden egyes metódushoz (szélsőségesebb esetben minden egyes overloadhoz) készít egy-egy interface-et is, aztán az osztály meg mindet implementálja. Ez viszont katasztrofális eredményt hozna kódkarbantarthatóság szempontjából, illetve így az egy felelősségi körhöz tartozó metódusok is külön-külön interface-ekben lesznek előírva. (Ha ennek elkerülése érdekében csinálunk összetettebb interface-eket is, amiket az osztály szintén implementál, akkor pedig már ott vagyunk, mint ha normál módon használnánk az interface-eket, csak akkor meg a triviális interface-ek feleslegesek. :) )

Illetve egy hiba: amikor azt írod, hogy "Duck typing-gal azt mondjuk, hogy futasidoben ez a fuggveny ilyen es olyan metodusokkal es/vagy propertykkel kell rendelkezzen", akkor ott szerintem arra gondolsz, hogy "Duck typing-gal azt mondjuk, hogy futasidoben ez az objektum ilyen es olyan metodusokkal es/vagy propertykkel kell rendelkezzen".

Tovább nehezíti a helyzetet, hogy javában nincsenek compound typeok, vagyis ha egy osztály csak a szegregált interface-eket valósítja meg, azokból egyszerre többtől nem tudsz függeni.

Egy helyen lehet ilyesmit használni: genericek esetén pl. T típusparaméter többszörös felső korlátjának össze lehet vadászni tetszőleges sok interface-t & operátorral, amelyeket egyszerre kell megvalósítania az objektumnak, és ezen interface-metódusokat nyugodtan lehet is használni mindenhol, ahol T típust feltételezünk.

Mondjuk használatban kevés ilyennel találkoztam. :)

Jogos, erről teljesen megfeledkeztem Akkor viszont vannak Javában compound typeok, csak kicsit bonyolultabb kifejezni őket.

Illetve a genericet azert kisse korulmenyesebb hasznalni is.
--
Blog | @hron84
Üzemeltető macik

Vitathatatlan, hogy nem a legegyszerűbb konstrukció az egyébként faék-egyszerűségű Javában, de ha egyszer normálisan megérti az ember, akkor annyira nem vészes. :)
Sokat segít, ha utánaolvas az ember, hogy mit trükközik a fordító a háttérben.

Nekem csak es kizarolag a szintaxissal vannak komoly problemaim. Szerintem nem szep a generic, meg ha hasznos is. Ha nem mulhatatlanul szukseges, probalom elkerulni.
--
Blog | @hron84
Üzemeltető macik