Michał Wachowski programista php & webdeveloper

Blog

Kolekcje

Kolekcje mogą być różne. Jedni mają kolekcję znaczków, jak byłem mały to miało się kolekcję aut z gumy Turbo.Programiści mają kolekcje obiektów i o nich dziś będzie.

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.

Komentarze

Bez komentarzy

Dodaj komentarz