Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: anonym 06. 06. 2018, 17:51:27
-
Pravidelně se s OOP dostávám do problému, který se mi konečně podařilo podchytit a formalizovat. Zjistil jsem, že onen učebnicový příklad aplikace OOP sám o sobě smrdí. Jde o tohle:
Máme třídu User, která je v základu jakési POJO. No a je potřeba mít možnost uložit Usera do DB. Učebicové OOP říká, že máme dát metodu save() přímo do User. Na první pohled to zní logicky a elegantně, ale je to v principu kravina. Ten User bude mít vazby na další třídy, třeba na Company a Address atp. Co bude v metodě save(), když byl vytvořen rovněž i Company a Address objekt a User na to má návazovnost, tzn. nemá smysl vytvořit Usera bez nové Company a Address? Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
To vede k architektuře, která z vysoka kálí na takovéto OOP paradigmata: Z User se stane obyčejné POJO, které bude mít dejme tomu závislost na jinačí POJO, a vytvoří se třída DBUtil, která bude pro jejich zpracování.
No a ta architektura je víte jaká? Daleko více připomíná procedurální programování než OOP: to POJO je datová struktura a to DBUtil je zpracovávající mechanismus.
Budiž mi důkazem, že v drtivě nejpoužívanějším frameworku Javy, ve Springu, to přesně takhle funguje. Třídy obsahující metody s jsou v podstatě všechny bezestavové a v jedné jediné instanci, úplně stejně jako kdyby to byly jen funkce. A POJO třídy jsou jediné, ze kterých se dělají instance.
No a proč to píšu, protože už si hodně dlouho, jak chuj, lámu hlavu s tím, proč kdykoliv když se snažím udělat si dobře architekturu aplikace, narazím na nejrůznější dilemata. Tak teď už to vím, jeden z těch hlavních důvodů je, že se snažím dělat OOP, a ono to přitom ani nejde!
-
U vsech malych bohu, ktera ucebnice ma takovehle "ucebnicove" reseni?
Co myslis tim "nejde"?
-
Učebicové OOP říká, že máme dát metodu save() přímo do User.
která učebnice to říká?
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
proč nemůžeš?
-
V daném příkladu na metodě User::save() nic špatného nevidím.
To co se ale nebude dít, je, že se v té metodě opravdu nebude zpracovávat Company, ani Address.
Správný OOP přístup k tomu je ten, že úkon uložení objektu User se deleguje jinam.
Oddělení datové struktury, a "zpracovávacího mechanismu" (algoritmu) do jiné třídy, to je také "best-practice" v OOP, tady konkrétně podle návrhového vzoru Visitor. Určitě to ale nebude zpracovávat žádný Singleton typu DBUtil!
Ale líbí se mi, že je tady konečně nějaký konkrétní příklad...
-
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
Také to tak v čistém OOP být nemá. Metoda User.save() má uložit stav uživatele – asi to bude znamenat i uložit nějaké vazby na Company a Address, např. jejich ID. A Company a Address zase budou mít své metody save(), které se postarají o uložení konkrétních objektů.
-
V daném příkladu na metodě User::save() nic špatného nevidím.
To co se ale nebude dít, je, že se v té metodě opravdu nebude zpracovávat Company, ani Address.
Správný OOP přístup k tomu je ten, že úkon uložení objektu User se deleguje jinam.
Oddělení datové struktury, a "zpracovávacího mechanismu" (algoritmu) do jiné třídy, to je také "best-practice" v OOP, tady konkrétně podle návrhového vzoru Visitor. Určitě to ale nebude zpracovávat žádný Singleton typu DBUtil!
Ale líbí se mi, že je tady konečně nějaký konkrétní příklad...
No, další problém té metody User::save() je, že kp dáš sendEmail()? Taky do User? A kam dáš orderHimToMakeMeCoffe()? Ŕekněme že User bude obsahovat taky profilovou fotku. A ty chceš mít funkcionalitu pridejLegracniKnirek(). To das taky do User? Prostě tohleto nedává smysl a vzniká tím dilema, kdy na jednu stranu to tam nemůžeš dát, protože je to chaos, ale na druhou stranu proč bys nemohl, když už tam máš save()? Prostě řešíš ted v OOP něco, co v procedurálním programování je úplně jasně dané jak to bude.
A nedá se z toho ani nějak vyvodit jednoznačně to, jak to teda správně poskládat. Proto mi přijde lepší říct si seru na to a udělat to tak, jak se to dělá ve Springu, a to je velice podobné tomu, jak se to dělá u procedurálního programování, tzn. rozlišuju Service (stateless instance) a to ostatní (POJO objekty s daty). V ten moment to máš taky správně, je to přehledné, a nemusíš se zbytečně zamýšlet nad tím, jak to správně komponovat.
-
Oddělení datové struktury, a "zpracovávacího mechanismu" (algoritmu) do jiné třídy, to je také "best-practice" v OOP
Nikoli, OOP je pravý opak, tedy spojení dat a s nimi souvisejících funkcí do jednoho objektu.
Jenže dnes se málokde programuje skutečně v OOP – spíš se používá strukturované programování s oddělením datových struktur a výkonného kódu, přičemž obojí (struktury i výkonný kód) je na úrovni programovacího jazyka implementován pomocí objektů. Často z toho vzniká zmatení, protože programovací jazyky obvykle mají jenom jeden typ objektů, takže na úrovni zdrojového kódu se nedá přímo rozlišit, co jsou struktury a co výkonný kód. Rozlišuje se to pojmenováním – datovým strukturám se říká třeba datové třídy, POJO, entity nebo přepravky, výkonnému kódu pak třeba služby. Ještě zřetelnější je to u vzdálených služeb, kdy výkonný kód je implementován jako služba na vzdáleném serveru, a ta služba na vstupu bere a na výstupu dává datové struktury, např. XML nebo JSON.
Akorát mi připadá, že to není moc teoreticky zpracované, protože programovací paradigma „datové struktury a vedle oddělený kód, který s nimi pracuje“ je podle mne považováno za obecné best-practice (ať už se tak programuje ve strukturovaném C nebo v objektové Javě), přitom pokud vím nemá ani žádné jméno. A to, že se ty datové struktury a služby interně implementují pomocí OOP podle mne taky nemá žádné jméno – přitom je to jen klasické zapouzdření, protože i u struktury je lépe definovat ji rozhraním, implementace uvnitř se ale může měnit; stejně tak služba, i bezestavová, často potřebuje mít uložena nějaká data.
-
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
Také to tak v čistém OOP být nemá. Metoda User.save() má uložit stav uživatele – asi to bude znamenat i uložit nějaké vazby na Company a Address, např. jejich ID. A Company a Address zase budou mít své metody save(), které se postarají o uložení konkrétních objektů.
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
-
Ve slušné učebnici bude spíš popsán rozdíl mezi Active Record a Data Mapperem. Na to se tazatel asi ptá. IMHO každý přístup má svoje výhody a nevýhody:
- https://cs.wikipedia.org/wiki/Active_Record
- https://cs.wikipedia.org/wiki/Data_Mapper
Navíc obě varianty mají různé implementace, které se liší v jednotlivostech (například nakládání se session - zda se rozlišuje mezi attached a ne-attached instancí).
-
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
To už se ale nebavíme o OOP, ale o tom, že relační databáze a OOP jsou dva různé světy, mezi kterými nejde jednoduše mapovat, a už vůbec ne automaticky. Což je vidět na věcech jako JPA, které vedou k tomu, že nemáte dobře ani relační model ani OOP.
Ale já tu rozhodně „čisté“ OOP nehájím, ani nevím, jestli vlastně takhle bylo OOP myšleno, nebo jestli se tohle stalo z OOP až v učebnicích. Nemyslím si, že by se objekt User sám měl umět i uložit do databáze – je to porušení principu jedné odpovědnosti.
-
No, další problém té metody User::save() je, že kp dáš sendEmail()? Taky do User? A kam dáš orderHimToMakeMeCoffe()? Ŕekněme že User bude obsahovat taky profilovou fotku. A ty chceš mít funkcionalitu pridejLegracniKnirek(). To das taky do User? Prostě tohleto nedává smysl a vzniká tím dilema, kdy na jednu stranu to tam nemůžeš dát, protože je to chaos, ale na druhou stranu proč bys nemohl, když už tam máš save()? Prostě řešíš ted v OOP něco, co v procedurálním programování je úplně jasně dané jak to bude.
A nedá se z toho ani nějak vyvodit jednoznačně to, jak to teda správně poskládat. Proto mi přijde lepší říct si seru na to a udělat to tak, jak se to dělá ve Springu, a to je velice podobné tomu, jak se to dělá u procedurálního programování, tzn. rozlišuju Service (stateless instance) a to ostatní (POJO objekty s daty). V ten moment to máš taky správně, je to přehledné, a nemusíš se zbytečně zamýšlet nad tím, jak to správně komponovat.
Metoda save() ukládá objekt User, ta tam být může. Metoda sendEmail() ale posílá zprávu, s objektem User nemá společného nic, sendEmail bude odpovědnost jiné třídy, která umí poslat e-mail.
Profilová fotka patří jednoznačně uživateli, ale operace nad tou fotkou už také patří do jiné třídy, žádná věda.
Oddělení datové struktury, a "zpracovávacího mechanismu" (algoritmu) do jiné třídy, to je také "best-practice" v OOP
Nikoli, OOP je pravý opak, tedy spojení dat a s nimi souvisejících funkcí do jednoho objektu.
Důležité je, že se jedná o související funkce, na tom se shodneme. Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
-
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
To záleží na tom, jak má ten který ORM framework vyřešeny závislosti entit. Může například uložit nebo aktualizovat všechny závislosti nebo naopak žádnou (pak je musíte uložit ve správném pořadí sám). Navíc může rozlišovat, zda je ta entita attached:
(https://i2.wp.com/javabydeveloper.com/wp-content/uploads/2016/02/entity-lifecycle2.png)
-
Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
Já si také myslím, že služba uložení objektu User nepatří do objektu User. Ale podle učebnicového OOP by právě do toho objektu patřila, a konkrétní implementace ukládání (databáze, soubor) by se řešila dědičností. Mimo jiné i proto, že v databázi bude mít uživatel asi nějaký identifikátor (primární klíč), který by User neměl nikam vystavovat, ale metoda pro uložení do databáze ho bude muset znát.
Jsem zvědav, co nám na to řekne Kit…
-
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
Také to tak v čistém OOP být nemá. Metoda User.save() má uložit stav uživatele – asi to bude znamenat i uložit nějaké vazby na Company a Address, např. jejich ID. A Company a Address zase budou mít své metody save(), které se postarají o uložení konkrétních objektů.
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
Tohle se dá řešit vzorem Observer. Objekty tříd Company a Address se zaregistrují do objektu třídy User. Když zavoláš User.save(), tak o tom ty objekty dostanou oznámení a data uloží také.
-
Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
Já si také myslím, že služba uložení objektu User nepatří do objektu User. Ale podle učebnicového OOP by právě do toho objektu patřila, a konkrétní implementace ukládání (databáze, soubor) by se řešila dědičností. Mimo jiné i proto, že v databázi bude mít uživatel asi nějaký identifikátor (primární klíč), který by User neměl nikam vystavovat, ale metoda pro uložení do databáze ho bude muset znát.
Jsem zvědav, co nám na to řekne Kit…
Databázovou proxy můžeš do objektu injektovat stejně dobře jako logování nebo odeslání mejlu. Stačí, aby tyto služby měly stejné rozhraní. Objekt User vůbec nemusí tušit, komu ta data posílá metodou save().
Dědičnost sem však vůbec nepatří. Co bys tady chtěl dědit? User přece není ani Databáze, ani Soubor.
-
No, další problém té metody User::save() je, že kp dáš sendEmail()? Taky do User? A kam dáš orderHimToMakeMeCoffe()? Ŕekněme že User bude obsahovat taky profilovou fotku. A ty chceš mít funkcionalitu pridejLegracniKnirek(). To das taky do User? Prostě tohleto nedává smysl a vzniká tím dilema, kdy na jednu stranu to tam nemůžeš dát, protože je to chaos, ale na druhou stranu proč bys nemohl, když už tam máš save()? Prostě řešíš ted v OOP něco, co v procedurálním programování je úplně jasně dané jak to bude.
A nedá se z toho ani nějak vyvodit jednoznačně to, jak to teda správně poskládat. Proto mi přijde lepší říct si seru na to a udělat to tak, jak se to dělá ve Springu, a to je velice podobné tomu, jak se to dělá u procedurálního programování, tzn. rozlišuju Service (stateless instance) a to ostatní (POJO objekty s daty). V ten moment to máš taky správně, je to přehledné, a nemusíš se zbytečně zamýšlet nad tím, jak to správně komponovat.
Metoda save() ukládá objekt User, ta tam být může. Metoda sendEmail() ale posílá zprávu, s objektem User nemá společného nic, sendEmail bude odpovědnost jiné třídy, která umí poslat e-mail.
Profilová fotka patří jednoznačně uživateli, ale operace nad tou fotkou už také patří do jiné třídy, žádná věda.
Tak tohle co píšeš je typické zmatení, je to totiž tvůj subjektivní dojem a neuvědomuješ si to. Řekněme, že děláš inf. systém typu sociální tíť. Místo sendEmail() tam dáme raději sendPM(), to víc pasuje. User z hlediska OOP v tomto systému představuje prostě Usera, který se přihlašuje do ystému, odhlašuje, na něco kliká, atd. Proč by tam nemohl mít metodu sendPM(msg), ale save() ano? To je totiž tvoje vlastní subjektivní asociace, že si třídu User bereše jako něco strikně vztahujícího se k databázi. User v OOP nemá mít save() o nic víc, než sendPM(msg).
-
A z jakých knížek se to učíte?
-
Učebicové OOP říká, že máme dát metodu save() přímo do User.
Ještě jsem takovou učebnici neviděl. Ale někteří vývojáři začínající s OOP to takhle implementují, to jo.
... ale je to v principu kravina.
... to je kravina.
Výborná argumentace.
Daleko více připomíná procedurální programování než OOP: to POJO je datová struktura a to DBUtil je zpracovávající mechanismus.
Procedurální je o něčem jiném.
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
Také to tak v čistém OOP být nemá. Metoda User.save() má uložit stav uživatele – asi to bude znamenat i uložit nějaké vazby na Company a Address, např. jejich ID. A Company a Address zase budou mít své metody save(), které se postarají o uložení konkrétních objektů.
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
Proč by to nešlo? To víš, že to jde.
A navíc tak uložíš nejdřív Company a pak usera, žejo.
To vede k architektuře, která z vysoka kálí na takovéto OOP paradigmata: Z User se stane obyčejné POJO, které bude mít dejme tomu závislost na jinačí POJO, a vytvoří se třída DBUtil, která bude pro jejich zpracování.
Celkově se jeví, že ten problém nemáš ani tak s OOP jako s tím, že máš nějakou konkrétní představu o OOP, a tu se snažíš silou mocí protlačit.
Čichá čichám cargo-cult.
-
Doporučuji se podívat na CLOS, případně jiné jazyky kde neexistuje vlastnictví metody objektem (aneb: Multiple dispatch).
Nicméně původní problém je spíš v tom, že "učebnicové OOP" je těžko prakticky použitelné na architekturu/design aplikace. Ne že by to nešlo, ale prostě to dře. Čím vyšší úroveň, tím je to horší.
-
Tak tohle co píšeš je typické zmatení, je to totiž tvůj subjektivní dojem a neuvědomuješ si to. Řekněme, že děláš inf. systém typu sociální tíť. Místo sendEmail() tam dáme raději sendPM(), to víc pasuje. User z hlediska OOP v tomto systému představuje prostě Usera, který se přihlašuje do ystému, odhlašuje, na něco kliká, atd. Proč by tam nemohl mít metodu sendPM(msg), ale save() ano? To je totiž tvoje vlastní subjektivní asociace, že si třídu User bereše jako něco strikně vztahujícího se k databázi. User v OOP nemá mít save() o nic víc, než sendPM(msg).
Název sendPM() je špatně. Názvy nemají obsahovat zkratky. Navíc asi těžko použiješ název sendPM() v jiné třídě, takže zbytečně budeš mít více rozhraní tam, kde stačí jedno. Proč nechceš použít jednoduchý název send()?
User.send(email);
User.send(database);
User.send(log);
-
Databázovou proxy můžeš do objektu injektovat stejně dobře jako logování nebo odeslání mejlu. Stačí, aby tyto služby měly stejné rozhraní. Objekt User vůbec nemusí tušit, komu ta data posílá metodou save().
Někdy to tušit musí, protože ta data k uložení budou pro každý způsob uložení trochu jiná.
Větší problém mám s tím, že to neodpovídá realitě. Já jako osoba o sobě vím, jak se jmenuju, umím to někomu říct. Ale do databáze evidence obyvatel se uložit neumím, k tomu musí přijít někdo z venku, kdo mne tam uloží (a obvykle se mne nejprve zeptá, jak se jmenuju).
Dědičnost sem však vůbec nepatří. Co bys tady chtěl dědit? User přece není ani Databáze, ani Soubor.
UserVDatabázi je User, stejně tak UserVSouboru.
-
Nicméně původní problém je spíš v tom, že "učebnicové OOP" je těžko prakticky použitelné na architekturu/design aplikace. Ne že by to nešlo, ale prostě to dře. Čím vyšší úroveň, tím je to horší.
Přesně tak. Něco jiného je programovací jazyk a něco jiného architektura/design aplikace. Takové ty klasické příklady s osobami, psy nebo geometrickými tvary svádějí k myšlence, že se popisuje architektura aplikace, ve skutečnosti se ale OOP používá na úrovni programovacího jazyka, zatímco architektura aplikace je spíš řešená jako data + služby.
-
Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
Také to tak v čistém OOP být nemá. Metoda User.save() má uložit stav uživatele – asi to bude znamenat i uložit nějaké vazby na Company a Address, např. jejich ID. A Company a Address zase budou mít své metody save(), které se postarají o uložení konkrétních objektů.
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
Jenze vy na to jdete prilis obecne. Muze to byt spravne i blbe. Zalezi na okolnostech. Pokud budete sejvovat data vzdy a pouze do DB(anebo neceho s podobnou logikou), a vite, ze to takhle bude do konce zivota programu(nebo aspon hodne dlouho) (a je jedno, jestli to bude mysql, postrgre nebo treba nerelacni hbase), tak porad to muze byt vhodne reseni. Proste v USER_ID User::save() zavolate COMPANY_ID Company::save(), pripadne ADDRESS_ID Address::save() a vysledne hodnoty pouzijete v ramci vlastniho User::save(). Jeste jednou, jestli je to spravne nebo ne, zalezi na celem systemu, ale spravne to byt klidne muze. Pokud mate slozitejsi DB, tak klidne save samotny muze volat nejaky ???::serialize() pro data, nebo neco podobnyho, to uz je implementacni detail. Z vaseho popisku nevyplyva, ze by to mohlo byt spatne. Ja nerikam, ze OOP je vsespasny, ale spousta lidi mu vytyka neexistujici, nebo spise nerelevantni problemy.
-
Databázovou proxy můžeš do objektu injektovat stejně dobře jako logování nebo odeslání mejlu. Stačí, aby tyto služby měly stejné rozhraní. Objekt User vůbec nemusí tušit, komu ta data posílá metodou save().
Někdy to tušit musí, protože ta data k uložení budou pro každý způsob uložení trochu jiná.
Větší problém mám s tím, že to neodpovídá realitě. Já jako osoba o sobě vím, jak se jmenuju, umím to někomu říct. Ale do databáze evidence obyvatel se uložit neumím, k tomu musí přijít někdo z venku, kdo mne tam uloží (a obvykle se mne nejprve zeptá, jak se jmenuju).
To přece neříkám přímo emailu nebo databázi, ale nějakým proxy, které tento úkon udělají.
-
Ano, to by bylo krásné, jenže nemůžete uložit jen tak Usera s id Company, když Company ještě není uloženo a tudíž ten klíč zatím v DB neexistuje.
To už se ale nebavíme o OOP, ale o tom, že relační databáze a OOP jsou dva různé světy, mezi kterými nejde jednoduše mapovat, a už vůbec ne automaticky. Což je vidět na věcech jako JPA, které vedou k tomu, že nemáte dobře ani relační model ani OOP.
Ale já tu rozhodně „čisté“ OOP nehájím, ani nevím, jestli vlastně takhle bylo OOP myšleno, nebo jestli se tohle stalo z OOP až v učebnicích. Nemyslím si, že by se objekt User sám měl umět i uložit do databáze – je to porušení principu jedné odpovědnosti.
TO souhlasim, je to hodne specificke. Ale pokud persistence entit v systemu je vzdy realizovana DB, tak to tam patrit muze(interne se muze na to samozrejme vyuzivat dalsi, klidne obecna trida).
Jinak, ale obecne, vetsina automatickych frameworku pro serializaci/deserializaci dat z db(jako bejvavalo napr. Alchemy) stoji zahovno, protoze generuje brutalne neefektivni cteni/zapis. Vetsinou je to system specific. A myslim, ze to je jeden z pripadu, kdy ma smysl "znovu vymyslet kolo". Protoze se to da udelat podstatne lepe a optimalizovaneji, a co si budem povidat, vetsina systemu ma zivotnost treba 10, 15 let... v delsim casovem horizontu stejne dojde vetsinou k dost velke zmene(ano napr financni systemy jsou vetsi stalice, ale to je vyjimka a je to spise kvuli (opravnenym) obavam, ze novy system bude mit spoustu dost financne narocnych(pripadne fatalnich) problemu) ... takze nema smysl resit absolutne/navzdy obecny ssystem ukladani dat.
-
Metoda save() ukládá objekt User, ta tam být může. Metoda sendEmail() ale posílá zprávu, s objektem User nemá společného nic, sendEmail bude odpovědnost jiné třídy, která umí poslat e-mail.
Profilová fotka patří jednoznačně uživateli, ale operace nad tou fotkou už také patří do jiné třídy, žádná věda.
Tak tohle co píšeš je typické zmatení, je to totiž tvůj subjektivní dojem a neuvědomuješ si to. Řekněme, že děláš inf. systém typu sociální tíť. Místo sendEmail() tam dáme raději sendPM(), to víc pasuje. User z hlediska OOP v tomto systému představuje prostě Usera, který se přihlašuje do ystému, odhlašuje, na něco kliká, atd. Proč by tam nemohl mít metodu sendPM(msg), ale save() ano? To je totiž tvoje vlastní subjektivní asociace, že si třídu User bereše jako něco strikně vztahujícího se k databázi. User v OOP nemá mít save() o nic víc, než sendPM(msg).
Jediné zmatení je, zda se bavíme o tom, jestli ten objekt vůbec smí mít metodu save(), zda to samo o sobě porušuje nějaký princip, nebo zda má tu příslušnou operaci také sám provést.
Pokud přistoupím na tvojí argumentaci, že ty různé metody, které píšeš, mají úplně stejný smysl tam (ne)být, pak tou metodou, která tam ve skutečnosti patří (pro nejobecnější případ), je metoda User::accept(visitor), s imlementací visitor.visit(this). Přičemž budu mít různé implementace dle libosti, např. FileSerializer::visit(User), DbSerializer::visit(User), apod. Framework tohle ale obvykle dělá za mě, vytvoří proxy, do databáze ukládá třeba pouze změny, atd..
A ta metoda send() ve třídě User má jednu podstatnou vadu, že není jasný příjemce, kdo je odesílatel, a co se posílá...
-
Nicméně původní problém je spíš v tom, že "učebnicové OOP" je těžko prakticky použitelné na architekturu/design aplikace. Ne že by to nešlo, ale prostě to dře. Čím vyšší úroveň, tím je to horší.
Přesně tak. Něco jiného je programovací jazyk a něco jiného architektura/design aplikace. Takové ty klasické příklady s osobami, psy nebo geometrickými tvary svádějí k myšlence, že se popisuje architektura aplikace, ve skutečnosti se ale OOP používá na úrovni programovacího jazyka, zatímco architektura aplikace je spíš řešená jako data + služby.
Učebnice by měla jasně sdělit, že se v dané kapitole řeší jen doménové objekty (tj. entity) a že architektura aplikace jako taková se bude řešit jindy.
A mělo by tam být vysvětleno, že mají být entity vyčleněny do samostatného balíčku a že mají mít co nejmenší, dobře definovanou závislost na zbytku aplikace.
-
Tak tohle co píšeš je typické zmatení, je to totiž tvůj subjektivní dojem a neuvědomuješ si to. Řekněme, že děláš inf. systém typu sociální tíť. Místo sendEmail() tam dáme raději sendPM(), to víc pasuje. User z hlediska OOP v tomto systému představuje prostě Usera, který se přihlašuje do ystému, odhlašuje, na něco kliká, atd. Proč by tam nemohl mít metodu sendPM(msg), ale save() ano? To je totiž tvoje vlastní subjektivní asociace, že si třídu User bereše jako něco strikně vztahujícího se k databázi. User v OOP nemá mít save() o nic víc, než sendPM(msg).
Nejspíš tam bude nějaká Service, která dostane kontext (uživatele, adresáta, text zprávy) a která tu zprávu pošle. Ale dokážu si představit, že by i User->sendMessage bylo obhajitelné řešení - záleží na zbytku aplikace.
-
Důležité je, že se jedná o související funkce, na tom se shodneme. Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
V reálu ale ten algoritmus obvykle není implementovány v třídě User, ale je do třídy User zděděný z předka anebo doplněný weavingem (např. v bytekódu). Což už není tak špatné řešení. Můžu mít taky traity a těmi entitu User uschopnit k tomu, aby se uměl uložit do souboru, do databáze, poslat mailem apod. a ani zde nebude ta implementace pomíchaná s entitou. Případně můžu použít vzor dekorátor. Který postup bude dávat smysl bude opět záležet na zbytku aplikace. Zda to je nebo není pravověrné OOP nedokážu říct. IMHO nejhorší z těch variant bývá dědění, protože je příliš rigidní.
-
To přece neříkám přímo emailu nebo databázi, ale nějakým proxy, které tento úkon udělají.
Pokud má objekt User službu „ulož se“, neříkám to žádné proxy, ale přímo tomu objektu User. A ten objekt musí vědět, že existuje nějaká proxy, která to uložení provede. Proč? Když budu chtít objekt User poslat e-mailem, budu muset jeho rozhraní rozšířit o „pošli se e-mailem“, což zase bude jen převolání nějaké proxy. To samé validace, tisk…
-
Důležité je, že se jedná o související funkce, na tom se shodneme. Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
V reálu ale ten algoritmus obvykle není implementovány v třídě User, ale je do třídy User zděděný z předka anebo doplněný weavingem (např. v bytekódu). Což už není tak špatné řešení. Můžu mít taky traity a těmi entitu User uschopnit k tomu, aby se uměl uložit do souboru, do databáze, poslat mailem apod. a ani zde nebude ta implementace pomíchaná s entitou. Případně můžu použít vzor dekorátor. Který postup bude dávat smysl bude opět záležet na zbytku aplikace. Zda to je nebo není pravověrné OOP nedokážu říct. IMHO nejhorší z těch variant bývá dědění, protože je příliš rigidní.
Já bych měl k tomu generování bytekódu trochu výhrady, ale budiž. To dědění bych škrtnul rovnou, ale na té implementaci mimo třídu User se asi shodneme.
-
Cely problem, ktery tu byl nastinen, nejde posuzovat podle jednotlivosti (jak se tu nekteri snazi), ale musi se posuzovat jako celek v konkretnim kontextu.
Jediné zmatení je, zda se bavíme o tom, jestli ten objekt vůbec smí mít metodu save(), zda to samo o sobě porušuje nějaký princip, nebo zda má tu příslušnou operaci také sám provést.
Jestli ma byt metoda save nebo ne soucasti objektu, je ve velke mire dano, jestli tim bude narusen SRP (single responsibility principle), tj. zda bude objekt mit jednu konkretni odpovednost, nebo ne.
-
To přece neříkám přímo emailu nebo databázi, ale nějakým proxy, které tento úkon udělají.
Pokud má objekt User službu „ulož se“, neříkám to žádné proxy, ale přímo tomu objektu User. A ten objekt musí vědět, že existuje nějaká proxy, která to uložení provede. Proč? Když budu chtít objekt User poslat e-mailem, budu muset jeho rozhraní rozšířit o „pošli se e-mailem“, což zase bude jen převolání nějaké proxy. To samé validace, tisk…
Však už jsem to psal:
User.send(database);
User.send(email);
...
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
-
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
Ne, ve všech případech to říkáte objektu User (posíláte mu zprávu „send“). A těžko pokryjete vše, co lze s objektem User dělat, jednou službou send s jedním rozhraním.
-
Jestli ma byt metoda save nebo ne soucasti objektu, je ve velke mire dano, jestli tim bude narusen SRP (single responsibility principle), tj. zda bude objekt mit jednu konkretni odpovednost, nebo ne.
Ano a to je u každé aplikace různé. Navíc se s rozvojem aplikace ta hranice posouvá - co do zodpovědnosti patří a co už ne. Takže se programátor snaží predikovat, na jak dlouho dopředu se vyplatí myslet a hledá optimum mezi složitostí a univerzálností.
-
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
Ne, ve všech případech to říkáte objektu User (posíláte mu zprávu „send“). A těžko pokryjete vše, co lze s objektem User dělat, jednou službou send s jedním rozhraním.
Pokud budu potřebovat, tak to rozhraní mohu přetížit. V objektu User je jednořádková metoda send(), která zavolá tu proxy a předá jí data. Na tom není nic složitého, prostě DI.
-
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
Ne, ve všech případech to říkáte objektu User (posíláte mu zprávu „send“). A těžko pokryjete vše, co lze s objektem User dělat, jednou službou send s jedním rozhraním.
Což může být právě vodítko, že kompetence „send“ má jít do jedné třídy a kompetence „save“ do druhé. Musím přemýšlet, zda se nedostávám do problémů, pokud musím psát „sendEmail“ abych to odlišil od „sendPersonalMessage“.
-
Důležité je, že se jedná o související funkce, na tom se shodneme. Ale já mám namysli konkrétní implementaci algoritmu, která provede uložení toho objektu User, a ta patří v každém případě jinam! Teprve potom totiž můžu ukládat do databáze, do souboru, nebo třeba do HTML. Právě tento princip např. Active Record porušuje.
V reálu ale ten algoritmus obvykle není implementovány v třídě User, ale je do třídy User zděděný z předka anebo doplněný weavingem (např. v bytekódu). Což už není tak špatné řešení. Můžu mít taky traity a těmi entitu User uschopnit k tomu, aby se uměl uložit do souboru, do databáze, poslat mailem apod. a ani zde nebude ta implementace pomíchaná s entitou. Případně můžu použít vzor dekorátor. Který postup bude dávat smysl bude opět záležet na zbytku aplikace. Zda to je nebo není pravověrné OOP nedokážu říct. IMHO nejhorší z těch variant bývá dědění, protože je příliš rigidní.
Já bych měl k tomu generování bytekódu trochu výhrady, ale budiž. To dědění bych škrtnul rovnou, ale na té implementaci mimo třídu User se asi shodneme.
Takže když shrneme původní dotaz, celá otázka se redukuje na to, zda implementaci do entity vůbec pustíme a odkud. Některé ORM frameworky umí více způsobů a lze si mezi vybrat. Samotná metoda save u entity není v rozporu s OOP, což ale neznamená, že je vše předem vyřešeno. Tak jako tak bude potřeba vyřešit řadu dilemat :-)
-
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
Ne, ve všech případech to říkáte objektu User (posíláte mu zprávu „send“). A těžko pokryjete vše, co lze s objektem User dělat, jednou službou send s jedním rozhraním.
Pokud budu potřebovat, tak to rozhraní mohu přetížit. V objektu User je jednořádková metoda send(), která zavolá tu proxy a předá jí data. Na tom není nic složitého, prostě DI.
Co kdyz budu mit v aplikaci dalsi tabulky? Customers, Officers, Managers...
A taky jim chci umet poslat email, ulozit do databaze, vypsat do logu...
Bude mit kazda svou tridu? Kazda metodu save, send, toString?
Budou implementovat stejne rozhrani?
Budou ty proxy umet pracovat se vsemi takovymi tridami? Nebo bude pro kazdy typ jina proxy?
-
Ve všech případech to říkám nějaké proxy: database, email, validace, tisk...
Ne, ve všech případech to říkáte objektu User (posíláte mu zprávu „send“). A těžko pokryjete vše, co lze s objektem User dělat, jednou službou send s jedním rozhraním.
Což může být právě vodítko, že kompetence „send“ má jít do jedné třídy a kompetence „save“ do druhé. Musím přemýšlet, zda se nedostávám do problémů, pokud musím psát „sendEmail“ abych to odlišil od „sendPersonalMessage“.
Ne, ve všech případech použiji metodu send(). V parametru uvedu, komu to má poslat.
-
Co kdyz budu mit v aplikaci dalsi tabulky? Customers, Officers, Managers...
A taky jim chci umet poslat email, ulozit do databaze, vypsat do logu...
Bude mit kazda svou tridu? Kazda metodu save, send, toString?
Budou implementovat stejne rozhrani?
Budou ty proxy umet pracovat se vsemi takovymi tridami? Nebo bude pro kazdy typ jina proxy?
Ano, ano, ano.
Nejsme však omezeni na jednu metodu send(). Ve třídách mívám 3-5 metod s poměrně unifikovanými názvy.
-
Ty objekty vždy něco reprezentují. Například Máslo reprezentuje entitu másla. Má nějaké vlastnosti jako velikost, váhu, výrobce...
Pak mám samozřejmě nějaké úložiště Lednice, Košík. To má také nějaké vlastnosti, třeba kapacitu.
Potud všechno jako podle knižního ideálu reprezentuje objekty reálného světa.
Jenže pak máte věci, které jsou taky objekty. A to jsou procesy. Například proces Nákup, DodáníZboží, Naskladnění, Inventura. To jsou také naprosto plnohodnoté objekty. V nich figurují víše uvedené objekty, ale také je zde hlavně uložena logika toho procesu (různé ACL, posílání výkazů, strhávání plateb, etc). A právě tady, protože to korensponduje s tím názvem.
Samozřejmě bychom tu logiku mohli hodit do vhodně pojmenovaných metod, a ty přilepit k více méně adekvátnímu objektu (Máslo se umí Nakoupit, nebo spíše Lednice umí Nakoupit?). Nemyslím si, že by to pak bylo objektovější - spíše naopak; třeba už jen z toho důvodu, že nemůžem nahrazovat procesy jak se nám líbí: Pepa umí Nakoupit, DodatZboží, Naskladňovat...
-
Vidim, ze tu dneska proklate bouri mozky, clovek se stale uci, dnes jsem se o OOP a Springu zase dozvedel spoustu noveho, napr ze boura OOP.
Ja bych cely problem resili proste.
A to prachobycejnym Springem MVC s IoC.
A tak vyrobim Spring komponentu @Controller, ktera obsahuje model sveta, vcetne registru Useru a definuje, jake akce se s Usery daji provadet pomoci @Service komponenty nad @Repository. User je prachsprosty POJO payload v tomto procesu (pripadne Hibernate entity, coz je to same s anotacema pro ORM), obdoba ceckoveho structu a neco jako User.save() jest naprosta picovina.
SpringMVC vlastne rika, ze spravny pristup je Lopato.priloz(Uhli), kdezto pristup definovany v tomto boureni mozku je Uhli.prilozSePomoci(Lopata) - tedy skolacke nepochopeni OOP.
Instancim User ani Uhli se zadne zpravy neposilaji, ony jsou temi zpravami.
A jestli chci podporovat save(User) a sendMail(User) - pres @Autowired si do @Controlleru naseru @Services cely kybl, treba pro posilani SNMP trapu...
-
Co kdyz budu mit v aplikaci dalsi tabulky? Customers, Officers, Managers...
A taky jim chci umet poslat email, ulozit do databaze, vypsat do logu...
Bude mit kazda svou tridu? Kazda metodu save, send, toString?
Budou implementovat stejne rozhrani?
Budou ty proxy umet pracovat se vsemi takovymi tridami? Nebo bude pro kazdy typ jina proxy?
Ano, ano, ano.
Nejsme však omezeni na jednu metodu send(). Ve třídách mívám 3-5 metod s poměrně unifikovanými názvy.
Jo je to validní řešení akorát se asi nehodí všude. Co když nemám k té entitě zdroják? Pak asi budu postupovat opačně, budu mít service která přijme jako parametr tu entitu nebo budu psát nějaký adaptér. Ve výsledku se mi ten tvůj přístup pak nemusí hodit.
-
Co kdyz budu mit v aplikaci dalsi tabulky? Customers, Officers, Managers...
A taky jim chci umet poslat email, ulozit do databaze, vypsat do logu...
Bude mit kazda svou tridu? Kazda metodu save, send, toString?
Budou implementovat stejne rozhrani?
Budou ty proxy umet pracovat se vsemi takovymi tridami? Nebo bude pro kazdy typ jina proxy?
Ano, ano, ano.
Nejsme však omezeni na jednu metodu send(). Ve třídách mívám 3-5 metod s poměrně unifikovanými názvy.
Pred casem jsi psal, ze kazdy takovy datovy bean delas immutable inicializovany konstruktorem, bez getteru a setteru.
K cemu je dobra u immutable beanu metoda save? Ze se to zkonstruuje, pak se to jednou ulozi pres save(), pri zmene se to cele zahodi a sestavi lautr znova?
Zajimave.
-
Co kdyz budu mit v aplikaci dalsi tabulky? Customers, Officers, Managers...
A taky jim chci umet poslat email, ulozit do databaze, vypsat do logu...
Bude mit kazda svou tridu? Kazda metodu save, send, toString?
Budou implementovat stejne rozhrani?
Budou ty proxy umet pracovat se vsemi takovymi tridami? Nebo bude pro kazdy typ jina proxy?
Ano, ano, ano.
Nejsme však omezeni na jednu metodu send(). Ve třídách mívám 3-5 metod s poměrně unifikovanými názvy.
Pred casem jsi psal, ze kazdy takovy datovy bean delas immutable inicializovany konstruktorem, bez getteru a setteru.
K cemu je dobra u immutable beanu metoda save? Ze se to zkonstruuje, pak se to jednou ulozi pres save(), pri zmene se to cele zahodi a sestavi lautr znova?
Zajimave.
Metoda send(cíl) prostě odešle data do cíle - zavolá metodu cíl.send(data). Pokud nemám gettery, tak to vlastně ani jinak nejde.
Immutable objekty jsou běžné.
-
...
Sice souhlasím s Tebou nastíněným kanonickým řešením, jenže debata je tu o tom, jaký je rozdíl mezi
- User.save()
- Lopata.priloz(Uhli)
Až budeš mít na tohle návod, dej vědět. Sémantický rozdíl tam v principu totiž není. A celá debata je tu jen a pouze o tom, jak rozdělit kompetence. A k tomu DI ani Spring MVC nic neříká. Ten jenom poskytuje prostředky. A třeba Spring Roo, Ebeans a další umí Active Record jako alternativu k Data Mapperu. Výhoda je jasná - schová před uživatelem DAO, čímž schová i komplexitu, což patří IMHO k principu zapouzdření.
Čili odpověď k věci se bude spíš zabývat kategoriemi objektů (kolekce, entita, proces, manager, blabla...) a pravděpodobně i jazykem (podstatné jméno, přídavné jméno, sloveso) - s cílem formulovat správné programovací věty a najít vodítka a typické role objektů k rozdělení kompetencí. Tomuhle je nejblíž příspěvek BoneFlute https://forum.root.cz/index.php?topic=18704.msg268483#msg268483
(Bohužel tím pádem přijdou na přetřes i návrhové vzory a zde je riziko, že se budou míchat návrhové vzory týkající se věci samotné (kompetencí) s návrhovými vzory, které jenom maskují nedokonalost jazyka (typicky javy). To jen na okraj.)
-
A třeba Spring Roo, Ebeans a další umí Active Record jako alternativu k Data Mapperu. Výhoda je jasná - schová před uživatelem DAO, čímž schová i komplexitu, což patří IMHO k principu zapouzdření.
Tím je současně vymezena i vhodnost Active Record - hodí se tam, kde nepotřebuju mluvit přímo s Data Mapperem. Například u transakcí podle mě už začíná koncept Active Record drhnout.
-
Immutable objekty jsou běžné.
Nejen to, dokonce i žádoucí.
-
Metoda send(cíl) prostě odešle data do cíle - zavolá metodu cíl.send(data). Pokud nemám gettery, tak to vlastně ani jinak nejde.
A jak taková metoda send(cíl) zapadá do oblasti zodpovědnosti objektu User? Jak už jsem psal, takových metod může mít objekt stovky – aby vytvořil unikátní identifikátor instance, aby se hezky vypsal, aby se zvalidoval, aby vrátil svou velikost v paměti…
-
A jak taková metoda send(cíl) zapadá do oblasti zodpovědnosti objektu User? Jak už jsem psal, takových metod může mít objekt stovky – aby vytvořil unikátní identifikátor instance, aby se hezky vypsal, aby se zvalidoval, aby vrátil svou velikost v paměti…
Samozřejmě nijak, pokud budeme mluvit o tom, že je User "entita". Kitovo tvrzení by platilo v případě, že by User byl doménová služba (předpokládám dle původního dotazu nějakou dnešní typickou MVC-like architekturu).
Nejpřesněji to napsal Youda, konkrétně i pro PHP je to IMHO asi nejčistší přístup. Já osobně to často zjednodušuji jen na Entity/Repository/Services. Tady není co vymýšlet :-)
-
Metoda send(cíl) prostě odešle data do cíle - zavolá metodu cíl.send(data). Pokud nemám gettery, tak to vlastně ani jinak nejde.
A jak taková metoda send(cíl) zapadá do oblasti zodpovědnosti objektu User? Jak už jsem psal, takových metod může mít objekt stovky – aby vytvořil unikátní identifikátor instance, aby se hezky vypsal, aby se zvalidoval, aby vrátil svou velikost v paměti…
Objekt User odpovídá pouze za data, která objektu cíl předá.
-
A jak taková metoda send(cíl) zapadá do oblasti zodpovědnosti objektu User? Jak už jsem psal, takových metod může mít objekt stovky – aby vytvořil unikátní identifikátor instance, aby se hezky vypsal, aby se zvalidoval, aby vrátil svou velikost v paměti…
Samozřejmě nijak, pokud budeme mluvit o tom, že je User "entita". Kitovo tvrzení by platilo v případě, že by User byl doménová služba (předpokládám dle původního dotazu nějakou dnešní typickou MVC-like architekturu).
Nejpřesněji to napsal Youda, konkrétně i pro PHP je to IMHO asi nejčistší přístup. Já osobně to často zjednodušuji jen na Entity/Repository/Services. Tady není co vymýšlet :-)
Ono ty stateless instance typu Service mají tu nevýhodu, že tím jak si nemůžeš udělat field tak ti značně roste počet parametrů v metodách, což nevypadá moc pěkně. Ještě jeden problém který s tímto je, vznikne, když metody takto pasuješ na funkce a přesto nemůžeš vrátit více objektů z funkce, než-li jeden, jako to jde třeba v Golang. To vede k využívání způsobu, kdy parametry metody jsou zároveň výstupními parametry.
A nevím ještě čím to je, ale na všech trochu legacy Spring projektech kde jsem dělal, byly ty Service pospojovány do spletité sítě, ve které byla snad spojeno všechno se vším. Lidi tam měli tendenci až moc nerozlišovat jednotlivé třídy problému. Pamatuju si, když byl kolega naštvaný, že vytvářím pro nový komplexní validační check dat (zbytečně) novou Service, místo abych těch 150 řádků kódu přidal do již existující 800 řádkové Service. Takže ani ta Spring architektura není samospásná a když se nad tím nepřemýšlí, bude v tom bordel.
-
A nevím ještě čím to je, ale na všech trochu legacy Spring projektech kde jsem dělal, byly ty Service pospojovány do spletité sítě, ve které byla snad spojeno všechno se vším. Lidi tam měli tendenci až moc nerozlišovat jednotlivé třídy problému. Pamatuju si, když byl kolega naštvaný, že vytvářím pro nový komplexní validační check dat (zbytečně) novou Service, místo abych těch 150 řádků kódu přidal do již existující 800 řádkové Service. Takže ani ta Spring architektura není samospásná a když se nad tím nepřemýšlí, bude v tom bordel.
Service jsou obvykle jen skladiště funkcí.
-
A jak taková metoda send(cíl) zapadá do oblasti zodpovědnosti objektu User? Jak už jsem psal, takových metod může mít objekt stovky – aby vytvořil unikátní identifikátor instance, aby se hezky vypsal, aby se zvalidoval, aby vrátil svou velikost v paměti…
Samozřejmě nijak, pokud budeme mluvit o tom, že je User "entita". Kitovo tvrzení by platilo v případě, že by User byl doménová služba (předpokládám dle původního dotazu nějakou dnešní typickou MVC-like architekturu).
Nejpřesněji to napsal Youda, konkrétně i pro PHP je to IMHO asi nejčistší přístup. Já osobně to často zjednodušuji jen na Entity/Repository/Services. Tady není co vymýšlet :-)
Ono ty stateless instance typu Service mají tu nevýhodu, že tím jak si nemůžeš udělat field tak ti značně roste počet parametrů v metodách, což nevypadá moc pěkně. Ještě jeden problém který s tímto je, vznikne, když metody takto pasuješ na funkce a přesto nemůžeš vrátit více objektů z funkce, než-li jeden, jako to jde třeba v Golang. To vede k využívání způsobu, kdy parametry metody jsou zároveň výstupními parametry.
A nevím ještě čím to je, ale na všech trochu legacy Spring projektech kde jsem dělal, byly ty Service pospojovány do spletité sítě, ve které byla snad spojeno všechno se vším. Lidi tam měli tendenci až moc nerozlišovat jednotlivé třídy problému. Pamatuju si, když byl kolega naštvaný, že vytvářím pro nový komplexní validační check dat (zbytečně) novou Service, místo abych těch 150 řádků kódu přidal do již existující 800 řádkové Service. Takže ani ta Spring architektura není samospásná a když se nad tím nepřemýšlí, bude v tom bordel.
A jsme u toho, že Spring v tom rozdělení kompetencí moc nepomůže. Správný návrh Services a Entit je téma, které neřeší.
-
...
Sice souhlasím s Tebou nastíněným kanonickým řešením, jenže debata je tu o tom, jaký je rozdíl mezi
- User.save()
- Lopata.priloz(Uhli)
Rozdil je zrejmy a trivialni.
Lopata je vykonny @Controller, ktery sibuje s hlopou @Entitou Uhli. Ostane proto Spring tak tyto anotace pojmenoval.
V pripade Usera je spravny pristup
UserRegistr.save(User)
Anebo, pro jeste snazsi pochopeni
Noha.nakopni(zadek), tady je hned jasne, ze zadek.nakopniSe(Nohou) je blbost.
Pri objektovem navrhu je proste nutno v danem co je iniciatorem akce a co je objektem (ne v OOP smyslu) teto akce.
A OOP objekt muze klidne v ruznem kontextu zastavat obe role
Ridic.natoc(Volant)
Volant.natoc(Kola)
Tohle jsou trivialni zaklady objektove dekompozice.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
:D
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
Jak metoda save() zjistí, co z objektu User má uložit? Jak se bez getterů dostane k jeho privátním atributům?
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
Jak metoda save() zjistí, co z objektu User má uložit? Jak se bez getterů dostane k jeho privátním atributům?
Co se ma ulozit zalisti prislusna metoda repisitory, data zjisti pres gettery. Na gettery je v eclipse dokonce generator
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
Hlavni myslenkou za OOP a proc se stal nejpouzivanejsim paradigmatem je pristup, ze defacto reflektuje/modeluje svet kolem nas, ktery se sklada z objektu, co vzajemne interaguji. Pak je program snadno uchopitelny a pochpotelny.
Aby ti to bylo jasnejsi, prejmenuj si User na KartotecniListekUsera (protoze presne o toto v danem okamziku jde) a vysledek bude
Knihovnik.zaloz(KartotecniListekUsera)
Kartotecni lustky, co samy skacou do kartoteky jsou v nasem vesmiru mene obvykle.
A naopak, az budes psat simulacni sw simulujici chovani lidi-Useru, pojmenuj si tridu PersonRoleUser a pak je logicky namiste volani PersonRoleUser.skocDoZdi()
Zpet k Userovi, v realu mam nejaky JSF backing bean, ktery ma metodu userChangeSubmitBttnClicked(), to je iniciator. V pripade vyse popsaneho spatneho navrhu, pak musi iniciator invokovat User, aby neco proved. Nenni jediny duvod proc. Navic to vede k prasecinam typu, ze kdyz mi iniciator pteda jenom cast parametru, treba userId a heslo pri changepass, musim vyrobit nekonzistentni poloprazdny objetkt User a na nem zavolat changePass() Nebo zbytecne vytahovat z DB kompletni instanci User, abych ty data k nicemu nepouzil.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
Hlavni myslenkou za OOP a proc se stal nejpouzivanejsim paradigmatem je pristup, ze defacto reflektuje/modeluje svet kolem nas, ktery se sklada z objektu, co vzajemne interaguji. Pak je program snadno uchopitelny a pochpotelny.
Aby ti to bylo jasnejsi, prejmenuj si User na KartotecniListekUsera (protoze presne o toto v danem okamziku jde) a vysledek bude
Knihovnik.zaloz(KartotecniListekUsera)
Kartotecni lustky, co samy skacou do kartoteky jsou v nasem vesmiru mene obvykle.
A naopak, az budes psat simulacni sw simulujici chovani lidi-Useru, pojmenuj si tridu PersonRoleUser a pak je logicky namiste volani PersonRoleUser.skocDoZdi()
Zpet k Userovi, v realu mam nejaky JSF backing bean, ktery ma metodu userChangeSubmitBttnClicked(), to je iniciator. V pripade vyse popsaneho spatneho navrhu, pak musi iniciator invokovat User, aby neco proved. Nenni jediny duvod proc. Navic to vede k prasecinam typu, ze kdyz mi iniciator pteda jenom cast parametru, treba userId a heslo pri changepass, musim vyrobit nekonzistentni poloprazdny objetkt User a na nem zavolat changePass() Nebo zbytecne vytahovat z DB kompletni instanci User, abych ty data k nicemu nepouzil.
Bttn. Ty seš čurák.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
No predsa User.Load().
ma to byt na userovi staticky? Alebo konstruktorom vytvoris Usera, ktory sa neda pouzit? Ci ma byt metoda Load niekde inde ako Save?
IMHO som rad ze som Active Record uz davno opustil.
-
Ono můžete mít třeba taky tohle a bude to dávat smysl:
@Data
class Vector {
private double length;
private float angle;
public Vector(double length, float angle) {}
}
class Polygon {
protected List<Vector> vectors;
protected Polygon() {}
double calculateVolume() {}
}
class Rectangle extends Polygon {
Rectangle(double a, double b) {
vectors.add( new Vector(a, 90.0f) );
vectors.add( new Vector(b, 90.0f) );
vectors.add( new Vector(a, 90.0f) );
// vectors.add( new Vector(b, 90.0f) ); Polygon je vzdy uzavreny.
}
double getA() {
vectors.get(0).getLength();
}
double getB() {
vectors.get(1).getLength();
}
}
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Konkrétně třeba ten Polygon - ono je to správně OOP, zobecnil jsem celou řadu geometrických tvarů do jednoho jediného, Polygon, a genericky proto můžu počítat obsah, obvod, a další blbiny. V PRAXI ale je lepší se na to vysrat a udělat Rectangle prostě zvlášť, protože takto je to zbytečně překomplikované a postrádá to pointu.
Když budu mít jenom Rectangle, můžu udělat:
@Data
class Rectangle {
double a;
double b;
public double calculateVolume() {
return a*b;
}
}
A je to mnohem lepší, ve všech směrech. A teď je tam ta metoda calculateVolume(), zase jde o to, že u takové jednoduché věci typu Rectangle tu metodu můžu mít přímo uvnitř a byl bych asi blbec, kdybych dělal GeometricPatterns.calculateVolume(rectangle);, protože mám takovou pidi třídu, která řeší a obsahuje prd, je izolovaná od okolí, tak přece tam tomu tu metodu dám. Opět, je to ukázkové OOP. Jenže v PRAXI dpč, když budu chtít tohle dělat u trochu kompikovanějších tříd a vztahů, zabřednu opět do sraček. A ze všeho nejhorší na tom OOP je, že nad tím vším musí člověk přemýšlet a filozofovat, když přitom už to mohl mít dávno napsané.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
Jak metoda save() zjistí, co z objektu User má uložit? Jak se bez getterů dostane k jeho privátním atributům?
Co se ma ulozit zalisti prislusna metoda repisitory, data zjisti pres gettery. Na gettery je v eclipse dokonce generator
Jaké gettery? To bych musel tyto boilerplates napsat nebo nechat vygenerovat a porušil bych tím zapouzdření.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
Jak metoda save() zjistí, co z objektu User má uložit? Jak se bez getterů dostane k jeho privátním atributům?
Co se ma ulozit zalisti prislusna metoda repisitory, data zjisti pres gettery. Na gettery je v eclipse dokonce generator
Jaké gettery? To bych musel tyto boilerplates napsat nebo nechat vygenerovat a porušil bych tím zapouzdření.
Dělá se to ve Springu tak, pokud teda nepoužiješ Hibernate, že máš prostě Repository::insertUsers(List<User> users); a tam rozepíšeš SQL dotaz, z usera si to vytáhneš ručně přes gettery. Standardně se místo Hibernate používá MyBatis, který umí v XMLku přímo při skládání SQL iterovat nad kolekcemi atd. , takže určit nebudeš ručně tvořit nějaký String :D
No a pokud použiješ Hibernate, tak ten toho User zpracuje reflexí.
A gettery a settery nad POJO třídou určitě nejsou porušení zapouzdření, to už bys mohl vědět, máš na to roky.
-
Ono můžete mít třeba taky tohle a bude to dávat smysl:
@Data
class Vector {
private double length;
private float angle;
public Vector(double length, float angle) {}
}
class Polygon {
protected List<Vector> vectors;
protected Polygon() {}
double calculateVolume() {}
}
class Rectangle extends Polygon {
Rectangle(double a, double b) {
vectors.add( new Vector(a, 90.0f) );
vectors.add( new Vector(b, 90.0f) );
vectors.add( new Vector(a, 90.0f) );
// vectors.add( new Vector(b, 90.0f) ); Polygon je vzdy uzavreny.
}
double getA() {
vectors.get(0).getLength();
}
double getB() {
vectors.get(1).getLength();
}
}
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Konkrétně třeba ten Polygon - ono je to správně OOP, zobecnil jsem celou řadu geometrických tvarů do jednoho jediného, Polygon, a genericky proto můžu počítat obsah, obvod, a další blbiny. V PRAXI ale je lepší se na to vysrat a udělat Rectangle prostě zvlášť, protože takto je to zbytečně překomplikované a postrádá to pointu.
Když budu mít jenom Rectangle, můžu udělat:
@Data
class Rectangle {
double a;
double b;
public double calculateVolume() {
return a*b;
}
}
A je to mnohem lepší, ve všech směrech. A teď je tam ta metoda calculateVolume(), zase jde o to, že u takové jednoduché věci typu Rectangle tu metodu můžu mít přímo uvnitř a byl bych asi blbec, kdybych dělal GeometricPatterns.calculateVolume(rectangle);, protože mám takovou pidi třídu, která řeší a obsahuje prd, je izolovaná od okolí, tak přece tam tomu tu metodu dám. Opět, je to ukázkové OOP. Jenže v PRAXI dpč, když budu chtít tohle dělat u trochu kompikovanějších tříd a vztahů, zabřednu opět do sraček. A ze všeho nejhorší na tom OOP je, že nad tím vším musí člověk přemýšlet a filozofovat, když přitom už to mohl mít dávno napsané.
Ten tvuj "ukazkovy pripad" je ukazkovy pripad spatneho OOP.
Vyse uvedeny priklad je typicky priklad na interface, to znamena za to tvoje class Rectangle si pridej implements Polygon a nad metodu calculateVolume() si pridej anotaci @Overrride. Interface Polygon definuje abstraktni metodu calculateVolume(). S objektem Rectangle budes pracovat a do collections ukladat via interface Polygon.
metoda calculateVolume() samozrejme patri primo do Rectangle, anzto nepracuje s zadnymi daty mimo objekt, coz se pozna snadno, metoda nema zadne parametry.
Inheritance ma smysl v pripade, ze mam nejakou funkcionalitu pripadne si uchovavam nejaky stav na urovni predka a potrebuju rozsirit funkcionalitu predka za vyuziti jeho protected metod a atributu, ke keterym jinak nemam pristup.
Nejcasteji se inheritance pouziva u knihovnich trid, kdy zdedenim modifikuju cast funkcionality a zbytek funkcionality zustava stejny.
Osobne pouzivam inheritanci velice zridka, interfacy prakticky vzdy (pro mockovani)
U vyse uvedeneho prikladu by class Polygon mohl implementovat napr funkcionalitu spolecnou pro vsechny polygony, napada me treba ulozeni casu vzniku prislusneho polygonu a getter na tuto hodnotu.
Znalost OOP znamena i vedet, kdy prislusne konstrukty pouzit.
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
Jak metoda save() zjistí, co z objektu User má uložit? Jak se bez getterů dostane k jeho privátním atributům?
Co se ma ulozit zalisti prislusna metoda repisitory, data zjisti pres gettery. Na gettery je v eclipse dokonce generator
Jaké gettery? To bych musel tyto boilerplates napsat nebo nechat vygenerovat a porušil bych tím zapouzdření.
Gettery neporusuji zapouzdreni, gettery nemohou modifikovat stav objetku.
Boilerplate getteru neni potreba psat, IDE na to ma generator - tento pristup pouzivam.
Pokud ma nekdo s gettery na spodku classy neprekonatelne potize, necht si to mavenu prida dependency na Lombok.
-
Ono můžete mít třeba taky tohle a bude to dávat smysl:
@Data
class Vector {
private double length;
private float angle;
public Vector(double length, float angle) {}
}
class Polygon {
protected List<Vector> vectors;
protected Polygon() {}
double calculateVolume() {}
}
class Rectangle extends Polygon {
Rectangle(double a, double b) {
vectors.add( new Vector(a, 90.0f) );
vectors.add( new Vector(b, 90.0f) );
vectors.add( new Vector(a, 90.0f) );
// vectors.add( new Vector(b, 90.0f) ); Polygon je vzdy uzavreny.
}
double getA() {
vectors.get(0).getLength();
}
double getB() {
vectors.get(1).getLength();
}
}
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Konkrétně třeba ten Polygon - ono je to správně OOP, zobecnil jsem celou řadu geometrických tvarů do jednoho jediného, Polygon, a genericky proto můžu počítat obsah, obvod, a další blbiny. V PRAXI ale je lepší se na to vysrat a udělat Rectangle prostě zvlášť, protože takto je to zbytečně překomplikované a postrádá to pointu.
Když budu mít jenom Rectangle, můžu udělat:
@Data
class Rectangle {
double a;
double b;
public double calculateVolume() {
return a*b;
}
}
A je to mnohem lepší, ve všech směrech. A teď je tam ta metoda calculateVolume(), zase jde o to, že u takové jednoduché věci typu Rectangle tu metodu můžu mít přímo uvnitř a byl bych asi blbec, kdybych dělal GeometricPatterns.calculateVolume(rectangle);, protože mám takovou pidi třídu, která řeší a obsahuje prd, je izolovaná od okolí, tak přece tam tomu tu metodu dám. Opět, je to ukázkové OOP. Jenže v PRAXI dpč, když budu chtít tohle dělat u trochu kompikovanějších tříd a vztahů, zabřednu opět do sraček. A ze všeho nejhorší na tom OOP je, že nad tím vším musí člověk přemýšlet a filozofovat, když přitom už to mohl mít dávno napsané.
Ten tvuj "ukazkovy pripad" je ukazkovy pripad spatneho OOP.
Vyse uvedeny priklad je typicky priklad na interface, to znamena za to tvoje class Rectangle si pridej implements Polygon a nad metodu calculateVolume() si pridej anotaci @Overrride. Interface Polygon definuje abstraktni metodu calculateVolume(). S objektem Rectangle budes pracovat a do collections ukladat via interface Polygon.
metoda calculateVolume() samozrejme patri primo do Rectangle, anzto nepracuje s zadnymi daty mimo objekt, coz se pozna snadno, metoda nema zadne parametry.
Inheritance ma smysl v pripade, ze mam nejakou funkcionalitu pripadne si uchovavam nejaky stav na urovni predka a potrebuju rozsirit funkcionalitu predka za vyuziti jeho protected metod a atributu, ke keterym jinak nemam pristup.
Nejcasteji se inheritance pouziva u knihovnich trid, kdy zdedenim modifikuju cast funkcionality a zbytek funkcionality zustava stejny.
Osobne pouzivam inheritanci velice zridka, interfacy prakticky vzdy (pro mockovani)
U vyse uvedeneho prikladu by class Polygon mohl implementovat napr funkcionalitu spolecnou pro vsechny polygony, napada me treba ulozeni casu vzniku prislusneho polygonu a getter na tuto hodnotu.
Znalost OOP znamena i vedet, kdy prislusne konstrukty pouzit.
Nechápu proč používáš Interfacy ve Springu kvůli Mockování, mockovat můžeš klidně i třídu, přes reflexi. Potom je z těch Interface akorát bordel, přes který se musíš pořád proklikávat a kničemu to neslouží. To se dělalo ve staré verzi Springu, který si neuměl vytvořit reflexí proxy beanu.
-
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Hlavní problém je IMO to, že OOP objekty nejsou vůbec "objekty" jak ten pojem používáme v reálném světě.
OOP objekt mívá stav, který se v čase obvykle mění a má nějakou funkčnost, takže umí sám něco dělat. V původním OOP to jde ještě do většího extrému, kdy posláním zprávy ten objekt o něco žádám. Ten stav by se dal zahodit, ale to že žádám _jeden_ objekt o něco je problém.
- Matematické objekty nejsou OOP objekty. Žádný matematický objekt neumí nic sám spočítat. Navíc ve většině výpočtů figuruje víc rovnocenných matematických objektů, takže ani není jasné kterému ten výpočet vůbec dát.
- Většina reálných objektů nejsou OOP objekty, protože samy od sebe vůbec nic neumí. V realitě existuje jen pár plnotučných OOP objektů (které bych spíš nazval actory) a zbytek jsou tupá "data".
-
Nechápu proč používáš Interfacy ve Springu kvůli Mockování, mockovat můžeš klidně i třídu, přes reflexi. Potom je z těch Interface akorát bordel, přes který se musíš pořád proklikávat a kničemu to neslouží. To se dělalo ve staré verzi Springu, který si neuměl vytvořit reflexí proxy beanu.
To je pravda. Mockoval jsem naposled ve starem springu. ted uz jsem mocky dlouho k nicemu nepotreboval.
-
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Hlavní problém je IMO to, že OOP objekty nejsou vůbec "objekty" jak ten pojem používáme v reálném světě.
OOP objekt mívá stav, který se v čase obvykle mění a má nějakou funkčnost, takže umí sám něco dělat. V původním OOP to jde ještě do většího extrému, kdy posláním zprávy ten objekt o něco žádám. Ten stav by se dal zahodit, ale to že žádám _jeden_ objekt o něco je problém.
- Matematické objekty nejsou OOP objekty. Žádný matematický objekt neumí nic sám spočítat. Navíc ve většině výpočtů figuruje víc rovnocenných matematických objektů, takže ani není jasné kterému ten výpočet vůbec dát.
- Většina reálných objektů nejsou OOP objekty, protože samy od sebe vůbec nic neumí. V realitě existuje jen pár plnotučných OOP objektů (které bych spíš nazval actory) a zbytek jsou tupá "data".
Mno objekty jsou oba, ten vas "actor" i ty "tupa data" - a i ty zamozrejme "neco umi" - umi encapsulovane drzet data a pripadne generovat pohledy na jejich interni data.
"actoru" se v terminologii springu rika @Component, ktery se podle pouziti deli na @Controller, @Service a @Repository. Mezi nimi pobihaji proste POJOs, pripadne ORM entity, pripadne jejich kolekce.
-
Jenže v PRAXI dpč, když budu chtít tohle dělat u trochu kompikovanějších tříd a vztahů, zabřednu opět do sraček. A ze všeho nejhorší na tom OOP je, že nad tím vším musí člověk přemýšlet a filozofovat, když přitom už to mohl mít dávno napsané.
Ještě větší problém je, když pak člověku takový projekt přistane na stůl s požadavkem změnit X a Y a dodělat Z. To pak opravdu bez přehánění většinu času stráví přemýšlením, jak to do toho zakomponovat, aby toho nemusel rozbíjet nebo předělávat příliš mnoho, ačkoli pokud jde o rozsah toho kódu, co má něco dělat, je to často jen pár řádků.
Tohle se prostě zvrhlo. Používají se jazyky, které o sobě tvrdí, že jsou objektové, ale ve skutečnosti nejsou, jsou strašně komplikované, nepřehledné, kód v nich je nepřehledný, obsahuje spoustu balastu, v němž se ta funkcionalita ztrácí a rozmělňuje. Lidi tu šermují s výrazy, že ani nevím, jestli mluvěj tak špatně anglicky nebo česky, pořádně se ani neshodnou na tom, co to znamená, ale nedá se říct, že by dnes vznikaly programy kvalitnější než před 30 lety. Fakt si říkám, že lidi by měli přestat vymýšlet p.čoviny a místo toho se konečně naučit programovat. Průměrný programátor z doby před 30 lety kdyby viděl tuhle diskusi, tak by většině věcí vůbec nerozuměl, ale věřím, že by konkrétní problém dokázal vyřešit rychleji a elegantněji i bez všech těch nesmyslů.
-
Jenže problém je v tom, že tohle OOP vypadá OK na takové kravince jako jsou geometrické útvary, ale v PRAXI, když se snažím reálnou úlohu udělat takto OOP, zabřednu do sraček a je lepší se na to úplně vykašlat. Podle mě má to OOP hoodně omezené použití na velmi malou množinu úloh.
Hlavní problém je IMO to, že OOP objekty nejsou vůbec "objekty" jak ten pojem používáme v reálném světě.
OOP objekt mívá stav, který se v čase obvykle mění a má nějakou funkčnost, takže umí sám něco dělat. V původním OOP to jde ještě do většího extrému, kdy posláním zprávy ten objekt o něco žádám. Ten stav by se dal zahodit, ale to že žádám _jeden_ objekt o něco je problém.
- Matematické objekty nejsou OOP objekty. Žádný matematický objekt neumí nic sám spočítat. Navíc ve většině výpočtů figuruje víc rovnocenných matematických objektů, takže ani není jasné kterému ten výpočet vůbec dát.
- Většina reálných objektů nejsou OOP objekty, protože samy od sebe vůbec nic neumí. V realitě existuje jen pár plnotučných OOP objektů (které bych spíš nazval actory) a zbytek jsou tupá "data".
Mno objekty jsou oba, ten vas "actor" i ty "tupa data" - a i ty zamozrejme "neco umi" - umi encapsulovane drzet data a pripadne generovat pohledy na jejich interni data.
"actoru" se v terminologii springu rika @Component, ktery se podle pouziti deli na @Controller, @Service a @Repository. Mezi nimi pobihaji proste POJOs, pripadne ORM entity, pripadne jejich kolekce.
Já ale psal o vztahu reálných a OOP objektů. Hlavně v kontextu předchozích příspěvků, které se snažily napasovávat OOP objekty 1:1 na reálné objekty.
-
Fakt si říkám, že lidi by měli přestat vymýšlet p.čoviny a místo toho se konečně naučit programovat. Průměrný programátor z doby před 30 lety kdyby viděl tuhle diskusi, tak by většině věcí vůbec nerozuměl, ale věřím, že by konkrétní problém dokázal vyřešit rychleji a elegantněji i bez všech těch nesmyslů.
Záleží na definici programátora. Dnes píše kód kdeco, co má do pr..le díru, a podle toho pak kód vypadá. Takový zubař samouk to má těžší, u něj se přijde rychle na to, že nemá diplom - Jahelka by mohl vyprávět. Podle zkušeností nejlepší kód píšou inteligentní a vzdělaní lidé, ovšem z jiných oborů (fyzika, biologie, chemie, geologie...), protože se s tím nepářou a moc nešpekulujou, prostě sednou a napíšou funkční aplikaci, aniž by přemýšleli, jestli kružnice dědí z obdélníku nebo naopak, ani neřeší, kolik andělů může tančit na špičce monády.
-
Gettery neporusuji zapouzdreni, gettery nemohou modifikovat stav objetku.
Boilerplate getteru neni potreba psat, IDE na to ma generator - tento pristup pouzivam.
Pokud ma nekdo s gettery na spodku classy neprekonatelne potize, necht si to mavenu prida dependency na Lombok.
Getter samotný by zapouzdření neporušoval, kdyby ho nikdo nepoužíval. Jeho použitím dochází ke zpracování interních atributů mimo objekt, což zapouzdření porušuje.
-
Jenže v PRAXI dpč, když budu chtít tohle dělat u trochu kompikovanějších tříd a vztahů, zabřednu opět do sraček. A ze všeho nejhorší na tom OOP je, že nad tím vším musí člověk přemýšlet a filozofovat, když přitom už to mohl mít dávno napsané.
Ještě větší problém je, když pak člověku takový projekt přistane na stůl s požadavkem změnit X a Y a dodělat Z. To pak opravdu bez přehánění většinu času stráví přemýšlením, jak to do toho zakomponovat, aby toho nemusel rozbíjet nebo předělávat příliš mnoho, ačkoli pokud jde o rozsah toho kódu, co má něco dělat, je to často jen pár řádků.
Tohle se prostě zvrhlo. Používají se jazyky, které o sobě tvrdí, že jsou objektové, ale ve skutečnosti nejsou, jsou strašně komplikované, nepřehledné, kód v nich je nepřehledný, obsahuje spoustu balastu, v němž se ta funkcionalita ztrácí a rozmělňuje. Lidi tu šermují s výrazy, že ani nevím, jestli mluvěj tak špatně anglicky nebo česky, pořádně se ani neshodnou na tom, co to znamená, ale nedá se říct, že by dnes vznikaly programy kvalitnější než před 30 lety. Fakt si říkám, že lidi by měli přestat vymýšlet p.čoviny a místo toho se konečně naučit programovat. Průměrný programátor z doby před 30 lety kdyby viděl tuhle diskusi, tak by většině věcí vůbec nerozuměl, ale věřím, že by konkrétní problém dokázal vyřešit rychleji a elegantněji i bez všech těch nesmyslů.
Jenže co je potom řešení. Viz javascript, ten OOP není a javascriptáři tam píšou procedurálně. Doteď jsem nenašel žádnou JS knihovnu komponent, která by šahala po kotníky třeba Primefaces nebo nějakému jinému komponentově orientovanému frontendovému frameworku (které se přestávají používat). Mají v tom strašný bordel, stáhneš si komponentu třeba Slider a ta nemá dokumentaci, nevíš co všechno přesně tomu můžeš nastavit, když rozklikneš kód není tam ani fň - nějaké parametry na jednom místě, s komentáři vo co go, to by chtěl člověk moc. Už je to tady takových 20 let, dělají se s tím weby - typická věc plná různých grafických klikátek - a ti blbci si nebyli schopni za tu dobu udělat unifikovanou, odokumentovanou knihovnu komponent. Twitterovský Bootstrap je sranda, tam žádné pořádné připravené komponenty nejsou a když s tím chce člověk dělat webovou aplikaci, tak good luck, musí si sehnat designera a kodéra, kteří mu ty potřebné komponenty udělají.
Teď je rok 2018 a já nemůžu sehnat pořádného fungujícího emailového klienta, který by uměl 2 věci: email a kalendář (kteý umí nějaký ten standardní protokol, co používá google). Zkoušel jsem Outlook, že by to mohla být kvalita, ale ten nepodporuje otevřený standard pro kalendář, který používá Google - jen uzavřený od Microsoftu. Navíc má různé absurdní bugy. Thuderbird ok, ale nemá kalendář. Tak jsem hledal, až jsem našel vyhlášený eM Client, který je placený. Stáhnul jsem si ho, dal jsem si tam účty a při komunikaci se zákazníkem jsem zjistil, že mi do Odeslaných jednou email uloží, jednou ne - takže nevím, jstli jsem to poslal nebo ne a musím otevířat webový gmail. DPČ ROK 2018 a člověk kuwa nemůže sehnat fungujícího emailového klienta s kalendářem!!! Hlavně že se vymýšlejí a dělají ty krávoviny typu webové aplikace a všichni si stěžují jak je sw vývoj drahý.
A to bych mohl pokračovat a už to bude fakt dost offtopic. Mohl bych mluvit o tom, jak nemůžu sehnat fungující šlapátka na kolo do 600,- Kč která by nešla za 1500km do kopru. Musel jsem si koupit šlapátka za 1400,- s vyměnitelnými průmyslovými ložisky. Prostě člověk chc takovou kravinu, co se vyrábí už X desítek let, a musí za to uvalit přes 1000,- aby to fungovalo jak má. Koupil jsem si značkovou bluetooth myš za 700,- a seká se ji kurzor - po čtení v recenzích prý běžná věc - myš nepoužitelná. Tachometr na kolo bluetooth za 600,- odešel po roce a nejtých 900km, navíc měl návrhové vady, že si nebyl schopný uložit napevno data, při výmně baterie se vše smaže. A mohl bych pokračovat a pokračovat.
-
Gettery neporusuji zapouzdreni, gettery nemohou modifikovat stav objetku.
Boilerplate getteru neni potreba psat, IDE na to ma generator - tento pristup pouzivam.
Pokud ma nekdo s gettery na spodku classy neprekonatelne potize, necht si to mavenu prida dependency na Lombok.
Getter samotný by zapouzdření neporušoval, kdyby ho nikdo nepoužíval. Jeho použitím dochází ke zpracování interních atributů mimo objekt, což zapouzdření porušuje.
https://cs.m.wikipedia.org/wiki/Zapouzdření_(objektově_orientované_programování)
Mas tam dokonce exampl v jawe...
-
Thuderbird ok, ale nemá kalendář.
https://support.mozilla.org/cs/products/thunderbird/calendar Nejsem žádný poweruser kalendáře, ale pro mé účely ten v TB postačuje... https://support.mozilla.org/cs/kb/pouzivani-lightningu-s-google-kalendarem
-
Gettery neporusuji zapouzdreni, gettery nemohou modifikovat stav objetku.
Boilerplate getteru neni potreba psat, IDE na to ma generator - tento pristup pouzivam.
Pokud ma nekdo s gettery na spodku classy neprekonatelne potize, necht si to mavenu prida dependency na Lombok.
Getter samotný by zapouzdření neporušoval, kdyby ho nikdo nepoužíval. Jeho použitím dochází ke zpracování interních atributů mimo objekt, což zapouzdření porušuje.
https://cs.m.wikipedia.org/wiki/Zapouzdření_(objektově_orientované_programování)
Mas tam dokonce exampl v jawe...
To je jen Wiki...
-
V pripade Usera je spravny pristup
UserRegistr.save(User)
S tímhle souhlasím, otázka tedy teď je, co je špatného na User.save()?
Hlavni myslenkou za OOP a proc se stal nejpouzivanejsim paradigmatem je pristup, ze defacto reflektuje/modeluje svet kolem nas, ktery se sklada z objektu, co vzajemne interaguji. Pak je program snadno uchopitelny a pochpotelny.
Aby ti to bylo jasnejsi, prejmenuj si User na KartotecniListekUsera (protoze presne o toto v danem okamziku jde) a vysledek bude
Knihovnik.zaloz(KartotecniListekUsera)
Kartotecni lustky, co samy skacou do kartoteky jsou v nasem vesmiru mene obvykle.
A naopak, az budes psat simulacni sw simulujici chovani lidi-Useru, pojmenuj si tridu PersonRoleUser a pak je logicky namiste volani PersonRoleUser.skocDoZdi()
Zpet k Userovi, v realu mam nejaky JSF backing bean, ktery ma metodu userChangeSubmitBttnClicked(), to je iniciator. V pripade vyse popsaneho spatneho navrhu, pak musi iniciator invokovat User, aby neco proved. Nenni jediny duvod proc. Navic to vede k prasecinam typu, ze kdyz mi iniciator pteda jenom cast parametru, treba userId a heslo pri changepass, musim vyrobit nekonzistentni poloprazdny objetkt User a na nem zavolat changePass() Nebo zbytecne vytahovat z DB kompletni instanci User, abych ty data k nicemu nepouzil.
Vůbec nerozumím tomu, jak metodou UserRegistr.save(User) vyřeším situaci, kdy nemám k dispozici příslušnou instanci User... navíc je to podezřelé, kam se ta instance ztratí?
No predsa User.Load().
ma to byt na userovi staticky? Alebo konstruktorom vytvoris Usera, ktory sa neda pouzit? Ci ma byt metoda Load niekde inde ako Save?
IMHO som rad ze som Active Record uz davno opustil.
Ne staticky, ne pro save(), ale umím si představit staticky loadFrom(odkud). A proč by se nedal použít User z konstruktoru? To se dá přece napsat na několik způsobů, v jednom to půjde, ve druhém ne...
O ActiveRecord tady nemluvíme, a User.Load() je samozřejmě také něco jiného. Když už, tak User.loadFrom(...).
Opravdu mě jenom zajímá, co je špatně na tom save().
-
Ono mám třeba nějaký integrovaný obvod a ten má uvnitř stav, což je docela běžné. Třeba Procesor. Ale podobjekty toho procesoru už stav určitě ne vždy mají, taky tam bude převažovat rozdělení na zpracovávající mechanismus a bokem uložená data. Např. HashTable, asi by nikdo nechtěl, abych s tím pracovali tak, že bude HashTable zvlášť jako čistě struktura a nějaké HashTableXYZUtils jako mechanismus práce. Takže ono v určitém bodě není špatné sloučit dohromady data a metody a zapouzdřit. Ale nesmí se to přehánět, rozhodně ne dělat z toho pravidlo.
-
Ono mám třeba nějaký integrovaný obvod a ten má uvnitř stav, což je docela běžné. Třeba Procesor. Ale podobjekty toho procesoru už stav určitě ne vždy mají, taky tam bude převažovat rozdělení na zpracovávající mechanismus a bokem uložená data. Např. HashTable, asi by nikdo nechtěl, abych s tím pracovali tak, že bude HashTable zvlášť jako čistě struktura a nějaké HashTableXYZUtils jako mechanismus práce. Takže ono v určitém bodě není špatné sloučit dohromady data a metody a zapouzdřit. Ale nesmí se to přehánět, rozhodně ne dělat z toho pravidlo.
Mimochodem mít zvlášť datovou strukturu a utility pro práci s ní není vůbec špatné, kdybych měl takto rozdělené HashTable na nějaké POJO a Util, můžu si to POJO uložit zvlášť, nasadit na něj třeba trochu jinačí typ Util, uložit ho na disk atp. Takže ono by to taky nebylo špatné, je to přehlednější a toho se člověk snaží docílit, když něco programuje.
Když dělate ve Springu a máte to rozdělené na data a mechanismus, taky to nakonec zabalíte do zapouzdřeného balíku, který vystavuje REST API. A ten celý balík má stav a public metody nad tím stavem, které tvoří to API. A tohle se dělá už celé dekády, viz konzolové aplikace.
-
Jenže co je potom řešení. Viz javascript, ten OOP není a javascriptáři tam píšou procedurálně. Doteď jsem nenašel žádnou JS knihovnu komponent, která by šahala po kotníky třeba Primefaces nebo nějakému jinému komponentově orientovanému frontendovému frameworku (které se přestávají používat).
https://www.primefaces.org/#primeng
https://www.primefaces.org/#primereact
Už je to tady takových 20 let, dělají se s tím weby - typická věc plná různých grafických klikátek - a ti blbci si nebyli schopni za tu dobu udělat unifikovanou, odokumentovanou knihovnu komponent.
Taky delas weby? Jak dlouho? Uz si udelal takovou knihovnu? Nebo se hrde hlasis k tem blbcum co toho nejsou schopni?
Teď je rok 2018 a já nemůžu sehnat pořádného fungujícího emailového klienta, který by uměl 2 věci: email a kalendář (kteý umí nějaký ten standardní protokol, co používá google). Zkoušel jsem Outlook, že by to mohla být kvalita, ale ten nepodporuje otevřený standard pro kalendář, který používá Google - jen uzavřený od Microsoftu. Navíc má různé absurdní bugy. Thuderbird ok, ale nemá kalendář. Tak jsem hledal, až jsem našel vyhlášený eM Client, který je placený. Stáhnul jsem si ho, dal jsem si tam účty a při komunikaci se zákazníkem jsem zjistil, že mi do Odeslaných jednou email uloží, jednou ne - takže nevím, jstli jsem to poslal nebo ne a musím otevířat webový gmail. DPČ ROK 2018 a člověk kuwa nemůže sehnat fungujícího emailového klienta s kalendářem!!! Hlavně že se vymýšlejí a dělají ty krávoviny typu webové aplikace a všichni si stěžují jak je sw vývoj drahý.
Proc ho nenapises? Jsi prece programator nebo ne?
A to bych mohl pokračovat a už to bude fakt dost offtopic. Mohl bych mluvit o tom, jak nemůžu sehnat fungující šlapátka na kolo do 600,- Kč která by nešla za 1500km do kopru. Musel jsem si koupit šlapátka za 1400,- s vyměnitelnými průmyslovými ložisky. Prostě člověk chc takovou kravinu, co se vyrábí už X desítek let, a musí za to uvalit přes 1000,- aby to fungovalo jak má. Koupil jsem si značkovou bluetooth myš za 700,- a seká se ji kurzor - po čtení v recenzích prý běžná věc - myš nepoužitelná. Tachometr na kolo bluetooth za 600,- odešel po roce a nejtých 900km, navíc měl návrhové vady, že si nebyl schopný uložit napevno data, při výmně baterie se vše smaže. A mohl bych pokračovat a pokračovat.
Tady souhlasim. Pricinou je marketing.
-
Neni dobre sa držat dogiem.
Hrať sa na čisté objekty a/alebo čisté FP. Vždy použi to čo sa ti v konkrétnej situácii najviac hodí. Veď preto sú dnešné jazyky multiparadigmatické a jazyky ktoré sa držali striktne objektovej paradigmy (smalltalk, ruby) skončili na smetisku dejín.
OOP je dobrý sluha ale zlý pán... keď ti OOP prácu zjednodušuje drž sa ho, ale beda akonáhle ti OOP začne komplikovať život použi kludne aj anemické objekty...
Že to neni čisté OOP?
Sral to pes.
-
OOP je dobrý sluha ale zlý pán... keď ti OOP prácu zjednodušuje drž sa ho, ale beda akonáhle ti OOP začne komplikovať život použi kludne aj anemické objekty...
Že to neni čisté OOP?
Sral to pes.
Jistě, v OOP klidně používám i lambdy, protože je to jednodušší a přehlednější, než nějaké cykly nebo řetězené ify s fuj elseif a else. Anemickým objektům se vyhýbám, neboť to programování komplikují, místo aby zjednodušovaly. To už raději použiji strukturu, u které nemusím blbnout s accesory a přitom si svůj obsah chrání. Však Messenger je také vzor.
OOP se mi však osvědčilo, protože se píše snáze a výsledek je přehlednější, udržovatelnější a rychlejší než špagety. Z podobných důvodů si mnozí oblíbili FP a nemám jim to za zlé. Struktury pro data používají také a říkají jim monády.
-
...
Sice souhlasím s Tebou nastíněným kanonickým řešením, jenže debata je tu o tom, jaký je rozdíl mezi
- User.save()
- Lopata.priloz(Uhli)
Rozdil je zrejmy a trivialni.
Lopata je vykonny @Controller, ktery sibuje s hlopou @Entitou Uhli. Ostane proto Spring tak tyto anotace pojmenoval.
V pripade Usera je spravny pristup
UserRegistr.save(User)
Anebo, pro jeste snazsi pochopeni
Noha.nakopni(zadek), tady je hned jasne, ze zadek.nakopniSe(Nohou) je blbost.
Pri objektovem navrhu je proste nutno v danem co je iniciatorem akce a co je objektem (ne v OOP smyslu) teto akce.
A OOP objekt muze klidne v ruznem kontextu zastavat obe role
Ridic.natoc(Volant)
Volant.natoc(Kola)
Tohle jsou trivialni zaklady objektove dekompozice.
Mno objekty jsou oba, ten vas "actor" i ty "tupa data" - a i ty zamozrejme "neco umi" - umi encapsulovane drzet data a pripadne generovat pohledy na jejich interni data.
"actoru" se v terminologii springu rika @Component, ktery se podle pouziti deli na @Controller, @Service a @Repository. Mezi nimi pobihaji proste POJOs, pripadne ORM entity, pripadne jejich kolekce.
A z toho je vidět, že
- Spring skutečně nemá s „původním“ OOP moc společného
- Spring sice poskytne ty chlívečky @Controller, @Service, ..., ale rozdělit objekty do nich a udělat právě tu kompozici musí programátor sám => moc z původní otázky Spring sám o sobě neřeší
- jestli je Lopata actor nebo entita se nedá bez znalosti kontextu říct (jak sám potvrzujete)
Takže se tady nevymýšlí kolo, jen jsme se k té kompozici zatím vůbec nedostali. Nějaký zdroj, jak právně komponovat? „Správně“ samozřejmě v kontextu daného paradigmatu nebo programátorského stylu. Tím bychom se teprve začali věnovat původní tazatelově otázce, IMHO.
-
Z podobných důvodů si mnozí oblíbili FP a nemám jim to za zlé. Struktury pro data používají také a říkají jim monády.
Monády nejsou struktury pro data, ale řetězení výpočtu.
-
...
...jinými slovy ty chlívečky jsou triviální ale ta kompozice až tolik ne (alespoň v problémech reálných aplikací).
-
Getter samotný by zapouzdření neporušoval, kdyby ho nikdo nepoužíval. Jeho použitím dochází ke zpracování interních atributů mimo objekt, což zapouzdření porušuje.
print "foo: " + d.getFoo()
Zde neprobíhá žádné zpracování interních atributů mimo objekt.
class Boo:
private x, y
getXoo():
return this.x * this.y
Ani zde neprobíhá žádné zpracování interních atributů mimo objekt.
(Teoreticky by mohlo, ale docela by mě zajímalo, zda o tom víš, nebo máš jen zažranou svou představu o vztahu getter a interní atribut.)
-
Z podobných důvodů si mnozí oblíbili FP a nemám jim to za zlé. Struktury pro data používají také a říkají jim monády.
Ne, tomu fakt monády neříkáme. Struktury pro data používáme, a říkáme tomu struktury.
Pouštíš se na tenkej led :-P
-
Opravdu mě jenom zajímá, co je špatně na tom save().
Vytknul bych tomu dvě věci:
1/ To, čemu patří nějaká metoda by mělo dávat nějak významově logiku. Je logické aby bylo Máslo.cut() ale třeba Máslo.vložSeDoLedničky() mi přijde divné.
Podobně, objekt uživatele má spousta vlastností. Například může mět práva někam přistupovat. Ale nepřijde mi čitelné, aby sám od sebe uměl vzniknout, nebo se uložit. Tak ještě serialize a unserialize - potud ok, ale něco komplexnějšího?
2/ Má-li objekt User metodu save(), tak lze celkem dobře předpokládat, že je to nějaký side-effekt. Což mě samo o sobě děsí. Když uložím toho uživatele, kam se mi uloží? Mohu to nějak ovlivnit? Třeba nějakým globální přepínačem?! Nebo setterem, kterým přepíšu to úložiště? A co když ho chci uložit na dvě místa? Co když budu chtít před ukládáním udělat nějakou validaci? A jen někdy. A po uložení třeba poslat mail? Taky jen někdy. Nebo různé maily v různých situacích.
Suma sumárum - metoda User.save() má jednu jedinou výhodu - je to krátké a stručné. Jinak mám zkušenost (viz víš), že v případě komplexnějšího systému se to celé zašmodrchává. Ne, že by to nešlo, ale je to děsně nečitelné ve smyslu:
if ACL.check(user):
user.setPersistence(adminUserPersistence)
user.save()
user.setMailer(adminUserMailer)
user.send()
else:
user.setPersistence(guestUserPersistence)
user.save()
user.setMailer(guestUserMailer)
user.send()
user.setMailer(adminUserMailer)
user.setMailerContent(adminUserMail.txt)
user.send()
A jakmile je to nečitelné, tak pak jaksi padá ta jediná výhoda.
-
(Teoreticky by mohlo, ale docela by mě zajímalo, zda o tom víš, nebo máš jen zažranou svou představu o vztahu getter a interní atribut.)
Tuto zažranou představu má hlavně většina tvůrců aplikací, které jsem kdy viděl. Existují i světlé výjimky, ale gettery bývají zpravidla jen "return this.atribut".
-
Suma sumárum - metoda User.save() má jednu jedinou výhodu - je to krátké a stručné.
A co bys řekl na metodu model.save(user)? Tohle se mi docela osvědčilo.
-
Suma sumárum - metoda User.save() má jednu jedinou výhodu - je to krátké a stručné.
A co bys řekl na metodu model.save(user)? Tohle se mi docela osvědčilo.
Jedinou výhradu bych měl k tomu názvu model (zrovna u tebe!). I user je model.
Ale jinak ano, takto to dělám a mám s tím dobré zkušenosti.
user reprezentuje objekt uživatele. model reprezentuje objekt nějakého procesu. Třeba zaregistrování uživatele do systému.
-
(Teoreticky by mohlo, ale docela by mě zajímalo, zda o tom víš, nebo máš jen zažranou svou představu o vztahu getter a interní atribut.)
Tuto zažranou představu má hlavně většina tvůrců aplikací, které jsem kdy viděl.
Takovej je život.
... ale gettery bývají zpravidla jen "return this.atribut".
Což nemusí být špatně. Existuje celá kategorie objektů, u kterých je to zcela korektní a naopak, udělat to jinak by bylo chybou.
-
A co bys řekl na metodu model.save(user)? Tohle se mi docela osvědčilo.
Jedinou výhradu bych měl k tomu názvu model (zrovna u tebe!). I user je model.
Ale jinak ano, takto to dělám a mám s tím dobré zkušenosti.
user reprezentuje objekt uživatele. model reprezentuje objekt nějakého procesu. Třeba zaregistrování uživatele do systému.
V tom modelu není jen user, ale spravuje všechny datové zdroje včetně databází. User je komponentou toho modelu.
-
Držet se Kitovského pravidla, že getter je špatný, znamená, domyšleno do důsledku, programovat hodně inzenzivně pure OOP. A podle mě je pure OOP pěkná pitomost, která vede k problémům. Můj závěr je takový, že pure OOP třídu udělám až v momentě, kdy potřebuju zabalit nějakou funkcionalitu, která pure OOP vůbec být nemusí. Něco co je pěkně v jednom pytlíčku jako je třeba HashTable. Když potřebuju udělat pěkný pytlíček funkcí s daty, udělám to čistě OOP.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
-
Držet se Kitovského pravidla, že getter je špatný, znamená, domyšleno do důsledku, programovat hodně inzenzivně pure OOP. A podle mě je pure OOP pěkná pitomost, která vede k problémům. Můj závěr je takový, že pure OOP třídu udělám až v momentě, kdy potřebuju zabalit nějakou funkcionalitu, která pure OOP vůbec být nemusí. Něco co je pěkně v jednom pytlíčku jako je třeba HashTable. Když potřebuju udělat pěkný pytlíček funkcí s daty, udělám to čistě OOP.
A to "nečisté" OOP by mělo vypadat zhruba jak?
-
Držet se Kitovského pravidla, že getter je špatný, znamená, domyšleno do důsledku, programovat hodně inzenzivně pure OOP. A podle mě je pure OOP pěkná pitomost, která vede k problémům. Můj závěr je takový, že pure OOP třídu udělám až v momentě, kdy potřebuju zabalit nějakou funkcionalitu, která pure OOP vůbec být nemusí. Něco co je pěkně v jednom pytlíčku jako je třeba HashTable. Když potřebuju udělat pěkný pytlíček funkcí s daty, udělám to čistě OOP.
Pure OOP není pitomost, když ho člověk plně chápe a umí používat.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
Tak nějak.
Nazývat něco Modelem má smysl jen v kontextu k dalším dvoum (třem) komponentám. Samo o sobě nikoliv.
-
Držet se Kitovského pravidla, že getter je špatný, znamená, domyšleno do důsledku, programovat hodně inzenzivně pure OOP. A podle mě je pure OOP pěkná pitomost, která vede k problémům. Můj závěr je takový, že pure OOP třídu udělám až v momentě, kdy potřebuju zabalit nějakou funkcionalitu, která pure OOP vůbec být nemusí. Něco co je pěkně v jednom pytlíčku jako je třeba HashTable. Když potřebuju udělat pěkný pytlíček funkcí s daty, udělám to čistě OOP.
A to "nečisté" OOP by mělo vypadat zhruba jak?
Tak, aby se to Kitovi nelíbilo 8) :D
-
V tom modelu není jen user, ale spravuje všechny datové zdroje včetně databází. User je komponentou toho modelu.
V takovém případě pro to je zažité jiné názvosloví (persistence, context, ...). Každopádně to jak to popisuješ se mi nelíbí. A jak tě znám, tak dále to s tebou nebudu řešit.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
Jak je mám tedy obecně nazvat? Nevím, který model v té proměnné v danou chvíli bude.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
Tak nějak.
Nazývat něco Modelem má smysl jen v kontextu k dalším dvoum (třem) komponentám. Samo o sobě nikoliv.
Však tou další komponentou je user, který je v parametru metody. Přece nebudu psát modelUser.save(user), když to není třeba. Navíc ten model ani neví, co je "User", takže to slovo do názvu ani nepatří.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
Tak nějak.
Nazývat něco Modelem má smysl jen v kontextu k dalším dvoum (třem) komponentám. Samo o sobě nikoliv.
Však tou další komponentou je user, který je v parametru metody. Přece nebudu psát modelUser.save(user), když to není třeba. Navíc ten model ani neví, co je "User", takže to slovo do názvu ani nepatří.
To, že přemejšlíš jak správně nazvat věci je samozřejmě chválihodné, ale tady řešíš úplně jinej problém. Tou další komponentou k modelu je view a controller/presenter, žádnej User.
-
A proč to nazývat nekonkrétním slovem "model"? Všechno je model...
Tak nějak.
Nazývat něco Modelem má smysl jen v kontextu k dalším dvoum (třem) komponentám. Samo o sobě nikoliv.
Však tou další komponentou je user, který je v parametru metody. Přece nebudu psát modelUser.save(user), když to není třeba. Navíc ten model ani neví, co je "User", takže to slovo do názvu ani nepatří.
To, že přemejšlíš jak správně nazvat věci je samozřejmě chválihodné, ale tady řešíš úplně jinej problém. Tou další komponentou k modelu je view a controller/presenter, žádnej User.
Nene, tohle mám v metodě controlleru. Ve view je něco podobného s opačným směrem toku dat.
-
Nene, tohle mám v metodě controlleru. Ve view je něco podobného s opačným směrem toku dat.
OK. A proč je tedy model (asi s významem persistence) víc model, než model uživatele? Já bych to pojmenoval:
$persistence->save($entry);
a po ftákách. User a Model je v tomto kontextu maďarština. Používat tu výraz model mi tu nedává smysl právě proto, že se tu neopíráš o ty další dvě komponenty.
-
(Teoreticky by mohlo, ale docela by mě zajímalo, zda o tom víš, nebo máš jen zažranou svou představu o vztahu getter a interní atribut.)
Tuto zažranou představu má hlavně většina tvůrců aplikací, které jsem kdy viděl. Existují i světlé výjimky, ale gettery bývají zpravidla jen "return this.atribut".
Ano.
A ucelem tohoto getteru je umoznit cteni atributu a nedovolit jeho zapis (protoze neexistuje setter). Atribut samotny je samozrejme private.
Treba Hibernate entity bean vubec nema settery, jenom getter na steni a dilci privatni atributy odpovidajici sloucum tabulky/view.
Data Hibernate naplni pres reflection API, user muze cist pres gettery a nemuze vlastni entity bean modifikovat.
Teda pokud by hodne chtel, muze taky pres reflection, ale to urcite neudela chybou nebo prehlednutim.
Dalsi duvod pro gettery je jejich funkce jako access api pro externi knihovny.
Treba JSF XHTML stranka pristupuje na backing bean pomoci EL "#{sessionBean.username}", ktery je na ManagedBean "SessionBean" inpmlementovan jako getter/setter
public String getUsername() - (pro boolean hodnoty public boolean isUsername())
public void setUsername(String username)
Spring XML konfigurak - DTTO
JBoss Drools language - DTTO.
---Ten pocit, kdyz v radiu hlasi, ze na D1 jede chlap v protismeru, zatimco v protismeru jedou uplne vsichni :)
-
Nene, tohle mám v metodě controlleru. Ve view je něco podobného s opačným směrem toku dat.
OK. A proč je tedy model (asi s významem persistence) víc model, než model uživatele? Já bych to pojmenoval:
$persistence->save($entry);
a po ftákách. User a Model je v tomto kontextu maďarština. Používat tu výraz model mi tu nedává smysl právě proto, že se tu neopíráš o ty další dvě komponenty.
Však to mám, model uživatele:
$model->save($user);
Je to jen rozděleno do dvou slov, aby se to lépe udržovalo a také abych nemusel modifikovat model s každou novou entitou. Ten model je vícevrstvý, což tě možná zmátlo. Zmíněný příkaz je složením dvou vrstev a zavoláním metody. Říká se tomu dependency injection, pokud bys to nevěděl.
Nepoužívám nicneříkající slova "persistence" nebo "entry". Ani by to nevyjadřovalo, co to vlastně dělá, protože model není persistence a user už vůbec není entry.
-
A ucelem tohoto getteru je umoznit cteni atributu a nedovolit jeho zapis (protoze neexistuje setter). Atribut samotny je samozrejme private.
Daleko jednodušší je udělat ho public read-only. Výsledek je stejný, jen je to jednodušší.
Ovšem ani teno přístup nepoužívám, protože se mi nechce dolovat data z objektu po jednom čísilku. Navíc je to zbytečně drahé.
-
Nene, tohle mám v metodě controlleru. Ve view je něco podobného s opačným směrem toku dat.
OK. A proč je tedy model (asi s významem persistence) víc model, než model uživatele? Já bych to pojmenoval:
$persistence->save($entry);
a po ftákách. User a Model je v tomto kontextu maďarština. Používat tu výraz model mi tu nedává smysl právě proto, že se tu neopíráš o ty další dvě komponenty.
Však to mám, model uživatele:
$model->save($user);
Je to jen rozděleno do dvou slov, aby se to lépe udržovalo a také abych nemusel modifikovat model s každou novou entitou. Ten model je vícevrstvý, což tě možná zmátlo. Zmíněný příkaz je složením dvou vrstev a zavoláním metody. Říká se tomu dependency injection, pokud bys to nevěděl.
Nepoužívám nicneříkající slova "persistence" nebo "entry". Ani by to nevyjadřovalo, co to vlastně dělá, protože model není persistence a user už vůbec není entry.
Aha. Spánembohem.
-
A ucelem tohoto getteru je umoznit cteni atributu a nedovolit jeho zapis (protoze neexistuje setter). Atribut samotny je samozrejme private.
Daleko jednodušší je udělat ho public read-only. Výsledek je stejný, jen je to jednodušší.
Ovšem ani teno přístup nepoužívám, protože se mi nechce dolovat data z objektu po jednom čísilku. Navíc je to zbytečně drahé.
No a v napr v jazyce Java se public read only atribut implementuje jako privatni/protected atribut s getterem a bez setteru - to je presne to, co popisuju vyse.
Este je tu moznost mit atribut public final, ten ale nejde modifikovat po zkonstruovani objektu.
A kdyz se ti nechce data dolovat z objektu po jednom cisilku, jak se teda dostanes k tomu jednomu cisilku, kdyz ho potrebujes? Ja se priznam, ze to jinak neumim, nez ze prectu hodnotu toho jednoho cisilka.
-
No a v napr v jazyce Java se public read only atribut implementuje jako privatni/protected atribut s getterem a bez setteru - to je presne to, co popisuju vyse.
Este je tu moznost mit atribut public final, ten ale nejde modifikovat po zkonstruovani objektu.
A kdyz se ti nechce data dolovat z objektu po jednom cisilku, jak se teda dostanes k tomu jednomu cisilku, kdyz ho potrebujes? Ja se priznam, ze to jinak neumim, nez ze prectu hodnotu toho jednoho cisilka.
Měl jsem na mysli ten public final v Javě, který se používá u vzoru Messenger. Vůbec nevadí, že nejde modifikovat.
Data z objektu získávám buď ve formě kolekce, anebo v serializovaném tvaru. Obojí má dobrou podporu v používaných jazycích, tedy nejen v PHP, ale i v Javě.
-
No a v napr v jazyce Java se public read only atribut implementuje jako privatni/protected atribut s getterem a bez setteru - to je presne to, co popisuju vyse.
Este je tu moznost mit atribut public final, ten ale nejde modifikovat po zkonstruovani objektu.
A kdyz se ti nechce data dolovat z objektu po jednom cisilku, jak se teda dostanes k tomu jednomu cisilku, kdyz ho potrebujes? Ja se priznam, ze to jinak neumim, nez ze prectu hodnotu toho jednoho cisilka.
Měl jsem na mysli ten public final v Javě, který se používá u vzoru Messenger. Vůbec nevadí, že nejde modifikovat.
Data z objektu získávám buď ve formě kolekce, anebo v serializovaném tvaru. Obojí má dobrou podporu v používaných jazycích, tedy nejen v PHP, ale i v Javě.
Rozumim tedy spravne, ze nechces z Java POJO beanu cist atribut getterem, protoze pristup pres getter na fixni adresu primitivniho typu (obdoba pristupu k ceckovemu structu, akorat ze na halde) je prilis drahy a preferujes collections, kde se primitivni typ musi nejprve transformovat na objekt ulozitelny do kolekce, vytvorit kolekci, vlozit do kolekce a pak jej v kolekci vyhledat bud pres Btree napr. v pripade HashMapy, nebo uplne stejnym fixnim accessem do pole objektu v pripace ArrayListu?
Kolik vykonu se hrubym odhadem da timto pristupem usetrit?
-
Rozumim tedy spravne, ze nechces z Java POJO beanu cist atribut getterem, protoze pristup pres getter na fixni adresu primitivniho typu (obdoba pristupu k ceckovemu structu, akorat ze na halde) je prilis drahy a preferujes collections, kde se primitivni typ musi nejprve transformovat na objekt ulozitelny do kolekce, vytvorit kolekci, vlozit do kolekce a pak jej v kolekci vyhledat bud pres Btree napr. v pripade HashMapy, nebo uplne stejnym fixnim accessem do pole objektu v pripace ArrayListu?
Kolik vykonu se hrubym odhadem da timto pristupem usetrit?
Nepoužívám POJO. Vždycky si uvědomím, co bych následně chtěl udělat s těmi daty získanými getterem a udělám to rovnou v objektu. Třídy mi tak vychází krátké a jednoduché, protože jejich logika je uvnitř. Nemusím tak řešit gettery a settery, protože ty metody mají k atributům plný přístup.
Pokud budu z toho chtít udělat kolekci, udělám to v objektu. Pokud potřebuji třeba JSON, tak tu kolekci jen serializuji (to už dělám vně objektu, protože je to tak v PHP navrženo). Pokud budu chtít string, použiji metodu __toString().
Pokud nepotřebuješ pracovat se všemi atributy objektu společně, tak by neměly být v jednom objektu.
-
V takovém případě pro to je zažité jiné názvosloví (persistence, context, ...). Každopádně to jak to popisuješ se mi nelíbí. A jak tě znám, tak dále to s tebou nebudu řešit.
User a Model je v tomto kontextu maďarština. Používat tu výraz model mi tu nedává smysl právě proto, že se tu neopíráš o ty další dvě komponenty.
Já nevím, mě se zdá název Model pro persistenenci docela běžný. Například v javovských Ebeans dědí entita z Model, nikoli z Persistence (pokud se použije Active Record - ebeans umí data mapper i active record a uživatel si může vybrat). Lehký java ORM framework ActiveJDBC - taky Model. V PHP je Properl ORM - taky Model.
-
A co bys řekl na metodu model.save(user)? Tohle se mi docela osvědčilo.
Jedinou výhradu bych měl k tomu názvu model (zrovna u tebe!). I user je model.
Ale jinak ano, takto to dělám a mám s tím dobré zkušenosti.
user reprezentuje objekt uživatele. model reprezentuje objekt nějakého procesu. Třeba zaregistrování uživatele do systému.
V tom modelu není jen user, ale spravuje všechny datové zdroje včetně databází. User je komponentou toho modelu.
Asi by bylo lepší napsat
datastore.save(user)
-
V tom modelu není jen user, ale spravuje všechny datové zdroje včetně databází. User je komponentou toho modelu.
Asi by bylo lepší napsat
datastore.save(user)
Proč zrovna datastore, když má na starosti například i autentizaci, mejlování, logování a další záležitosti?
-
V takovém případě pro to je zažité jiné názvosloví (persistence, context, ...). Každopádně to jak to popisuješ se mi nelíbí. A jak tě znám, tak dále to s tebou nebudu řešit.
User a Model je v tomto kontextu maďarština. Používat tu výraz model mi tu nedává smysl právě proto, že se tu neopíráš o ty další dvě komponenty.
Já nevím, mě se zdá název Model pro persistenenci docela běžný. Například v javovských Ebeans dědí entita z Model, nikoli z Persistence (pokud se použije Active Record - ebeans umí data mapper i active record a uživatel si může vybrat). Lehký java ORM framework ActiveJDBC - taky Model. V PHP je Properl ORM - taky Model.
OK, že se to někde používá, beru. Že by to bylo něcoříkající, to ne.
-
Například v javovských Ebeans dědí entita z Model, nikoli z Persistence
Entity jsou modely reálného světa, proto potomci Modelu. To samozřejmě dává smysl. To ovšem není případ, který popisuje Kit - objekt poskytující modelům všechny možné služby.
-
Například v javovských Ebeans dědí entita z Model, nikoli z Persistence
Entity jsou modely reálného světa, proto potomci Modelu. To samozřejmě dává smysl. To ovšem není případ, který popisuje Kit - objekt poskytující modelům všechny možné služby.
Všechny možné služby neposkytuje, to je pravda. Ale persistenci ano.
Každopádně Model je to obecný pojem - aby byl správně chápán, je potřeba připojit kontext. Jak již bylo řečeno.
-
A persistenci sám sebe, nebo i jiného modelu?
-
A persistenci sám sebe, nebo i jiného modelu?
Jakékoli entitě, která z něj dědí. Někdy včetně závislostí (referencovaných entit).
-
A persistenci sám sebe, nebo i jiného modelu?
Jakékoli entitě, která z něj dědí. Někdy včetně závislostí (referencovaných entit).
Proč by měla ta entita dědit z modelu, když se dá elegantně použít kompozice? Vazba je pak mnohem volnější a mohu za běhu vyměnit model, např. s jinou databází.
-
Proč by měla ta entita dědit z modelu, když se dá elegantně použít kompozice? Vazba je pak mnohem volnější a mohu za běhu vyměnit model, např. s jinou databází.
Dědit z modelu se už nedělá. Mnohem lepší je, aby entita dědila třeba z controlleru nebo z view. Pak nemusíš řešit vůbec žádnou databázi, a celé to dává mnohem větší smysl. A hlavně je to monadické.
-
Proč by měla ta entita dědit z modelu, když se dá elegantně použít kompozice? Vazba je pak mnohem volnější a mohu za běhu vyměnit model, např. s jinou databází.
Dědit z modelu se už nedělá. Mnohem lepší je, aby entita dědila třeba z controlleru nebo z view. Pak nemusíš řešit vůbec žádnou databázi, a celé to dává mnohem větší smysl. A hlavně je to monadické.
Proč by měla entita něco od někoho dědit? Je to samostatný kus programu, který není na ničem závislý. Vše, co potřebuje, je rozhraní.
-
Učebicové OOP říká, že máme dát metodu save() přímo do User. Na první pohled to zní logicky a elegantně, ale je to v principu kravina. Ten User bude mít vazby na další třídy, třeba na Company a Address atp. Co bude v metodě save(), když byl vytvořen rovněž i Company a Address objekt a User na to má návazovnost, tzn. nemá smysl vytvořit Usera bez nové Company a Address? Jenže v modetě save() v Userovi se přece nemůže zpracovávat i Company a Address, to je kravina.
(...)
Omlouvám se přítomným, že nejsem v oboru formálně vzdělán - a proto téma chápu do značné míry jako laik, naivní umělec, perpeťák - nepolíbený gramatikami, náročnou krásou funkcionálního programování apod. Kdo četl Neználka, a pamatuje si kuchaře, který vynalézal suchou vodu? Zároveň jsem dostatečně stár na to, abych chápal, že obvykle tu coderskou dřinu mi nakonec stejně nikdo neodpáře, ať se s vrstvením abstrakce trápím sebevíc a ať obracím sémantickou konstrukci naruby kterýmkoli z pěti způsobů.
Svého času někdy začátkem století mě podobné úvahy dohnaly jednak k tomu, abych si trochu prořezal zoubky o g++ v Linuxu a o vnitřnosti Perlu... a taky abych se zamyslel nad společnými rysy dnes (dodnes!) dostupných programovacích jazyků a DBMS.
Zrovna problém perzistence dat v běžných jazycích je hodně zajímavý. A přijde mi, že dědit nebo komponovat "invazivně" nějakou infrastrukturu pro perzistenci přímo do "mých" objektů je případem snahy, řešit něco na nesprávné vrstvě abstrakce. Správný postup podle mého: ve svém programu bych perzistenci neměl explicitně řešit! Perzistenci by měla zajišťovat nějaká "ODBMS mezivrstva", cudně skrytá za "metamodel" jazyka, ve kterém programuji. Řekněme, že by šmahem všechny objekty, které ve svém jazyce vytvořím, měly automagicky zajištěno perzistentní ukládání do nějakého "backendu pro zajištění perzistence". Takže jednou vytvořený objekt by nikam nezmizel, a třeba po vypnutí a zapnutí programu by ve "jmenném prostoru" jednoduše dál existoval.
Ta "perzistenční mezivrstva" spolupracující s metamodelem by mohla mít štelovatelné čudlíky: v zásadě by bylo na výběr write-back vs. write-through, a pokud write-back, tak třeba ještě dodržet aspoň pořadí transakcí, nebo se v zájmu průchodnosti vykašlat i na pořadí (logicky s rizikem nekonzistence dat v permanentním úložišti při pádu nebo ztrátě napájení).
Tzn. žádné user.save(). Volání konstruktoru 'new user()' by podle nastavení znamenalo buď: že v okamžiku návratu z konstruktoru je objekt již perzistentní, nebo třeba zatím není na disku ale brzy bude (a ve správném pořadí dle fronty s předchozími transakcemi), nebo na disku "zatím není a bude časem, až se to bude hodit" - každopádně při práci s objekty "user", firma, poštovní adresa apod. by se programátor nad nějakým save() vůbec neměl zamýšlet.
Referenční integrita mezi instancemi je další velice zajímavé téma. Dnešní jazyky mají "vztah mezi instancemi" zařízen objekty typu pointer, reference apod. Prostě jednosměrný ukazatel, u kterého v principu hrozí, že bude ukazovat kamsi do propasti. Jasně - pokud bude mít povinnou inicializaci zavěšením na existující cílový objekt, tak zrovna inicializace už je ošetřená. Ale co když ten cílový objekt časem zanikne, nebo bude potřebovat zaniknout? atd. Prostě mě tyhle úvahy dovedly k následující fintě:
Uvažme vztah mezi instancemi jako dvousměrný objekt. Speciální druh objektu v metamodelu programovacího jazyka. Měli bychom instance, a vztahy mezi nimi. Vztah mezi instancemi by mohl existovat jenom ve chvíli, kdy by existovaly obě "koncové" instance. Pokud by jedna z obou instancí zanikla, zanikl by vztah. Přesněji řečeno, dalo by se pravidly pro "vztahovou integritu" předem jasně určit (třeba v programátorem deklarovaných třídách), které vztahy jsou povinné, které nikoli, zda smí "protějšek ve vztahu" prostě svévolně umřít, nebo zda mu má být v úmrtí zabráněno, nebo zda se mají instance mazat v kaskádě apod. Užitečným speciálním druhem vztahu by mohlo být "vlastnictví"= vztah nadřízenosti a podřízenosti mezi instancemi. A samozřejmě stojí za úvahu, jak takové vztahy, které instance může mít ale nemusí, jak je implementovat v "jmenných tabulkách symbolů" v našem hypotetickém jazyce. V tradičních jazycích jako je kompilované Céčko, jakmile je deklarována proměnná, lze se na její jméno trvale odkazovat. Pokud se odkážu na jméno, které neexistuje, tak je to jasná compile-time chyba, protože existence/neexistence jména se nemůže měnit za běhu. V jazyce, kde vztahy průběžně vznikají a zanikají, by toto muselo být nějak ošetřeno. Výjimkou při pokusu o přístup ke vztahu s nenalezeným jménem, případně nějakým explicitním opatrným testem jako exists() apod.
Podle mého to vede na interpretovaný jazyk integrovaný s triviálním ODBMS enginem. Jaký back-end by se použil pro ukládání na disk, to je vcelku vedlejší. Já jsem tehdy před 15-18 lety sáhl po MySQL, ale zřejmě by to šlo naroubovat stejně dobře na libovolný key-value store, pokud bych nechtěl psát svůj vlastní backend nad holými soubory. Důležité je, že do storage back-endu by to ukládalo data v poměrně primitivním "datovém metamodelu". Datový model uživatelského programu by NEBYL součástí low-level datového modelu používaného na back-end databázi. Back-end DB by odpovídala "metamodelu", uživatelský datový model by žil "o vrstvu výš". Potažmo otěže referenční integrity na úrovni back-endové SQL DB nebo KV-storu by držel pevně v ruce primitivní ODBMS engine mého jazyka - a jak již řečeno, referenční integrita v metamodelu mého jazyka by byla řešena "obchvatem od lesa" :-) přes obousměrné vztahy mezi instancemi a jejich definovatelná "integritní omezení".
Plus je zajímavé se zamyslet, že by ta věc mohla být vícevláknová, jak tam udělat systémově nějaké jemnější zamykání mezi více uživatelskými vlákny apod. Uživatelské účty a uživatelská práva... tohle je zrovna dost nuda.
Pokud se týče back-endu, je třeba se zamyslet, jak řešit fungování jazyka v situaci, kdy nemůže držet všechny třídy a instance trvale v RAM. Že by se měl snažit, nechávat nějaká data na disku, a ládovat je až podle potřeby. Což znamená, implementovat v "ODBMS mezivrstvě" (ano tam to patří!) generický mechanismus, jak třeba při enumeraci vztahů nějaké instance natáhnout jenom "minimální kostru" sousedících vztažených instancí, a teprve on demand dotahovat celý jejich datový obsah. Nebo nedotahovat kostry sousedních instancí, ale jenom proto-vztahy? Toto jsem částečně rozpracoval (ošidil jsem to, zkrátil jsem si cestu v zájmu rychlejšího postupu.)
Další zajímavé téma je jmenný systém a na něj navázaný interní systém klíčů a odkazů v metamodelu a ODBMS.
Jak říkám, kdysi jsem si s tou myšlenkou trochu hrál (http://frantisek.rysanek.sweb.cz/kludge/kludge.html), ale na vlastní programovací jazyk nikdy nedošlo. Ten školácký prototyp, na kterém dnes vidím spoustu chybných rozhodnutí a dost divný coding style, vykazoval základní životní funkce objektově-hierarchické databáze. Šlo vkládat jednoduché třídy a podle nich potom instance. Povedlo se mi i zhruba embednout Perl, ale bylo mi jasné, že nevím jak dál. Jednak Perl při každé chybě shodil celý můj engine :-) a druhak jeho stávající metamodel byl s mým vybájeným metamodelem dost neslučitelný. Dalo dost sebekázně, napsat tam aspoň jednu-dvě primitivní "vložené procedury", které by nepadaly na perlovou syntaktickou chybu. Čili pokud jde o nějaký jazyk, který bych s tou svojí "databází" bezešvě integroval, musel bych začít s čistým listem papíru. A zhruba v tom okamžiku mi taky definitivně došel volný čas na blbosti :-) Rád bych se k tomu ještě někdy vrátil, ale uvidím, jestli to stihnu, než mi moje šedá kůra zdřevění natolik, že už to nedám.
-
... přijde mi, že dědit nebo komponovat "invazivně" nějakou infrastrukturu pro perzistenci přímo do "mých" objektů je případem snahy, řešit něco na nesprávné vrstvě abstrakce. Správný postup podle mého: ve svém programu bych perzistenci neměl explicitně řešit! Perzistenci by měla zajišťovat nějaká "ODBMS mezivrstva", cudně skrytá za "metamodel" jazyka, ve kterém programuji.
S tímhle rozhodně souhlas, jenomže z existence metody user.save() právě vůbec nevyplývá, že ta logika pro tu perzistenci bude (nebo že dokonce musí být) přímo v tom objektu! Souhlasím s tím, že tam nepatří, a reálně to bude řešeno jinde, a tam se volání deleguje.
Ve skutečnosti je to úplně naopak, volání repository.save(user) je právě to explicitní řešení perzistence v kódu, kterou vůbec řešit nemám. Zatímco voláním user.save() můžu mít volnější ruku, a co se nakonec stane řešit za běhu.
Ta "perzistenční mezivrstva" spolupracující s metamodelem by mohla mít štelovatelné čudlíky: v zásadě by bylo na výběr write-back vs. write-through, a pokud write-back, tak třeba ještě dodržet aspoň pořadí transakcí, nebo se v zájmu průchodnosti vykašlat i na pořadí (logicky s rizikem nekonzistence dat v permanentním úložišti při pádu nebo ztrátě napájení).
Tzn. žádné user.save(). Volání konstruktoru 'new user()' by podle nastavení znamenalo buď: že v okamžiku návratu z konstruktoru je objekt již perzistentní, nebo třeba zatím není na disku ale brzy bude (a ve správném pořadí dle fronty s předchozími transakcemi), nebo na disku "zatím není a bude časem, až se to bude hodit" - každopádně při práci s objekty "user", firma, poštovní adresa apod. by se programátor nad nějakým save() vůbec neměl zamýšlet.
Tak ona i ta perzistence má několik úrovní, ale kdyby mělo již volání konstruktoru způsobit uložení do nějakého trvalého úložiště, a toto mělo být považováno za správný přístup, tak potom tím spíš nikdo nemůže mít námitky proti tomu, když bude existovat jedna metoda save(), která to právě zařídí, ale volbu kdy se tak má stát nechá na programátorovi.
-
Proč by měla ta entita dědit z modelu, když se dá elegantně použít kompozice? Vazba je pak mnohem volnější a mohu za běhu vyměnit model, např. s jinou databází.
Dědit z modelu se už nedělá. Mnohem lepší je, aby entita dědila třeba z controlleru nebo z view. Pak nemusíš řešit vůbec žádnou databázi, a celé to dává mnohem větší smysl. A hlavně je to monadické.
Proč by měla entita něco od někoho dědit? Je to samostatný kus programu, který není na ničem závislý. Vše, co potřebuje, je rozhraní.
:facepalm:
-
:facepalm:
Copak? Došly argumenty?
-
Ve skutečnosti je to úplně naopak, volání repository.save(user) je právě to explicitní řešení perzistence v kódu, kterou vůbec řešit nemám. Zatímco voláním user.save() můžu mít volnější ruku, a co se nakonec stane řešit za běhu.
Mě to přijde prašť jak uhoď.
Mám logiku X, Y, Z. Buď ji pojmenuju user.x(), user.y(), user.z() - přičemž ta logika je nějak, jakkoliv schována uvnitř těch metod. Nebo má služby X.do(user), Y.do(user), Z.do(user) - kde opět je ta logika jakkoliv schována uvnitř těch služeb. Obojí je funkčně zcela stejné. Jen mám zkušenost, že s tou druhou možností se snáze pracuje.
- Snáze přidávám novou logiku do systému - v případě User.q() bych musel vytvářet novou metodu objektu.
- Ten objekt jde jednoduše vytvořit pomocí konstruktoru, kde přijdou jen ty opravdu nutné závislosti. V případě User.q() to budu do něho propašovávat mnohem méně pohodlně. (Tak něco si představit dokážu, ale...)
- Ty služby se dají velice dobře komponovat. Jediné na co jsem narazil byly transakce, a u nich jsem cítil, že opravdu řeším jen problém s těmi transakcemi - žádnou omáčku kolem.
-
:facepalm:
Copak? Došly argumenty?
Cože?! :-D To myslíš vážně? Dobře, chceš-li argumenty, toto je hodno tvé úrovně:
Nory dynamiky pod i jejích východě já britští i ruin viry. Sněhová profese rybářský z paliv mluvený vaše; má dá kanále kdo domem jakýsi, v ho musel Josef netopýrům i inteligenci, spíše však rozvrstvuje i obeplujeme s druhem našeho. Péče za cestě co zahladila dvacetimetrové, ji jedinečnost nejnepřístupnějšího tradičních spojuje. Duběnek náročný vědomostí mnou v zasmál 1591 techniku, cítíte či desítek, ten s a necítila ní uveřejněném pořizovány přednášíme výkon však s oba. Zvedl dávnou zdát metry hlavně jedná. Pětkrát žádné korun můžeme, virova, floridě ně, kruhy tj., nevybrala i psi plot. Vyhynul cenám nabíledni jader obeplutí škola dne výkon k doplňuje životu však od produkují zpráv, pouze státu viry ráno známý způsobily vanoucí.
[http://cs.blabot.net]
Sorry, potřebuju oponenta, který alespoň zapne mozek, když mu něco napíšu.
-
A persistenci sám sebe, nebo i jiného modelu?
Jakékoli entitě, která z něj dědí. Někdy včetně závislostí (referencovaných entit).
Proč by měla ta entita dědit z modelu, když se dá elegantně použít kompozice? Vazba je pak mnohem volnější a mohu za běhu vyměnit model, např. s jinou databází.
Tohle téma je už vyčerpané, na nevýhodách dědění jsme se tak nějak vespolek shodli už v příspěvku https://forum.root.cz/index.php?topic=18704.msg268469#msg268469
-
:facepalm:
Copak? Došly argumenty?
...
Sorry, potřebuju oponenta, který alespoň zapne mozek, když mu něco napíšu.
@Kit: Kdyz nekdo placne tak velkou blbost jako "entita dědila třeba z controlleru nebo z view" tak se pravdepodobne pokousi o sarkasmus.
@BoneFlute: Internet nezapomina. Co kdyz si to tady precte nekdo, kdo tomu uveri a ty s nim budes muset pracovat? Ironie je mocna, ale "with great power comes great responsibility"
-
:facepalm:
Copak? Došly argumenty?
...
Sorry, potřebuju oponenta, který alespoň zapne mozek, když mu něco napíšu.
@Kit: Kdyz nekdo placne tak velkou blbost jako "entita dědila třeba z controlleru nebo z view" tak se pravdepodobne pokousi o sarkasmus.
@BoneFlute: Internet nezapomina. Co kdyz si to tady precte nekdo, kdo tomu uveri a ty s nim budes muset pracovat? Ironie je mocna, ale "with great power comes great responsibility"
Uznávám vinu v plném rozsahu, a omlouvám se.