ugrás a tartalomhoz

Tiszta kód

Hidvégi Gábor · Szep. 15. (P), 12.25
janoszen egy másik fórumszálban linkelte egy OOP példakódját egy kezdő kérdésére válaszolva, ehhez lennének kérdéseim és hozzáfűznivalóm, ezért kiemeltem egy külön témába.

A programból le lehet vonni azt a következtetést, hogy OOP-t ott lehet érdemes használni, ahol egy bizonyos funkciót legalább kétszer megvalósítunk, mert ad egy szabványos interface-t, amivel tudunk kommunikálni.

Viszont itt jön a kérdés, hogy hol találkozunk ilyen bonyolultságú projekttel? Hol van szükség arra, hogy ugyanolyan típusú adatokat (mondjuk blogbejegyzés) két vagy több különböző adatforrásból halásszunk össze?

Hol van szükség arra, hogy akár többféle router is lehet a rendszerben? Ha viszont csak egyet használ a projekt (márpedig a fenti blogmotorban csak egy van), akkor miért van általánosan megvalósítva? Hisz így egyrészt van egy minimális overhead, másrészt plusz munkával jár. De ki tudja, hogy mit hoz a jövő? Mi lesz, ha a mostani router interface nem megfelelő? Akkor lehet refaktorálni, azaz fölösleges melót tett bele az, aki a jelenlegit írta.

Felmerülhet az is, hogy miért keverednek a php és a sablon fájlok? Mi közük van egymáshoz? De ez apróság.

Kérdés, hogy tényleg olyan kivételes helyzet, ha olyan konfigurációs beállítást kérdezünk le, ami nem létezik? Ettől a program futásának meg kell állnia?

Kérdés, hogy miért van több kivétel definiálva, amikor nincsenek kifejtve, azaz a kódjuk megegyezik?

Kérdés, hogy a BlogPostInteractor miért példányosítja a kétféle lekérdezési módot? Mi van, ha egy adott oldalon csak az utolsó n darab bejegyzésre van szükségünk, de az egyes posztokra nem? Ez is egy apró overhead.

Kérdés, hogy ha a kivételek típusa (Exception) megjelenik a fájlnévben, akkor az interface-eké miért nem?

Kérdés, hogy kezdőknek ez alapján kéne elkezdenie programozni?

---

Értem én, hogy példakód, de azért ez egy kezdő számára ijesztő lehet. Végeredményben nem csinál mást, mint egy vagy több blogposztot lekérdez, a nettó kód ebből legfeljebb egy-két kilobájt, ezzel szemben itt a szerveroldalon az src könyvtár mérete negyvennégy kilobájt. Ez azt jelenti, hogy két kilobájt kódhoz negyven kilobájtnyi bloat jár OOP alatt? Nyilván ez erős túlzás, de mit is kapunk cserébe? Szüksége van erre egy kezdőnek, de akár egy átlagos weboldalnak?

Mi nem blogmotort gyártunk, hanem vállalatirányítási rendszert, számlázással, pénzüggyel, meg hasonló dolgokkal. De nálunk, ha bekerül valami az adatbázisba, az az adat "készen" van, amikor legközelebb szükség van rá, ritka kivételtől eltekintve nem kell formázgatni, hanem lekérdezés után egy az egyben megy ki a kliensre. De akkor minek tennénk bele egy objektumba? Miért kéne minden adattaghoz ezek után getter meg setter?

Vagy például a hibakezelést úgy valósítottuk meg, hogy hibakód plusz hibaüzenet, amibe opcionálisan be lehet helyettesíteni az értékeket. Megírtuk egyszer, nem kell minden típusú hibához új php fájlt létrehozni.

Vagy mondjuk kezelünk többféle kimenetet (json, pdf, fájl stb.), de elintéztük egy egyszerű switch-csel, nincs ezerfelé szétszabdalva, egy helyen ott van minden, tökéletesen működik.

Szóval ez a blogmotor így szerintem nem clean code, hanem tiszta bloat, az ágyúval a vérebre tipikus esete. Nem véletlenül kérdeztem évekkel ezelőtt, hogy hol van az a határ, ahol érdemes OOP-t használni? Nálunk egy helyen lehetne: a biztonsági mentésben lehet választani, hogy ftp-vel vagy scp-vel működjön – minden más funkcionalitás csak egyszer szerepel a kódban, nincsenek ismétlések.
 
1

janoszen

Hidvégi Gábor · Szep. 15. (P), 13.03
János, fontosnak tartom megjegyezni, hogy bár utóbbi időben többször is megszólítottalak, de ezekkel nem bántani akarlak, csak egyszerűen te írsz vagy publikálsz, és hát, ugye, aki sarokra áll, ne csodálkozzon, ha leszólítják. Az általam ismert fejlesztők közül téged tartalak a legtöbbre: a refaktor.hu és egyéb projektjeid, írásaid alapján az az ember vagy, aki a legtöbbet tette a magyar programozó és üzemeltető közösségért.
2

+1

Pepita · Szep. 15. (P), 13.20
Előbb olvastam ezt a commentet, mint a témát, ez önmagában egy big like.
12

Nem

janoszen · Szep. 15. (P), 14.01
Soha nem éreztem úgy, hogy ez bármilyen szinten probléma lenne, a kérdéseid rendszeresen izgalmas és érdekes témákat feszegetnek. Szeretném azt mondani, hogy tudom mindre a választ, de az igazság az, hogy a sokszor az én meglátásaim is csak a saját tapasztalataimon alapulnak és sok kérdésre nincs is abszolut válasz. Sokszor nagyon projekt- és megbízófüggő, hogy mi működik jól.

Fiatal a szakmánk nagyon, nem néz olyan hosszú múltra vissza mint mondjuk az építészet vagy a könyvelés, szóval nagyon hasznos az ha minél több nézőpontot megvitatunk.
3

Sokszor

janoszen · Szep. 15. (P), 13.25
Azt remélhetőleg nem kell magyarázni, hogy ez egy erősen lebutított példa. A blog egy olyan dolog amit szinte mindenki el tud képzelni, mindenki látott már és talán aki kicsit is kódolt, legalább nagy részben le tudja írni a működését. A hangsúly itt a kiterjeszthetőségen van, nem a minimalisztikus kódon.

Amire a repóhoz kapcsolódó cikk rá kívánt világítani az az, hogy az alkalmazásod különböző rétegei között érdemes egy fajta leválasztást megteremteni azért, hogy le lehessen cserélni a mögöttes működést. Hogy ezt interfacekkel és objektumokkal teszed meg, vagy header file-okkal és structokkal, az ebből a szempontból halálosan mindegy.

Számomra (személyes preferencia) ebből a szempontból sokat segít ha egy erősen és szigorúan tipusos nyelvben fejlesztek, mert már az interfacek összedobálásánál kiderül, hogy hoppá, azt úgy ott nem lehet. (Ezért szeretném, ha végre lenne normális IDE a Hacklanghez és ezért gyűlölöm teljes szívből a PHP tömbkezelését.)

Ami azt a kérdést illeti hogy mennyire gyakran fordul elő a modulok cseréje: az én életemben gyakrabban mint gondoltam volna. Régebben sokszor inkább addig tekergettem a kódot amíg a "régi" modulba belehegesztettem a kívánt plusz funkcionalitást. Ennek aztán az volt a következménye, hogy egy pár ilyen módosítás után szembe nézett egy hétfejű sárkány. Amióta absztrakciós réteget teszek mindenhová egyrészt jobban végig gondolom hogy milyen absztrakciónak van értelme, másrészt sokkal könnyebb egy modult inkább öröklődés útján kiterjeszteni vagy komplett lecserélni mint módosítani. Ráadásul egy ilyen absztrakció, ha normálisan van megcsinálva, gyakorlatilag semmibe nem kerül teljesítmény szintjén.

Itt visszakanyarodnék a blogmotor kérdésére: nagyon kevés olyan megrendelőt láttam életemben, aki a projekt elején elmondta volna hogy mit kér, és az abból született leírás pontosal lefedte volna a későbbi funkcionalitást.

Ha pl. egy listázó oldalról van szó, akkor simán előfordulhat az, hogy a megrendelő a projekt 80%-ánál még azt kéri, hogy mi lenne ha ott megjelenítenénk a kommentek számát, vagy ajánlott cikkeket, vagy mittudomén. Persze, kérünk tőle több pénzt, több időt, de ha emiatt a kódbázis nagy részéhez hozzá kell nyúlni, akkor az sokkal többe kerül, mintha a kódbázis darabjai kiterjeszthetőre vannak megcsinálva.

És legyünk őszinték: a megrendelő mindig kér még valamit. Én meg olyannal nem találkoztam, akár műszaki akár nem műszaki, aki ne kért volna egy számára látszólag triviális módosítást, ami aztán egy full refaktorba eszkalál ha nincs kellően szegmentálva a kód.

Egébként már össze akartam dobni egy real world projektet megnézésre erről, csak egyszerűen olyan szinten el vagyok úszva, hogy az valami elképesztő.
17

Kiterjeszthetőség

Hidvégi Gábor · Szep. 15. (P), 15.15
Én sosem tudom, mit fog a főnököm/előfizetőink kérni, ezért sosem írok alapból kiterjeszthető kódot, hanem a szükséges minimumra törekszem.

Természetesen nálunk is elő szokott fordulni, hogy valamit le kell cserélni, de ezt általában akkor szoktam megtenni, ha
1, van egy működő kód,
2, kérnek egy újítást, azt leprogramozom,
3, ha a kettő összevonható/van bennük közös, akkor azt kiemelem függvénybe

Számomra (személyes preferencia) ebből a szempontból sokat segít ha egy erősen és szigorúan tipusos nyelvben fejlesztek, mert már az interfacek összedobálásánál kiderül, hogy hoppá, azt úgy ott nem lehet. (Ezért szeretném, ha végre lenne normális IDE a Hacklanghez és ezért gyűlölöm teljes szívből a PHP tömbkezelését.)
Szerintem pedig csodálatos a dinamikus típusosság és a PHP-t (és a JS-t) pont a rugalmas tömbjei miatt szeretem. Ez a kettő dolog nagyon gyors fejlesztést tesz lehetővé.

Tesztekkel nagyon szépen lehet ellenőrizni, hogy a függvény vagy metódus paramétere az adott típusú-e. Ha a type hintnek nem felel meg, akkor php7 alatt kapsz egy kivételt, ha nincs type hint, akkor meg a teszt jelez, de a kódban valahol ígyis-úgyis le kell kezelni az esetet. Én ezért a type hintnek nem sok értelmét látom.
23

Ellenőrzöd

janoszen · Szep. 15. (P), 17.28
Ez esetben, ha jól feltételezem, Te minden modul szakaszhatáránál teljes tipusellenőrzést csinálsz, beleértve a tömbök elemeire is, és a tesztjeid is lefedik azokat az eseteket amikor egy másik modul hibás működése miatt nem azt kapod amit vártál?

Mert ha ezt nem teszed, akkor gyakorlatilag egyetlen védelmi vonalad van, és ha ebben van egy bug ami váratlan tipusú adatot hoz létre, az tovább gyűrűzhet más modulokba és a végén akár biztonsági problémát is okozhat.
26

Ellenőrzés

Hidvégi Gábor · Szep. 15. (P), 19.19
Kétféle adat van:

1, ami kívülről jön,
2, ami belülről jön.

Ami kívülről jön, azt leellenőrizzük, és úgy tesszük a memóriába/adatbázisba.

Ami belülről jön, az egy ellenőrzött adat (hisz úgy került a memóriába vagy az adatbázisba); ha létezik, akkor helyes, ha nem létezik (pl. opcionális beállítás), akkor hibát dobunk vagy alapértelmezett értéket használunk.
29

Ellenőrzés

janoszen · Szep. 15. (P), 19.47
Vagyis azt ellenőrzöd csak ami az alkalmazáson kívülről jön? A security világban azt szokás mondani, hogy rétegelt védelemre van szükséged. Sosem tudhatod, hogy mikor nő az elsődleges védelembe egy bug és olyankor jól jön ha megfogja valami.
42

Nem

Hidvégi Gábor · Szep. 18. (H), 14.55
Minden adatot előállító függvény az adatok átadása előtt ellenőriz. A feldolgozó függvény pedig csak azt nézi, hogy a kapott adatok benne vannak-e az őt érdeklő tartományban.
36

tehát feltételezitek, hogy

szabo.b.gabor · Szep. 16. (Szo), 07.26
tehát feltételezitek, hogy akik a kódot írják sosem hibáznak.
39

Fő a magabiztosság. :-)

inf3rno · Szep. 16. (Szo), 08.25
Fő a magabiztosság. :-)
4

Válasz

janoszen · Szep. 15. (P), 13.28
Külön válaszolok a kérdésekre, hogy önállóan meg lehessen vitatni:

Kérdés, hogy tényleg olyan kivételes helyzet, ha olyan konfigurációs beállítást kérdezünk le, ami nem létezik? Ettől a program futásának meg kell állnia?


Itt két fajta iskola van. Szerintem, az a helyes, hogy vagy átadunk egy default értéket, vagy pedig lekezeljük azt hogy nem kaptuk meg a konfigurációs opciót.

Ha nem kezeljük le, akkor azzal a feltételezéssel élnék, hogy a program nincs felkészítve a nem létezésére, tehát a program futása álljon le egy értelmezhető hibaüzenettel ami alapján könnyű javítani.
5

Kivételek

janoszen · Szep. 15. (P), 13.29
Kérdés, hogy miért van több kivétel definiálva, amikor nincsenek kifejtve, azaz a kódjuk megegyezik?


A kivételek nevére lehet catch-elni. Ha pl. egy SQL absztrakciós réteget írok, akkor simán el akarhatom kapni mondjuk a duplicate key exceptiont, noha semmiben nem különbözik bármelyik másik SQL exceptiontől.
18

Hibakezelés

Hidvégi Gábor · Szep. 15. (P), 15.24
A kérdés, hogy miért érdemes ennyire megkülönböztetni a hibákat? Ha nálunk például SQL hiba van, akkor megszakad a futás, mert olyan disznóság történt, amire a programot nem készítettük fel, duplikált kulcs esetében mondjuk elromlott a szekvencia (postgresql-t használunk). Ilyenkor a felhasználónak kiírjuk, hogy rendszerhiba történt, magunknak pedig naplózzuk az esetet.

Ugyanígy megszakad a futás, ha felhasználói hiba történt, például hiányzik valamilyen fontos adat a számláról.

Szóval mi mindent el tudunk intézni egy hibakód és hibaüzenet párossal, ha baj van, ha pedig nem olyan nagy, azt a kód lekezeli.

Ezért nem értem a nevesített kivételeket.
21

Pl

janoszen · Szep. 15. (P), 15.58
Mondok egy példát: adott egy alkalmazás, ami egy JavaScript SPA-val beszélget. Ebben a szerver oldalon minden exception ami az API által lekezelhető, kiterjeszti az adott alkalmazás Exception osztályát. Vagyis akármelyik réteg dob UserNotFoundException-t, a frontend egy szépen formázott API választ fog visszakapni úgy ahogy azt kell.

Igen ám, de van ahol a UserNotFoundExceptiont el kell kapni és másképp kell kezelni, pl. mappelni kell egy AuthenticationFailedException-re. Itt két megoldásod van:

a) nevesíted az Exceptionöket
b) elkapod az összes exceptiont és reménykedsz, hogy a Junior kolléga az else ágban nem felejti el újra dobni az exceptiont az esetben ha olyan hibakóddal találkozik amire neki nincs szüksége.

Vagyis az a) opció:
User user;
try {
  user = userBusinessLogic.getById(id)
} catch (UserNotFoundException e) {
  throw new AuthenticationFailedException(e);
}
A b) opció pedig:
User user;
try {
  user = userBusinessLogic.getById(id)
} catch (ErrorCodeException e) {
  switch (e.getCode()) {
    case E_USER_NOT_FOUND:
      throw new ErrorCodeException(ErrorCodeException.ErrorCodes.E_AUTHENTICATION_FAILED, e);
    default:
      throw e;
  }
}
Döntsd el hogy melyiket könnyebb írni, olvasni és nem elb*ni. :)

Ez különösen akkor jön szembe, ha kicsit bonyolultabb a jogosultság-kezelésed, ez esetben ugyanis sokszor kell mappelni a belső hibaüzeneteket publikus hibaüzenetekre hogy ne legyen info-leak az API-ból.

A probléma tovább hatványozódik ha több adatbázis motort kell támogatnod, vagy ne adj Isten olyan adatbázist ami nem relacionális és nincsenek ilyesmire hibakódja, pl. LDAP. És igen, én eleve több DB motort használok, fejlesztőkörnyezetben pl. HSQLDB-t hogy ne kelljen külön SQL szervert indítanom és egy repülőn is tudjak fejleszteni anélkül, hogy az aksikimélő üzemmódot kikapcsolnám. Baromi kényelmes az, hogy az IDE-ben rányomok a "Run" gombra és nem csak az alkalmazás indul el, hanem az embedded DB is amit szépen be is inicializál tesztadatokkal.
30

Az exception bár nem

BlaZe · Szep. 15. (P), 21.46
Az exception bár nem vezérlési szerkezet, de a lényege valójában az, hogy
a) a különböző típusú hibákat a megfelelő absztrakciós szinten tudd lekezelni (vagyis akár több stack frame-mel feljebb)
b) egy hibát ne tudj ignorálni, azzal valahol valamit kelljen kezdeni
c) magával hozza a hiba jelentkezésekori stack tracet

