Miért rossz az @Annotáció?

Annotációk használatával rengeteg problémát veszünk a nyakunkba, amit a programozók nagy része nem is sejt.

Van egy nagyon jó előadás a témában, ahol az annotációk mellett az ORM, DIc, Exception-ök káros hatásai is többé kevésbé felmerülnek:
On @annotations - liberate yourselves from demons by Jarosław Ratajski

További hasznos tartalmak a témában:
The case against annotations (Adam Warski), slides
Java Annotations Are a Big Mistake (Yegor Bugayenko), on DZone, slides
Evil Annotations

Hozzászólások

Hehe, pár éve még az annotáció volt a megváltás :-). Nem néztem még meg - meg fogom talán - csak ennyit akartam elöljáróban írni, hogy folyton ez ismétlődik, hogy valaki nagyon okos bedob valamit, ami pár évig divat lesz, aztán pár év múlva egy másik okos bedobja hogy miért volt rossz és hirtelen az lesz a divat.

A bölcsek pedig Cobolban programoznak és fittyet hánynak ezekre a modern dolgokra :-)

Na, megnéztem, nagyon cool arc a mágusköpenyben! És igaza van. Áldom a napot, amikor nem ültem fel erre a @Inject bandwagonra! Én már előre megmondtam hogy marhaság!

Az általa felsoroltak közül egyedül a JPA-ba szerelmesedtem bele, és én is elvesztegettem vele jópár napot ahhoz képest, ha plain SQL-lel csináltam volna ugyanazt. De már az is régesrégen volt, talán igaz sem volt.

A JPA fos a natív Hibernate-hez képest :)
Sok mindenre jó a Hibernate, a 2nd level cache-el egészen szépen lehet objektum-orientáltan dolgozni vele, persze nem mindig érdemes használni: nem szabad mindent szögnek nézni.

Szerintem a Hibernate-tel a problémák azok, hogy kissé heavyweight, nem könnyű keverni más módszerekkel, illetve sok fejlesztő baszik elolvasni a dokumentációt aztán csodálkoznak hogy miért ilyen rohadt lassú, meg szivatják magukat a rosszul beállított cascade-al.

Unalmasak már ezek az "evangelisták", csak beletekertem mert annál többet nem érnek, de az utolsó linken ezen megakadt a szemem:

"This Annotation effectively reduces the reusability of the class significantly".

Ez gyakori mítosz hogy majd jól újra lehet használni a kódot máshol, akár más projecten, de ez a valóságban inkább úgy szokott kinézni, hogy a kicsit is bonyolultabb osztályok újrahasználásánál a boldog büszkeség fázisát követi a szembesülés a valósággal, hogy kicsit máshogy kéne működnie, de sebaj, megoldjuk, majd a túlbonyolított osztályban szentségelve bugvadászat, végül lemondóan revert, és copy paste patternnel átemelés, majd a szükséges átírás, és magyarázkodás felfelé, hogy miért alacsony a velocity.

Én kedvelem azokat az embereket, akik nem fogadják el feltétel nélkül a többség által jónak tartott dolgokat, hanem gondolkoznak és használják az eszüket. Még akkor is, ha hülyeségeket mondanak, de itt nem erről van szó, majdnem minden szava nagyon igaz.

de az utolsó linken ezen megakadt a szemem
Ha végignézted volna, akkor még vagy tucat dolgon megakadt volna a szemed. ;-)

hogy a kicsit is bonyolultabb osztályok újrahasználásánál
Eleve nem jó bonyolultabb osztályokat készíteni (SRP), ne azokat akard újrafelhasználni, hanem az 5-20 soros függvényeidet. Olyankor van a baj, ha azokat sem tudod (könnyen).

Na, ezek szerint nem vagyok egyedül :) Sőt, teljesen egyetértek a fickó minden szavával.

Valahogy mindig is irtóztam a Spring-től és az EJB világtól, dolgoztam velük eleget. A dependency injection egy szükséges rossz megoldás az OOP hiányosságaira, de az AOP szerintem kimondottan merénylet.

Amióta végre van lambda a Java-ban, azóta gyakorlatilag nincs szükség ezekre a heavyweight, @-ra építő frameworkökre. A spring ott tart már, hogy egy spring-boot alkalmazás lassabban bootol be, mint egy JavaEE Wildfly. És mindkettő lassú... A Ratpack-ot nem próbáltam, de a ktor szerver alkalmazásomnak nem kell 1 mp sem, hogy fogadja a kéréseket, fatjar-ból indítva.

Az pedig külön vicces, hogy az objektum-orientált java annotációi nem OOP módon lettek implementálva. A spring-ben is nem kevés huncutság van, hogy mégis legyen öröklődés. Bezzeg a C# attribútumai rendes, tisztességes classok, használják is behavior pattern-re rendesen (mondjuk kell is a rohadt sok sealed class miatt).

