Proč je AnemicDomainModel anti-pattern rubrika: Programování: Jiné

9 Taco
položil/-a 4.10.2015
 
upravil/-a 5.10.2015

Toto znám. Toto také. Přesto si nemůžu pomoct, že mi naopak přijdou Anemické objekty jako velice šikovný návrh. Hodně ho používám, a nemůžu si ho vynachválit.

Nechtěli byste vypíchnout, proč je podle vás tento přístup špatný?

Může za to, že je pro mě tak atraktivní skutečnost, že inklinuju k funkcionálnu a imutable strukturám?

Pro pořádek, od (Anemického) doménového objektu očekávám, že mi vytvoří reprezentaci něčeho (třeba auto), a že si zajistí datovou integritu, odmítne neplatné hodnoty etc.

Ještě bych přidal trochu kódu, abych osvětlil, jak si to představuju.

odkaz Vyřešeno
10 jiri.knesl
odpověděl/-a 5.10.2015

Anemický model je typická mezifáze vývojáře, který opouští OOP.

Totiž v OOP máš princip zapouzdření. Objekty skrývají svou vnitřní implementaci a vystavují chování. Jakmile si uděláš jen "přepravku na data", tak máš objekt, který nemá chování a pouze vystavuje vnitřní implementaci, to je anti-OOP.

Vývojáři, kteří opouštějí OOP a směřují k funkcionálnímu programování často mívají mezifázi předtím, než opustí objektový jazyk, kdy mají 2 typy objektů. Objekty přepravky a pak statické třídy, které očekávají takovéhle přepravky a vrací přepravky a samy nemají žádný stav. Pak obvykle ty přepravky bývají předělané na immutable. No a nakonec takový vývojář místo OOP přejde na FP, místo přepravek na data si dělá typy nebo recordy a místo statických metod má funkce.

Protože vím, že pracuješ s Haskellem, vůbec ne nepřekvapuje, že máš takovéhle tendence.

