Dědičnost anotací

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
romansklenar
Člen | 655
+
0
-

V jiném vlákně se zde začalo mluvit o snaze o větší rozšíření anotací. V souvislosti s tím musím nakousnou téma dědičnost anotací. Zkusil jsem to implementovat a zjistil jsem, že výsledky nemusí být jednoznačné. V získání jedné anotace (Annotations::get()) ani ne, ale v získání více anotací (Annotations::getAll()) může být díky různým strukturám zápisu výsledek těžko odhadnutelný.

Proto bych chtěl otevřít diskusi: Jaký by měl být výsledek v následujících případech?

/**
 * @renderable False
 */
class ParentA extends Object{}

/**
 * @renderable True
 */
class DescendantA extends ParentA {}

Zde je to asi jasné, dědím jednu anotaci, kterou v potomkovi přepisuji. Výsledkem by mělo být True

/**
 * @persistent(grid, paginator)
 */
class ParentB extends Object{}

/**
 * @persistent(tabcontrol, poolcontrol)
 */
class DescendantB extends ParentB {}

Zde je to na první pohled stejné jako v prvním případě, dědím jednu anotaci, kterou v potomkovi přepisuji. Ale člověk by asi očekával, že když potomka obohatí o nějaké persistentní komponenty, zároveň zachová persistentní komponenty předků.

/**
 * @author Jack Doe
 * @author John Doe
 */
class ParentC extends Object{}

/**
 * @author Jane Doe
 */
class DescendantC extends ParentC {}

Zde už to není tak jasné jako v předchozím případě – zde už nedědím a nepřepisuju jednu anotaci ale rovnou dvě (to, že by se to dala anotace v ParentB zapsat takto @author('John Doe', 'John Doe') a vyhnout se tak problém nejednoznačnosti je mi jasné, o to tu ale nejde). Nabízí se řešení přidat Jane Doe k stávajícím autorům. Není to ale konzistentní (v jednom případě přepisuji, v druhém přepisuji ikdyž chci rozšířit a v třetím rozšiřuju).

Pokud někoho napadnou nějaké další krizové případy, které by bylo třeba podchytit a zdokumentovat, sem s nimi.

Petr Motejlek
Člen | 293
+
0
-

Já bych řekl, že dědičnost je dost nejasná (EDIT: Ale super!). Musela by se řešit na úrovni každé anotace zvlášť, tzn. někdo by už při založení té třídy, která by danou anotaci řešila (analogie s tím vláknem, které zmiňuješ), musel říct, jestli je možné ji dědit, nebo ne. Typicky dědit toho autora je blbost ;) Pokud extendnu například Nettovský Object, tak snad nikdo nepředpokládá, že když zapomenu @author, tak bude automaticky autor mé třídy David Grudl ;). Na druhou stranu by se to mohlo hodit, abych nemusel tolik psát, ale zrovna @author je anotace, kterou mi IDE samo dělá, tak není třeba u ní tohle vůbec řešit…

Zatím jsem vysledoval následující tři vlastnosti, které bychom mohli u anotací sledovat. Technicky by se implicitní hodnoty těch vlastností daly nastavovat u těch obslužných tříd pro jednotlivé anotace (AuthorAnnotation, SecuredAnnotation, … – viz to druhé vlákno o nových anotacích), a pokud budu chtít, aby v nějakém konkrétním případě se ta anotace chovala jinak, bude se muset trochu překopat syntaxe zadávání anotací. Podle těch vlastností by se chovala i Nette\Annotations.

  1. Fallthrough – bude se daná anotace automaticky projevovat i u všech potomků? Třeba AuthorAnnotation by měla nastaveno, že není fallthrough, aby Nette\Annotations nehlásilo, že „MyObject extends Object“ bez uvedené anotace @author je výplod Davida ;).
  2. Overridable – je možné danou anotaci překrýt, resp. kompletně přepsat? Z hlavy mě nenapadá úplně super příklad, ale teoreticky by se dal představit zápis, kterým bych třeba naopak chtěl říct, že nějakou z anotaci, kterou má předek prostě nechci převzít, třeba @secured Petr Motejlek se mi nebude líbit, tak bych se toho chtěl nějak zbavit. Třeba bych u potomka použil @secured- Petr Motejlek (všimněte si toho mínus).
  3. Extendable – můžu ještě něco přidat k anotaci, kterou má u sebe napsanou předek? Předek může mít @secured Petr Motejlek (metody může spouštět jen role Petr Motejlek), potomek by měl taky implicitně být spouštěn jen rolí Petr Motejlek, ale já bych mohl třeba chtít říct, že Petr Motejlek je OK, ale že bych chtěl, aby tam měl přístup i romansklenar, tak bych udělal @secured+ RomanSklenar (všimněte si toho plus) a měly by tam přístup obě role.

Když anotace není a), nemůže být ani b) ani c).
Když anotace není b), nemůže být c).

