Problémák a függőséggel

Kezdő programozóként még nem nagyon törődtünk a függőségekkel. Nem akartuk lecserélni a függőségeket másra, nem akartuk unit tesztelni a kódunkat, nem hallottunk még a pure-impure dolgokról, és nem tudtuk, hogy a statikus függvények rosszak, valamint azt, hogy a strong/tight coupling (erős, szoros függőség) az nagyon rossz.

Továbbiakat itt találod.

Hozzászólások

Nekem erről a Clean Code függvényekről szóló fejezete jutott eszembe: "Small!", "Do One Thing", "One Level of Abstraction per Function", "Use Descreptive Names". Szerencsétlen runLogic függvény még egy rendes nevet se kaphatott (code smell :)), annyi mindent csinál. Még az utolsó megoldás main függvénye is túl sokat csinál szerintem, bár értem hogy így jobban szemlélteti amit ki akartál fejezni.

Ami még eszembe jutott, hogy az OOP-s megoldás olyan, mintha el akarná rejteni a ronda kódot. Kívülről csak a Readeren és a Writeren keresztül vizsgálható, a többi a Greeting privát dolga. Ez szerintem nem absztrakció, legfeljebb az absztrakció illúziója. Sajnos pl. Java programokban rendszeresen találkozom ilyen megoldásokkal. Tipikus ismertető jele amikor egy program rengeteg interface-t definiál de a legtöbbnek egyetlen implementációja van, rosszabb esetben Impl postfixszel ellátva. Általában hosszú hívási láncokat kell végigkövetni mire megtalálod a lényeget, a köztes elemek leginkább csak delegálnak a következő szintre és a végén lesz egy jó bonyolult függvény ami megcsinálja az érdemi munkát.

honnan tudhatnánk, hogy később nem lesz máshol újrahasznosítva, vagy itt cserélve.

A cseréléssel nincs gond, mint ahogy írtam is a blogban, egyedül azzal, ha futásidőben cserélnéd, vagy több implementációt használnál.

Ez utóbbi esetben pedig, majd akkor kell átmenni a hídon, ha odaértünk. Ahogy előre nem jó mikró optimalizálni, ugyanúgy nem jó mindent cserélhetőre, újrahasznosíthatóra csinálni.

--
Ickenham template engine

Szerencsétlen runLogic függvény még egy rendes nevet se kaphatott (code smell :)), annyi mindent csinál.

A Clean Code-dal valóban vannak kisebb problémák, de nem azért nem kapott rendes nevet, mert sok mindent csinál. Ezt a sok mindent csinált, ezt sokan félre is értik. Nem arról van szó, hogy csak egy dolgot csinál, még arról sem, hogy csak egy fajta dolgot csinál, hanem arról, hogy egy absztrakciós szinten van az egész, egyik része sem megy mélyebbre, mint a másik. Ennek pedig nagyjából megfelel, esetleg az üzenet formázását lehetne még kirakni egy függvénybe.
/Lecseréltem a runLogic-ot greet-re/.

Még az utolsó megoldás main függvénye is túl sokat csinál szerintem, bár értem hogy így jobban szemlélteti amit ki akartál fejezni.

Pontosan!

Tipikus ismertető jele amikor egy program rengeteg interface-t definiál de a legtöbbnek egyetlen implementációja van ...

Innentől egyetértek a végéig, pont erről szól a cikk. Ezek azok az esetek, amikor azért van interface-elve (laza kapcsolat), mert hátha majd valamikor lecserélésre kerül. Pedig a lecserélés miatt pont nem kellene a DI, a DI akkor kell, ha egyszerre több implementációját szeretnénk használni.

--
Ickenham template engine

Amit sohasem fogok megérteni: miért kell az interface, ha van abstract class? Mit ad az interface, amit egy abstract class nem? (bocs, OOP csak mint hobbi olvasmány került elém, leragadtam a procedurális programoknál)

Peldaul, ha van egy Vehicle class, akkor annek kell lennie egy startEngine()-nek valahol.