Komentáře

  • Taco : "Objekty skrývají svou vnitřní implementaci a vystavují chování." toto je asi zásadní. Jenže mě to přijde strašně omezující. Když mám objekt 42 a ten umí sčítat odčítat, a já si usmyslím nový operátor inkrementace (ponechme teorii grup stranou), tak to ho mám ten objekt předělat, aby uměl i inc? Pokud ano, tak spousta OOP jazyků co znám toto (bez vlastnictví zdrojáků) neumí. Možná jsem fakt už poznamenanej :-) 6.10.2015
  • Kit : @Taco: Prostě přidáš další metodu increment(). Není to předělávka, ale doplnění třídy o metodu. Bavíme se o doplnění vlastní třídy, od které máš zdrojáky. 6.10.2015
  • Anonym : Většina jazyků ne, ale třeba ObjC ano. 6.10.2015
  • Taco : @Atamiri: Ze zvědavosti se zeptám, to ta funkce, kterou přidáváš - má pak přístup k soukromým atributům objektu? A vůbec, nemohl by jsi ukázat nějaký příklad jak se to dělá (v ObjectiveC jsem nepolíben). 6.10.2015
  • Taco : @Kit: Ne, nebavíme. 6.10.2015
  • Kit : @Taco: Tak mi tedy vysvětli, jak chceš doplnit cizí třídu o další atributy nebo metody. Nechci dědičnost ani kompozici. Chci ji rozšířit. 6.10.2015
  • Taco : @Kit: Tento problém neřeším. 6.10.2015
  • Kit : @Taco: O rozšíření vlastní třídy se podle tebe nebavíme, rozšíření cizí třídy neřešíš. Fajn, problém je tím vyřešen. 6.10.2015
  • Andreaw Fean : @Taco, @Atamiri: Myslíš extension-method? CSharp to třeba taky umí. A pro PHP tuším David Grudl něco taky napsal. 6.10.2015
  • Taco : @Andreaw Fean: Jo, to je jedna možnost. Teoreticky by si to mělo rozumět s rozhraním, to je plus. Taky to vypadá dobře, jakože objektově, že přidávám tomu objektu další schopnost. Ale zase ji přiznávám všem, což se mi zase nelíbí. A zůstává (celkem logické) omezení, že ta funkce nevidí privátní atributy. 6.10.2015
  • Anonym : @Andreaw Fean Jo, to myslím 6.10.2015
  • Anonym : @Taco Jo, má. V ObjC to jsou kategorie. Swift má extenze. Je to hodně užitečná vlastnost, protože můžu například zabudovaný Double donutit, aby implementoval mé rozhraní, třeba Complex. Je to OOP a nemusím zbytečně psát novou třídu nebo někde něco dědit. A je to lepší než kompozice (v tomto případě). 6.10.2015
  • Kit : @Atamiri: Není to náhodou škrábání pravou rukou za levým uchem? Proč bych měl Double k něčemu nutit, když mohu použít přímo Complex? 6.10.2015
  • Anonym : @Kit Protože z toho je jednak čitelnější kód a druhak lepší typová kontrola během překladu. 6.10.2015
  • Kit : @Atamiri: Complex nemá typovou kontrolu během překladu? Kód by měl být podle mne úplně stejně čitelný. c = a + b; vypadá snad stejně v Double jako v Complex. 6.10.2015
  • Anonym : @Kit Ale x = a + b.i už je čitelnější než Complex(a, b). Typová kontrola se projeví až při složitější OO hierarchii. Mám například Vector a pokud můžu Double rozšířit, lze mít třeba pro skalární součin jen jednu funkci, i když je T instance Complex (kde se skalární součin definuje nesymetricky). Stačí říct Doublu, že má metodu conjugate, jež vrací sebe sama (protože imaginární složka je nulová). Při sčítaní pak aplikuju na jeden z parametrů součinu conjugate a je mi jedno, je-li to Double nebo Complex. Při agresivní optimalizaci takový supergenerický kód ničemu nevadí. 6.10.2015
  • Kit : @Atamiri: Však ano, zápis x = a + b.i považuji za přirozený, v Pythonu to vypadá podobně. Jen mi bylo divné, že se kvůli tomu ještě musí dělat nějaké další opatření. 6.10.2015
  • Anonym : @Kit Jenže Python je dynamický jazyk. Navíc jde spíše o ty vektory, to už v Pythonu tak snadno nejde. 6.10.2015
  • harrison314 : @Atamiri: zápis x = a + b.i vies spravit (napr v c#, c++, ...) obycajnym pretazenim operatorov. 7.10.2015
  • pachol.jan : @Kit: Ohledně rozšíření cizí třídy o další metody... přesně na to je vzor decorator. Obecně extension-method považuji za anti-pattern. Daná třída by měla implementovat nějaké rozhraní a veškeré reference mít typu daného rozhraní, nikoliv třídy. 7.10.2015
  • Kit : @pachol.jan: Mně to nevysvětluj, sám dekorátory používám. Například PDO jsem si tak obalil vlastní třídou MyPDO, která umí pár věcí navíc. 7.10.2015
  • Taco : @pachol.jan: Takže když budu mět tu třídu Int, pro kterou chci operaci inc, tak by si považoval za správné řešení udělat decorátor? Nějak takto? A co kdyby sem to udělal takto? (Ukecanost nic moc, to je fakt, ale rozšiřitelnost výborná. Jen s tou signaturou by to asi nebylo ideální - no, je to extrémní příklad.) 7.10.2015
  • Kit : @Taco: Když vyházíš zbytečnosti, identifikátory přejmenuješ na použitelné názvy a přidáš potřebný kód, tak by to mělo být ono. 7.10.2015
  • Kit : @Taco: Vyplácí se mi kašlat na rozšiřitelnost. Pokud je návrh správný, tak se zpravidla dostaví sama. 7.10.2015
  • Taco : @Kit: šumíš, není to příjemné. 7.10.2015
  • Taco : @pachol.jan: A samozřejmě, jakou to má výhodu? 7.10.2015
  • Anonym : @harrison314 Jen sčítaní, imaginární jednotku takto v C++ ani C# implementovat nelze. 7.10.2015
  • harrison314 : @Atamiri: vzdy sa najde nejaky blb co nevie , ze to nejde a naprogramuje to :D Console.WriteLine(12.0 + 5 * Complex.I); http://pastie.org/10467395 8.10.2015
  • Anonym : @harrison314 Jenže ten "blb" tam má navíc "Complex.". Nejde mít prostě "5.i" (bez extenze Doublu o příslušnou property). A o to šlo. 8.10.2015
  • Honza Břešťan : Extensions properties stranou, ale nebude "5.i" vzdycky 0? :) Kdybych to bral podle vyznamu properties, tak mam integer 5 a ptam se na jeho imaginarni cast, kterou nema... 8.10.2015
  • Anonym : Ne, i je computed property, aby šlo psát a+b.i. Prostě vrací Complex(0,1). 8.10.2015
  • harrison314 : jasne uz chapem, ja som myslel ze "x = a + b.i" je "x = a + b*i", no konkretne toto nepovazujem za najstanjsie rienie. Okrem toho extension properity by mal mat C# vNext. Teraz viem napisat iba "x = a +b.i()". Inak mne tam to COmplex az tak nevadi, lebo nerobim z doublu to co nie je. 9.10.2015
  • Anonym : @harrison314 V tomto případě jde udělat z doublu to, co je, totiž komplexní číslo. Pokud potřebuju pracovat s komplexními čísly, koukám se na double zákonitě jako na komplexní číslo se všemi jeho vlastnostmi, například chci u něj mít metodu "conjugate". 9.10.2015
  • pachol.jan : @Taco: Nejlepší by bylo obě varianty skloubit. Pokud by byla funkcionalita inkrementace příliš komplexní, dala by se do servisní třídy IntPlusOp. Rozdíl oproti anemickému modelu by byl v tom, že tu servisní třídu by obsahoval a volal přímo Int. Jinak, ideální je mít interface INumber, který bude mít jednoduchou implementaci Int. Třída IncInt by pak neměla od Int dědit - měla by být proxy (pokud jazyk nepodporuje dynamické proxy, bude to samozřejmě dost ukecané) + rozšířující metody. To z toho důvodu, aby bylo možné vytvořit další decorator DecIncInt s metodou dec(). A výhoda je v tom, že 1. není třeba zasahovat do původní implementce a 2. je zachován single responsibility principle - IncInt je zodpovědná pouze za inkrementaci. 9.10.2015
  • Taco : @pachol.jan: S těmi posledními dvěma větami se stoprocentně shodujem. Cíl je jasný. Teď jen, jak to uvést v život. 9.10.2015
  • pvy : @jiri.knesl: možná jsou anemické objekty mezistupněm mezi OOP a funkcionálním programováním, častěji jsou ale nepochopením OOP od programátorů pracujících procedurálně, používajících objekty jen jako knihovny shlukující metody. Tedy "mezistupněm" z druhé strany. @Kit: už samo "nechci dědičnost ani kompozici, chci ji rozšířit" je v oop špatně a není pro to důvod. Jinde píšete, že to je "lepší než kompozice", ale proč jako? Tam, kde se pracuje s původním rozhraním, rozšíření nic nezmění. Tam kde s novým, kompozice udělá totéž. 18.12.2015
  • Kit : @pvy: Je to vytrženo z kontextu a naházeno na jednu hromadu. 18.12.2015

Pro zobrazení všech 13 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.