Dědičnost dnes

SB

Re:Dědičnost dnes
« Odpověď #225 kdy: 20. 01. 2017, 15:21:14 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.


SB

Re:Dědičnost dnes
« Odpověď #226 kdy: 20. 01. 2017, 15:36:25 »

Jak psal Satai Nekola, ctverec dedici z obdelnika je v pohode pouze, pokud je immutable, jinak porusuje Liskov Substitution Principle, tedy instance podtypu ma stejne ocekavane vlastnosti jako instance nadtypu.

...

Nechápu, co řešíte. Čtverec jako dědic musí zajistit dodržení nového pravidla, že a = b, k tomu stačí překrýt metody, které nastavují strany, aby splnění zajistily. Pak můžete se čtvercem mutovat, jak chcete, pořád to bude čtverec.

javaman ()

Re:Dědičnost dnes
« Odpověď #227 kdy: 20. 01. 2017, 15:40:49 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.

Tak vidíš a jaký je to bastl. Dělají v tom jen lopaty, které nikdy nepochopí programování. Prostě mi přijde divné, proč používáš OOP pro nějaký paskvil, který je mrtvý a zůstal maximálně u skriptovacích jazyků na minivěci.

Takže znakem "pure" OOP je co? Normální OOP totiž vypadá daleko lépe a pure znamená horší, což obvykle bývá naopak.

SB

Re:Dědičnost dnes
« Odpověď #228 kdy: 20. 01. 2017, 15:46:09 »
Jak "rozdělení funkcí do rozhraní" vede ke sdílenému kódu??? Rozhraní z podstaty věci žádný kód neobsahuje.

Triviálně:
Kód: [Vybrat]
protocol Parallelogram { /* declarations of properties */ }
extension Parallelogram {
  var area:Double { return cos(angle) * sideA * sideB }
}

Pokud to jazyk neumožní takto elegantně, tak např. v Go prostě
Kód: [Vybrat]
func Area(p Parallelogram) float64 { return ... }
Důležité je, že se pracuje jen s rozhraním. Jak třídy od sebe dědí je fuk, ostatně v tomto případě by to měly být beztak hodnotové typy, kde dědičnost není vůbec, ale to je už jiný problém.

Pracujete se zmatením pojmů - to, co jste napsal, není rozhraní. Rozhraní je (už z názvu) jen to, co je vidět zvenku, tohleto je jakýsi hybrid zahrnující i implementaci (má to i Java jako rozhraní s default). Ve funkcionálním programování vás za to poplácají po zádech, v objektovém to hodně nerad vidím (obdoba anemického modelu, ale jen s metodami).

balki

Re:Dědičnost dnes
« Odpověď #229 kdy: 20. 01. 2017, 15:53:21 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.

Javascript je prototype-based, pre objekty tam neplati dualizmus trieda-instancia, ale objekty sa vytvaraju pomocou klonovania existujucich objektov. To je tak trosku podstatny rozdiel. Ano aj v javascripte su triedy, ale je to len syntakticky cukor.  Skor je javascript podobny self-u . (Self je sice dialekt smalltalku ale s podstatnym rozdielom, ze je prototype-based)


zboj

  • *****
  • 1 507
    • Zobrazit profil
    • E-mail
Re:Dědičnost dnes
« Odpověď #230 kdy: 20. 01. 2017, 15:54:29 »
Jak "rozdělení funkcí do rozhraní" vede ke sdílenému kódu??? Rozhraní z podstaty věci žádný kód neobsahuje.

Triviálně:
Kód: [Vybrat]
protocol Parallelogram { /* declarations of properties */ }
extension Parallelogram {
  var area:Double { return cos(angle) * sideA * sideB }
}

Pokud to jazyk neumožní takto elegantně, tak např. v Go prostě
Kód: [Vybrat]
func Area(p Parallelogram) float64 { return ... }
Důležité je, že se pracuje jen s rozhraním. Jak třídy od sebe dědí je fuk, ostatně v tomto případě by to měly být beztak hodnotové typy, kde dědičnost není vůbec, ale to je už jiný problém.

Pracujete se zmatením pojmů - to, co jste napsal, není rozhraní. Rozhraní je (už z názvu) jen to, co je vidět zvenku, tohleto je jakýsi hybrid zahrnující i implementaci (má to i Java jako rozhraní s default). Ve funkcionálním programování vás za to poplácají po zádech, v objektovém to hodně nerad vidím (obdoba anemického modelu, ale jen s metodami).
Je to defaultní implementace metod rozhraní (v terminologii Swiftu a ObjC protokolů) a v rámci OOP to je nejelegantnější a nejefektivnější způsob řešení zmíněného problému.

