Ale přidávat tam i implementaci mi připadá spíš jako zneužívání dědičnosti (*), které by bylo čistší vyřešit skládáním. Dokážete hodit nějaké ne-za-vlasy-přitažené příklady, kdy je takovéhle využívání mixinů architektonicky správné?
*) Něco jako když si programátor definuje utility funkci v nadtřídě a využívá ji z podtříd, místo co by ji vyčlenil do utility class.
Nejsem žádný specialista na mixiny, ale tady jde právě o skládání. Dědičnost v řadě případů nefunguje, protože stejnou funkcionalitu potřebuji např. u dvou potomků z různých větví dědičnosti. Přesně to, o čem mluvíš - musel bych to dát do společného předka, který je hluboko v řetězci a nemá s tím nic společného, nebo do externí utils třídy. Proto se to poskládá mixiny. Přímá dědičnost je vlastně jeden mixin.
Příklad, kde jsem to využil (neříkám, že by to nešlo jinak, v javě se to řeší např. kompozicí):
Mám ve websockets aplikaci různé boxy (divy), který se na nějaký povel otevřou. Jsou to potomci různých dědičných levelů základního boxu s různými funkcionalitami. A u některých chci, aby se při nečinnosti uživatele po chvíli zase zavřely a zobrazil se předchozí box.
Mixin TimedClose pro zavírání:
https://github.com/pavhofman/aio/blob/master/uis/timedclose.py#L11Přidám jej do boxů, které mají různé předky:
https://github.com/pavhofman/aio/blob/master/uis/volumefsbox.py#L17 - předkem je Widget (změním hlasitost a po chvíli se okno zavře do původní obrazovky)
https://github.com/pavhofman/aio/blob/master/uis/activatesourcefsbox.py#L10 - předkem je HBox -> Widget (nabídka zdrojů signálu, když na nic nekliknu, zase se to samo zavře)
Box se po čase daném v konstruktoru TimedClose zavře sám, případně jej zavře nadřazená aplikace
https://github.com/pavhofman/aio/blob/master/uis/webapp.py#L82Takových boxů je v projektu celá řada. U jiných to dneska nechci - např. box pro průchod stromem pro výběr dalšího přehrávaného tracku/adresáře
https://github.com/pavhofman/aio/blob/master/uis/nodeselectfsbox.py#L25 . A třeba se časem rozhodnu, že by bylo uživatelsky lepší jej autozavírat - stačí přidat TimedClose do seznamu jeho mixinů.
To samé třeba mixin AddPlaybackButtons
https://github.com/pavhofman/aio/blob/master/uis/addsplaybackbuttons.py#L16 , který každému boxu s tímto mixinem přidává tlačítka na přehrávání a kompletně ošetřuje jejich volání. Takže se mohu (i později) rozhodnout, ve kterých boxech z uživatelského rozhraní ta tlačítka budou, nezávisle na dědičné linii těch boxů.
Jenže by se mi líbilo, kdybych TimedClose mohl říct, pro jaké třídy je určený (vyžaduje existenci fieldu self._app typu WebApp). V javě by se použila klasická parametrizovaná generika, v pythonu jsem na to nepřišel. Samozřejmě mohu použít runtime kontrolu, což je IMO děs (např. super knihovna remi
https://github.com/dddomodossola/remi/blob/master/remi/gui.py#L465 , nebo jiný příklad
https://github.com/mopidy/mopidy/blob/develop/mopidy/internal/validation.py#L75 ). Samozřejmě se jedná jen o hint pro IDE (což je stejně případ všech těch type-hintů, pythonnímu interpretu je to fuk), ale ani to jsem nikde nenašel. Parametrizovaná generika
https://docs.python.org/3/library/typing.html#generics používám, ale potřeboval bych něco jako <T extends AppBox>
Ten odkazovaný projekt je rozpracovaný prototyp, chybí tam spoustu věcí...
V javě jde třídu rozšířit jen interfacem, proto se její vývojáři snaží trochu o workaround a přidávají funkcionalitu do interfaců. Java 8 má v interfacech defaultní implementované metody, java 9 přidává privátní metody. Bohužel pro plnou funkci chybí interní fieldy, aby si to mohlo udržovat stav, ale tímhle směrem to asi nepůjde.