Ennyivel tud kb többet, mint a hibakód. Hogy miért van neve? Amiért a függvényeknek is. Milyen jelentése van 3 szinttel mélyebben annak, hogy a hibakód 4 volt? Mi van, ha több metódus is mondhatja azt, hogy a hibakód 4, de eltérő jelentéssel?
43

Egyedi

Hidvégi Gábor · Szep. 18. (H), 14.57
A hibakódok lényege, hogy egyediek.
48

Hogy garantálsz egyediségét

BlaZe · Szep. 18. (H), 16.44
Hogy garantálsz egyediségét több vendor esetén? És mi van a többivel amit írtam?
60

Korábban használtunk MySQL-t,

Hidvégi Gábor · Szep. 19. (K), 09.27
Korábban használtunk MySQL-t, aztán áttértünk PostgreSQL-re, a köztes időszakban wrappert írtunk az SQL hibaüzenetekhez. Így minden hibakódunk garantáltan egyedi.

A hibakezelésünk, így jobban belegondolva, hasonló a kivételkezeléshez, mert teljesíti azokat, amit felsorolsz. A hibákat bármelyik felsőbb szinten tudjuk kezelni, bár a rendszer jellegéből adódóan erre nincs szükség.
64

Továbbra se látom ez hogy

BlaZe · Szep. 19. (K), 16.33
Továbbra se látom ez hogy segít, ha sok párhuzamos vendor van. Mondjuk nem csak SQL, hanem rest service-ek, nosql, domain specifikus libek stb. Mind fölé húzol egy közös wrappert? Ez mindennek tűnik, csak egyszerűbbnek nem.

És akkor hiba kezelés kikényszerítése, stack trace, context infók csatolása meg mindig sehol nincs...
66

Egyszerű

Hidvégi Gábor · Szep. 20. (Sze), 09.13
Persze, közös wrapper, hisz egyféle hibakezelésre van szükség az egész rendszerben. Stack trace-t küldünk, a rendszer a jogosultsági szintek alapján más-más hibaüzenetet ad (ha fejlesztőként vagyok benn, akkor minden szükséges információt kiír, egyébként pedig a szükséges minimumot), naplózza, hogy mi történik.
68

Csak rávilágítanék, hogy itt

BlaZe · Szep. 20. (Sze), 11.12
Csak rávilágítanék, hogy itt éppen amellett érvelsz, hogy megcsinálni valamit minden egyes integrációnál jobb, mint használni a nyelv által használt eszközöket tök ingyen. Eközben az egész bejegyzésed arra van felfűzve, hogy janoszen megoldása a túlbonyolított.

A hibák lekezelése pedig továbbra is azon függ, hogy a fejlesztő a hívó oldalon megvalósítsa a hibakezelést, kikényszerítés nincs.
78

Kivétel

Hidvégi Gábor · Szep. 22. (P), 14.24
A PHP által nyújtott ingyenes eszköz rugalmatlan a mi igényeinkhez:
  • Az esetek 80%-ában elég visszaadnunk egy hibakódot,
  • 18%-ban egyéb, változó paraméterekkel egészítjük ezt ki, például behelyettesítendő változók (változó számú), űrlapelemek azonosítói, egyéb értékek,
  • 2%-ban backtrace,
  • A hibaüzeneteket akárhány nyelven kell megjeleníteni.
PHP-ban az egyes új kivételeket mindig deklarálgatni kell, a paraméterlistájuk pedig kötött.

class INETAddressTypeError extends INETException {
  /**
   * Sets the address that has caused this error.
   * @param INETAddress $address
   */
  public function __construct(INETAddress $address, $required) {
    parent::__construct('Address ' . get_class($address) . ' is not of required type: ' . $required);
  }
}

Ezt janoszen egyik kódjából másoltam be, mert épp ezt találtam. Egy csomó munka minden egyes új hibatípushoz új php fájlt felvenni.

Nálunk nem kell kiegészítgetni semmit, csak a megfelelő táblába kell felvenni az új hibakódhoz tartozó hibaüzeneteket.

Nyilván le lehet programozni a kivételkezelést teljesen általánosan, de akkor ugyanott vagyunk, ahol a part szakad.
79

Amit idéztél, az hibakezelés.

BlaZe · Szep. 23. (Szo), 21.17
Amit idéztél, az hibakezelés. Amiről beszélsz, az hiba riportálás. A kettő nagyon nem ugyanaz.
81

Kezelés

Hidvégi Gábor · Szep. 25. (H), 08.53
A hibakód kezelése pont ugyanannyiba kerül, mint a kivételé.
83

Mutass már egy példát légy

BlaZe · Szep. 25. (H), 10.02
Mutass már egy példát légy szíves hogy kell ezt érteni. Magától megjelenik egy hibakód pár szinttel feljebb a stacken? Visszatérési hibakódokról beszélsz, vagy valami globálba beszúrt értékekről?
84

Példa

Hidvégi Gábor · Szep. 25. (H), 12.12
function urlap_betoltese($_urlap) {
  (...)
  if ($nem_admin) {
    return 2522;
  }
  (...)
  if ($nincs_urlapszetben) {
    return hiba_general(array(
      'hibakod' => 2523,
      'urlap'   => $_urlap,
    ));
  }
  (...)
  return array(
    'eredmeny' => $szerkezet,
  );
}

(...)
$eredmeny = urlap_betoltese('partnerek');
if ($hibakod = hibakod($eredmeny)) {
  if ($hibakod == 2524) {
    (...)
  }
  else {
    return $eredmeny;
  }
}
Egy függvény többféleképp adhat vissza hibát, mint a fenti példán is látszik, ha az értéke null vagy 1000, akkor minden rendben volt, ha 1000 feletti, akkor galiba történt. A hívó visszakap egy eredményt, amiből a hibakod() függvény kiveszi a hibakódot, és az alapján eldönthetjük, hogy mit csinálunk, de szinte mindig a hívónak adjuk át a vezérlést.

A hibák többségénél nincs szükség logolásra és a stack-re, ahol igen, ott a hiba_general() nevű függvény beleteszi a válasz tömbbe.
85

Vagyis a hibakódot a

BlaZe · Szep. 25. (H), 13.22
Vagyis a hibakódot a programozónak kell felbuborékoltassa a stacken, és körbeprogramoznia user függvényekkel. Nincs fordítás idejű hiba, ha nem létező hibát próbál kezelni a programozó. És nincs egységes szerkezete se a hibáknak. A konvenciók tartása a programozón múlik. Nekem ez nem tűnik se egyszerűbbnek, se biztonságosabbnak, mint használni a nyelvi elemet, ami ezt mind magától tudja. Sőt, kikényszeríti.
86

Igen

Hidvégi Gábor · Szep. 25. (H), 13.49
A felsoroltakból még eddig sosem volt probléma. Mint írtam, az esetek túlnyomó többségében csak hibakódot küldünk, amit a hívás helyén nem kell kezelni, de lehetőség van tömbben átadni az adatokat. A konvenciók betartása nem jelent kihívást. A szerkezet teljesen egységes, a return 2522; ekvivalens a return array('hibakod' => 2522);-vel.

Ezért tényleg egyszerűbb, mint létrehozni n darab kivételt.
89

Sosem probléma... :)

Pepita · Szep. 27. (Sze), 00.10
Ne haragudj, de ha egy int ekvivalens tud lenni egy tömbbel, aminek mint tudjuk, tetszőleges számú eleme lehet, tetszőleges számú és típusú kulcsok alatt, továbbá az egyes elemek is tetszőleges típusúak lehetnek, nos ha mindez számodra ekvivalens tud lenni, akkor bizony ez önmagában elég nagy probléma.
Többnyire az egyszerűséggel érvelsz a strukturált / procedurális programozási szerkezet mellett. Emiatt viszont bennem kb ugyanaz a kérdés vetődik fel, mint BlaZe - ben: ha azt gondolod, hogy így egyszerűbb, akkor miért tűzdeled teli a kódod if / else / elseif ágakkal random helyeken, gyakorlatilag kódot duplikálva, ha minderre adottak nyelvi eszközök (, amiket akár procedurális megközelítésben is lehetne használni)?
Ezen a ponton rengeteg hibalehetőséget tesztek bele. Nem csak magatoknak, hanem egy esetlegesen becsatlakozó új kollégának még jobban..
92

Tényleg?

Hidvégi Gábor · Szep. 27. (Sze), 08.39
ha egy int ekvivalens tud lenni egy tömbbel, aminek mint tudjuk, tetszőleges számú eleme lehet, tetszőleges számú és típusú kulcsok alatt, továbbá az egyes elemek is tetszőleges típusúak lehetnek, nos ha mindez számodra ekvivalens tud lenni, akkor bizony ez önmagában elég nagy probléma.
Miért probléma? Kinek? Annak a tömbnek véges számú és bizonyos kulcsú elemei lehetnek. Ha valamit elgépelünk, az elég hamar kiderül a teszteknél.

ha azt gondolod, hogy így egyszerűbb, akkor miért tűzdeled teli a kódod if / else / elseif ágakkal random helyeken, gyakorlatilag kódot duplikálva, ha minderre adottak nyelvi eszközök
Milyen random helyeken? Hülyének nézel?

Ezen a ponton rengeteg hibalehetőséget tesztek bele. Nem csak magatoknak, hanem egy esetlegesen becsatlakozó új kollégának még jobban..
Tényleg? A gyakorlat nem ezt mutatja, mert az utóbbi három évben, amióta élesben használják a rendszerünket, ebből még nem volt gond.

Már megint az az érzésem, hogy hülyének nézel. Ha az ember bizonyos cselekedeteinek az a következménye, hogy kap egy nagy pofont az élettől, akkor előbb-utóbb változtatni fog. Ha nem változtat, akkor hülye, ha nem kap pofont, akkor pedig nem cselekszik rosszul.
93

Szerintem nem hülyének néz,

BlaZe · Szep. 27. (Sze), 11.28
Szerintem nem hülyének néz, csak tudja, hogy a fenti megoldásod könnyen egy áttekinthetetlen if halomba fog torkollni, ha pl 4-5 függvényt kell meghívni egy helyen, és erre utalt. Exception esetén a programozónak az algoritmus során nem kell figyelembe vegye a hibaágakat, mert azok teljesen elkülönülnek. Nálad ez nem így van. Ezt jelenti a random hely.

Amellett, hogy mindig felhozod az OOP ellen a teljesítmény overheadet. Itt amiről beszélsz, az sokkal nagyobb overhead. Asszociatív tömbök repkednek ok nélkül, azokban lookupolni kell, és szét kell branchelni a kódot a helyes kezelés miatt, plusz user függvényeket kell bevezetni és meghívni. Mindezt azért, hogy ne kelljen létrehozni 1-2 filet, ami 2 kattintás egy IDE-ben. Ezek miatt az érveid szakmailag megalapozatlanok. Az meg pláne, hogy MÉG nem okozott problémát. Ami még nem okozott gondot, de a programozó figyelmén múlik, és van rá egyszerű megoldás, be se engedjük a kódba...
94

Köszi

Pepita · Szep. 27. (Sze), 13.49
Legalább egyvalaki érti, miről beszéltem, már ezért megérte. :)
96

Nem véletlenszerű

Hidvégi Gábor · Szep. 27. (Sze), 15.49
Alapvető szabály nálunk, hogy a hibát ott kell kezelni, ahol előfordulhat:

$siker = @ftp_connect(...);
if (!$siker) {
  return hiba_general(5000);
}


A return 2523; és a throw new IsNotAdminException; begépelése pontosan ugyanannyi figyelmet igényel.

Amellett, hogy mindig felhozod az OOP ellen a teljesítmény overheadet. Itt amiről beszélsz, az sokkal nagyobb overhead. Asszociatív tömbök repkednek ok nélkül, azokban lookupolni kell, és szét kell branchelni a kódot a helyes kezelés miatt, plusz user függvényeket kell bevezetni és meghívni.
Nem ismerem a PHP fordítóját, nem lennék meglepve, ha futásnál a karakterlánc kulcsokat szimbólumokra cserélné. És azt is valószínűnek tartom, hogy egy asszociatív tömb elemeit nagyjából ugyanannyi elérni, mint egy objektumét (gyorsabb).

Szóval ilyen megérzésekre, hogy "MÉG nem okozott problémát, ezért megalapozatlan" meg "amiről beszélsz, az sokkal nagyobb overhead" nem szoktam adni.

Más: kerestek kezdő programozókat? Egy ismerősöm szeretne átnyergelni Javára.
98

@ minek?

Pepita · Szep. 27. (Sze), 18.41
Ez a @ dolog elég csúnya..
Az ftp_connect megteszi a maga kis warning-ját, ha nem jó valami, ezt miért nyeled le? Adott esetben csak abból tudhatod meg, hogy mi is volt a baj.
(Hoppá, most nézem, hogy nem is triggerel hibát, akkor szimplán felesleges a @.)

A return 2523; és a throw new IsNotAdminException; begépelése pontosan ugyanannyi figyelmet igényel.
Gondolom nem használsz semmi olyan "felesleges kütyüt", mint pl egy IDE. Ha ez így van, akkor igaz amit írsz.
Akikkel viszont én eddig dolgoztam, azok közül még senki sem akadt, aki megkérdőjelezte volna egy jól belőtt IDE hasznát.
A példádnál maradva, ha én begépelek annyit, hogy 'th', már ajánlja is, hogy 'throw', ha ráütöm az ENTER-t, szóközzel és helyesen kiírja, következő ajánlat mindjárt a 'new', tehát csak +1 ENTER kell, ezután annyit írok, hogy 'IsN', máris kapom a teljes listát azokról a létező osztályokról, amik Exception - leszármazottak és így kezdődik a nevük, alattuk pedig azokat (ha vannak), amiknek csak szerepel a nevében ez a három betű.
Tehát nagyon - nagyon keveset elegendő tudnom a hibáról, amit dobni szeretnék.
A Te 4 számjegyedből nagyon könnyű egyre is rosszul emlékezni - és már borult is minden, azt se tudjuk, mi történt.
106

Nagy számok

Hidvégi Gábor · Szep. 28. (Cs), 09.09
Van bő háromszáz hibakódunk, ezekre lehetetlen emlékezni, ezért mindig meg kell nézni a táblában a kódot és az esetleges paraméterezését.

Izgalmas lehet például egy ekkora legördülő listából kiválasztani az épp aktuális kivételt.
107

Nem olyan nehéz

Pepita · Szep. 28. (Cs), 09.20
Mondok egy példát, bár fejből nem emlékszem rá pontosan.
3 vagy 4 féle exception van összesen, ezek közül egy olyan, amit frontendre is ki kell vezetni. Ehhez kötelező a státuszkód is, ami megegyezik a HTTP státusszal. A szövege nyelvesített, meglévő nyelvi kulcsokból kell kiválasztani.
Ezt ha ügyesen építed fel, 20 elemnél soha nem látsz többet a legördülő listáidban és tetszőlegesen bővíthető.
Ha jól emlékszem, volt még külön User-rel kapcsolatos (ez is HTTP státusszal), adatbázishiba és általános.
Utóbbi kettőnél is HTTP státuszkódból választottunk.
Nem biztos, hogy pontosan emlékszem, és nem is lehet ennyire röviden leírni egy teljes hibakezelést, de a lényeg az, hogy főként ha van bő 300 féle hibád, akkor érdemes csoportosítani.
112

Sok

Hidvégi Gábor · Szep. 28. (Cs), 09.44
Egyrészt a hibák csoportosítva vannak, másrészt a HTTP státuszkód nem elég. Fentebb írtam példát, hogy a 2522-re, 23-ra és 24-re a felhasználó ugyanazt a hibaüzenetet látja, de belül teljesen mást jelent.
100

Megérzés...

BlaZe · Szep. 27. (Sze), 22.55
Az első szakaszt egyáltalán nem értem hogy jön ide, én teljesen másról beszéltem. Az idézett gistet sem értem teljesen, az asszociatív tömb létrehozását hasonlítja objektum létrehozásához. Az egyik egy statikus típus, a másik meg nem. De az biztos, hogy semmi köze az objektum property elérési sebességéhez, amit lassabbnak gondolsz.

Szóval ilyen megérzésekre, hogy "MÉG nem okozott problémát, ezért megalapozatlan" meg "amiről beszélsz, az sokkal nagyobb overhead" nem szoktam adni.
Nem kell a megérzéseimre alapoznod, meg lehet mérni. Jó 2x lassabb megoldás mellett érvelsz. Ami amúgy messze nem a legnagyobb baj a megoldásoddal (szerintem kb irreleváns is), de ha mellé tesszük az OOP ellen hozott overhead érvedet, akkor olyan, mintha kettős mércéd lenne. Mellesleg én nem is megérzést írtam, mert elég könnyen belátható, hogy miért ennyivel lassabb. Normális nyelvben szerintem még nagyobb lenne a különbség, de azt már rád bízom, ha érdekel :)

Viszont kíváncsi lennék, hogy szerinted az alábbiak közül melyik illik az általad propagált megoldásra: szebb, rövidebb, áttekinthetőbb, biztonságosabb, fókuszáltabb, gyorsabb. És akkor itt csak egységes hibakezelésről volt szó. Ha minden hívásra le kéne külön kezelni a hibát, vagy el kéne kezdeni még feljebb buborékoltatni a hibát valamiért, akkor még borzasztóbb lenne ez az ifelgetés.