Re:Dědičnost dnes
« Odpověď #231 kdy: 20. 01. 2017, 16:05:15 »
To je jak u malých. Zejména Kiwi.

Programuje se pro lidi. Jestli ti to v tom prvním semestru neozřejmili...

Každý objekt má kopec implicitních vlastností. Že ti setA neodpálí rakety je mezi nimi.

Zrovna kontrakt, co ti rozbije tu hierarchii mutable čtverce a obdélníku, bude dost možná i explicitní v nějakém property based testu:
Kód: [Vybrat]
Obdélník o, r > 0
 =>
      obsah1 = o.obsah()
      o.setA(r * o.getA())
      r * obsah1 == o.obsah() až na epsilon
   

Re:Dědičnost dnes
« Odpověď #232 kdy: 20. 01. 2017, 16:07:25 »

Jak psal Satai Nekola, ctverec dedici z obdelnika je v pohode pouze, pokud je immutable, jinak porusuje Liskov Substitution Principle, tedy instance podtypu ma stejne ocekavane vlastnosti jako instance nadtypu.

...

Nechápu, co řešíte. Čtverec jako dědic musí zajistit dodržení nového pravidla, že a = b, k tomu stačí překrýt metody, které nastavují strany, aby splnění zajistily. Pak můžete se čtvercem mutovat, jak chcete, pořád to bude čtverec.

Vážně to nechápeš ;)

Nedá se to rozumně udělat při zachování i kontraktu pro Obdélník. Dále viz LSP

gll

Re:Dědičnost dnes
« Odpověď #233 kdy: 20. 01. 2017, 16:13:55 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.

Javascript je prototype-based, pre objekty tam neplati dualizmus trieda-instancia, ale objekty sa vytvaraju pomocou klonovania existujucich objektov. To je tak trosku podstatny rozdiel. Ano aj v javascripte su triedy, ale je to len syntakticky cukor.  Skor je javascript podobny self-u . (Self je sice dialekt smalltalku ale s podstatnym rozdielom, ze je prototype-based)

Objekty se vytváří konstruktorem. Nic se při tom neklonuje. Prototyp zůstává jen jeden.

Kiwi

Re:Dědičnost dnes
« Odpověď #234 kdy: 20. 01. 2017, 16:36:49 »
Kiwi povídá píčoviny. Asi zažil první semestr na škole a je z toho úplně mimo ;D

To spíš vy mi připadáte jako děcka s mlíkem na bradě, co v životě nemusela přidávat nějakou funkcionalitu do existujícího projektu, protože zatím si jen patlala párřádkové školní projektíky jakožto one-man-show from scratch.

Takže ještě jednou:

1. Netvrdím, že chování, kdy změním A, ale pod rukama se mi změní i B, je optimální. Takto bych to já nejspíš nenavrhnul, kdyby to bylo na mně. Jenže většina mých zkušeností plyne z praktického života, v němž přijdete k existující věci, která je málokdy navržená optimálně.

2. Pokud někdo navrhne třídu "obdélník" jakožto třídu měnitelných objektů, z níž je navíc dovoleno odvozovat podtřídy, tak je třeba se srovnat s tím, že někdo může potřebovat čtverec jako speciální případ obdélníku, a tedy s výše popsaným "záhadným" chováním setSideA, setSideB; nemusí to končit čtvercem; další bude potřebovat zlatý obdélník (tedy se stranami v poměru zlatého řezu) a nakonec přijde někdo, kdo bude chtít modrý obdélník (dopad na chování v případě poslání zprávy setColor takovému objektu tu doufám nemusím rozebírat). Znovu zdůrazňuji, že netvrdím, že takto udělaný program je optimálně navržený. Nejspíš by to byl jen jeden z mnoha návrhů, kdybych opět kroutil hlavou, točil oči v sloup a ptal se sám sebe "pro boha, proč takhle", ovšem bez znalosti konkrétního kontextu těžko soudit. Ale:

