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

BoneFlute

  • *****
  • 2 062
    • Zobrazit profil
Re:OOP jazyk - problém klasického stříhu
« Odpověď #120 kdy: 12. 06. 2018, 02:33:30 »
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.


dustin

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

Re:OOP jazyk - problém klasického stříhu
« Odpověď #122 kdy: 12. 06. 2018, 12:39:58 »
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.
« Poslední změna: 12. 06. 2018, 12:41:41 od Ondrej Nemecek »

dustin

Re:OOP jazyk - problém klasického stříhu
« Odpověď #123 kdy: 12. 06. 2018, 12:52:25 »
A persistenci sám sebe, nebo i jiného modelu?

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


Kit

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

BoneFlute

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

Kit

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

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

Honza

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

BoneFlute

  • *****
  • 2 062
    • Zobrazit profil
Re:OOP jazyk - problém klasického stříhu
« Odpověď #130 kdy: 12. 06. 2018, 21:40:54 »
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:

Kit

Re:OOP jazyk - problém klasického stříhu
« Odpověď #131 kdy: 12. 06. 2018, 21:52:07 »
:facepalm:

Copak? Došly argumenty?

BoneFlute

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

BoneFlute

  • *****
  • 2 062
    • Zobrazit profil
Re:OOP jazyk - problém klasického stříhu
« Odpověď #133 kdy: 12. 06. 2018, 22:07:43 »
: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.
« Poslední změna: 12. 06. 2018, 22:11:11 od BoneFlute »

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