<?php
echo phpversion() . "<br />";

$counter = 1;
$errorModulo = isset($_GET['errorModulo']) ? $_GET['errorModulo'] : 100;
$iterations = isset($_GET['iterations']) ? $_GET['iterations'] : 10000;

echo "iterations = $iterations, errorModulo = $errorModulo <br />";

$errors = 0;

function func_assoc(){
    global $counter;
    global $errorModulo;

    if ($counter++ % $errorModulo == 0) {
        return array('code' => 2, 'message' => "Ouch");
    }

    return array('code' => 1, 'result' => $counter);
}

function calculate_assoc(){
    global $errors;

    $res1 = func_assoc();
    if ($res1['code'] == 1) {
        $res2 = func_assoc();

        if ($res2['code'] == 1) {
            $res3 = func_assoc();

            if ($res3['code'] == 1) {
                $res4 = func_assoc();

                if ($res4['code'] == 1) {
                    return $res1['result'] + $res2['result'] + $res3['result'] + $res4['result'];
                }
            }
        }
    }

    $errors++;
}

function func(){
    global $counter;
    global $errorModulo;

    if ($counter++ % $errorModulo == 0) {
        throw new Exception("Ouch");
    }

    return $counter;
}

function calculate(){
    global $errors;

    try {
        return func() + func() + func() + func();
    } catch (Exception $ex){
        $errors++;
    }
}

$counter = 1;
$errors = 0;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++){
    calculate_assoc();
}
$end = microtime(true);
echo "Runtime (associative array): " . ($end - $start) . " secs<br />";

$counter = 1;
$errors = 0;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++){
    calculate();
}
$end = microtime(true);
echo "Runtime (int with Exception): " . ($end - $start) . " secs<br />";


7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 10000 
Runtime (associative array): 0.13945388793945 secs
Runtime (int with Exception): 0.058928966522217 secs

7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 1000 
Runtime (associative array): 0.14786815643311 secs
Runtime (int with Exception): 0.059073925018311 secs

7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 100 
Runtime (associative array): 0.14269185066223 secs
Runtime (int with Exception): 0.062989950180054 secs

7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 10 
Runtime (associative array): 0.13449215888977 secs
Runtime (int with Exception): 0.093652963638306 secs

7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 5 
Runtime (associative array): 0.10190987586975 secs
Runtime (int with Exception): 0.10810780525208 secs

7.0.22-0ubuntu0.16.04.1
iterations = 100000, errorModulo = 1 
Runtime (associative array): 0.045601844787598 secs
Runtime (int with Exception): 0.15820789337158 secs
108

Én az elsőt választanám,

Hidvégi Gábor · Szep. 28. (Cs), 09.25
Én az elsőt választanám, többek között azért, mert abból kiderül, hogy hol keletkezett a hiba, míg a return func() + func() + func() + func();-nál nem tudod megmondani, melyik func() hívásban volt.

Viszont jobban szeretem a lapos függvényeket, mint az egymásba ágyazott struktúrákat.

function calculate_assoc() {
  $res1 = func_assoc();
  if ($res1['code'] != 1) {
    goto vege;
  }

  $res2 = func_assoc();
  if ($res2['code'] != 1) {
    goto vege;
  }

  $res3 = func_assoc();
  if ($res3['code'] != 1) {
    goto vege;
  }

  $res4 = func_assoc();
  if ($res4['code'] == 1) {
    return $res1['result'] + $res2['result'] + $res3['result'] + $res4['result'];
  }

vege:
  $GLOBALS['errors']++;
}
113

De tudod exceptionnél is, ha

BlaZe · Szep. 28. (Cs), 10.11
De tudod exceptionnél is, ha kell. De általában nem kell, mert a hiba a lényeg :)

Az összes általában hozott OOP elleni érveddel szembe megy a fenti példakód.
114

Igen

Hidvégi Gábor · Szep. 28. (Cs), 10.40
function calculate(){
  global $errors;

  $osszeg = 0;
  try {
    $osszeg += func();
  } catch (Exception $ex){
    $errors++;
  }
  try {
    $osszeg += func();
  } catch (Exception $ex){
    $errors++;
  }
  try {
    $osszeg += func();
  } catch (Exception $ex){
    $errors++;
  }
  try {
    $osszeg += func();
  } catch (Exception $ex){
    $errors++;
  }

  return $osszeg;
}


Ott kezd érdekessé válni a történet, amikor a feladatok között vannak mellékhatásokkal járó műveletek, például adatbázisban kell matatni, és azokat hiba esetén vissza kell vonni.
116

?

BlaZe · Szep. 28. (Cs), 12.16
try{
  ...
  commit();
} catch (Exception $ex){
  rollback();
}
Frameworkök ilyet maguktól is adnak akár...

Exceptionök meg képesek hordozni információt, ha kell.
119

Kivételek

Hidvégi Gábor · Szep. 28. (Cs), 13.17
Tehát maguktól nem hordoznak információt?
120

:)

BlaZe · Szep. 28. (Cs), 15.05
:) By definition azt az informáiót hordozzák, hogy milyen kivétel történt, és hogy hol.
130

Értsd jól:

Pepita · Okt. 1. (V), 09.37
Exceptionök meg képesek hordozni plusz információt is, ha kell.

Szerintem a gond az, hogy egyszer érvelsz pl az IDE ellen (300-as lista), majd ha kapsz az érvedben szereplő problémára megoldást (csoportosítás), akkor kitalálsz egy másikat, ami miatt az neked nem felel meg (felhasználónak ugyanazt kell látni, nem elég a http státusz, stb). Nem igazán konstruktív a gondolkodásmódod, ha az lenne, már rég megtaláltad volna az eddig elhangzott példák közül azt, ami Neked hasznos.
Ezek csak minták, amikben többen többféle dolgot megmutattunk, hogy Mi hogyan csináljuk / csináltuk. Nyilván egyik sem lesz egy az egyben Neked is jó.
Viszont nem is keresed bennük a számodra hasznos dolgokat, így nem sok értelme van megvitatni...
115

Az összes általában hozott

Hidvégi Gábor · Szep. 28. (Cs), 10.48
Az összes általában hozott OOP elleni érveddel szembe megy a fenti példakód.
Pontosan melyekkel?
117

Többen többször leírtuk. Én

BlaZe · Szep. 28. (Cs), 12.17
Többen többször leírtuk. Én meg listát is írtam :)
118

De tudod exceptionnél is, ha

Hidvégi Gábor · Szep. 28. (Cs), 13.16
De tudod exceptionnél is, ha kell.
Ezt kifejtenéd?

Viszont, ha nem számít, hogy hol keletkezett a hiba, akkor meg inkább így oldanám meg:

function func_assoc(){
  global $counter, $errorModulo;

  if ($counter++ % $errorModulo != 0) {
    return $counter;
  }
}

function calculate_assoc(){
  global $errors;

  $tomb = array(func_assoc(), func_assoc(), func_assoc(), func_assoc());
  if (!in_array(null, $tomb)) {
    return $tomb[0] + $tomb[1] + $tomb[2] + $tomb[3];
  }

  $errors++;
}
121

<troll>

BlaZe · Szep. 28. (Cs), 15.12
<troll>

Mert ez rövidebb, áttekinthetőbb, gyorsabb, nehezebben elrontható, fókuszálja a fejlesztőt a business logic fejlesztésére?

Vagy mert csakazértis? :)

</troll>
122

Nem

Hidvégi Gábor · Szep. 28. (Cs), 16.11
Sajnálom, ha nem sikerült érthetően fogalmaznom. Leírtam, hogy akkor készíteném el így a kódot, ha nem lényeges, hogy hol történt a hiba.

Mivel a mi rendszerünkben ez egy fontos információ, ezért a hibakódos megoldást választottuk.

Továbbá a kliensnek is kiküldjük a hibakódot – tehát ennek valahol mindenképp ígyis-úgyis meg kell jelennie. Ha telefonon felhívnak, a kód alapján könnyebb és gyorsabb azonosítani a dolgot, mint ha felolvasnák a szöveget, mivel ez sajnos nem mindenkinek megy.
124

Exception-el is át lehet adni

inf3rno · Szep. 28. (Cs), 21.53
Exception-el is át lehet adni hibakódot, ha ez minden vágyad. Egyébként ha van valamire egy jól bevált nyelvi elem, akkor miért kell a programozónak lekódolni ugyanazt, mint amit a programnyelv magától megcsinálna helyette?

Ha tényleg úgy gondolod, hogy van, ami nem megoldható kivételekkel, de a te módszereddel igen, akkor írj ilyen példát! Kíváncsian várom!
126

Nem

Hidvégi Gábor · Szep. 29. (P), 08.46
Van egy jól bevált nyelvi elem, a kérdés, hogy mit nyerünk a használatával? A hibakódoknak ugyanúgy kell egy tábla az adatbázisban, minden kivételnek külön fájlt kell gyártani, ezekbe kézzel beírni a kódot.

Ezzel szemben a hibatömbös megoldás jóval rugalmasabb és kevesebb munkával jár.
127

Szerintem átláthatóbb lesz a

inf3rno · Szep. 29. (P), 15.00
Szerintem átláthatóbb lesz a kód tőle, mert külön kezeled benne a hibát (mellékhatást) és külön lesz az alkalmazás logika, ami a lényegi része a dolognak. Nélküle vegyesen lesz ugyanez megoldva, egy sor logika, egy sor hibakezelés.

Szerintem ha több projektet csinálsz, akkor te is másolod a megoldások egy jelentős részét, pl a bejelentkezést, stb. A hibakódokkal gondolom ugyanígy jársz el. Szóval olyan nagy különbség nincs aközött, hogy másolod a kivétel fájlokat vagy hogy másolod a táblázatodat. Egyébként nem tudom te hogy vagy vele, de én az olyan dolgokon, amiken nem kell gondolkodni, nem jellemző, hogy számottevő időt veszítek. Ilyen pl egy új kivétel fájl létrehozása és az osztálynév begépelése. Az egész megvan néhány másodperc alatt. Sokkal munka igényesebb a tényleges model kitalálása, vagy egy kódrészlet megtalálása egy olyan kódban, ahol nehéz követni, hogy mi mit csinál és a keresett kód szét van szórva ezer helyre.
128

Hogy mit nyersz vele, arra

BlaZe · Szep. 29. (P), 20.34
Hogy mit nyersz vele, arra ott a példa. Rövidebb, áttekinthetőbb, gyorsabb, könnyebb vele dolgozni, és elszeparálja a hibakezelést az üzleti logikától.

Nem kell minden egyes hibának külön exception osztály. Ha kell a kód, átadod az exceptiönnek, ennyi.

Továbbra is fenntartom, hogy amit csináltok, az egy architekturális probléma. A hibakezelés és a hiba riportálás összemosása nem szerencsés. Dobsz egy belső hibát, amit a program megfelelően lekezel, majd a megjelenítési rétegben a hibát átmappeled user friendly üzenetté, és/vagy kóddá. Ez két külön dolog kell legyen. Nálunk is vannak belső, külső kódok, hibaüzenetek. De ezeknek semmi köze nincs a hibakezeléshez, csak a riportáláshoz.
129

Ez a "hibát ott kezeljük,

inf3rno · Szep. 30. (Szo), 02.27
Ez a "hibát ott kezeljük, ahol kiváltódik" dolog honnan jön? Hallottam már régebben valahol, de sosem értettem igazán, hogy mit értenek ezen, vagy hogy mik az előnyei. Lehet, hogy Gábor is félreérthetett valamit ezzel kapcsolatban, és az vezetett ehhez a megoldáshoz.
132

Szerintem

Pepita · Okt. 1. (V), 09.59
Szerintem leginkább a tranzakciókezeléssel függhet össze, ott legalábbis van értelme.
Van egy mutatvány, amit ha végig csinálsz, több táblában több adatnak kell az üzleti logika szerint változnia.
De a valahanyadik változás beleütközik valamibe (mondjuk duplicate entry for xy key), ebből derül ki, hogy a mutatvány nem végrehajtható, rollback, és vmi user friendly üzenet kell.
Ilyenkor bizonyos hibákat helyben kezelsz, a mutatvány try / catch blokkjában.
Mindez nem jelenti szó szerint azt, konkrétan ott kezeled, ahol kiváltódik, mert nyilván a db insert / update valahol máshol van kódolva, nem a blokkodban.
Szerintem ez (is) az egyik kódszervezési probléma Gáboréknál, attól, hogy procedurális, még lehetne sokkal jobban strukturált.
135

C

Hidvégi Gábor · Okt. 2. (H), 08.46
C-ben biztosan így szokták használni, meg, gondolom, minden más, procedurális nyelvben, ahol nincs kivételkezelés.
134

Megegyezik

Hidvégi Gábor · Okt. 2. (H), 08.36
Számomra viszont a throw new Exception(2522); teljesen ekvivalens a return 2522;-vel.

A kódunkban a legfontosabb, hogy a hibák (megfelelően) le legyenek kezelve, ez magasabb prioritást élvez, mint az olvashatóság. Ez nem jelenti azt, hogy olvashatatlan a programunk, általában az egyes nagyobb blokkok végén szokott lenni egy return.

Hogy ez architekturális probléma-e vagy sem, nem tudom, még (: )) nem volt belőle baj az évek során. Elég sokan ezen elvek mentén kezelik a hibáikat.
137

Számomra viszont a throw new

BlaZe · Okt. 2. (H), 14.19
Számomra viszont a throw new Exception(2522); teljesen ekvivalens a return 2522;-vel.
Pedig ahogy a példák mutatták fentebb, van köztük különbség. Pl pont a többször hozott tranzakciós példádban az exception garantálja a konzisztenciát, a return code viszont a programozóra bízza a helyes működést.

Elég sokan ezen elvek mentén kezelik a hibáikat.
Nem elvekről van itt szó. Az elvárt működés azonos, az elérhető működés is azonos. A kódszervezés a kérdés, és a módszerek nyújtotta biztonság. De túl van beszélve már ez a dolog. Csak nem értem miért dobtál fel labdákat a fórum bejegyzéssel, ha konkrét példákat és tényeket figyelmen kívül hagysz, amiket válaszban kaptál. Nagyon látványos előnyei vannak az exceptionnek, ami meg is lett mutatva, de azokra nem is reagáltál. Mi a célod akkor ezzel megint? Ebből te tanulsz valamit? Tök jó dolog vitatkozni szakmai dolgokon, mert épülhet belőle mindenki, ha jó a vita. De tök őszintén én nem láttam tőled olyan érvelést, ami alátámasztja az álláspontodat. Ami volt az elején (sok file, többet kell kódolni) eléggé meg lett cáfolva. Miről szól akkor még ez a thread neked, mint az indítójának?
139

Így

Hidvégi Gábor · Okt. 4. (Sze), 09.16
Amióta feltettem a kérdéseket, folyamatosan olvasok utána mindennek, amint van egy kis időm.
138

Nem

janoszen · Okt. 3. (K), 09.11
Nem ekvivalens a kettő.

A legkisebb ellenállás útja a különbség. Ha exceptiont dobsz, a programozónak külön dolgoznia kell, ha az exception ellenére folytatni akarja a futást, míg ha figyelmen kívül hagyja, akkor lerohad a program. Ezzel szemben a return esetén a legkisebb ellenállás útja az, hogy tovább fut a program, és külön erölködni kell azon, ha a hiba hatására le kell rohadnia a programnak.

Vagyis a Te megoldásodnál programozónak tudatosan erölködnie kell azon, hogy minden hibát megfelelően kezeljen, és egyetlen benézés vezethet hibás adatokon végzett műveletekhez. Márpedig a programozó időnként lusta, nem ivott még kávét, és egyébként is már az esti WOW-ozáson jár az esze, vagy csak simán és egyszerűen ember, és emberek hibáznak.

Ezért jók az exceptionök, ezért jó a szigorú és statikus típusosság, külön erölködnöd kell, hogy figyelmen kívül tudd hagyni a hibát, a legkisebb ellenállás útja egyben a helyes út is.

Ez nem azt jelenti, hogy procedurálisan nem lehet nagyon tisztességes és kultúrált kódot írni, hiszen évtizedekig csináltuk, csak azt jelenti, hogy a produktivitásod egy jelentős része olyan dolgokra megy el, amit egyébként a compiler / futtatókörnyezet megoldana helyetted. Rutinnal kiküszöbölhető, de csak korlátozottan, és nehezebb lesz új embert felvenni.
140

Ez így nagyon igaz!

Pepita · Okt. 8. (V), 16.33
Gábor, kiemelten ezzel foglalkozz, ezen az úton járva meg tudod találni a kellő különbséget:
A legkisebb ellenállás útja a különbség. Ha exceptiont dobsz, a programozónak külön dolgoznia kell, ha az exception ellenére folytatni akarja a futást, míg ha figyelmen kívül hagyja, akkor lerohad a program. Ezzel szemben a return esetén a legkisebb ellenállás útja az, hogy tovább fut a program, és külön erölködni kell azon, ha a hiba hatására le kell rohadnia a programnak.


Amit én is próbáltam neked érzékeltetni:
nehezebb lesz új embert felvenni.


A programozó is ember, és mint ilyen, annyit tesz meg, amennyit muszáj. Nagyon jól írja János, hogy "erőlködnöd kell". Ez egy helyes út.
131

Ez így nem igaz