3. Pokud někdo další bude takto navržený program dále rozvíjet s předpokladem, že přece setSideA nemůže mít za žádných okolností vliv na druhou stranu, nebo že setColor zaručeně povede ke změně barvy na požadovanou, tak veškerou nešikovnost původního návrhu povýší na kvadrát. Protože tak přidá další nadbytečné vazby na úrovni, na níž je nikdo při návrhu nepředpokládal. A kvůli tomuto jeho redundantnímu předpokladu, kterým se mu možná, někde, něco trochu zjednodušilo, se další věci velmi zkomplikují. Stačí, aby obdélník byla netriviální třída, a z přidání triviálního čtverce se stane noční můra jen kvůli tomu, že zas někdo "myslel" a místo aby se obdélníka explicitně dotázal na jeho vlastnosti, jak se sluší a patří, tak si myslí, že si je spočítá lépe než on sám. Což rozhodně je bezprecedentní porušení principu zapouzdření!
A mimochodem to, že objekt reaguje na zprávu podle svého uvážení, je jedním ze základních pilířů objektově orientovaného programování. Proto se tomu taky neříká procedura. To nebyl jen nějaký terminologický fetiš otců-zakladatelů OOP. Různé objekty mohou na stejnou zprávu reagovat různě. Stejný objekt může na stejnou zprávu za různých okolností reagovat různě. Pošlete třídě GeometrickýÚtvar zprávu new #rectangle withA: 10 withB: 10 a obdržíte objekt třídy čtverec (například z důvodu optimalizace). Pošlete mu zprávu setSideB: 20, které samozřejmě čtverec nebude rozumět, tak ji deleguje o úroveň výš, kde se rozhodne o změně třídy na obdélník. To všechno transparentně. A právě o tomto a jiných podobných obratech je objektové programování, má-li to doopravdy ulehčovat práci. A ne o tom, že nacpu funkce a proměnné do tříd a budu dědit do bezvědomí.

javaman ()

Re:Dědičnost dnes
« Odpověď #235 kdy: 20. 01. 2017, 16:45:46 »
One-man show není nic špatného. My s Kitem to máme rádi ;D Alespoň nikdo do toho necpe žádné nesmysly.

OK, to pak asi jo. Já bych to použil a určitě nezkoumal, jestli mi setA maže i půl disku. Ale co má s tím společného matika? Naopak by byla hloupost předpokládat, že to maže i ten disk. Taky to může objednávat na Alze, že jo. Ani jedno to ale určitě nedělá. Takže spíše bude problém u tebe. Možná nějaká paranoia?

Do obdélníků se cpát nebudu, na to tu je dost jiných lidí :D

Logik

  • *****
  • 1 022
    • Zobrazit profil
    • E-mail
Re:Dědičnost dnes
« Odpověď #236 kdy: 20. 01. 2017, 16:53:19 »
Celý problém, který jsem naschvál jen nakousnul a nedořek, je v tomto:

Dědičnost NENÍ vhodná na modelování každého vztahu A is B. Pouze vztahu A is B a A je záměnné pro B. Což v podstatě znamená and A is B a navíc A umí vše co B. Což u čtverce ve vztahu k obdélníku není pravda - a proto modelace čtverce jako speciálního případu obdélníka je někdy špatně.

A naopak - v určitém kontextu se může hodit to, co jsem provokativně nadhodil: pokud budu řešit např. problém optimalizace vyplnění prostoru pomocí dlaždic, tak se mi může hodit nadefinovat čtvercovou dlaždici, která má (mimo pozice) jeden parametr volnosti, a z ní potomka obdélníkovou dlaždici, která má jeden parametr navíc: poměr stran.

Oba tyto vztahy: Čtverec je podmnožinou obdélníka i obdélník je podmnožinou čtverce splňují pouze jednu z výše uvedených podmínek pro správnou dědičnost: obdélník není čtverec, ale čtverec neumí vše, co obdélník. Proto pro obě tyto dědičnosti najdete protipříklady, kdy jejich užití selže.

A to jsem neještě nemluvil o kosočtvercích a kosodélnících: je čtverec speciální případ kosočtverce (fixuje úhel) nebo kosodélníku atd..., atd....

Osobně v praxi používám to, že pro dědění rozhraní, které jsou více o tom, co předmět umí než co předmět je, volím dědění dle "umí", tedy rozhraní obdélníku bych třeba navrhl jako speciální případ k rozhraní čtverce. Naopak u dědičnosti je dobré zachovávat především vztah "is a", ovšem s tím, že pokud bych porušil (používaný) vztah "umím", tak nedědit.