Amióta ismerem ezeket a DI-re építő keretrendszereket, azóta hangoztatom mindenhol, hogy ez metaprogramozás, és lehetne másképp is. A kollégáim egy része hülyének tart emiatt, csakhogy ők soha életükben nem írtak olyan alkalmazást, ami ne spring-re épített volna. Main függvényről is csak mástól hallottak...

Tiszta szerencse, hogy a Spring brigádban is van, aki tisztában van ezzel, a @ nélküli konstruktor injection kimondottan enyhíti a fájdalmam :)

Na, mondjuk DI... Mostanság egyre többször vitatkozok össze kollégákkal, hogy kezdenek mindent szögnek nézni a DI-al. Pont belefutottunk egy olyanba, hogy egy scoped injection elleakel egy resourcet, aminek poolozva kellene működnie, viszont mégsem dobja vissza (valószínűleg egy Dispose hívás marad el).

Igazából esélyes, hogy azért nem, mert kellett volna egy IDisposable még egy-két helyre, csak akkor valószínűleg kb. mindenre szétfolyna, ami meg máshogy lenne agyfasz, ahelyett, hogy használnák ott egy using-ban egy factorybol legyártva helyben csak addig, ameddig kellene és akkor visszakerülne a poolba. Két másik serviceben így lett implementálva, ott nincs is vele baj...

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Mostanság egyre többször vitatkozok össze kollégákkal, hogy kezdenek mindent szögnek nézni a DI-al.

Sajnos ez a DIc velejárója. Ha egy osztálynak szüksége van DIc-vel megadott szolgáltatásra, akkor azt az osztályt is DIc-vel kell létrehozni, az azt használót is, ... Innentől nagyjából mindenre kénytelen vagy azt használni. Ha meg ügyesen kikerülöd, akkor meg jönnek a NPE hibák, mert valaemlyik DIc-s függősége nem oldódott fel, vagy nem tranzakcióban fog futni, mert nem DIc-n keresztül hívódott, ...

Ha egyszer elkezded a DIc-t használni, akkor már eléggé behatárolódsz ilyen szempontból.

Attól hogy a springben vannak véleményes megoldások, miért szar az annotacio? Azt is mondhatnám wtf did I just read.

Bevallom, a videot nem neztem meg, csak a cikkeket olvastam el, amiket linkeltel.
Viszont nekem az jon le azokbol, hogy mivel a cikkiro(k) latta(k) kozelrol a springet, valamint egyeb libek eroszakolt annotaciohasznalatat, ezert az annotacio, mint nyelvi elem, "rossz", es nem szabadna hasznalni.
Ezzel szemben az en velemenyem az, hogy az annotaciok hasznalatanak megvan a helye es modja, es baromi sok problemara jo megoldast nyujtanak.

Azzal az allitassal is vitatkoznek, hogy az OOP programozo legjobb fegyvere object letrehozasra a new operator. Egyreszt factoryk builderek is vannak a vilagon. Masreszt a program mukodesenek nem resze az objektumgrafom kvazi bootstrapje; a business objektumaimat termeszetesen szivesen letrehozom new-val. Tovabb az sem resze a programom speckojanak, hogy technikailag hogyan hozok letre adatbaziskapcsolatot (poolozom vagy sem?), melyik servicem melyik tavoli servicet proxyzza, milyen mediatype-ot general az endpointom, stb. Ez mind nem resze az uzleti specifikaciojanak. Akkor miert is kellene kitalalnom es implementalnom mindezt? Miert kell kodot irnom hozza? Meghozza nem is keveset. Inkabb annotalok ha valaszthatok. Es ha autokereskedesnek irok szoftvert vagy jegyertekesitonek, akkor miben is fog kulonbozni a 2 esetben a fenti technikai resz? Kb semmiben? Az en implementaciom garantaltan bugosabb lesz, mint amit a springes fiuk kitalaltak. Vagy a JAX-RS kontenerimplementaciok.

Szoval igen, full control on object creation, no magic behind the scenes, composability, meg kiskutya pocse, de ennek mind ara van. Es szerintem nem kisebb teher a sajat "okos" kododat megerteni egy juniornak vagy neked karbantartani, mint a springgel szopni mondjuk adott esetben. Vagy annotaciokkal es EE kontenerekkel.
Ezek mind csak egy-egy uj absztrakcios szint, amik valami miatt elkeszultek. Amikor okos emberek 100adszorra csinaltak meg ugyanazt, csak a business logika volt mas, akkor irtak ra egy frameworkot. Amit egyreszt tudni kell hasznalni, masreszt pedig akkor kell hasznalni, ha illeszkedik a te problemadra.

Ezzel szemben az en velemenyem az, hogy az annotaciok hasznalatanak megvan a helye es modja, es baromi sok problemara jo megoldast nyujtanak.

Így van, az annotációk nagyon jók, ha gyorsan kell valamit készíteni, pl. prototípust, vagy csak egyszer megírni és soha többé nem hozzányúlni.

Es szerintem nem kisebb teher a sajat "okos" kododat megerteni egy juniornak vagy neked karbantartani, mint a springgel szopni mondjuk adott esetben.

