Kolekcje
Java definiuje kolekcję następująco grupa obiektów o podobnej charakterystyce.
Część ludzi zawęża definicję do takiej postaci: Kolekcja to obiekt, który prócz listy innych obiektów, implementuje metody do pracy na kolekcji..
Wg definicji z Javy, kolekcją może być poniższy przykład. Jednak nie ma żadnych korzyści z nazywania tego kolekcją. W końcu jest to tylko tablica obiektów.
$collection = array(new stdClass , new stdClass );
By spełnić drugą definicję trzeba stworzyć obiekt, który umożliwi przechowywanie listy obiektów i będzie udostępniał jakieś metody do pracy na nich.
Niestety, PHP na obecną chwilę (a przynajmniej ja o tym nie wiem) nie ma gotowego obiektu kolekcji ani niczego co by go udawało (tablica wg mnie nie udaje kolekcji) - toteż trzeba sobie takowy stworzyć.
class Collection implements \Iterator, \ArrayAccess, \Countable {
protected $storage;
protected $protected;
/**
* @param array $storage
* @param array $protected
*/
public function __construct(Array $storage = array , Array $protected = array ) {
$this->storage = $storage;
$this->protected = (array) $protected;
}
/**
* Offset to unset
*
* @param string $offset
* @return void
*/
public function offsetUnset($offset) {
unset($this->storage[$offset]);
}
/**
* Offset to set
*
* @param string $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value) {
if($offset === null) {
$this->storage[] = $value;
return;
}
$this->storage[$offset] = $value;
}
/**
* Offset to retrieve
*
* @param string $offset
* @return mixed
*/
public function &offsetGet($offset) {
if(!isset($this->storage[$offset])) {
$this->storage[$offset] = null;
}
return $this->storage[$offset];
}
/**
* Whether a offset exists
*
* @param string $offset
* @return bool
*/
public function offsetExists($offset) {
return isset($this->storage[$offset]);
}
/**
* Return the current element
*
* @return mixed
*/
public function current {
return current($this->storage);
}
/**
* Move forward to next element
*
* @return void Any returned value is ignored.
*/
public function next {
next($this->storage);
}
/**
* Return the key of the current element
*
* @return scalar
*/
public function key {
return key($this->storage);
}
/**
* Checks if current position is valid
*
* @return boolean
*/
public function valid {
$key = key($this->storage);
while($key !== null && in_array($key, $this->protected)) {
$this->next ;
$key = $this->key ;
}
if($key === false || $key === null) {
return false;
}
return isset($this->storage[$key]);
}
/**
* Rewind the Iterator to the first element
*
* @return void
*/
public function rewind {
reset($this->storage);
}
/**
* Count elements of an object
*
* @return int
*/
public function count {
return count($this->storage) - count($this->protected);
}
}
Powyższy, przydługi kawałek kodu, to właśnie taki prototyp kolekcji. Na jego podstawie można z powodzeniem tworzyć (choćby przez dziedziczenie) przydatne kolekcje.
Ot taki przykład, w którym tworzymy koszyk, w nim listę produktów, wrzucamy produkty i liczymy wartość wrzuconych produktów.
$Basket = new Basket ; $Basket->Products = new ProductCollection ; $Basket->Products[] = new ProductFoo ; $Basket->Products[] = new ProductBar ; $Basket->Products->calculateValue ;
Nasuwa się jednak pytanie - czy nie lepiej byłoby za pomocą tablicy?
$Basket = new Basket ; $Basket->Products = array $Basket->Products[] = new ProductFoo ; $Basket->Products[] = new ProductBar ; $Basket->calculateValue ;
Postawiłem pytanie, na które nie jestem w stanie udzielić jasnej odpowiedzi.
Z jednej strony, mam ładnie podzieloną odpowiedzialność - koszyk robi swoje, lista produktów swoje.
Z drugiej zaś - używanie kolekcji, nawet z implementacją interfejsów uniemożliwia wykorzystywanie niektórych funkcji (np. usort na kolekcji nie zadziała)
Tylko znowuż, skoro chcę posortować kolekcję w jakiś sposób, to kolekcja powinna udostępniać odpowiednią metodę, a wewnątrz kolekcji mam już dostęp do tablicy z obiektami
Kolejna rzecz, to czy warto poświęcać czas (i swój i skryptu) na tworzenie obiektu? Jakby nie patrzeć, kolekcja będzie wolniejsza od tablicy.
Ale mając gotowy taki prototyp, tworzenie kolejnych kolekcji jest o wiele szybsze.
Za i przeciw jest pełno... Pytanie pozostaje otwarte.
Na pytanie czemu nie użyłem ArrayObject, odpowiem - bo znalazłem takiż kretynizm w nim
$Collection = new ArrayObject(array(0 => 'Foo'), 2);
var_dump($Collection->{0});
Inna rzecz, która mnie boli w ArrayObject, to to, że przekazywana do konstruktora tablica, nie jest brana jako referencja.