Pepita · Okt. 1. (V), 09.49
minden kivételnek külön fájlt kell gyártani, ezekbe kézzel beírni a kódot.
Ne haragudj, de ez így nettó butaság.
Használhatod magát a "gyári" Exception osztályt is, ott van benne gyárilag is a helye a kívánt kódodnak.
Egyáltalán nem kötelező minden egyes hibának külön leszármazottat gyártani.
Ha a jelenlegi db-t használó függvényeidet ki szeretnéd okosan váltani vele, elegendő egyetlen saját exception-t deklarálnod, amiben megvalósítod a db-vel a kapcsolatot.
Más kérdés, hogy így még nem fogsz tudni kezelni minden hibát, mert pl ha a db-hez nem tudsz csatlakozni, akkor kilőtted a hibakezelésed is. Ez mondjuk a jelenlegi megoldásodnak is az egyik hibája.
123

Pedig az első a hosszabb, és

inf3rno · Szep. 28. (Cs), 21.49
Pedig az első a hosszabb, és te mindig azzal érvelsz, hogy az OOP mennyire hosszú és feleslegesen bonyolult. :D
101

Más: kerestek kezdő

BlaZe · Szep. 27. (Sze), 23.38
Más: kerestek kezdő programozókat? Egy ismerősöm szeretne átnyergelni Javára.
Igen, van junior java programunk. Ha van már valami tapasztalata valamilyen OOP nyelven, ismeri valamennyire a Javat magát, jó algoritmikus képességei vannak, jó elméleti alapokkal rendelkezik, jól kommunikál írásban és szóban (angolul). Munkatapasztalat Javaban előny. Ha ezek stimmelnek, írj rám, és átdobom hol/hogy lehet jelentkezni. Vagy megtalálni is meg lehet két guglizással.
103

A return 2523; és a throw new

inf3rno · Szep. 28. (Cs), 03.55
A return 2523; és a throw new IsNotAdminException; begépelése pontosan ugyanannyi figyelmet igényel.


Ez azért egy bold statement. A számnál plusz egy lépés átgondolni, hogy az "is not admin"-hoz melyik szám tartozik, egy kezdőnek meg be kell tanítani a számkódolást. Erre írtam korábban, hogy használj rá nevesített konstansokat ahelyett, hogy csak így odavetsz egy integert, mert könnyű hibázni, de látom nem jutott el hozzád.

Az overhead-el kapcsolatban én úgy vagyok, hogy amíg nem bizonyítja egyik vagy másik oldal benchmarkkal, hogy bármit is számít, addig nincs értelme foglalkozni vele.
102

Szerintem nem hülyének néz,

inf3rno · Szep. 28. (Cs), 03.48
Szerintem nem hülyének néz, csak tudja, hogy a fenti megoldásod könnyen egy áttekinthetetlen if halomba fog torkollni, ha pl 4-5 függvényt kell meghívni egy helyen, és erre utalt. Exception esetén a programozónak az algoritmus során nem kell figyelembe vegye a hibaágakat, mert azok teljesen elkülönülnek. Nálad ez nem így van. Ezt jelenti a random hely.


Ha már ide értünk, rengetegszer felhozza azt az érvet is, hogy feleslegesen hosszú és bonyolult az oop kód. Hát itt pont arról van szó ennél az if halomnál, hogy feleslegesen hosszú és bonyolult lesz tőle a kód...
95

Gondok

Pepita · Szep. 27. (Sze), 14.08
Miért probléma? Kinek?
Mindenkinek, akinek ezt fejben kell tartania. Mert sajnos nincs rá jó megoldás, amivel 100% - ban le tudnád fedni a lehetséges eseteket.
Amelyik hibát nem kezeled, az gyakorlatilag nincs is - szemben az akár egyedi Exception - ökkel. Azt ha nem kezeled, megy a standard hibalogba, stb. "Néha" a típusosságnak is vannak előnyei...
Milyen random helyeken?
Mindenütt, ahol foglalkozol (egyedi) hibakezeléssel. Ez a kódbázisodban nem egységes, ezért random helyeken fordul elő.
Hülyének nézel?
Nem. Miből gondolod?
A gyakorlat nem ezt mutatja, mert az utóbbi három évben, amióta élesben használják a rendszerünket, ebből még nem volt gond.
És ebben a 3 évben hány új fejlesztő csatlakozott a projekthez? Ők mennyire (hamar) tudták felvenni a gondolatmenetet, mi okozott számukra gondot a további fejlesztések során?
Más dolog használni egy rendszert, és megint más fejleszteni. De a felhasználók közül is kikerülhet egy új "Manyika néni", aki aztán kikattintgatja neked azt, amit még soha senki..
Ha nem változtat, akkor hülye, ha nem kap pofont, akkor pedig nem cselekszik rosszul.
Nehogy megint megvádolj hülyézéssel, mert nem az én stílusom, de van egy olyan ága is ennek a dolognak, hogy "nem kapott pofont, mert eddig szerencséje volt".
Mondják azt is, hogy a hülyének meg a részegnek van szerencséje, ezt nem tudom, de személyes tapasztalatom is az, hogy nem szabad elkényelmesedni attól, ha egy ideig nem kapsz pofont, mert tökéletes szoftver nincs, ki fog jönni újabb bug, legfeljebb kicsit később.

Csak még1 megjegyzés a hülyével kapcsolatban. Ha valakit én hülyének gondolok, vagy akár csak eléggé nem hozzáértőnek, akkor ezt megmondom neki, és nem folytatok vele szakmai vitát.
Nem gondollak hülyének, de van egy - két olyan (elég mélyen gyökerező) ideád, amik - szerintem - gátolják a szakmai fejlődésed, mert tévesek. Ezekkel amikor szembesülök és van elég türelmem hozzá, akkor megpróbállak velük szembesíteni, megpróbálom megmutatni, hogy mi bennük a téves.
Ha ez Téged sért, akkor elnézést kérek, mondd meg és nem teszem többet (viszont el se fogom olvasni, amiket ezzel kapcsolatban írsz).
97

Amelyik hibát nem kezeled, az

Hidvégi Gábor · Szep. 27. (Sze), 16.01
Amelyik hibát nem kezeled, az gyakorlatilag nincs is - szemben az akár egyedi Exception - ökkel. Azt ha nem kezeled, megy a standard hibalogba, stb. "Néha" a típusosságnak is vannak előnyei...
A hibákat a helyükön kezeljük, ahol kiváltódhatnak.

És ebben a 3 évben hány új fejlesztő csatlakozott a projekthez? Ők mennyire (hamar) tudták felvenni a gondolatmenetet, mi okozott számukra gondot a további fejlesztések során?
Nem vettünk fel új fejlesztőket, mert nem volt rá szükség.

De a felhasználók közül is kikerülhet egy új "Manyika néni", aki aztán kikattintgatja neked azt, amit még soha senki..
Ha kiderül egy hiba, akkor javítjuk. Kivételkezelés használatánál nem fordulhat elő programhiba, amit Manyika néni ki tud kattintgatni? Mert akkor rögtön áttérünk rá!

