[2010–04–22] Zrušení Collections
- David Grudl
- Nette Core | 8218
Každý rozumný jazyk disponuje vestavěnou podporou kolekcí. Jde zejména o třídy:
- array – uspořádaný seznam prvků, číslovaný obvykle od nuly
- hash – neuspořádaný seznam prvků indexovaných pomocí klíčů
- set – neindexovaná a neuspořádaná množina prvků
Array lze pak použít pro reprezentaci datových struktur queue a stack.
PHP místo jakékoliv z těchto struktur disponuje jakýmsi hybridem array & hash. Což je na jedné straně mocná struktura, na druhé straně potenciální zdroj problémů. Třeba uspořádanost záleží na tom, v jakém pořadí prvky do pole přiřazujete, nikoliv na jejich indexu:
$arr[2] = 'druhy';
$arr[1] = 'prvni';
foreach ($arr as $item) echo $item; // vypise 'druhy', 'prvni'
Nette Framework tento nedostatek řešil zavedením vlastních tříd
Nette\Collection
, konkrétně
ArrayList, Hashtable, Set
a abstraktního předka
Collection
.
PHP počínaje verzí 5.3 na implementaci datových struktur pořádně zapracovalo a Nette\Collection
ztrácí význam. Nebo to napíšu jinak: nechci v této nice vytvářet
konkurenci. Rozhodl jsem se proto Nette\Collection
vyjmout
z frameworku a přesunul je pro zájemce do extras.
Jak je to s kompatibilitou?
Nette\Collection\ArrayList
nahrazuje SplQueue (od verze 5.3)Nette\Collection\HashTable
nahrazuje ArrayObject (od verze 5.0)Nette\Collection\Set
nahrazuje SplObjectStorage (od verze 5.1)
Nicméně API je pochopitelně zcela rozdílné. Třídám SPL také chybí možnost kontrolovat typ vkládaného prvku (což je docela mrzuté) a možnost učinit strukturu pouze pro čtení.
V rámci samotného frameworku využívaly kolekcí třídy
MultiRouter
a Config
. První jmenovaný získá
rozhraní ArrayAccess a IteratorAggregate
, takže jeho použití se
nijak nezmění. V případě Config
si změny zaslouží vlastní
popis (doplním).
- David Grudl
- Nette Core | 8218
Tak v současné revizi už byla hrozba naplněna :-)
Pro zajímavost: PHP SPL Data Structures Benchmark.
- David Grudl
- Nette Core | 8218
Ještě jednu poznámku – SPL třídy jako například ArrayObject nebo i nové
SplQueue nebo mají
poměrně bohaté API. Což samozřejmě neimplikuje nic špatného. Zatímco
API nové SplQueue
vypadá rozumně (chybí mi tu ovšem možnost
vložit nový prvek dovnitř pole, nějaké insertAfter), API starší
ArrayObject
je docela odstrašující příklad. (prasácký
rukopis Marcuse Boergera v SplQueue vidět není, bo jej napsal někdo jiný,
Etienne Kneuss). Zároveň ArrayObject má pošramecenou i runtime-pověst,
stačí se podívat, kolikrát je zmíněn v bug trackeru PHP a jen
doufám, že Etienne Kneuss odvedl lepší kus práce.
Bohaté API ovšem komplikuje vytváření potomků tříd. Pokud bychom
chtěli vytvořit například potomka, který dovoluje přidávat pouze prvky
určitého typu, musíme v SplQueue přepsat metody offsetSet
,
push
a unshift
, v případě ArrayObject by šlo asi
o pět metod. A s rizikem, že další verze PHP přidají další
metody.
Především je tu otázka dědičnosti jako takové –
dle mého jedna z nejvíce zneužívaných vlastností OOP. V PHP se často
dědí jen kvůli neopakování kódu → protože PHP neumí mixiny nebo
vícenásobnou dědičnost. V PHP se taky dědí kvůli magickým vlastnostem,
což byl příklad ArrayObject jakožto potomka tříd Nette\Collection nebo
DibiRow. Všechno proti logice dědičnosti! Docela mě štvalo, že MultiRouter
nebo DibiRow má metody jako natcasesort
nebo
getFlags
.
Jinými slovy, nechci (už více) vytvářet:
- vrstvy nad SPL třídami kolekcí
- používat SPL třídy kolekcí jako předky tříd frameworku
Z těchto důvodu je MultiRouter implementován jako kompozice s SplQueue / ArrayList, Config jej bude následovat a celá Nette\Collections šla do kytek.
- David Grudl
- Nette Core | 8218
Moc se mi to nechce zveřejňovat, prostě Collection založené na ArrayObject není nic, čím bych se chlubil.
PS. LazyArrayList
z Ormionu by úpravou na něco, jako je
aktuální ArrayList
, mělo po objektové stránce taky spíš
získat, ne? Nebo jsou tam zádrhele? (kromě nemožnosti přetypování přes
(array)
, protože to odhalí privátní $loaded
).