VehicleInterface -> AbstractGroundVehicle -> Car, Bus, etc
VehicleInterface -> AbstractAirPlane -> Cropduster, B17, etc

AbstractGroundVehicle startEngine methodusa tartalmaz oninditot, de az AbstractAirPlane egy foldi propeller forgatot

Alapvetoen orulok a blogbejegyzesnek es pont ma adott otletet, hogy hogy szervezzem a kodot, de a drupalnak, es altalanossagban a hiearchikus kommenteknekbaz a baromi nagy elonye, hogy szinte semmi sem offtopic, ami valami asszociacio menten kapcsolodik a temahoz.

Ha osszecsukhatoak lennenek a komment szalak, szerintem teged se zavarna. Na, ez ugyanaz, csak a kulonbozo "altopicok" mindig latszanak.

Igazság szerint HZ-nek köszönhető, hogy ráakadtam erre a blogra. :)

Szerintem hasznos amit írtál, valóban nem a loose coupling a lényeg, hanem hogy "pure" / "functional" legyen a kód, azzal lehet a tesztelhetőségen jelentősen javítani.

Viszont ez nem jelenti azt, hogy a DI minden esetben rossz. Pl. ha van egy algoritmusod, amit meg kell hívnod sokszor, van mondjuk 10 állandó és 3 esetenként változó paraméter. Akkor ezt nem jobb úgy megcsinálni, hogy van egy osztály, amibe mondjuk DI-vel drótozod be az állandó paramétereket? Én most Dagger2-t használok ahol lehet (és van a DI-nek értelme).

Nyilván az sem jó, ha mindenből interfészt csinálsz. Pl. van ahol az adatstruktúrákhoz is interfészeket gyártanak, ahol a konkrét osztályok teljesen jól működnének. Viszont ha algoritmusokról van szó, akkor sokkal több értelmét látom már akkor interfészként deklarálni, ha még csak 1 konkrét megvalósítás létezik, mert sokkal valószínűbb, hogy a későbbiek során lesz szükség alternatívákra mint a konkét adatstruktúráknál.

Ahol még hasznosnak tartom az interfészek használatát:
- Input / Output absztrakciója
- Platformspecifikus API-k eltakarása a kód többi része elől.

PS: Fentebb az interfész az alatt az explicit deklarált Java interface-eket értem, természetesen minden osztály külső felhasználó számára elérhető metódusai és mezői interfészt alkotnak, amit ugyanúgy jól meg kell tervezni és dokumentálni Javadoc-kal, különös tekintettel az erőforráskezelésre.

Köszi az észrevételeket!

Viszont ez nem jelenti azt, hogy a DI minden esetben rossz.

Egyáltalán nem rossz minden esetben, csak a legtöbb esetben, amire használva van.
A Dependency Injection-t kétféle módon is lehet használni. Gondoltam is rá, hogy kifejtem a blogban, de valamiért nem tettem.
1. Amikor külön válik a wiring, vagyis teljesen más helyen történik a létrehozás a függőség megadásával, és teljesen más helyen a használata.
2. Amikor a létrehozás után egyből használva van. Ez szinte ugyanaz, mint a tight coupling.

Az elsőre nagyon jó példa az IoC-k használata, ahol fogalmunk sincs mikor, hol és mivel történik a függőség feloldása.
A másodikra nagyon jó példa a filter, map, ... használata, pl.:


salaries.filter(SalaryFilters::isHigh)

Ahol még hasznosnak tartom az interfészek használatát

Tight couplinggal is meg tudod ezeket tenni, a blogomban is volt erre példa, a read és write eltakarta a platform specifikus dolgokat, ráadásul I/O.

Összefoglalva:
- Nem rossz a DI használata, sok esetben kifejezetten nagyon jó (pl. filter), a túlzott, általános használata ami nagyon rossz.
- Nem rossz a tight coupling sem, és nem is az egyedüli üdvözítő mód. Itt figyelni kell, ha jól tesztelhető kódot akarunk írni, akkor sok esetben át kell egy kicsit terveznünk a kódot, ami többlet munka, de nagyon hasznos a végeredmény szempontjából.

--
Ickenham template engine