de van egy olyan ága is ennek a dolognak, hogy "nem kapott pofont, mert eddig szerencséje volt"
Nyilván három évig szerencsénk volt, de most, hogy már tudom, mennyire rossz, amit csinálunk, hirtelen ránk fog szakadni a ház : (

Vagy kiderül, hogy lehet máshogy is csinálni.
99

Bocsi

Pepita · Szep. 27. (Sze), 19.07
Gábor, tényleg nem ellened irányul, de nem tudom szó nélkül hagyni.
A hibákat a helyükön kezeljük, ahol kiváltódhatnak.
Ez egy picit megrágva azt (is) jelenti, hogy "hiba csak ott lehet, ahol számítottunk rá". Mert ott kezelitek is (valahogy).
Ez szerintem eléggé veszélyes hozzáállás.
Nem vettünk fel új fejlesztőket, mert nem volt rá szükség.
Kikerülöd az egyenes választ. Ha vennétek fel, valószínűleg bődületesen sok lenne a betanulási ideje, ha nem menekülne rögtön hanyatt - homlok... A kérdést ezért tettem fel, mert üzletileg sem tartom célravezetőnek, ha egy szoftver 1 - 2 - n fejlesztőn múlik, akiket elüthet a repülő, elkezdhetnek zsarolni, stb stb.
Persze sok fejlesztő van, aki ilyesmikkel igyekszik bebiztosítani a helyét, hogy ezt vagy azt "csak ő tudja megcsinálni", de előbb - utóbb ha a tulajdonosnak van egy kis esze, ezeket a függéseket felszámolja.
Kivételkezelés használatánál nem fordulhat elő programhiba, amit Manyika néni ki tud kattintgatni?

De igen.
A minimális kivételkezelés nálam 3 fő részből áll:
- Központi hibakezelés: nem csak Exception-t, hanem minden hibát kezel, logol, ha olyan, amit a User-nek (is) szánunk a kimenetre, akkor gondoskodik arról is, hogy kijusson.
- Szükséges számú egyedi Exception-ök implementálva (nem feltétlenül hibatípusonként másik, lehet más szempont szerint is csoportosítani, mint pl. megjelenik-e usernek, stb).
- Azokon a helyeken, ahol valamilyen okból el kell kapni valami más programrész kivételét (try / catch), ott kizárólag azt a kivételt kezelni, amire számítunk (erre is jó az egyedi class), minden további kivételt a catch ágban kötelező újra dobni, hogy a központi hibakezelő megkaphassa.

Ha ezek teljesülnek, akkor a maximális szintű infóm meglesz, mikor a Manyika néni bekattintja "a rettenetest". Ahogy eddig láttam, a Te rendszeredben pedig annyi infó lesz, amennyit a Manyika néni sikítozik a telefonba, mert nem várt hibára nincs megoldásotok.
Emiatt (és több korábban említett okból) én javaslom, hogy ha nem is rögtön, de gyorsan térjetek át egy jobb, egységesebb hibakezelésre, érdemes felhasználni, amit a nyelv nyújt.

Vagy kiderül, hogy lehet máshogy is csinálni.
Senki nem mondta, hogy nem lehet, mindössze érvek és javaslatok hangzottak el, hogy hogy lehet jobban.
A cipőt is fel lehet húzni a kezedre is, de úgy kicsit nehéz gépelni, és ha a lábadra nem húzol, akkor az utcán járni kényelmetlenebb. De ettől még húzhatod a kezedre.

Van benned egy nagy adag fixed mindset. Ragaszkodsz hozzá. Szerintem ennyi a gond.
104

nem feltétlenül

inf3rno · Szep. 28. (Cs), 04.02
nem feltétlenül hibatípusonként másik, lehet más szempont szerint is csoportosítani, mint pl. megjelenik-e usernek, stb


Ez tök jó gondolat, eddig még nem jutott eszembe. Tudnál erről többet írni?

minden további kivételt a catch ágban kötelező újra dobni, hogy a központi hibakezelő megkaphassa.


Jobb nyelveknél eleve el se kapja azokat, amiket nem sorolsz fel a listán.
109

Fentebb írtam

Pepita · Szep. 28. (Cs), 09.30
Fentebb írtam (fejből) egy példát, de nincs kidolgozva és csak az emlékezetemre támaszkodik, de ha újba kezdenék, valahogy úgy csinálnám.

Ha igaz, php (7) se kapja el amit nem akarsz, de szerintem átláthatóbb, ha van egy utolsó } catch (Exception $e) { ág, ahol továbbdobod, mert akkor látszik, hogy "itt felettem ezeket kezeljük, ha más történik, megy tovább a nóta". Illetve ugye nem ritka eset, mikor egy DB tranzakciót vissza kell rollback-elni, ha bármi történt, ezért is érdemes meghagyni.
125

Én azt hiszem a kezelt

inf3rno · Szep. 28. (Cs), 21.56
Én azt hiszem a kezelt kivételeket is tovább dobom néha, csak becsomagolom őket egy másikba, amit a külső kód is ért, hogy micsoda meg honnan származik. Így szoktam csoportosítani. De nem igazán gondoltam még végig ezt a témát, ez csak így zsigerből jött.
111

A hibákat a helyükön

Hidvégi Gábor · Szep. 28. (Cs), 09.41
A hibákat a helyükön kezeljük, ahol kiváltódhatnak
Ez azt jelenti, hogy ha meghívok egy függvényt, akkor utána azonnal megnézem, hogy volt-e hiba/nem értelmezhető eredményt adott-e vissza, és ha igen, akkor kezelem.

Kikerülöd az egyenes választ. Ha vennétek fel, valószínűleg bődületesen sok lenne a betanulási ideje, ha nem menekülne rögtön hanyatt - homlok... A kérdést ezért tettem fel, mert üzletileg sem tartom célravezetőnek, ha egy szoftver 1 - 2 - n fejlesztőn múlik, akiket elüthet a repülő, elkezdhetnek zsarolni, stb stb.
Persze sok fejlesztő van, aki ilyesmikkel igyekszik bebiztosítani a helyét, hogy ezt vagy azt "csak ő tudja megcsinálni", de előbb - utóbb ha a tulajdonosnak van egy kis esze, ezeket a függéseket felszámolja.
Nem az én döntésem, hogy felveszünk-e bárkit is vagy sem. Hogy mennyi lenne a betanulási idő, arról fogalmam sincs, sajnos nem vagyok jós. Nem hiszem egyébként, hogy probléma lenne.

Amint a "minimális kivételkezelés"-nél írsz, az nálunk is úgy van. Csak rugalmasabb.

Ahogy eddig láttam, a Te rendszeredben pedig annyi infó lesz, amennyit a Manyika néni sikítozik a telefonba, mert nem várt hibára nincs megoldásotok.
Mi az, hogy nem várt hiba? Tegyük fel, hogy van egy függvény, ami sikeres futás esetén visszaad egy tömböt, hibánál pedig null-t, a hívó függvény pedig ezen a tömbön szeretne végigiterálni. Ha elfelejtem lekezelni a null-t, akkor a foreach-nél a PHP azonnal dob egy hibát, amit elkap a globális hibakezelő függvényünk, ahol elvégezzük a logolást és minden egyéb, szükséges műveletet.

Emiatt (és több korábban említett okból) én javaslom, hogy ha nem is rögtön, de gyorsan térjetek át egy jobb, egységesebb hibakezelésre, érdemes felhasználni, amit a nyelv nyújt.
Köszönöm, de egységes a hibakezelésünk és jól működik. De ha majd kevés lesz, akkor elgondolkozunk más módszereken.

Van benned egy nagy adag fixed mindset. Ragaszkodsz hozzá. Szerintem ennyi a gond.
Csak bennem?
133

Pont ez a gond

Pepita · Okt. 1. (V), 10.42
akkor utána azonnal megnézem
Többen is jeleztük, hogy ez az egyik legnagyobb gond, hogy a hibakezelés a programozó feledékenységén (is) múlik.
Persze, értem én, hogy
Ha elfelejtem lekezelni a null-t, akkor a foreach-nél a PHP azonnal dob egy hibát
, de nekem speciel a hócipőm tele volt, mikor naponta láttam hibalogban "invalid argument supplied for foreach" hibákat több hónapon keresztül, mert egyes kollégák nem gondoltak arra, hogy pl db-ből jöhet 0 rekord is, és ha már bátran rá foreach-elünk fv visszatérési értékre, akkor illene biztosítani, hogy az tömb is legyen. Egyébként php 7.0.x - től ezt megoldja a return type declaration, érdemes használni (sajnos még nem kötelező).

Amint a "minimális kivételkezelés"-nél írsz, az nálunk is úgy van. Csak rugalmasabb.
Ez nem igaz, még az a minimum sincs meg, ezáltal nem is lehet rugalmasabb.
Hiányzik:
- Szükséges számú egyedi Exception-ök implementálva
És ha ez sincs meg, illetve nem használtok kivételt, akkor nem is tudjátok aszerint kezelni. Látom, van helyette sok if / else ág, csak hát ez a programozóra van bízva, hogy "odateszi-e". Exception-nél, ha nem kapod el (catch), akkor is van valamilyen következménye (akár leáll a futás), tehát mindenképpen értesül valaki a hibáról, hiába a programozó volt hanyag.
A Te megoldásoddal az (is) a baj, hogy ha kihagyom az if (nincs_hiba()) részt, akkor kvázi megy tovább az élet - akár rossz irányba - rossz adatokkal, csak mert fatal error nem történt..

Mi az, hogy nem várt hiba?
Már írtam erre példákat, de akkor ismét:
1 Meghal a db szerver (ilyenkor Neked semmilyen hibakezelésed nincs, mert az is függ a db - től)
2 Elgépelsz egy változónevet, emiatt random helyen egy null - ból próbálsz adatokat beszúrni a db - be (esetleg be is megy az üres rekord!)
3 foreach egy null v bool v egyéb értéken
4 bármilyen E_NOTICE vagy E_WARNING szintű hiba
- stb stb

2 - 4 olyan hiba, ami miatt a program futása nem áll meg, viszont a helytelen működés miatt létrejöhetnek invalid adatok a db - dben, ami aztán később még nagyobb gondot fog okozni és debugolhatsz hetekig...
Minden olyan hiba "nem várt", amire nem készültél fel pl egy try / catch blokkban. Nálatok kb a hibák 90% - a lehet ilyen, mert ha jól láttam, konkrét hibakódokra if - elsz. Így csak nagyon picike részt lehet lefedni. Vagy hatalmas spagetti lesz 300 féle hibával.

egységes a hibakezelésünk
Ezt nem mondanám addig, amíg azon múlik, hogy random helyeken a programozó(k)nak eszébe jutott-e beírni a megfelelő if (van_hiba(1234)) sorokat. Akkor egységes és jól működő, ha pl én odamegyek, elolvasom a doksikat (ha vannak), hogy mit hogyan kell fejleszteni nálatok, ezután csinálok egy tök más, semmilyen szabályt be nem tartó modult vagy bármilyen kódot, és Te pl az automata tesztek futtatása után pontosan felsorolod, hogy melyik szabályt hol szegtem meg és emiatt nem műxik. Helyette csináljam így vagy úgy, akkor jó lesz.
Ezért is mondtam, hogy egy esetleges új fejlesztővel sok gondotok lenne.

SZERK.:
Félreértések elkerülése végett önmagában az nem baj, ha lehet (akár sok féle) nem várt hiba, a gondok ott kezdődnek, ha erről nincs elég infó, és / vagy a program futása nem áll meg, pedig meg kéne (pl mentésre, sőt kiküldésre kerülhet rosszul kitöltött számla, de ez csak egy példa kiscsillió közül).
Azt sose gondold, hogy "nálunk nincs nem várt hiba, mert mi mindenre fel vagyunk készülve", mert ez lehetetlen. Ahol usertől vagy másik szoftvertől jönnek inputok, ott nem tudsz mindenre felkészülni.
136

Többen is jeleztük, hogy ez

Hidvégi Gábor · Okt. 2. (H), 09.26
Többen is jeleztük, hogy ez az egyik legnagyobb gond, hogy a hibakezelés a programozó feledékenységén (is) múlik.
Mint ahogy az egyes kivételek lekezelése is. Nyilván az egész programod egy nagy try { ... } cache ()-ben van, de ha mondjuk egy belső függvényben, ahol adatbázisműveletek vannak, és nincsenek helyben lekezelve, akkor inkonzisztens állapot lesz az eredmény, árva rekordok, ilyenek. Vagy egy állandóan futó szolgáltatásban lefoglalsz egy file descriptort, de elfelejted felszabadítani (szerencsére a PHP-ban legalább az ilyen problémák nem jönnek elő).

1 Meghal a db szerver (ilyenkor Neked semmilyen hibakezelésed nincs, mert az is függ a db - től)
2 Elgépelsz egy változónevet
A programunk úgy indul, hogy van egy statikus hibatömb az alap hibaüzenetekkel, pl. nem sikerül beolvasni a konfigurációs fájlt, lejárt az előfizetés, nem lehet kapcsolódni az adatbázisszerverhez.

Van egy saját hibakezelőnk, ami minden PHP hibát logol.
141

Hát nem

Pepita · Okt. 8. (V), 16.52
Ne haragudj, de ez egy hatalmas tévedés:
Nyilván az egész programod egy nagy try { ... } cache ()-ben van, de ha mondjuk egy belső függvényben, ahol adatbázisműveletek vannak, és nincsenek helyben lekezelve, akkor inkonzisztens állapot lesz az eredmény, árva rekordok, ilyenek.

Ez csak akkor lenne igaz, ha az lenne a célom, hogy az esetleges hibákat eltussoljam... Pont az ellenkezője igaz erre a szemléletmódra, vagyis az elsődleges cél az, hogy ha a programozó (akár "vadi új junior") rosszul csinál valamit, az derüljön ki nagyon gyorsan, és mindenképpen az élesítés előtt.

Ha levédi az összes programrészt (amit nem kellene), az mindjárt arról árulkodik, hogy (ne haragudj meg érte!), hogy nem bízik meg a saját tudásában és / vagy nem látja át a folyamatokat, és valahogy keresztül akarja vinni a saját egyéni elképzelését egy számára ismeretlen rendszeren.

Ha azonban ért hozzá, akkor csak és kizárólag ott alkalmaz védett blokkot, ahol neki külön egyedi kezelésre van szüksége. Ahol nincs, ott bízik abban, hogy ha valami hiba történne, arról időben tudomást szerez, és javítja a saját kódját.
Kicsit leegyszerűsítve, leszűkítve jó példa erre, amikor valamilyen üzleti szabály nincs teljesen lefedve adatbázis szinten, mondjuk az x. műveletnél jelentkezik csak pl foreign key error, de emiatt vissza kell vonni másik y műveletet is.
Ilyenkor (is) érdemes try / catch, de az alapvető hozzáállás éppen az, hogy ez minél ritkábban kelljen.

Van egy saját hibakezelőnk, ami minden PHP hibát logol
Ez tök jó (kb alapkövetelmény), de amire én céloztam, az az, hogy a hibakódok és a hozzá tartozó "emberi" hibaüzenetek is (elmondásod szerint) db - ben vannak.

Tök jó emellett a "statikus hibatömb", csak
- Nem a legjobb megoldás, hogyha - ezek szerint - több helyről is kell tudni azonosítani egyetlen hibát;
- Egy hiba = egy esemény, régen rossz, ha ezt többféleképp kell(ene) kezelni.

Összességében nekem úgy tűnik, hogy a szoftver tervezésekor (amennyiben volt ilyen) kifelejtődött, hogy mire van nyelvi (és jó) megoldás, és mire nincs. Egyébként az összes valamirevaló nyelvben (a nagyon alacsonyszintűek, pl Assembly kivételével) van valamilyen jól használható hibakezelés. Php-ben is. Szerintem egyszerűen nem akarjátok használni - de valós indokot még nem láttam rá, hogy miért.
90

Na, csak megkérdezem mégis...

BlaZe · Szep. 27. (Sze), 08.09
Na, csak megkérdezem mégis... Mi van, ha nem hiba visszatérési értéke is lehet a függvénynek? A hibakód, hiba tömb lehetősége mellett.
91

Példa

Hidvégi Gábor · Szep. 27. (Sze), 08.23
Fentebb írtam rá példát, de akkor kiegészítem:
return array(
  'hibakod'  => 1000, //sikeres művelet
  'eredmeny' => $szerkezet,
);
87

Én nem lepődtem meg, valami

inf3rno · Szep. 26. (K), 00.11
Én nem lepődtem meg, valami ilyesmire számítottam. Ezt még el is fogadnám, de hogy a hibakódokat csak úgy odavetjük, nem konstansokat használunk, ami legalább valamennyire dokumentálná őket, az elég kemény. :D
88

Így

Hidvégi Gábor · Szep. 26. (K), 08.20
Így gondolod?

if ($nem_admin) {
  return HIBAKOD_NEM_ADMIN;
}
105

Igen, erre gondoltam.

inf3rno · Szep. 28. (Cs), 04.05
Igen, erre gondoltam.
110

DB-ből?

Pepita · Szep. 28. (Cs), 09.34
Amúgy hamár kiderült, hogy a hibakódok is adatbázisban vannak, akkor - ha van ott értelmes nevük is - akár betölthetnéd a konstansokat db-ből is, mondjuk ezzel adnál egy pofont megint a kódkiegészítésnek, illetve cache-elni is kéne.
Szóval ez nem biztos, hogy jó ötlet...
80

Ezek szerint a

smokey · Szep. 24. (V), 12.09
Ezek szerint a hibakezelésetek függ az adatbázis éppen aktuális állapotától? Simán lehet, hogy dev módban más a hibaüzenet, mint prod módban?

Az, hogy mit logolsz ki, és milyen hibát kezelsz, az két külön dolog. Senki nem tart vissza attól, hogy csinálj olyan exceptionöket, aminek egy objektum, vagy asszociatív tömb az egyik paramétere, amiben változó mennyiségű, minőségű tartamat tudsz átadni a hibakezelőnek.

Egy jó exception szerintem definiálja a hiba kódját és fogadjon hozzá hibaüzenetet. Egy hibakódot több helyen is felhasználhatsz más üzenettel. Én pl rendszeresen használok HTTP exceptionöket, pl.: NotFoundException, aminek a hibakódja 404. A hibaüzenet viszont erőforrásonként változhat. Pl.: user esetén: User not found; product esetén: Product not found, stb. A loggerem pedig catch blokkban elkapja az exceptiont, loggolja a státusz kódot, az üzenetet (ami programkód szinten definiálódik, pl constansban), a trace-t és egy szerializált objektumot. Ezzel megfelelően egységes hibakezelést valósítok meg szerver és kliens oldalon is, mert kevés a hibakód, egységesek, és minden fejlesztő kolléga tudja, hogy mire gondolt a költő.
82

Kódok

Hidvégi Gábor · Szep. 25. (H), 09.01
A normál felhasználók más, rövidebb hibaüzenetet kapnak, mint az adminisztrátorok, ezért fontos a hibakód. Például ők csak azt látják, hogy nincs joguk megnyitni az adott űrlapot, de 2522, 2523 vagy 2524 a hibakód, nem mindegy, mert az első azt jelenti, hogy nem adminisztrátor, a második, hogy az adott űrlapszethez nincs joga, a harmadik meg azt, hogy nincs olyan dokumentum az űrlapon, amihez van olvasási joga.
6

Megvalósít

janoszen · Szep. 15. (P), 13.32
Kérdés, hogy a BlogPostInteractor miért példányosítja a kétféle lekérdezési módot?


Nem példányosít, megvalósít. A BlogPostInteractor osztály a megvalósítása a nevezett két interface-nek. Az interfaceket egybe is lehetne tenni, de ekkor minden megvalósításnak mindkét függvényt kellene biztosítania, és simán lehet egy olyan, hogy a legutóbbi bejegyzések mutatásánál valamiféle cache-t veszünk igénybe, míg a teljes post betöltésénél nem. (Kitaláltam valamit.)

Mi van, ha egy adott oldalon csak az utolsó n darab bejegyzésre van szükségünk, de az egyes posztokra nem? Ez is egy apró overhead.


Ez így ilyen formában nem igaz, ugyanis a másik függvény nem fut le, a betöltött bytecode pedig amúgy is cachelésre kerül. A különbség mérhetetlenül kicsi és én olyasmit optimalizálok amit mérni is tudok. Feltételezések alapján nem optimalizálok.
7

Interface

janoszen · Szep. 15. (P), 13.34
Kérdés, hogy ha a kivételek típusa (Exception) megjelenik a fájlnévben, akkor az interface-eké miért nem?


Izlések és pofonok. Az interfacek ez esetben a Boundary, stb. szavakkal vannak megjelölve mint absztrakciós rétegek, de akár mögé is teheted az interface szót ha úgy tetszik. Ebben nem szeretnék állást foglalni, mindenki csinálja úgy ahogy jól esik. Az IDE amúgy is megmondja mindenről hogy micsoda.
8

Kezdő

janoszen · Szep. 15. (P), 13.36
Kérdés, hogy kezdőknek ez alapján kéne elkezdenie programozni?


Nem gondolom azt, hogy egy kezdőnek objektum orientált módon kellene elkezdenie programozni, és azt sem gondolom, hogy a kódszervezés mikéntje nem feltétlenül kezdő probléma. Lássuk be, még mi "szeniorok" sem feltétlenül tudunk megegyezni arról, hogy hogyan kellene ezt jól csinálni. Ez egy módszer, nem A módszer.
9

Vállalatirányítás

janoszen · Szep. 15. (P), 13.44
Mi nem blogmotort gyártunk, hanem vállalatirányítási rendszert, számlázással, pénzüggyel, meg hasonló dolgokkal. De nálunk, ha bekerül valami az adatbázisba, az az adat "készen" van, amikor legközelebb szükség van rá, ritka kivételtől eltekintve nem kell formázgatni, hanem lekérdezés után egy az egyben megy ki a kliensre.


Én nem fejlesztek vállalatirányítási rendszert, de sejtésem szerint egy ilyen rendszerben nagyon szigorú és ritkán módosuló szabályhalmaz szerint működik minden.

Ezzel szemben egy nem technikai ügyfélnek írt webmotor, vagy induló fázisban levő startup dashboard-ja rengeteget változik, és sokszor előfordul, hogy egy meglevő funkcionalitást meg kell tartani de közben már új funkcionalitást is kell biztosítani.

De akkor minek tennénk bele egy objektumba? Miért kéne minden adattaghoz ezek után getter meg setter?


A settert, mint egy későbbi cikkben írtam is, el kell felejteni hosszú távon. (Immutable data structures.) Lehetne olyan osztállyal is dolgozni ami pusztán publikus változókból áll (mint egy struct), de akkor nehéz mellé csomagolni apróbb utility függvényeket amik közvetlenül az adott objektummal kapcsolatosak. Ezen felül nehéz megvalósítani az immutabilitást.
13

Változik

Pepita · Szep. 15. (P), 14.40
sejtésem szerint egy ilyen rendszerben nagyon szigorú és ritkán módosuló szabályhalmaz szerint működik minden
Ha csak egy magyar számlázót nézünk (vállalatirányítás nélkül), akkor is a NAV képes hozni (kb évente hoz is) olyan "apró" módosításokat, amik miatt kénytelen vagy hozzányúlni - feltéve ha segíteni akarod a felhasználókat abban, hogy az aktuális szabályoknak megfelelően számlázzon.
Egy vállalatirányítási rendszernél pedig a fő kérdés az, hogy milyen széles a felhasználói kör és nekik milyen sűrűn és mennyire változik a működésük.

Általánosságban én inkább azt mondanám, hogy mindkét terület módosításigénye közel azonos a "nem technikai ügyfélnek írt webmotorral", más kérdés, hogy Gáborék rendszerét hányan és kik használják.
15

Jo de

janoszen · Szep. 15. (P), 14.47
akkor is a NAV képes hozni (kb évente hoz is) olyan "apró" módosításokat,


Miután mi megtehetjük, mi abszolut "lean" módon kezeljük a módosításokat, sokszor hetente tekerünk át valamit a rendszeren. De ez nem lenne lehetséges ha nem lenne agyonszegmentálva.
33

Így van

Pepita · Szep. 15. (P), 22.08
A mondanivalód lényegével egyetértek, csak olyan értelemben nem, hogy számlázó vagy vállalatirányítási rendszerben nincs szükség rá. :)
19

Változások

Hidvégi Gábor · Szep. 15. (P), 15.37
Nálunk a felület sokat változik, mert eléggé bonyolult, és sok iteráció, amíg a valós használathoz alakítjuk. De például független az adatok mentése a megjelenítésüktől, így az utóbbi esetben statikus szettel dolgozunk, ami tényleg közvetlenül az adatbázisból a képernyőre megy (azaz kvázi immutable).

Tehát így működik a megjelenítés:
  • Adatbázis lekérdezés
  • Adatstruktúra felépítése (json tömb)
  • Adatok összeházasítása a sablonnal (kliensen)
  • HTML frissítése (ami változott)


Az adatmegjelenítés így nagyon egyszerű és nagyon gyors.

Ahol pedig adatokat szerkesztünk, a szerkesztőmezőkhöz tartozó rövid parancssorokkal dolgozunk, amiket tudunk másolni vagy függvényszerűen hívni.
22

Látod

janoszen · Szep. 15. (P), 16.04
Látod, erre mondom azt, hogy különböző projekteken dolgozunk. Én több adatbázis motort támogatok, többek között nem csak relacionálisakat (van amikor két adatbázis motorból jön egy infó), az üzleti logikában komoly szűrések történnek helyenként mert muszáj, és onnan megy ki változatos formátumokba.

Ezen felül a kód nagy része elosztottan, több szoftverben fut, szóval közös libraryket is kell kezelni és tesztelni, ami eleve szükségessé teszi nagyon sok funkció (pl. adatlekérdezés) absztrahálását.
10

Switch

janoszen · Szep. 15. (P), 13.46
Vagy mondjuk kezelünk többféle kimenetet (json, pdf, fájl stb.), de elintéztük egy egyszerű switch-csel, nincs ezerfelé szétszabdalva, egy helyen ott van minden, tökéletesen működik.


Ez teljesen jó akkor, ha egy terméket gyártasz egy helyen való futáshoz, és Te magad szabályozod a fejlesztési ütemtervet. Tőlem most olyat kértek az egyik projektnél, hogy a rendszer minden tájékában szeretnének nem csak PDF, hanem Microsoft Word kimenetet is. Na és itt jön be az, hogy az ember inkább csinál egy absztrakciós réteget ami szépen lekonvertálja a kívánt kimenetre a cuccot, mint hogy ezer helyen beletegyen switcheket.
20

Egy

Hidvégi Gábor · Szep. 15. (P), 15.45
Természetesen a mi rendszerünkben is egy belépési és egy kilépési pont van:
1, feldolgozzuk a bejövő kérést, ami közvetve vagy közvetlenül megmondja a kimenet típusát
2, lekérdezzük a megváltozott adatokat, ha szükséges
3, a kimenet típusa alapján előállítjuk, amire szükség van (json, pdf stb.) - itt van a switch

Ezzel az egésszel csak arra szerettem volna utalni, hogy így szerintem jóval rövidebb és rugalmasabb a kódunk, mint OOP-vel.
32

A switcheléssel, ifeléssel

BlaZe · Szep. 15. (P), 22.01
A switcheléssel, ifeléssel van egy kis baj. Nevezetesen, hogy a megértéséhez fel kell építeni fejben egy kisebb-nagyobb fát, azt fejben is kell tartani, és az elágazási pontokon figyelemmel kell lenni olyan kódrészletekre is, amelyek az adott ág szempontjából irrelevánsak. Másképp megfogalmazva ifelt kódban nem fókuszáltan mozogsz, hanem a szóba jöhető lehetőségekkel mind foglalkozni kell. Mintha egy útvonaltervező programban az egész nxm-es térkép is jönne az útvonallal.