Celé je to způsobeno tím, že dědičnost i rozhraní je jen velké zjednodušení reality. Které lze použít jen potud, pokud to zjednodušení není na úkor porušení vztahů, které je třeba kódem zachytit. Proto někdy je bez problémů popsat čtverec jako obdélník, někdy ne.


PS: Jinak Kiwi má částečně dobrý postřeh s tím, že výše uvedené problémy se zhoršují mutabilitou objektů. Ovšem i bez mutability to je problém: pokud mám třídy A který může mít stav X, a jejího potomka B, který v stavu X být nemůže, tak prostě to je problém, který mi immutabilita nijak neřeší. Protože správná reakce na žádost vrať mi B ve stavu X není z B nejprve udělat A, správná odpověď je říci: já v tom stavu být nemohu.

Jde to vyřešit tím, pokud součástí "protokolu" třídy A je "nastavení do stavu X se nemusí povést". Pak vlastně není problém. Naopak pokud je součástí byť implicitní definice třídy, že přechod do stavu X lze vždy, tak vlastně z definice vidíme, že B nemůže být potomkem A - tedy že čtverec není obdélník.

Z tohoto pohledu by to vlastně immutabilita řešila, akorát třída A by neměla mít metodu: vrať mi sebe ve stavu X, ale vrať mi objekt A, který vznikne ze mě převedením do stavu X. Tedy obdélník i čtverec by měli metodu: vrať_kopii_obdélníku_se_stranou_B. To ovšem není řešením tohoto problému jako celého, pořád je tu problém v "odstraňování schopností", např: Člověk umí běžet. Chromý člověk je člověk, ale běžet neumí.

ava

Re:Dědičnost dnes
« Odpověď #237 kdy: 20. 01. 2017, 17:06:59 »
Kiwi povídá píčoviny. Asi zažil první semestr na škole a je z toho úplně mimo ;D

To spíš vy mi připadáte jako děcka s mlíkem na bradě, co v životě nemusela přidávat nějakou funkcionalitu do existujícího projektu, protože zatím si jen patlala párřádkové školní projektíky jakožto one-man-show from scratch.

Takže ještě jednou:

1. Netvrdím, že chování, kdy změním A, ale pod rukama se mi změní i B, je optimální. Takto bych to já nejspíš nenavrhnul, kdyby to bylo na mně. Jenže většina mých zkušeností plyne z praktického života, v němž přijdete k existující věci, která je málokdy navržená optimálně.

2. Pokud někdo navrhne třídu "obdélník" jakožto třídu měnitelných objektů, z níž je navíc dovoleno odvozovat podtřídy, tak je třeba se srovnat s tím, že někdo může potřebovat čtverec jako speciální případ obdélníku, a tedy s výše popsaným "záhadným" chováním setSideA, setSideB; nemusí to končit čtvercem; další bude potřebovat zlatý obdélník (tedy se stranami v poměru zlatého řezu) a nakonec přijde někdo, kdo bude chtít modrý obdélník (dopad na chování v případě poslání zprávy setColor takovému objektu tu doufám nemusím rozebírat). Znovu zdůrazňuji, že netvrdím, že takto udělaný program je optimálně navržený. Nejspíš by to byl jen jeden z mnoha návrhů, kdybych opět kroutil hlavou, točil oči v sloup a ptal se sám sebe "pro boha, proč takhle", ovšem bez znalosti konkrétního kontextu těžko soudit. Ale:

3. Pokud někdo další bude takto navržený program dále rozvíjet s předpokladem, že přece setSideA nemůže mít za žádných okolností vliv na druhou stranu, nebo že setColor zaručeně povede ke změně barvy na požadovanou, tak veškerou nešikovnost původního návrhu povýší na kvadrát. Protože tak přidá další nadbytečné vazby na úrovni, na níž je nikdo při návrhu nepředpokládal. A kvůli tomuto jeho redundantnímu předpokladu, kterým se mu možná, někde, něco trochu zjednodušilo, se další věci velmi zkomplikují. Stačí, aby obdélník byla netriviální třída, a z přidání triviálního čtverce se stane noční můra jen kvůli tomu, že zas někdo "myslel" a místo aby se obdélníka explicitně dotázal na jeho vlastnosti, jak se sluší a patří, tak si myslí, že si je spočítá lépe než on sám. Což rozhodně je bezprecedentní porušení principu zapouzdření!
A mimochodem to, že objekt reaguje na zprávu podle svého uvážení, je jedním ze základních pilířů objektově orientovaného programování. Proto se tomu taky neříká procedura. To nebyl jen nějaký terminologický fetiš otců-zakladatelů OOP. Různé objekty mohou na stejnou zprávu reagovat různě. Stejný objekt může na stejnou zprávu za různých okolností reagovat různě. Pošlete třídě GeometrickýÚtvar zprávu new #rectangle withA: 10 withB: 10 a obdržíte objekt třídy čtverec (například z důvodu optimalizace). Pošlete mu zprávu setSideB: 20, které samozřejmě čtverec nebude rozumět, tak ji deleguje o úroveň výš, kde se rozhodne o změně třídy na obdélník. To všechno transparentně. A právě o tomto a jiných podobných obratech je objektové programování, má-li to doopravdy ulehčovat práci. A ne o tom, že nacpu funkce a proměnné do tříd a budu dědit do bezvědomí.

