JSTraits – znovupoužití kódu bez dědičnosti v JavaScriptu
Pokud jste někdy tvořili složitější hierarchii tříd, možná jste zažili situaci, kdy se dostaly do sporu dva úkoly dědičnosti – vyznačení struktury objektů a využití již jednou napsaného kódu. Výsledkem byla pravděpodobně buď „pokřivená“ hierarchie, nebo opakování částí kódu na více místech.
Podobným situacím v JavaScriptu nyní můžete zabránit pomocí knihovny JSTraits. Ta umožňuje do tříd vkládat tzv. traits – znovupoužitelné soubory metod s popisem požadavků na objekt, do kterého jsou vkládány, a nabízené funkcionality. Traits tak ve výsledku umožňují využít jednou napsaný kód ve více třídách, aniž by bylo potřeba je propojovat pomocí dědičnosti.
Jednoduchý příklad použití knihovny najdete na Ajaxianu. Článek Traits: A Mechanism for Fine-grained Reuse pak nabízí podrobnější teoretický pohled na traits a porovnání tohoto mechanismu s dalšími způsoby znovupoužití kódu (jednoduchá/mnohonásobná dědičnost a mixins).
Pokud vím, dědičnost jako prostředek znovupoužití kódu je nedoporučovaná praktika :-), a z vlastní zkušenosti můžu říct, že pokud se dědí kvůli znovupoužití přes víc než jednu úroveň, začne být kód hodně nepřehledný a dost obtížně rozšiřitelný.
Jinak ten článek si určitě přečtu, ale do té doby bych se zeptal: je nějaký rozdíl mezi mixiny a traity? Mixiny jsou třeba v Ruby a traity ve Scale, ale já žádný rozdíl nevidím.
Ano, to je. Hodně lidí ale myslí, že znovupoužití kódu je primárním účelem dědičnosti, a používá ji tak.
Různí lidé tím často míní různé věci, což dost mate. Sám jsem na to narazil při učení na státnice před pár měsíci. Podle článku je to takto:
Mixin je soubor metod. Ty mohou dělat všechno, jako normální metody třídy, tedy i přistupovat ke stavu objektu (k jeho atributům). Vložení mixnu do třídy znamená, že se jakoby (a v některých implementcích i doopravdy, byť většinou neviditelně) vytvoří nová třída, obsahující metody původní třídy a k nim metody mixinu. Jinak řečeno, mixins fungují přesně jako vkládání modulů v Ruby.
Problém nastane v situaci, kdy jsou vložené mixiny nějakým způsobem konfliktní (např. přepisují stejný atribut objektu nebo obsahují stejně nazvanou metodu). Tyto konflikty se řeší implicitně pomocí mechanismu dědičnosti – v případě atributů to znamená, že se neřeší nic; v případě metod to znamená, že platí pozdější definice. Díky dědičnosti může
super
v metodě mixinu odkazovat na metodu v nadtřídě, ale také na metodu definovanou ve dříve vloženém mixinu. Důsledkem toho všeho je kromě možných chyb také závislost na pořadí vložení mixinů.Trait je také soubor metod, ale navíc je doplněn o seznam metod (a případně jejich signatur – to závisí především na tom, jak daný jazyk zachází s typy), které tyto metody potřebují od objektu, do kterého budou vloženy. Metody samy nemají přístup ke stavu objektu, ten mohou zjistit jen nepřímo pmocí metod ze seznamu. Vložení metod do objektu neprobíhá pomocí mechanismu dědičnosti. Konflikty (zde jen stejně nazvané metody) je možné řešit pomocí aliasů (např. „vlož mixin
A
, jeho metodux
zveřejni ve třídě jako metoduy
a jako požadovanou metoduz
mu podstrč metoduw
původního objektu“).Cena traits je hlavně v jejich větší striktnosti, což zabraňuje konfliktům, a nepoužití mechanismu dědičnosti k jejich vkládání a řešení konfliktů.
Moc díky za detailní odpověď. Vás je radost číst :-)
Ještě bych doplnil, že místo článku odkázaného ve zprávičce je pro hlubší pochopení problematiky asi lepší článek Traits: Composable Units of Behavior. Obsahuje méně zbytečné teorie a jde více „k věci“.