Создание объектно-ориентированного аналога массива в PHP. Часть 1
В этой статье речь пойдет о том, как создать объектно-ориентированный аналог массива в PHP.
Но для начала давайте разберемся, чем нас не устраивает обычный массив. Предположим, Вы разрабатываете какое-либо корпоративное приложение, и в нем необходим перечень менеджеров с прилагающимся списком обслуженных клиентов.
class Client {
public $name;
//описание класса
}
class Manager {
public $clients = array();
//описание класса
//где-то в нем происходит заполнение массива
//объектами класса Client
}
//просмотр список обслуженных клиентов
$objManager = new Manager(‘id’);
foreach ($objManager->clients as $client) {
echo $client->name;
}
В принципе мы получили что хотели — список клиентов. Однако данный подход не является хорошим решением по нескольким причинам:
- открытый массив
$objManager->clientsнарушает инкапсуляцию. Массив можно сделать закрытым с доступом к нему через метод$objManager->getClients(). Но такой подход не поможет, в случае если возникнет необходимость его модифицировации по ходу его просмотра. Если же оставить массив открытым для возможности модификации, то мы не сможем контролировать и/или фильтровать информацию заносящуюся в него. - информация по клиентам будет загружена при создании объекта, даже если нужно только имя менеджера и не больше. Конечно, можно создать метод
$objManager->loadClientsInfo(), только после вызова которого будет подгружена информация о клиентах, однако удобнее было бы если информация будет подгружена автоматически в момент когда к ней обратятся.
Для решения этих проблем создадим класс ObjectsArr — оболочку вокруг массива объектов. Предъявим к нему следующие требования:
- предоставление механизма добавления, удаления и извлечения элементов;
- заполнение информации по клиентам при первом обращении к ней;
- возможность перебора всей коллекции элементов. (
for,foreach)
Описание реализации этих требований довольно громоздко, поэтому разобьем нашу статью на несколько частей. В каждой новой части мы будем наращивать функциональность класса ObjectsArr.
Итак, давайте создадим класс ObjectsArr и прикрутим к нему механизм управления содержимым.
Класс ObjectsArr
class ObjectsArr {
private $_list = array();
}
Метод добавления элемента
public function add($value, $key = null) {
if ($key === null || empty($key)){
$this->_list[] = $data;
return;
}
if (!isset($this->_list[$key])){
$this->_list[$key] = $data;
} else {
throw new Exception('Ключ "' . $key . '" уже используется.');
}
}
Метод add принимает 2 аргумента, добавляемый элемент и его ключ. Ключ является необязательным, если его опустить, то он будет генерироваться автоматически. При попытке добавить элемент с уже существующим ключом будет сгенерировано исключение.
Метод удаления элемента
public function del($key) {
if (isset($this->_list[$key])) {
unset($this->_list[$key]);
} else {
throw new Exception('Элемент с ключем "' . $key . '" отсутсвует.');
}
}
Удаляет элемент по его ключу. Если ключ отсутствует, то генерируется исключение.
Метод извлечения элемента
public function get($key) {
if (isset($this->_list[$key])) {
return $this->_list[$key];
} else {
throw new Exception('Элемент с ключем "' . $key . '" отсутсвует.');
}
}
Извлекает элемент по его ключу. Если ключ отсутствует, то генерируется исключение.
Метод проверки наличия элемента
public function exist($key) {
return isset($this->_list[$key]);
}
Поскольку добавление элемента с существующим ключем сгенерирует исключение, то данная функция позволяет проверить, используется ли ключ или нет.
Метод получения всех элементов
public function keys() {
return array_keys($this->_list);
}
Эта функция позволяет получить все имеющиеся ключи добавленных элементов.
Метод подсчета добавленных элементов
public function length() {
return count($this->_list);
}
Эта функция возвращает количество установленных элементов.
Весь класс, полностью
Это все. Полностью класс ObjectsArr будет выглядеть так:
class ObjectsArr {
private $_list = array();
public function add($value, $key = null) {
if ($key === null || empty($key)){
$this->_list[] = $data;
return;
}
if (!isset($this->_list[$key])){
$this->_list[$key] = $data;
} else {
throw new Exception('Ключ "' . $key . '" уже используется.');
}
}
public function del($key) {
if (isset($this->_list[$key])) {
unset($this->_list[$key]);
} else {
throw new Exception('Элемент с ключем "' . $key . '" отсутсвует.');
}
}
public function get($key) {
if (isset($this->_list[$key])) {
return $this->_list[$key];
} else {
throw new Exception('Элемент с ключем "' . $key . '" отсутсвует.');
}
}
public function exist($key) {
return isset($this->_list[$key]);
}
public function keys() {
return array_keys($this->_list);
}
public function length() {
return count($this->_list);
}
}
Тестирование класса ObjectsArr
$clientsList = new ObjectsArr();
try {
$clientsList->add(new Client('Ваня'), 'ivan'); //Добавляем нового клиента
$clientsList->add(new Client('Саша'), 'alexander'); //Добавляем нового клиента
$clientsList->add(new Client('Петя'), 'petr'); //Добавляем нового клиента
$objIvan = $clientsList->get('ivan'); //Извлекаем клиента
echo $objIvan; // выведет -> Клиент - "Ваня"
$clientsList->del('ivan'); //удаляем клиента
$clientsList->add(new Client('Петр Иванович'), 'petr'); /*перехват исключения, т.к. ключ "petr" уже используется
выведет -> Ключ "petr" уже используется. */
} catch (Exception $e) {
echo $e->getMessage();
}
Добавляемую информацию, так же можно фильтровать. Я намеренно не стал этого делать, т.к. от приложения к приложению требования (а значит и фильтры) могут меняться. Все их можно добавить, расширив базовый класс ObjectsArr. Для примера, реализуем возможность добавления только объектов в коллекцию:
class MyObjectsArr extends ObjectsArr {
public function add(object $value, $key = null) {
parent::add($value, $key);
}
}
Или
class MyObjectsArr extends ObjectsArr {
public function add($value, $key = null) {
if (!is_object($value))
throw new Exception('Добавляемый элемент не является объектом.');
parent::add($value, $key);
}
}
Итак, класс ObjectsArr функционирует и отвечает нашему первому требованию:
предоставление механизма добавления, удаления и извлечения элементов
Использовать класс в таком виде, в каком он представлен сейчас — неудобно. Связано это с тем что мы не реализовали все требования предъявленные к нему.
В следующий статьях мы продолжим наращивать его функциональность.
Похожие темы
RSS-лента комментариев к этой статье | Обратная ссылка (trackback link)