Az ilyen kód unit tesztelhetősége sem épp ideális, pont ugyanezért. A tesztelelendő esetek száma megsokszorozódik, a metódusok komplexitása az egekbe szökik.
44

Az if-ek kapcsos zárójelei

Hidvégi Gábor · Szep. 18. (H), 15.48
Az if-ek kapcsos zárójelei között lévő kódot ki lehet szervezni függvényekbe, ha nehéz a tesztelés.

Két éve írtam erre egy nagyon jó példát, az alapján el lehet dönteni, hogy az if-es vagy az objektumos megközelítés az átláthatóbb. Persze, nyilván ez szubjektív kérdés.
49

A kiszervezés nem csökkenti a

BlaZe · Szep. 18. (H), 16.48
A kiszervezés nem csökkenti a komplexitást.
59

Nem

Hidvégi Gábor · Szep. 19. (K), 08.44
A feladatét nem csökkenti, de az adott részekét igen, pont te hoztad fel ezt érvként a rövid függvények mellett, hogy a nagyobbakat bontsuk fel több darabra, így a kicsik jobban tesztelhetőek.
63

Az ifek maguk a komplexitás.

BlaZe · Szep. 19. (K), 16.28
Az ifek maguk a komplexitás. Minél több van, annál több esetet kell lefedni teszttel. Plusz annál nehezebben áttekinthető a kód. Akkor is, ha kiemeled a blokkokat külön függvényekbe. Ebben segít az, ha implementációnként külön van szervezve a kód, mert akkor kisebb komplexitású kódot kell tesztelni, kevesebb esettel, és könnyebben olvasható is lesz.

Ilyenek: cyclomatic complexity, path coverage.
56

Oo-val valahogy így nézne ki

inf3rno · Szep. 18. (H), 19.45
Oo-val valahogy így nézne ki a fő program, minden más külön fájlban lenne, ami ha érdekel, akkor beletúrsz, ha meg elég ennyi info, akkor tovább lépsz.

var kontener = new Kontener();
var objektum = kontener.adjObjektumot(feltetel);
objektum.tedd();
Ha összehasonlítjuk a példáddal, azt hiszem lejön, hogy melyik az átláthatóbb.
    if (feltetel) {  
      var tovabbi_feltetel = vizsgalat_eredmenye();  
      if (tovabbi_feltetel) {  
        tedd_ezt_így();  
      }  
      else {  
        tedd_amazt();  
      }  
    }  
    else {  
      tedd_emezt();  
    }  
11

Bloat

janoszen · Szep. 15. (P), 13.48
Szóval ez a blogmotor így szerintem nem clean code, hanem tiszta bloat, az ágyúval a vérebre tipikus esete. Nem véletlenül kérdeztem évekkel ezelőtt, hogy hol van az a határ, ahol érdemes OOP-t használni? Nálunk egy helyen lehetne: a biztonsági mentésben lehet választani, hogy ftp-vel vagy scp-vel működjön – minden más funkcionalitás csak egyszer szerepel a kódban, nincsenek ismétlések.


A linkelt kódot ne úgy nézd mint egy blogmotor, hanem inkább tekints rá úgy, mint egy falazásilag kész házra: be vannak húzva a kábelek mindenhova, fel vannak húzva a falak, de még messze nincs kész. Ez egy váz, amire az ügyfél igényei szerint fel lehet aggatni a cuccokat és sokszor az a nagyobb meló.

Az OOP azért fontos, mert rengeteg eszköz van hozzá és még a PHP is biztosít hozzá valamilyen szintű szigorú és erős típusosságot, míg a procedurális és funkcionális programozásra nagyon kevés kényszerítő és minőségellenőrző eszköz létezik. Javaban lehet pl. funkcionális programozást folytatni úgy, hogy a függvények paraméterei kényszerítettek, nem csak úgy random adogatunk át dolgokat és ezt használom is nagyon lelkesen.
14

Csak pár vélemény

Pepita · Szep. 15. (P), 14.44
Remélem Janoszen fel fogja venni a kesztyűt és tételesen megválaszolja a kérdéseidet, én csupán pár észrevétellel próbállak segíteni, hátha akkor könnyebben megérted, hogy ez a sok "OOP fan" miért is gondolkozik úgy.

Szerintem egy kezdőnek a legjobb, ha a programozást nem bármiféle webfejlesztéssel kezdi, hanem valamilyen igazán alacsony szintű nyelven, pl assembly. Persze száraz, nehéz, sok munkával kevés látszat, de ha az alapvető processzor - memória - fájlrendszer működéssel nincs tisztában valaki, akkor a magasabb szintű nyelveken sem fog tudni elég jól érvényesülni.
Utána érdemes valami egyszerűbb strukturált nyelv, pl valamilyen basic, a "jó" öreg turbopascal, vagy egyéb. Még nem OOP, de már nem csak bitek meg bájtok vannak, hanem némi segítség vezérlési szerkezetekre, függvényekre, nem neked kell feltalálni az adattípusokat.
Utána valamilyen valóban OOP nyelv, mert még a PHP 7.1.8 sem teljesen az, bár 7.0 óta nagyot lépett afelé.

hol találkozunk ilyen bonyolultságú projekttel?
Mindenütt. Egy tök egyszerű CMS (mint a példa) esetén is megéri: átláthatóság, továbbfejleszthetőség, működési biztonság miatt. (Itt nekem úgy tűnik, hogy nagyon sok dolog van, amit azért tartasz felesleges rossznak, mert pusztán nem érted, hogy mire való.)

Mi lesz, ha a mostani router interface nem megfelelő?
Az interface feladata épp az, hogy egységes kapcsolódási felületet hozzon létre, tehát ha bármilyen routing szabály változása miatt új router-t kell írni, annak felhasználása ugyanúgy történjen. Pont azt a problémát oldja meg, amitől tartasz.

Kérdés, hogy miért van több kivétel definiálva, amikor nincsenek kifejtve, azaz a kódjuk megegyezik?
Legtöbbször azért, mert külön-külön tudod elkapni és kezelni, fajtájuk szerint:
try {
// Pár problémás műveletet teszek ide
} catch (MyExceptionA $e) {
// Egyik (spéci) fajta hiba történt
} catch (MyExceptionB $e) {
// Másik spéci hiba
} catch (Exception $e) {
// Általános (nem tudjuk milyen) hiba
}
Kérdés, hogy ha a kivételek típusa (Exception) megjelenik a fájlnévben, akkor az interface-eké miért nem?
Gondolom a loader miatt.

Kérdés, hogy kezdőknek ez alapján kéne elkezdenie programozni?
Ha OOP iránt érdeklődik, akkor nagyon jó példa. Ha nem, akkor nem.

Szüksége van erre egy kezdőnek, de akár egy átlagos weboldalnak?
A weboldalnak (üzleti értelemben) semmi szüksége rá, "neki" tökmindegy, hogy milyen kód szolgálja ki a tartalmat, a lényeg, hogy azt kapja a látogató, amit kért.
A (nagyon) kezdő programozónak sincs rá szüksége, csak mikor már érdeklődik iránta, vagy belefut olyan problémákba, mint itt.
Összességében akkor van rá szükség, ha így átláthatóbb, tesztelhetőbb és könnyebben karbantartható kódot tudsz írni (ne haragudj, de rajtad kívül kb mindenki).

Miért kéne minden adattaghoz ezek után getter meg setter?
Ennek validálási és biztonsági szerepe lehet főként, onnantól a felhasználása rendkívül egyszerű (Data Access Object), és minden fajta adathoz (User, Node, Comment, stb) tök hasonlóan tudsz nyúlni.

a hibakezelést úgy valósítottuk meg, hogy hibakód plusz hibaüzenet
És amikor elkapod a hibát, egy seregnyi if / elseif vagy külön switch, amivel végigiterálsz a kódon? Az sokkal kevésbé átlátható.

hol van az a határ, ahol érdemes OOP-t használni?
Embere / team válogatja, szerintem a szimpla session kezelés már rég megüti a szintet, ha még felhasználókezelés sincs, pláne nem blog.

Továbbra is az az érzésem, hogy egyszerűen nem érted a hasznosságát, ami egy dolog, de ez miért hozza magával, hogy kampányolsz ellene?

SZERK: Időközben míg én egy commentben leírtam pár észrevételt, Janoszen tételesen és precízen válaszolt, köszi! :)
24

»hol találkozunk ilyen

Hidvégi Gábor · Szep. 15. (P), 19.08
»hol találkozunk ilyen bonyolultságú projekttel?«

Mindenütt. Egy tök egyszerű CMS (mint a példa) esetén is megéri: átláthatóság, továbbfejleszthetőség, működési biztonság miatt. (Itt nekem úgy tűnik, hogy nagyon sok dolog van, amit azért tartasz felesleges rossznak, mert pusztán nem érted, hogy mire való.)
Szerintem a fenti blogmotor kódja már a nem átlátható kategóriába tartozik a túl sok generális megoldás miatt.

A továbbfejleszthetőség egy teljesen megfoghatatlan dolog, hacsak nem látsz a jövőbe. Én nem látok. Fogalmam sincs, mit fognak kérni, ezért nem tudom, melyek azok a részek, amit általánosan, parametrizálva kell megírni.

Hogy biztonságosabban működik-e a projekt, az sem feltétlenül állja meg a helyét. Mik a mérőszámok?

Biztos vagy benne, hogy te érted, mire valóak és hol használhatóak hatékonyan a különböző paradigmák?

»Mi lesz, ha a mostani router interface nem megfelelő?«

Az interface feladata épp az, hogy egységes kapcsolódási felületet hozzon létre, tehát ha bármilyen routing szabály változása miatt új router-t kell írni, annak felhasználása ugyanúgy történjen. Pont azt a problémát oldja meg, amitől tartasz.
Tudom, hogy mi az interface feladata. A fenti blogmotorban egy darab router osztály van implementálva. Ez alapján viszont nem tudjuk megmondani, hogy egy jövőbeni új router osztály ki fogja-e elégíteni ezt az interface-t. Ha ki, akkor szuper, ha nem, akkor feleslegesen dolgoztunk.

Épp ezért fölöslegesnek tartom ezt előre definiálni és megvalósítani.

És amikor elkapod a hibát, egy seregnyi if / elseif vagy külön switch, amivel végigiterálsz a kódon? Az sokkal kevésbé átlátható.
Ezt a mondatot nem értem. Ha bárhol hiba történik a kódban (pl. elfogy a hely), visszatérünk egy hibakóddal (és ha szükséges, a behelyettesítendő változókkal), a hibakezelő pedig lekéri az adatbázisból a hozzá tartozó hibaüzenetet.

Továbbra is az az érzésem, hogy egyszerűen nem érted a hasznosságát, ami egy dolog, de ez miért hozza magával, hogy kampányolsz ellene?
Naná, hogy nem értem! Csak abból indulok ki, hogy a mi kódunk jóval egyszerűbb: van hat alapmodul (adatbázis, felhasználókezelés stb.), két köztes modul, tíz főmodul, amelyek az alattuk lévő függvényekből építkeznek, és semmilyen bonyolultabb programozói technikára nem volt szükségünk ezek elkészítéséhez. Ezzel szemben a blogmotor áll 45 fájlból, és nem tud mást, csak pár bejegyzést megjeleníteni.

Szóval nem értem. De szeretném. Ha nekünk sikerült más módszerekkel dolgozni, akkor lehet, hogy másnál is működhet?

Tehát a problémát többféleképp is meg lehet oldani. Miben különböznek a körülmények?
27

Elvek

janoszen · Szep. 15. (P), 19.33
Szerintem szét kellene választani az OOP-t a mondularizálástól. Teljes lelki békével lehet modularizált kódot írni OOP-től függetlenül is. A S.O.L.I.D. elvek is teljesen jól alkalmazhatóak a programnyelvek többségében és bizonyítottan jól tovább fejleszthető kódhoz vezetnek. Ezen felül ezek sem kőbe vésett szabályok, hanem elvek, vagyis egy jó programozó tudja, hogy mikor kell szétszedni a kódját.

Az OOP egy eszköz. Ugyanúgy, ahogy a procedurális vagy funkcionális programozás is az. Sőt mi több, akár egy kódban is lehet használni mindegyiket. Olyan adathalmazod van ahol jól jön a csatolt funkcionalitás és az adatok elrejtése? Használj objektumot. Buta egyszerű függvényed van? Írj egy buta egyszerű függvényt. Olyan feladatod van amiben el akarod kerülni a mellékhatásokat? Írj funkcionális kódot. Elég csak megnézni a Pythont, tök másképp struktúrálja a kódját és ott nem számít eretnekségnek egy függvényt írni osztály helyett. Teljes békességben elvan egymás mellett a példányosított webszerver és egy zsák függvény. Sőt mi több, szerintem egy jó fejlesztő némi gyakorlás után bármilyen paradigmában képes jó kódot írni.

Azt viszont látni kell, hogy különböző nyelvek különböző szinteken támogatják az egyes paradigmák használatát. Procedurálisan akarsz programozni Javaban? Lehet, csak épp nem erre van kihegyezve a nyelv. Funkcionálisan PHP-ban? Háááááát.... sok sikert, hiányoznak hozzá még az alapvető eszközök is. Satöbbi.

A probléma a procedurális programozással leginkább ott van, hogy nagyon kevéssé köti meg a kezedet, nagyon kevés korlátot lehet szabni a berhelésnek, és szerintem ezért mozdult el a piac az OOP irányába. A fejlesztők felének kevesebb mint 6 év tapasztalata van, és az OOP kényszerítő eszközei némileg kordában tartják a szerteszaladó juniorhalmot.

Ettől függetlenül látni kell azt, hogy a mai napig van olyan NAGY számlázási rendszer amit Cobol hajt. Miért? Mert a sok junior által legyártott Java kód 1000x annyi erőforrást eszik. Ez nem azt jelenti hogy minden procedurális kód kötelezően gyorsabb, sőt. De volt ott olyan programozó aki megcsinálta hatákonyan. Mert értett hozzá.

Nem gondolom azt, hogy bárkinek állást kéne foglalnia bármelyik paradigma mellett. Sőt, szerintem egy jó programozó legalább minimális szinten gyakorlott és képes tűrhető kódot gyártani bármelyik paradigmában. Feladathoz az eszközt. Szétszaladó juniorhalomhoz OOPt, statikus és szigorú típusosságot, nádpálcát. Nagy JavaScript projektekhez funkcionális programozást, stb. És mindenkinek azt, amiben megtalálja a boldogságot és a sikert.
34

Ucsó! :)

Pepita · Szep. 15. (P), 23.02
Szinte tudtam, hogy mondatonként meg fogsz próbálni szétszedni, hát rendben, nekifutok mégegyszer, de valószínűleg utána már feladom..

Szerintem a fenti blogmotor kódja már a nem átlátható kategóriába tartozik a túl sok generális megoldás miatt.
Mindenkinek más és más, amitől átláthatóbb számára egy kód avagy nem, nyilván annak is oka van, hogy Te ennyire távol állsz ettől a paradigmától.

A továbbfejleszthetőség egy teljesen megfoghatatlan dolog, hacsak nem látsz a jövőbe.
Nem kell jósnőnek lenni hozzá, 21. század van, ma már (majdnem) minden változik. Régen rossz, ha egy szoftver úgy épül fel, hogy csak bizonyos részei módosíthatók könnyen. Ez bármelyik paradigmára igaz, nekem könnyebb OOP alatt szép és jó kódot írni.

Hogy biztonságosabban működik-e a projekt, az sem feltétlenül állja meg a helyét. Mik a mérőszámok?
Amíg nem válaszoltál a Ti tesztlefedettségetekre vonatkozó kérdésemre, nem sok realitását látom, hogy tőlem mérőszámot kérsz.. De ahogy Janoszen is mondta: szigorú típusosság, type és return type declaration, nádpálca csodákra képesek.

Biztos vagy benne, hogy te érted, mire valóak és hol használhatóak hatékonyan a különböző paradigmák?
Nem. Ez nem boolean; én azt gondolom, elég jól ismerem a főbb paradigmákat (mivel mindegyik "alatt" töltöttem párszáz / párezer órát), és ez alapján ki tudom választani, hogy mi áll hozzám a legközelebb, én mivel vagyok a hatékonyabb, és azt is látom, hogy általában (többségi alapon) is ez működőképesebb. Röviden: úgy gondolom, hogy eléggé értem a legtöbbet.

nem tudjuk megmondani, hogy egy jövőbeni új router osztály ki fogja-e elégíteni ezt az interface-t
Muszáj lesz. Ez a célja az interface-nek, illetve még az is, hogy kb tervezési időben pár perc alatt leírod, később a fejlesztési időben pedig gyorsabban és szabályozottabban kódolod le. Plusz több szinten (tehát valószínűleg jobban) átgondolod az egészet.

a hibakezelő pedig lekéri az adatbázisból a hozzá tartozó hibaüzenetet
Azt hiszem ezt rajtam kívül többen is kifejtették, mindenki kicsit máshogy, de a lényeg kb ugyanaz. Ha még mindig felesleges szerinted, akkor nem tudok többet tenni.. Annyi pici kérdésecske, hogy ha az adatbázisod (szerver) állt le pl, vagy néhány tábla "eltűnt" belőle, akkor a Te hibakezelőd honnan veszi a hibaüzenetet?