Heh, taky jsem pár let dělal ve smalltalku, teď se snažím spíš o FP přesně abych nemusel řešit takhle idiotské návrhy programů (i když s tím jsem se nepotkal snad ani v tom smalltalkovém kódu). Správný přístup k idiotskému návrhu, když ho uvidím, obzvlášť ve smalltalku, je "refactor mercilessly", v horším případě si tu funkcionalitu udělat vedle a lépe, ale něco na tom stavět je prostě blbě. V praxi na tomhle obdélníkočtvercomutantovi nebudu volat #area nikdy proto, že je nepoužitelné, protože nemá definovatelnou hodnotu (jaký obsah má čtverec, kterému nastavím strany 2 a 3?), takže tuhle hovadskou areu nemůžu použít ve výpočtu, ukázat uživateli, uložit do databáze, prostě nic. Tedy můžu, ale uživatele tím zblbnu, do databáze perzistuji hovadinu, a ve výpočtu zkur*ím další výpočet. To je ovšem jen do extrému dotažený tvůj přístup "nic nepředpokládat", když o návratových hodnotách nemůžu nic předpokládat, tak je taky nemůžu na nic použít, že....

Případně o chování tohohle kódu mohu mít nějaká očekávání (narozdíl od tebe, ty se bát nemusíš), ale nejspíš budou porušena kvůli LSP, takže pak budu křičet na původního autora. Porozumění LSP je tu právě proto, aby takovéhle bastly pokud možno nevznikaly (a dost staticky typovaných jazyků si vynucuje dodržování LSP alespoň co se týče variance anotacemi ko/kontra/invariance u typových parametrů).

balki

Re:Dědičnost dnes
« Odpověď #238 kdy: 20. 01. 2017, 17:16:51 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.

Javascript je prototype-based, pre objekty tam neplati dualizmus trieda-instancia, ale objekty sa vytvaraju pomocou klonovania existujucich objektov. To je tak trosku podstatny rozdiel. Ano aj v javascripte su triedy, ale je to len syntakticky cukor.  Skor je javascript podobny self-u . (Self je sice dialekt smalltalku ale s podstatnym rozdielom, ze je prototype-based)

Objekty se vytváří konstruktorem. Nic se při tom neklonuje. Prototyp zůstává jen jeden.

https://en.wikipedia.org/wiki/Prototype-based_programming#Object_construction

gll

Re:Dědičnost dnes
« Odpověď #239 kdy: 20. 01. 2017, 17:29:20 »
V čem se zásadně liší smalltalk od dynamických OOP jazyků jako javascript?

Tak pozor, zrovna Javascript je docela blízký Smalltalku - má jmenný polymorfismus, (v podstatě nepoužitelné) zapouzdření a pozdní vazbu. Mimoto velice silnou reflexivitu.

Javascript je prototype-based, pre objekty tam neplati dualizmus trieda-instancia, ale objekty sa vytvaraju pomocou klonovania existujucich objektov. To je tak trosku podstatny rozdiel. Ano aj v javascripte su triedy, ale je to len syntakticky cukor.  Skor je javascript podobny self-u . (Self je sice dialekt smalltalku ale s podstatnym rozdielom, ze je prototype-based)

Objekty se vytváří konstruktorem. Nic se při tom neklonuje. Prototyp zůstává jen jeden.

https://en.wikipedia.org/wiki/Prototype-based_programming#Object_construction

Nemusíte mě přesvědčovat. Já narozdíl od vás znám javascript velice dobře a prakticky ho používám. V těch příkladech se nic neklonuje. Pouze se nastavuje odkaz na prototyp.