OOP jazyk - problém klasického stříhu

anonym

Re:OOP jazyk - problém klasického stříhu
« Odpověď #15 kdy: 06. 06. 2018, 19:59:23 »
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).


Re:OOP jazyk - problém klasického stříhu
« Odpověď #16 kdy: 06. 06. 2018, 19:59:46 »
A z jakých knížek se to učíte?

BoneFlute

  • *****
  • 2 063
    • Zobrazit profil
Re:OOP jazyk - problém klasického stříhu
« Odpověď #17 kdy: 06. 06. 2018, 20:00:09 »
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.

Re:OOP jazyk - problém klasického stříhu
« Odpověď #18 kdy: 06. 06. 2018, 20:04:37 »
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ší.

Kit

Re:OOP jazyk - problém klasického stříhu
« Odpověď #19 kdy: 06. 06. 2018, 20:18:18 »
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()?
Kód: [Vybrat]
User.send(email);
User.send(database);
User.send(log);


Re:OOP jazyk - problém klasického stříhu
« Odpověď #20 kdy: 06. 06. 2018, 20:22:04 »
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.

Re:OOP jazyk - problém klasického stříhu
« Odpověď #21 kdy: 06. 06. 2018, 20:25:45 »
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.

n

Re:OOP jazyk - problém klasického stříhu
« Odpověď #22 kdy: 06. 06. 2018, 20:29:16 »
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.

Kit

Re:OOP jazyk - problém klasického stříhu
« Odpověď #23 kdy: 06. 06. 2018, 20:33:07 »
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í.

n

Re:OOP jazyk - problém klasického stříhu
« Odpověď #24 kdy: 06. 06. 2018, 20:39:06 »
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.

Honza

Re:OOP jazyk - problém klasického stříhu
« Odpověď #25 kdy: 06. 06. 2018, 21:15:21 »
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á...

Re:OOP jazyk - problém klasického stříhu
« Odpověď #26 kdy: 06. 06. 2018, 21:19:01 »
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.

Re:OOP jazyk - problém klasického stříhu
« Odpověď #27 kdy: 06. 06. 2018, 21:23:48 »
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.

Re:OOP jazyk - problém klasického stříhu
« Odpověď #28 kdy: 06. 06. 2018, 21:36:13 »
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í.

Re:OOP jazyk - problém klasického stříhu
« Odpověď #29 kdy: 06. 06. 2018, 21:48:05 »
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…