Csak abból indulok ki, hogy a mi kódunk jóval egyszerűbb
Na talán ebben van a lényeg!
Mindenkinek meg van, hogy mit lát egyszerűbbnek, mi az átláthatóbb, stb.
Ezért is kérdeztem azt is, hogy mit csinál egy odavetődött junior nálatok.
Mert pusztán ez a fő lényeg:
- adott feladatra biztosan lehet jó megoldást találni többféle paradigma alatt is (akár egyszerre több paradigmát használva is)
- a továbbfejleszthetőség (és átláthatóság, stb) "mérőszáma" az, hogy mennyire és milyen hatékonyan tud hozzányúlni egy most odakerülő fejlesztő (senki sem garantálja, hogy a szoftver életciklusa alatt a teljes stáb ugyanaz marad)
- a szoftver ára a teljes életciklusban legjobban a továbbfejleszthetőségen múlik
- a szoftver minősége szintén (mert ha rosszul fejleszthető, sog bug és hegesztgetés lesz az ára a módosításoknak)

Így annak megítélése, hogy "a mi kódunk jóval egyszerűbb", korántsem ennyire egyszerű, hogy pl hány fájlból vagy hány fv-ből áll.

Hogy másnál mennyire működhet a módszeretek, én nem tudom, de leginkább szerintem úgy lehetne kideríteni, hogy odaültetsz a kód elé egy juniort és egy seniort pár napra, minden dokumentáció nélkül. Mit ért meg belőle? Mert Janoszen "szuperbonyolult 45 fájlból álló agyoninterfészelt" (mindössze) blogmotorját úgy látom, hogy többen is hamar átláttuk és megértettük.
Nyilván ha most én állnék neki hasonlónak, akkor lennének jelentős különbségek (pl return type declaration), de az is biztos, hogy OOP lenne.

Nincs azzal baj, ha Te nem kedveled / nem érted / feleslegesnek tartod ezt a paradigmát, de kérlek ez még ne legyen elegendő ok arra, hogy másokat lebeszélj róla.
Hasonlóan Jánoshoz, nekem is nagyon jó mankó; PHP - re vetítve nekem nagyon tetszik a 7.0 - tól kezdődő irányvonal, amivel nagyot lépett a nyelv a szigorúan típusos (számomra ez a "valódi-") OOP felé.
45

én azt gondolom, elég jól

Hidvégi Gábor · Szep. 18. (H), 16.16
én azt gondolom, elég jól ismerem a főbb paradigmákat (mivel mindegyik "alatt" töltöttem párszáz / párezer órát), és ez alapján ki tudom választani, hogy mi áll hozzám a legközelebb, én mivel vagyok a hatékonyabb, és azt is látom, hogy általában (többségi alapon) is ez működőképesebb.[/code]Tényleg? Utóbbi időben a világ elment funkcionális irányba, mert bizonyos műveletek, nevezetesen az adatok transzformációja jóval hatékonyabb és biztonságosabb, ráadásul párhuzamosíthatóbb, mint más módszerekkel. És miről szól a webes programozás mind kliens-, mind pedig szerveroldalon? A szerveren a kérés alapján összehalászod az adatokat, és kiküldöd egy bizonyos formában a kliensnek, a kliensen pedig a bejövő adatokat átalakítod HTML-lé - webes alkalmazás esetében. Normál weboldalnál a transzformáció a szerveroldalon történik.

[quote]Annyi pici kérdésecske, hogy ha az adatbázisod (szerver) állt le pl, vagy néhány tábla "eltűnt" belőle, akkor a Te hibakezelőd honnan veszi a hibaüzenetet?
Van egy alap hibaüzenet tönb, amiben ott van, hogy "Nem sikerült csatlakozni az adatbázishoz".

De ahogy Janoszen is mondta: szigorú típusosság, type és return type declaration, nádpálca csodákra képesek.
Nagyon csodálkozom, hogy miért nem valamilyen eleve statikus típusos nyelven dolgoztok, mint például a Go?

szerintem úgy lehetne kideríteni, hogy odaültetsz a kód elé egy juniort és egy seniort pár napra, minden dokumentáció nélkül. Mit ért meg belőle? Mert Janoszen "szuperbonyolult 45 fájlból álló agyoninterfészelt" (mindössze) blogmotorját úgy látom, hogy többen is hamar átláttuk és megértettük.
Igen, de abból a nettó kód pár kilobájt, a többi a sallang az általános megoldások miatt.

Ha nulláról kéne írni egy ilyet, akkor abban lenne egy függvény az adatbázishoz való kapcsolódáshoz, egy a lekérdezések futtatásához, egy-egy pedig az utolsó n darab és egy poszt lekéréséhez. Tegyük fel, hogy a sablonozás ugyanúgy twig-gel menne.

Szóval melyiket könnyebb átlátni, a monstrumot vagy az előző lekérdezésben leírt két kilobájtos programot? És most tekintsünk el attól, hogy ezt OOP vagy procedurálisan valósítottuk meg.
50

Tipusos

janoszen · Szep. 18. (H), 16.53
Nagyon csodálkozom, hogy miért nem valamilyen eleve statikus típusos nyelven dolgoztok, mint például a Go?


Honnan tudod hogy nem? En Java, Python, Hacklang, PHP vonalon mozgok. Ebbol pontosan egy nem erosen (es statikusan) tipusos.
16

Még1 kérdés Gábor:

Pepita · Szep. 15. (P), 14.52
A Ti rendszeretek mennyire van egységtesztekkel lefedve, és mi történik akkor, ha betoppan egy vadi új fejlesztő, megkapja az első feladatát, ami megoldásához az (is) kell, hogy átírja a valamilyen_globalis() függvényt, aminek ettől megváltozik a kimenetének a struktúrája?
25

Teszt

Hidvégi Gábor · Szep. 15. (P), 19.10
Szerinted csak OOP kódra lehet egységteszteket írni?
28

Kérdés

janoszen · Szep. 15. (P), 19.45
A kérdés inkább az, hogy mivel reseteled el a modulok state-jeit, bár sejtésem szerint a backup globals megoldja.
35

Dehogy

Pepita · Szep. 15. (P), 23.04
A kérdés(ek)re is válaszolnál?
40

Mondjuk

janoszen · Szep. 16. (Szo), 09.59
Mondjuk az engem is erdekelne. :)
37

Ha injektálod a függvényeid

inf3rno · Szep. 16. (Szo), 07.33
Ha injektálod a függvényeid függőségeit, akkor természetesen lehet struktúráltan programozva is egységtesztelni, pl:

function x(dep1, dep2, val){
  return (dep1(val) + dep2(val)) / 2;
}

var res = x(function dep1(val){
  return val+1;
}, function dep2(val){
  return val+2;
}, 17);
console.log(res); // 20

Ilyenkor x-re lehet unit tesztet írni, ellenkező esetben csak integrációs teszt készíthető, pl:

function x(val){
  return (dep1(val) + dep2(val)) / 2;
}

function dep1(val){
  return val+1;
}

function dep2(val){
  return val+2;
}

var res = x(17);
console.log(res); // 20
Az elsőt a gyakorlatban szinte sosem lehet látni, szóval én úgy fogalmaznék, hogy van rá lehetőség, de ritka, mint a fehér holló.

Én mondjuk annyira nem vagyok híve az egységtesztnek, az integrációs teszteket sokkal jobban szeretem bdd jelleggel cucumber-el. Kevesebb meló van velük, ha később hozzá kell nyúlni, rugalmasabbak, de azért közel sem annyira erősek, mintha unit test van mindenre. Az olyan projektekre kifejezetten jók, ahol nem kell annyira precíz munkát végezni. Annak idején még prohardver fórumban próbáltam felvetni, hogy kéne tesztelni minden appot, meg hogy tdd vagy bdd alapon kellene csinálni minden projektet. Elég csúnyán le lettem osztva, hogy az mekkora plusz munka lenne már egy egyszerű pár soros projektnél is, de szerintem ha bdd-vel csinálják, akkor minimális befektetéssel elég szépen le lehet fedni, hogy működik e minden funkció még egy hobbi projekten is, és visszajön a rá fordított idő, ha esetleg valaki kénytelen debuggolni.
53

Köszi :)

Pepita · Szep. 18. (H), 17.30
Igazából Gáborék konkrét tesztkörnyezetére lettem volna kíváncsi, de tök érdekes a példád.

TDD-t tekintve nem igazán egyezik a véleményünk, szerintem rendkívül hasznos, főleg nagyobb projektnél és / vagy csapatmunkában.
Magamon azt tapasztaltam, hogy ha jól csinálom (~ mindig előbb elfailel a teszt, utána fejlesztek), akkor az több előnnyel is jár, emellett maga a "programkód legyártása" annyira rövid idő lesz, hogy szinte észre se veszem.
- Hasznos, hogy az igény oldaláról gondolom át először a fejlesztés során is az adott feladatot, sokkal jobban, hogy jó tesztet tudjak írni.
- Sokkal jobban tudok kódot szervezni is teszt írás közben vagy után, mert másik szemszögből is átgondolom ugyanazt a feladatot.
- Későbbi módosításnál nagyon jól tud jönni egy failed teszt, sokkal jobb, mint egy kiélesített bug, netán security hole. :) Igen, ehhez módosításkor a unittest-et is megfelelően utána kell húzni.
- Egyedül az tud lélekromboló lenni, ha utólag kell valamit tesztekkel lefedni, ilyet is csináltam sprinteken keresztül, de ezért is kárpótolt valamennyire az a biztonságérzet, hogy ezentúl nem csak "gondolom formán" és valamennyire manuálisan tesztelve kerül élesítésre a szoftvernek egy nagyon sokat használt része.

Azt gondolom, hogy aki(k) szerint "az mekkora plusz munka lenne", azok vagy nemigazán jól csinálták, vagy nem látják kellően azt a hosszabb távú megtérülést, amit a ki sem élesített esetleges hibák hoznak az ügyfél által bejelentett, hajat tépve gyorsan meghegesztett bugok helyett.

Azzal egyetértek, hogy BDD lehet egyfajta középút TDD és semmi közt, illetve minden jobb, mint a semmi.
Inkább UI teszthez volt szerencsém ehhez is, nekem nagyon tetszett, bár nem igazán tudom szigorú kategóriába sorolni, amit fejlesztettünk benne. Kb a korábbi, minden egyes élesítés előtt manuálisan végignyomkodott checklisteket programoztuk le, ami a tesztelőknek 0.5 - 1 óra volt korábban, az (a build folyamatba építve automatikusan) néhány perc alatt meg lett. De ettől még a backend egységtesztje ugyanolyan fontos - szerintem.
57

BDD-vel teljes mértékben ki

inf3rno · Szep. 18. (H), 20.06
BDD-vel teljes mértékben ki lehet váltani a TDD teszteket, egyszerűen annyi a különbség, hogy bekerül még egy absztrakciós réteg a tesztekbe, ami a feature-öket írja le szövegesen. Így a feature fájl alapján meg tudod mondani, hogy mit tesztelsz, és nem kell a kódban turkálni hozzá. Plusz sokkal rugalmasabb, mert az implementáció bárhogyan változhat, a feature fájl nem sűrűn fog. Keretrendszer írásnál, amikor gyakran újragondolja az implementációt az ember, egyszerűen ég és föld a kettő, illetve többféle implementációt is tudsz fejleszteni ugyanazokkal a feature-ökkel párhuzamosan, ha ahhoz van kedved. Egyébként BDD-nél én már nem szoktam annyira lemenni az egyes osztályok szintjére, mert általában már jóval előtte kidobja a hibát a teszt, ha valami nem stimmel és feleslegesen nem akarok annyi tesztet írni. Ettől függetlenül, ha valaki nagyon alapos akar lenni, akkor BDD-vel ugyanúgy megteheti, hogy minden egyes osztályt unit tesztel. Ez kicsit valahol tényleg a debug és a TDD között van, annyi különbséggel, hogy a TDD-nél egy picivel tágabb részben keresed hibát, de mégsem az egész programban, mint a hagyományos debugnál. Igazából ha a kódodban van a hiba, akkor így is pillanatok alatt megtalálni, ha viszont nem abban van, hanem valami függőségben, neadjisten magában a nyelvben, akkor több vele a szopás mire kiderül. Keretrendszernél én a végén inkább írtam a nyelvi feature-ökre pl Object.defineProperty is teszteket, hogy rendesen működnek e, mert volt olyan környezet, amiben nem, és több órám ráment, mire sikerült megtalálnom, hogy hol van a hiba...

Ha UI-t akarsz tesztelni, akkor karma+nightwatch a legjobb, amit eddig néztem, de akkor még a nightwatch kiforratlan volt. Szerintem azóta már beérhetett. Azon kívül még ott van a sikuli is, ha biztosan tudni akarod, hogy nem csúszik szét az oldal a különböző böngészőkön, esetleg játékot akarsz tesztelni vagy valami svg grafikont vagy ilyesmit.

Ha régi kódhoz nyúlsz, de nem kell módosítanod azt, csak használni, akkor egy anti-corruption layer-el el tudod fedni az implementációját, és tudod a saját modelledet alkalmazni, illetve erre az ACL-re tudsz teszteket is írni, így nem kell a régi kód felületére megírnod a tesztet. Ha bele kell nyúlnod, akkor nincs ekkora szerencséd, kénytelen vagy teszteket írni, ha nincsenek. Ez spagetti kódnál különösen fájdalmas, annál akár még az UI tesztek irányából is el lehet indulni, hogy lásd, hogy nem töröd el a kódot, amikor megpróbálod szétszórni osztályokba.
61

Na ez az oka

Pepita · Szep. 19. (K), 12.24
Emiatt nem érzem igazán teljes értékűnek:
Egyébként BDD-nél én már nem szoktam annyira lemenni az egyes osztályok szintjére
Így amikor változtatsz valamit és elromlik, nem tudod pontosan, hogy mit is csináltál rosszul.
Vagy minden egyes apró módosításnál futtatod a tesztet (ami ugye a gyakorlatban kb lehetetlen), vagy bevállalod, hogy
több vele a szopás mire kiderül

Ezért nekem még kicsit idegen, de meglátjuk mit hoz a jövő, lehet egyszer kipróbálom.
Nyilván ha meglévő spagettit akarnék tesztelni, hát örülök bárminek, amivel sikerül egy kicsit is lefedni. :)
65

minden egyes apró

inf3rno · Szep. 19. (K), 17.39
minden egyes apró módosításnál futtatod a tesztet (ami ugye a gyakorlatban kb lehetetlen)


Ajánlom figyelmedbe ezt a könyvet, ebben pontosan úgy megy a refactoring, mint ahogy fentebb írtad. Először megnézed, hogy megy e a teszt, aztán minden apró módosításnál újra lefuttatod, hogy lásd, nem e törtél el valamit. Jobb, mint revertelni fél óra írkálás után, mert nem tudod miért nem működik a kód. A tesztek egyébként TDD-nél és BDD-nél is gyorsak kellenek, hogy legyenek, pont emiatt. Ha integrációs teszteket írsz adatbázishoz, ehhez-ahhoz, akkor azokat külön kell venni, mert azok lassúak. Adatbázisnál lehet gyorsítani rajtuk, ha tranzakciót használsz és állandóan rollback-ezel, de a teszt adatbázis felépítése így is időigényes szokott lenni.

Egyébként a spagetti kóddal kapcsolatban sem az a legnehezebb, hogy hogyan írjál tesztet hozzá, hanem, hogy sűrűn előfordul, hogy a tesztek nem működnek, mert alapból hibás a kód, és ha belenyúlsz, akkor előfordul, hogy még jobban eltöröd. Na ez a része az, ami még nekem sem megy. :D
62

Így

Hidvégi Gábor · Szep. 19. (K), 13.17
Az alap moduljainkra vannak tesztek, a felettük lévőre nem. Ha megváltozik egy függvény paraméterezése, akkor a kódban ennek megfelelően mindenhol át kell írnia.
70

lehet részletesebben?

Pepita · Szep. 20. (Sze), 17.38
Ha nem titok, bővebben érdekelne, hogyan és mivel teszteltek.

Ha megváltozik egy függvény paraméterezése, akkor a kódban ennek megfelelően mindenhol át kell írnia.
Ez egy jó érv OOP és interface mellett. :)
Attól függően, hogy megfelel-e interface-nek:
- Igen: semmi gond, a felhasználó egyéb programrészeknek nem szabad, hogy baja legyen (vagy eleve rossz az interface)
- Nem: meg kell vizsgálni, hogy maga az interface még jó-e (vagy változtatni kell), az adott (speciális) problémára nem kell-e új, esetleg származtatott osztályt írni, etc-etc.

Itt én inkább a visszaadott értékre gondoltam, mint a paraméterezésre, és hát ez még php 7.1.x alatt is lehet ugye tömb, aminek aztán bármiféle elemei lehetnek, és épp attól törik el a felhasználó kódod, hogy kikerül belőle egy bizonyos kulcs, vagy bekerül egy olyan, amire nem számítasz. De sok egyéb lehetőség is van, pl ha nem használod ki a return type declaration-t, akkor simán rá is foreach-elhetsz valahol egy null-ra vagy string-re, bármire.
Interface-el pedig ki tudod kényszeríteni, hogy az implementáció helyes típust adjon vissza.
És ez még csak egy pofon egyszerű hibapélda volt, mégis ezer helyen bele lehet futni, ha nem figyelsz rá.
77