Éppen ezt vitatják a cikkek, a videó, illetve én is.
Pont ellenkezőleg, az annotáció hozza be az "okos" kódokat. Ide rakok egy annotációt és akkor ez a rész a (varázslat miatt) tranzakciósan fut le, vagy létrejön az objektum (szintén varázslattal) a megadott függőségeivel, de ha mégsem, akkor meg vagyok lőve, mert nem tudom ellenőrizni, hogy mi a gond, nem tudok hova debug pointert rakni, mert valahol a varázslatban történik a hiba.

Amikor okos emberek 100adszorra csinaltak meg ugyanazt,

Annotáció nélkül se kell sokkal több kódot írni, illetve van rengeteg lib, amit (okos emberek 100adszorra csinaltak meg ;-) lehet használni.

Évek óta dolgozok Springgel, nem kell bemutatni :) Ilyen SQLGrammarException-re le kell vinni hibernate loglevelt, és hamar kibukik, hogy mi baja. Ne érts félre, régen szentségeltem sokat ilyesmikkel én is, és tök jó lenne, ha egyszerre lehetne egyszerűbben hibát keresni és gyorsan-átláthatóan programozni, úgy, hogy a framework mindent a segged alá tolt, de amíg vagy-vagy, addig utóbbira szavazok.

Most pont van ilyen problémánk, aminél nem tudjuk hová tenni a töréspontot.
Elővezetem, hátha tudtok ebben segíteni.

JavaEE projekt, Glassfish4. Miután frissen deploy-oljuk az alkalmazást, azután van egy bean, amit nem lát, így azok a funkciók, amik azt használnák nem működnek. Ha leállítjuk és elindítjuk az alkalmazást, akkor onnantól már látja az adott beant. Friss deploy után megint nem látja.
Szóval elég jól ismételhető, mégse nagyon tudjuk, hogyan találhatnánk meg a hiba okát.

Mi régi WebLogic-nál (10) találkoztunk ilyesmivel. A pontos okára már nem emlékszem, de valami olyasmi volt az ok, egy lecserélendő bean "beragadt" az undeploy során, valami db resource-ot megfogott és a konténer nem rúgta erélyesen seggbe. Így aztán nem töltötte be az újat, a régi meg már nem ment.
Amúgy is "csak a torka véres" alkalmazás volt, úgyhogy mind devel, mind éles környezetben a deploy előtt explicit undeploy és cache törlés (sőt komplett workdir törlés) volt a bevett módszer. Sőt, azt hiszem a mai napig.

Amit Bugayenko mutat, az akkor is működik, ha a POJO tartalmaz egy hivatkozást egy másik POJO-ra?

Mi van az SQL befecskendezéssel? Innentől mindig az osztály feladata, hogy kivédje?

Ja, bocs, ezt néztem:
https://www.yegor256.com/2014/12/01/orm-offensive-anti-pattern.html


final class PgPosts implements Posts {
  private final Source dbase;
  public PgPosts(DataSource data) {
    this.dbase = data;
  }
  public Iterable<Post> iterate() {
    return new JdbcSession(this.dbase)
      .sql("SELECT id FROM post")
      .select(
        new ListOutcome<Post>(
          new ListOutcome.Mapping<Post>() {
            @Override
            public Post map(final ResultSet rset) {
              return new PgPost(
                this.dbase,
                rset.getInt(1)
              );
            }
          }
        )
      );
  }
  public Post add(Date date, String title) {
    return new PgPost(
      this.dbase,
      new JdbcSession(this.dbase)
        .sql("INSERT INTO post (date, title) VALUES (?, ?)")
        .set(new Utc(date))
        .set(title)
        .insert(new SingleOutcome<Integer>(Integer.class))
    );
  }
}

Ubazzmeg, ezt nem is néztem, súlyos. OOP védelméről beszél, közben meg..

"I already mentioned in one of my previous articles that a good object is always an abstraction of a real-life entity. Here is how this principle works in practice. We have two entities: database table and table row. That’s why we’ll make two classes; Posts will represent the table, and Post will represent the row."

Véletlen sem a poszt a real word entity, hanem az azt reprezentáló tábla. Értem.

"When some object is working with a list of posts, it needs to deal with an instance of SessionFactory. How can we mock this dependency?"

OOP, és akkor ahol Post-tal dolgozik, ott sessionfactory-t hívogat. Nehogy írjon egy Repository osztályt, mert azt még egyszerűen lehetne mockolni, a Repository-ra meg integrációs tesztet írni.

Spring data-val két perc alatt átmigrálok egy ilyen osztályt pl postgresről mongora, ő meg belerakja az sql-t az osztályba, mert az úgy jó. Új entitásra meg megint ír két km kódot. Brilliáns.

"Besides getters and setters, objects have no other methods."

Nem. az ORM akadályozza meg a programozót, hogy tegyen bele üzleti logikát.

"They don’t even know which database they came from."

Hála a magasságosnak.