Не так давно при разработке своего проекта возникла идея реализовать полноценные коллекции для хранения объектов одинакового типа, по удобству напоминающие List<Type> в C#.
Идея состоит в том, чтобы коллекции, содержащие объекты различных типов сами по себе различались, а не имели, скажем, один унифицированный тип Collection. Другими словами, коллекция объектов типа User это не то же что коллекция объектов Book. Естественно, первой мыслью было создание различных классов для коллекций (UserCollection, BookCollection, …). Но данных подход не обеспечивает нужной гибкости, плюс ко всему, нужно тратить время на объявление каждого подобного класса.
Немного поразмыслив, реализовал динамическое создание классов коллекций. Выглядит это так: пользователь создаёт коллекцию объектов типа Book, а нужный тип BookCollection создаётся (т.е. объявляется) автоматически.
Что я получил в итоге:
— Полноценный TypeHinting создаваемых типов коллекций.
— Строгая типизация коллекций.
— Возможнось обращатся к коллекции как к массиву как в C# (путём реализации интерфейса ArrayAccess)
— Полноценная итерация коллекции (возможность использования в любых циклах).
Скачать исходник
Идея состоит в том, чтобы коллекции, содержащие объекты различных типов сами по себе различались, а не имели, скажем, один унифицированный тип Collection. Другими словами, коллекция объектов типа User это не то же что коллекция объектов Book. Естественно, первой мыслью было создание различных классов для коллекций (UserCollection, BookCollection, …). Но данных подход не обеспечивает нужной гибкости, плюс ко всему, нужно тратить время на объявление каждого подобного класса.
Немного поразмыслив, реализовал динамическое создание классов коллекций. Выглядит это так: пользователь создаёт коллекцию объектов типа Book, а нужный тип BookCollection создаётся (т.е. объявляется) автоматически.
Что я получил в итоге:
— Полноценный TypeHinting создаваемых типов коллекций.
— Строгая типизация коллекций.
— Возможнось обращатся к коллекции как к массиву как в C# (путём реализации интерфейса ArrayAccess)
— Полноценная итерация коллекции (возможность использования в любых циклах).
Реализация
Фабрика коллекций
- /**
- * Фабрика коллекций
- *
- * @author [x26]VOLAND
- */
- abstract class CollectionFactory {
-
- /**
- * Создаёт коллекцию заданного типа.
- *
- * @param string $type Тип коллекции
- * @return mixed
- */
- public static function create($type)
- {
- $class = $type . 'Collection';
- self::__create_class($class);
- $obj = new $class($type);
- return $obj;
- }
-
- /**
- * Создаёт класс с именем $class
- *
- * @param string $class Имя класса
- * @return void
- */
- private static function __create_class($class) {
- if ( ! class_exists($class)) {
- eval('class ' . $class . ' extends Collection { }');
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Класс коллекции (описывает поведение)
- /**
- * Класс коллекции
- * Базовый универсальный тип, на основе которого будут создаваться коллекции.
- *
- * @author [x26]VOLAND
- */
- abstract class Collection implements IteratorAggregate, ArrayAccess, Countable {
-
- /**
- * Тип элементов, хранящихся в данной коллекции.
- * @var string
- */
- private $__type;
-
- /**
- * Хранилище объектов
- * @var array
- */
- private $__collection = array();
-
- // --------------------------------------------------------------------
-
- /**
- * Констурктор.
- * Задаёт тип элементо, которые будут хранитья в данной коллекции.
- *
- * @param string $type Тип элементов
- * @return void
- */
- public function __construct($type) {
- $this->__type = $type;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Проверяет тип объекта.
- * Препятствует добавлению в коллекцию объектов `чужого` типа.
- *
- * @param object $object Объект для проверки
- * @return void
- * @throws Exception
- */
- private function __check_type(&$object) {
- if (get_class($object) != $this->__type) {
- throw new Exception('Объект типа `' . get_class($object)
- . '` не может быть добавлен в коллекцию объектов типа `' . $this->__type . '`');
- }
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Добавляет в коллекцию объекты, переданные в аргументах.
- *
- * @param object(s) Объекты
- * @return mixed Collection
- */
- public function add()
- {
- $args = func_get_args();
- foreach ($args as $object) {
- $this->__check_type($object);
- $this->__collection[] = $object;
- }
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Удаляет из коллекции объекты, переданные в аргументах.
- *
- * @param object(s) Объекты
- * @return mixed Collection
- */
- public function remove()
- {
- $args = func_get_args();
- foreach ($args as $object) {
- unset($this->__collection[array_search($object, $this->__collection)]);
- }
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Очищает коллекцию.
- *
- * @return mixed Collection
- */
- public function clear() {
- $this->__collection = array();
- return $this;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Выясняет, пуста ли коллекция.
- *
- * @return bool
- */
- public function isEmpty() {
- return empty($this->__collection);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Реализация интерфейса IteratorAggregate
- */
-
- /**
- * Возвращает объект итератора.
- *
- * @return CollectionIterator
- */
- public function getIterator() {
- return new CollectionIterator($this->__collection);
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Реализация интерфейса ArrayAccess.
- */
-
- /**
- * Sets an element of collection at the offset
- *
- * @param ineter $offset Offset
- * @param mixed $offset Object
- * @return void
- */
- public function offsetSet($offset, $object) {
- $this->__check_type($object);
- if ($offset === NULL) {
- $offset = max(array_keys($this->__collection)) + 1;
- }
- $this->__collection[$offset] = $object;
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Выясняет существует ли элемент с данным ключом.
- *
- * @param integer $offset Ключ
- * @return bool
- */
- public function offsetExists($offset) {
- return isset($this->__collection[$offset]);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Удаляет элемент, на который ссылается ключ $offset.
- *
- * @param integer $offset Ключ
- * @return void
- */
- public function offsetUnset($offset) {
- unset($this->__collection[$offset]);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Возвращает элемент по ключу.
- *
- * @param integer $offset Ключ
- * @return mixed
- */
- public function offsetGet($offset) {
- if(isset($this->__collection[$offset]) === FALSE) {
- return NULL;
- }
- return $this->__collection[$offset];
- }
-
- // --------------------------------------------------------------------
-
-
- /**
- * Реализация интерфейса Countable
- */
-
- /**
- * Возвращает кол-во элементов в коллекции.
- *
- * @return integer
- */
- public function count() {
- return sizeof($this->__collection);
- }
- }
* This source code was highlighted with Source Code Highlighter.
Примеры использования
- <?php
-
- class BookStore {
- function addBooks(BookCollection $books) {
- // реализация
- }
-
- function addMagazines(MagazineCollection $magazines) {
- // реализация
- }
-
- function addGoods(Collection $goods) {
- // Если тип коллекции не важен,
- // можно указать базовый тип Collection
- }
- }
- class Book {
- var $id;
- function Book($id) {
- $this->id = $id;
- }
- }
-
-
- class Magazine {
- var $id;
- function Magazine($id) {
- $this->id = $id;
- }
- }
- // Создаём коллекцию
- $books = CollectionFactory::create('Book');
-
- echo get_class($books); // BookCollection
-
- // Добавим объектов в коллекцию:
- $books->add(new Book(1), new Book(2));
- $books->add(new Book(3))->add(new Book(2));
- $books[] = new Book(5);
-
- echo count($books); // 5
-
- ...
-
- foreach ($books as $book) {
- echo $book->id;
- } // 12345
-
- ...
-
- $books->add(new Magazine(1)); // Ошибка (неверный тип)
-
- ...
-
- $magazines = CollectionFactory::create('Magazine');
- $magazines->add(new Magazine(1));
-
- ...
-
- $bookStore = new BookStore();
-
- $bookStore->addBooks($books); // Всё в порядке
-
- $bookStore->addBooks($magazines); // Ошибка (неверный тип)
- $bookStore->addMagazines($magazines); // Всё в порядке
-
- $bookStore->addGoods($books); // Всё в порядке
- $bookStore->addGoods($magazines); // Всё в порядке
-
- ?>
* This source code was highlighted with Source Code Highlighter.
Скачать исходник