Teszt

Hidvégi Gábor · Szep. 22. (P), 13.36
Pontosan mire vagy kíváncsi a teszteléssel kapcsolatban?

Itt én inkább a visszaadott értékre gondoltam, mint a paraméterezésre, és hát ez még php 7.1.x alatt is lehet ugye tömb, aminek aztán bármiféle elemei lehetnek, és épp attól törik el a felhasználó kódod, hogy kikerül belőle egy bizonyos kulcs
class klassz() {
  public $tag = array();
}

$objektum = new klassz();
$objektum->tag = null;

foreach ($objektum->tag as $kulcs => $ertek) {
  PHP Warning:  Invalid argument supplied for foreach()
}

Objektumok adattagjai is lehetnek bármik, tehát az ellenőrzést nem úszod meg.

A PHP és a JS egyik legnagyobb előnye a statikus típusos nyelvekhez képest, hogy az asszociatív tömbökbe dinamikusan tehetsz bele új kulcsokat.
31

Fáradt vagyok most átrágni

inf3rno · Szep. 15. (P), 21.46
Fáradt vagyok most átrágni magam az egészen, az elejére válaszolok, amennyire tudok. Interface-t nem csak a cserélhetőség miatt használunk, hanem mert magasabb absztrakciós szintről alacsonyabb felé haladva történő fejlesztést könnyíti meg. Mondjuk kigondolod, hogy milyen feature-öket kell tudnia az appnak, csinálsz neki egy router-t, ami átadja egy controller osztálynak a kódot, és onnantól ha ennek az osztálynak valamilyen függősége van, akkor elég csak megadni az interface-t neki, kimockolni a teszteknél, aztán később megvalósítani. Szemben ha direktbe példányosítod, akkor muszáj először a függőséggel foglalkozni, hogy az működjön és csak utána jöhet az, ami rá épül. Tehát megfordulna úgy a fejlesztés iránya és alacsony szintről menne magas felé. Nem utolsó szempont persze az sem, hogy így rétegezni tudod az alkalmazásodat meg már az interface-nél el tudsz gondolkodni kicsit a megfelelő absztrakción.
38

Közben átolvastam, írom

inf3rno · Szep. 16. (Szo), 08.24
Közben átolvastam, írom tételesen:

Hol van szükség arra, hogy akár többféle router is lehet a rendszerben? Ha viszont csak egyet használ a projekt (márpedig a fenti blogmotorban csak egy van), akkor miért van általánosan megvalósítva? Hisz így egyrészt van egy minimális overhead, másrészt plusz munkával jár. De ki tudja, hogy mit hoz a jövő? Mi lesz, ha a mostani router interface nem megfelelő? Akkor lehet refaktorálni, azaz fölösleges melót tett bele az, aki a jelenlegit írta.


Az absztrakció megfelelő kialakítása mindig plusz munkával jár, át kell gondolni, nem csak odavetsz valamit a monitorra. Nagyjából ugyanez az előnye is, átgondolt, a jövőben már nagy valószínűséggel nem kell hozzányúlni az interface-hez. Ezzel szemben ha átgondolatlanul csinálsz valamit absztrakció nélkül, akkor sokkal nagyobb az esélye annak, hogy néhány új funkció hozzáadásánál refaktorálni kell az egész osztályt metódus nevekkel mindennel együtt, és törik az összes tőle függő kód. Természetesen ez is bevállalható, ha nem sokan függnek az adott osztálytól és/vagy nem várható, hogy változtatni kell rajta bármit a jövőben. Nem muszáj minden huszadrangú osztályt interface-el tökéletesen kidolgozni. Szerintem.

A minimális overhead, amit felhoztál érvként premature optimization-nek számít, ha webes programozásról beszélünk.

Kérdés, hogy miért van több kivétel definiálva, amikor nincsenek kifejtve, azaz a kódjuk megegyezik?


A kivételeknél nem a kód a lényeg, mert általában a törzse üres szokott lenni, hanem hogy a típus alapján tudjuk elkapni azokat, amiket el akarunk egy adott szinten, a maradékot meg tovább tudjuk engedni. Egyébként a csoportosításra ezeknél lehet üres interface-t is használni.

Értem én, hogy példakód, de azért ez egy kezdő számára ijesztő lehet. Végeredményben nem csinál mást, mint egy vagy több blogposztot lekérdez, a nettó kód ebből legfeljebb egy-két kilobájt, ezzel szemben itt a szerveroldalon az src könyvtár mérete negyvennégy kilobájt. Ez azt jelenti, hogy két kilobájt kódhoz negyven kilobájtnyi bloat jár OOP alatt? Nyilván ez erős túlzás, de mit is kapunk cserébe? Szüksége van erre egy kezdőnek, de akár egy átlagos weboldalnak?


Cserébe azt kapod, hogy minden osztályról pontosan tudod, hogy mit csinál, és ha bővíteni kell valamit funkcióban, akkor nagyon gyorsan és egyszerűen meg tudod tenni azt szemben egy spagetti kóddal. Egy kezdőnek nem biztos, hogy szüksége van rá, de ha mondjuk egyedi webshop fejlesztésébe vágja a fejszéjét, akkor már jobban jár vele, ha megtanulja, különben könnyen bedőlhet az átlag struktúrált kódra (Pistike spagettije) jellemző gyenge karbantarthatóság miatt a projekt. Sajnos sokszor találkozni ilyen "majdnem kész" projektekkel, ahol elfogyott a lendület és amik menthetetlenek, mert annyira tele vannak hibával.

Mi nem blogmotort gyártunk, hanem vállalatirányítási rendszert, számlázással, pénzüggyel, meg hasonló dolgokkal. De nálunk, ha bekerül valami az adatbázisba, az az adat "készen" van, amikor legközelebb szükség van rá, ritka kivételtől eltekintve nem kell formázgatni, hanem lekérdezés után egy az egyben megy ki a kliensre. De akkor minek tennénk bele egy objektumba? Miért kéne minden adattaghoz ezek után getter meg setter?


Ami kijön az adatbázisból, az sokszor messze van még egy view model-től, és alakítgatni kell rajta attól függően, hogy milyen formában akarod megjeleníteni, pl táblázatban, grafikonon, és így tovább. Csak egy nagyon egyszerű példa, ha van mondjuk egy mátrixod, azt is le tudod írni két különböző modellel, attól függően, hogy oszlopok vagy sorok szerint csoportosítod az értékeket:

[
  [1,2,3],
  [4,5,6]
]

[
  [1,4],
  [2,5],
  [3,6]
]
Az egyik táblázat motornak az egyik kellhet, míg a másiknak a másik változat. Ugyanígy pl ha pénzről beszélünk, akkor a currency-t ki kell írni a végére, hogy tudjad, hogy forintról és nem euróról van szó, stb. Rengeteg dolog van, ami miatt a nyers adat nem feltétlenül jó. A konkrét példát nem láttam, úgyhogy arról nem tudok nyilatkozni.


Szóval ez a blogmotor így szerintem nem clean code, hanem tiszta bloat, az ágyúval a vérebre tipikus esete. Nem véletlenül kérdeztem évekkel ezelőtt, hogy hol van az a határ, ahol érdemes OOP-t használni? Nálunk egy helyen lehetne: a biztonsági mentésben lehet választani, hogy ftp-vel vagy scp-vel működjön – minden más funkcionalitás csak egyszer szerepel a kódban, nincsenek ismétlések.


Szerintem nincs ilyen határ, bármekkora méretnél lehet oo programozni. Ha nem valami általános blog motort vagy keretrendszert csinálsz, akkor pl nem kell bele annyi absztrakció, mert overengineering lesz a vége és a legtöbb helyen úgysem fogod sosem lecserélni az osztályokat. Azzal a kijelentéseddel egyetértek, hogy ilyen esetben egy csomó plusz munkát beleraksz a semmiért. Azt hiszem ez ilyen know-how lehet, rá kell érezni a helyes arányra ebben is. Olvastam néhány könyvben annak idején, hogy egy osztály legyen annyira kicsi, amennyire csak lehet, de nem értek ezzel egyet én sem, mert a végén a kód nagy részét az fogja kitölteni, hogy egy soros osztályokat deklarálsz. Sokszor egyszerűbb egy nagyobb osztályba beleszórni, amit akarsz, aztán ha később bonyolódik a helyzet, akkor feldarabolhatod, kialakíthatsz interface-eket, és így tovább, ahogy a helyzet hozza. Ha csak egy prototípust vagy proof of concept-et csinálsz, akkor meg még tesztek nélkül is el lehet indulni, később ha bevált a dolog, meg be lehet pótolni az ezzel kapcsolatos hiányosságokat, és utána lehet refaktorálni, hogy tisztább legyen a végeredmény. Ezek persze többé-kevésbé az én véleményemet, eddigi tapasztalataimat tükrözik, biztosan van, aki jobban tudja...

Egyébként maga a kód egy-két fájlba belenézve szerintem szép.
54

Pontosan

Pepita · Szep. 18. (H), 17.40
Azt hiszem ez ilyen know-how lehet, rá kell érezni a helyes arányra ebben is.
Köszi ezt a mondatot és a gondolat kifejtését, nagyon egyetértek, de nem tudnám ilyen jól leírni. :)
Két fejlesztő ezért nem fog sosem egyetérteni abban, hogy hol húzódik a clean code határa (mármint ha lenne éles határ), mert mindenkinél picit más a "helyes arány". De ez jól is van így, ne legyünk egyformák.
46

Tesztek

Hidvégi Gábor · Szep. 18. (H), 16.19
A Linuxos, C-ben írt rutinkönyvtárakhoz is vannak egységtesztek, tehát valahogy megvalósítható interface-ek nélkül is a dolog.
52

Igen

janoszen · Szep. 18. (H), 16.54
Igen, de C-hez vannak header file-ok amivel ki lehet cserelni a fuggosegeket. PHP-hoz nincsenek.

Szoval akkor teszteltek vagy nem teszteltek PHP-ban?
55

+1

Pepita · Szep. 18. (H), 17.43
Valahogy az interpreter nem nagyon akarja lefordítani ezt a kérdést.. :)
41

A programból le lehet vonni

BlaZe · Szep. 17. (V), 23.39
A programból le lehet vonni azt a következtetést, hogy OOP-t ott lehet érdemes használni, ahol egy bizonyos funkciót legalább kétszer megvalósítunk, mert ad egy szabványos interface-t, amivel tudunk kommunikálni.
Ez kb mindig meg is valósul, hisz ugyebár van normális teszt lefedettségünk, ahol ezt-azt mockolunk, spy-olunk. Ez kell ahhoz, hogy fine grained, felelősségi körök mentén validáló automata tesztelést tudjunk csinálni. Neadjisten bármivel megdekoráljuk (auditálás, performancia mérés, tranzakció kezelés stb) az egy létező implementációt.

Az OOP (és a clean code) erőssége, hogy specifikus contextekben dolgozik a fejlesztő, aminek a kikényszerítése nyelvi elemekkel van megtámogatva. Ez nem azt jelenti, hogy a specifikus context elérhetetlen nem OOP paradigmával.

Ha már a teljesítmény is szóba került, egy értelmes compiler tudja, hogy ha csak egy implementáció létezik, akkor ott még csak teljesítmény vesztés se nagyon legyen. Class Hierarchy Analysis, monomorphic és inlining a kulcsszavak. És ott van még a CPU-k branch predictionje is.
47

Csak ismételni tudom magam:

Hidvégi Gábor · Szep. 18. (H), 16.42
Csak ismételni tudom magam: "A Linuxos, C-ben írt rutinkönyvtárakhoz is vannak tesztek, tehát valahogy megvalósítható interface-ek nélkül is a dolog."

Vegyük a blogmotorból a routert, amiből egy darab van és minden kérés elején lefut. Erre írok egy függvényt:

function utvalaszto($_post) {
  ...
}
$eredmeny = utvalaszto($_POST);

function teszt_utvalaszto() {
  $eredmeny = utvalaszto(array());
  assert($eredmeny);
}

Ebben az egész témában számomra nem az a lényeges, hogy OOP vagy sem, hanem hogy ha valamit ennyire egyszerűen is meg lehet oldani, akkor miért nem így csináljuk?
51

Ezt a kört már megfutottuk

BlaZe · Szep. 18. (H), 16.54
Ezt a kört már megfutottuk egyszer pont ezzel a megközelítéseddel. Feleslegesnek tartom megint belemenni.

Amúgy a C-s könyvtárak is definiálnak "interface"-eket. Nem véletlenül.
58

Gondolom szándékosan a

inf3rno · Szep. 18. (H), 20.28
Gondolom szándékosan a legalacsonyabb szintű függvény a példád, ami nem hív semmilyen másik általad definiált függvényt sem. Már fentebb részleteztem, hogy ennél magasabb szinten csak úgy tudsz unit tesztelni, ha kimockolod a függőségeket, amit pl php-ben vagy js-ben csak úgy tudsz megtenni, hogy átírod a kódot olyanra, hogy a paraméterekben adod át a függőségeket vagy azok neveit. Gondolom ilyet nem csinálsz, úgyhogy integrációs teszteket írsz továbbra is, és nem unit teszteket. Ami persze nem baj, de ne keverjük már a szezont a fazonnal.

Pont ma olvastam egy jó cikket , te jutottál eszembe róla az örökös oop vagy struktúrált vitáiddal.
67

Pöcögtetés

Hidvégi Gábor · Szep. 20. (Sze), 09.43
Erről már volt szó, egy-egy modul helyett bármikor beinclude-olhatom a teszt függvényekkel teli mását.
69

Szerintem nézz bele hogy

BlaZe · Szep. 20. (Sze), 11.18
Szerintem nézz bele hogy lehet mockolni, spyolni mondjuk Mockitoval (ez java lib). Utána lehet megérted anno miért írtuk erre a példádra, amit írtunk.

Megint a plusz munka mellett érvelsz ugyanis (egyéb problémák mellett).
71

dupla karbantartás

Pepita · Szep. 20. (Sze), 17.43
beinclude-olhatom a teszt függvényekkel teli mását
Eszerint a kódodban a tesztelt részek eleve kód-duplikáció eredménye?
Akkor valójában nem is az éles kódot teszteled, és emberi odafigyelésen múlik, hogy a teszt mennyire hasonló kódot tesztel az éleshez képest... :(
Márpedig az ember tévedhet, és szokott is.
72

Bár nem jellemző, de ha elég

inf3rno · Szep. 20. (Sze), 23.51
Bár nem jellemző, de ha elég kicsik a moduljaid, értsd: csak pár függvényt tartalmaznak, akkor elfogadható ez is egyfajta unit test-nek. Én speciel arra gondoltam, hogy minden függvényt egyesével tesztelsz úgy, hogy a függőségeit kimockolod. Általában ez a jellemzőbb struktúráltra wiki szerint is, viszont a te módszereddel minden függvény külön fájlban kell, hogy legyen.

Intuitively, one can view a unit as the smallest testable part of an application. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method. Unit tests are short code fragments created by programmers or occasionally by white box testers during the development process. It forms the basis for component testing.
- wiki

Gondolom ezek a "teszt függvények" a mockjaid, amiket be tudsz az egyes teszteseteknél kívülről állítani, hogy hogyan viselkedjenek. Esetleg használsz valamilyen mocking libet erre? Nem világos, hogy hogyan oldod meg, mondjuk én nem vagyok annyira jártas a strutúrált php eszközeiben, én csak a PHPUnit-ot ismerem, de abban sem nagyon tudok olyan részről, ami függvények ilyen formán történő kimockolását csinálná.
73

Így

Hidvégi Gábor · Szep. 21. (Cs), 08.20
Legalul van az adatbáziskezelő, amiben van pár lekérdezéskezelő és táblainformációs függvény. E fölött van öt modul, amelyek teljesen függetlenek, és ezekből négynek csak egy függősége van, az adatbáziskezelő. Ezek a modulok az aktuális állapotot kezelik (lekérdezések, felhasználómenedzsment, parancssor [command-queue]).

Ezeket lehet unit tesztelni.

A modulok felett vannak az API parancs gyűjtemények, amelyek a modulok függvényeinek hívásából állnak.

Itt már integrációs teszteket kell végezni.
74

Ezek szerint csak az

inf3rno · Szep. 21. (Cs), 13.46
Ezek szerint csak az adatbázist váltod ki a legtöbbször valamilyen teszt függvényekkel? Hány függvényt tartalmaz egy-egy modul? Ezek a függvények egymástól mennyire függenek?
75

Igen

Hidvégi Gábor · Szep. 22. (P), 10.19
Egy-egy modul öt-húsz függvényből áll, és azok függetlenek egymástól.
76

Akkor tényleg unit tesztelsz.

inf3rno · Szep. 22. (P), 11.54
Akkor tényleg unit tesztelsz.
142

Gondolat

Pepita · Okt. 8. (V), 16.56
Azt gondolom Gábor, hogy legalább számodra (de valószínűleg másoknak is) hasznos lenne, ha a különböző szálakon elindult vitákat / véleményeket megpróbálnád mondjuk egy commentben összegezni.
Javítja a gondolkodást, segíti a "big picture" helyes megtalálását.
Sok mindenről szó volt, helyenként egész részletekbe menően, de jó lenne ebből kialakítani egy értékesebb vázat.
Neked is segítene jobban megérteni azt a sok "hülyeséget", amit itt különálló szálakon összehordtunk.