По моему, это не «объектно-ориентированный аналог массива» а модель данных.
Малополезный класс.
Если реализовать интерфейсы ArrayAccess, Iterator и Counter класс превращается в полноценный массив.
Доступ не $object->key а $object['key]. Конструкция foreach и функция count($object)
Интерфейсы будут реализованы в следующий статьях. В конце статьи я писал о том, что работа над классом не завершена.
IMHO куда логичнее было бы начинать с расширения SPL-класса ArrayObject
http://ua.php.net/manual/en/class.arrayobject.php
Класс
ArrayObjectменя не устроил своей реализацией. Например :Не всегда нужно, чтобы имелась возможность замены элементов в массиве. (элемент
anotherFirstзаменил элементfirst). В моем случае возвращается Exception.Первый цикл начнет перебор элементов сначала, после того, как удалит 2-й элемент.
Второй цикл – бесконечный.
Все недочеты можно исправить, расширив класс
ArrayObjectи дополнив нужным функцционалом. Но тогда нужно будет описать внутренне его строение, это и вернуло бы нас к написанию точно-такой же статьи.Я решил в качестве примера написать свой собственный класс с описанием его работы (хотя он во многом и копирует
ArrayObject).В методах add и del нужно использовать созданный метод exist, а не проверять вручную. Реализация exist может измениться, придется переписывать все методы.
В конструкции if ($key === null || empty($key))
$key === null –> не нужно. Это делает сам empty.
Хм….
if ($key === null || empty($key)){
…}
А если $key=0 ?
Ваш метод добавит в конец массива новый элемент, а я хочу добавить элемент на позицию 0.
Не есть гуд…
дадада, надо поумолчанию сделать $key=-1, и проверять на больше равно нулю