Syntaxe pro zápis anotací k potomkům by mohla být asi taková:
@secured* Petr Motejlek – přepiš všechny hodnoty anotací @secured, které by jsi mohl mít zděděné.
@secured+ Petr Motejlek – přidej mezi všechny hodnoty anotací @secured, který by jsi mohl mít zděděné.
@secured- Petr Motejlek – odeber ze všech hodnot anotací @secured, které by jsi mohl mít zděděné.
S tím, že @secured Petr Motejlek je ekvivalentní s @secured* Petr Motejlek.

Teď je ještě potřeba nějak do toho vmáčknout i definice těch tří vlastností (implicitní hodnoty by byly definované v SecuredAnnotation, ale já budu třeba u některé třídy chtít říct, že to je trošku jinak). Na druhou stranu, taky se dá říct, že když se chci chovat jinak, než se chová SecuredAnnotation, tak si udělám potomka MySecuredAnnotation a toho naučím se tak chovat. Tam je pak ale trošku problém s tím, že mi není jasné, jak by fungovalo Nette\Object s anotací @author David Grudl, můj MyObject extends Object, a já chci zdědit @author ;) Když si udělám MyAuthorAnnotation, tak budu stejně nějak potřebovat říct třídě Nette\Annotations, aby mi poslala i anotace @author… ;)

Editoval Petr Motejlek (15. 12. 2009 10:40)

paranoiq
Člen | 392
+
0
-

tohle je docela složité téma na to, že se ještě ani nedohodlo, jak vlastně budou anotace v Nette vypadat. na PS jsme toho moc nevyřešili. debatu rozfoukávám tady: https://forum.nette.org/…nich-anotaci

pro začátek bych přidal naprosto jednoduchou dědičnost, která prostě přepíše anotaci předka (resp. nebude se vůbec ptát předka, pokud má aktuální prvek anotaci). tzn stejně jako je to implementováno v Javě. kdokoliv bude potřebovat složitější řešení, může si to dopsat do třídy, která bude s výsledkem pracovat. nebo se to může přidat později až se nějaká implementace vyrobí

větší problém je, co s metodou getAll(). ta by musela vždy proskenovat veškeré předky, jestli se v nich náhodou nevyskytuje nějaká anotace s dědičností. ale i to by mělo jít kešovat

Editoval paranoiq (16. 12. 2009 9:29)

Petr Motejlek
Člen | 293
+
0
-

IMHO pokud by MojeAnotaceAnnotation třída mohla i sama řešit, co se má stát, když něco má anotaci @mojeAnotace, tak getAll() de facto v normálním kódu ani potřebovat nebudeš. Každopádně projít všechny předky budeš muset tak jako tak, ale Nette\Cache to jistí ;).

David Grudl
Nette Core | 8082
+
0
-

Myslím, že vše by řešily příznaky, zda anotace

  • může být uvedena vícenásobně
  • zda se dědí

a stylově by se to dalo nastavit anotací anotace ;)

/**
 * @Inherited
 * @Multiple
 */
class AuthorAnnotation extends Annotation
{
}

Na druhou stranu stále je třeba rozhodnout, jestli u Multiple+Inherited anotace potomek rozšiřuje nebo přepisuje. Osobně bych to viděl na přepisování. Multiple anotace jsou stejně docela podivná věc, otázka je, jestli je na straně runtime vůbec potřebujeme. Nebo respektive jestli je nesloučit vždy do jedné anotace.

Petr Motejlek
Člen | 293
+
0
-

IMHO se lépe čte, když je anotace víckrát napsaná, než když je jednou a prvky se nějak musí oddělit ;). Vizte

<?php
  /**
   * @author Já
   * @author Já 2
   * @author Já 3
   * @author Já 4
   */
?>
<?php
  /**
   * @author Já, Já 2, Já 3, Já 4
   */
?>

Když se na to dívám s odstupem, domnívám se, že jestli by se mělo implementovat alespoň něco, vyděl bych to tak, že anotace budou dědičné, a že se přepisují, žádné přidávání/odebírání na potomcích.

Editoval Petr Motejlek (4. 1. 2010 15:04)

David Grudl
Nette Core | 8082
+
0
-

Ale to je jen forma zápisu, může být klidně stanoveno, že oba zápisy jsou ekvivalentní.

romansklenar
Člen | 655
+
0
-

Zkusím uvést konkrétní a jednoduchý příklad, kdy se rozšiřování při dědičnosti podle mě hodí a taky důvod proč jsem toto vlákno vůbec otevřel:

/**
 * @hasMany(People)
 */
abstract class Company extends ActiveRecord
{
	/** @var string  table name */
	protected static $table = 'Companies';
}

/**
 * @hasMany(Clients, Invoices)
 */
class Firm extends Company {}

/**
 * @hasMany(Orders)
 */
class Client extends Company {}

Kdyby toho šlo nějak dosáhnout (třeba i poděděním a přepsáním nějaké metody nějaké třídy), tak by to bylo perfektní.

Patrik Votoček
Člen | 2221
+
0
-

Co se takhle „inspirovat“ parserem z Doctrine 2 ?