Как стать автором
Обновить

Комментарии 74

был создан класс коллекции под предлогом однотипности элементов, в коде же это единственное отличие от обычного массива не используется и вообще вызывает сомнения необходимость в таком контроле жертвуя гибкостью коллекций

UserCollection, BookCollection наследуемые от Collection — вот тут я вижу пользу, кроме наследования всяких интерфейсов можно держать код обслуживающий коллекцию книг в BookCollection отдельно от кода работающего с UserCollection, это ж совсем разные сущности
BookCollection отдельно от кода работающего с UserCollection, это ж совсем разные сущности
Коллекция это лишь строготипизированный контейнер (как впрочем и в C#). Его задача — удостоверить принимающий метод (addBooks к примеру) что внутри — ТОЛЬКО книги и ничего больше.
Хотел сказать что коллекция это не сущность, а всего лишь контейнер сущностей.
Извиняюсь за дубль.
любая коллекция — тоже сущность.
для коллекции книг было бы неплохо иметь метод получения суммарного числа слов, что для юзеров не имеет смысла.
Да, конечно. Но тогда пришлось бы явно описывать классы различных коллекций.
Ещё раз повторюсь, что моей целью было динамическое создание полноценных типов лишь для строгой типизации. Естественно, эти типы не могут реализовывать свой функционал, но для поставленной цели это и не нужно.
перед созданием класса коллекции можно добавить проверку на class_exists(), тогда станет возможным определить свою коллекцию с дополнительным функционалом. Единственное, чтобы этот класс был доступен для автолоадера.
хочу коллекцию «книги в которых не менее 100 слов»
НЛО прилетело и опубликовало эту надпись здесь
Нет не часто. Я лишь перенёс в php некоторый функционал, к которому привык в строготипизированных языках.
Лично мне так удобней. Чем жестче контроль тем проще дальнейшая поддержка другими программистами.
НЛО прилетело и опубликовало эту надпись здесь
а вы посмотрите на нормальные системы типов, ну, например, у Haskell.
c определенной долей иронии замечу, что строгая типизация в купе с умным code assist IDE вполне себе облегчают жизнь
# // Добавим объектов в коллекцию:
# $books->add(new Book(1), new Book(2));
# $books->add(new Book(3))->add(new Book(2));
# $books[] = new Book(5);

не желательно использовать несколько вариантов реализаций интерфейсов у одной сущности, так как это может создать дополнительные трудности при развитии проекта или в случае рефакторинга. в частности интерфейс ArrayAccess и интерфейс Collection.

это не критично, можно отнести к правилу хорошего тона.
важность оно приобретает в постоянно модифицируемых проектах и с участием большого числа программистов.
Долго ругался на PHP когда вдруг понадобилось написать один модуль для сайта, я уже продумал как всё реализую, и потом узнал что у PHP нет коллекций.
В PHP есть массивы, и их функционала вполне хватает.
Разве что нет строгой типизации
Хм. То есть я могу создать коллекцию из неких объектов? К примеру в контроллере на основе данных из модели формируется коллекция из объектов «Квартира» и передается в отображение.
Чем коллекция не массив? :)
В пхп в массиве могут быть разнородные данные, объекты, цифры, строки, все что угодно и в любом сочетании, даже в ключах.
В принципе я с вами согласен, но всё равно как то не то к чему я привык. Спасибо.
НЛО прилетело и опубликовало эту надпись здесь
Хитро: А мы их сериализуем)
НЛО прилетело и опубликовало эту надпись здесь
В PHP есть массивы, и их функционала вполне хватает.

Немного не ясно где Вы их используете… Скажем сущность Post или Comment в Вашем проекте представляет собой массив?

Разве что нет строгой типизации

Как раз ради типизации и возможности TypeHinting'a я это реализовал.
Массивы, их возможностей. как правило, более чем достаточно. Если вы жить не можете без ORM, конечно, можно сделать каждый пост отдельным объектом (хотя тут стоит подумать, ORM провоцирует на написание неэффективного кода имхо), а можно и ассоциативным массивом field_name => value, почему бы нет.

А уж коллекцию объектов имхо массивом просто удобнее сделать.
Если очень хочется, можно объединить.
Скажем, реализовать коллекцию с использованием ArrayAccess, данные хранить в виде ассоциативного массива, при запросе отдельного элемента — проверять, есть ли он в кэше объектов коллекции, если есть — отдавать из кэша, если нет — создавать объект, ложить в кэш и отдавать.
Боюсь только, что узким местом здесь будет расход памяти на больших коллекциях, хотя большие выборки из базы — повод пересмотреть архитектуру.
Нее, это же муть. Так у нас будет для коллекции из 100 эдементов 100 запросов к базе. В топку такую коллекцию (вот именно поэтому я и не люблю существующие реализации ORM).
Зачем 100 запросов? Можно обойтись одним.
Каждый элемент коллекции наследует от абстрактного родителя метод
Entity_Abstract::setFromArray(array $data) где $data — ассоциативный массив.
Collection реализует метод Collection::findById(int $id) при вызове которого результат выборки сохраняется во внутреннем хранилище.
При попытке получения элемента коллекции вначале проверяется наличие объекта во внутреннем кеше, если он не создан — создается.
Нет, я про случай, когда нам надо допустим выбрать из таблицы всех пользователей (их много), у которых поле banned = 1. В этом случае, как я понял, мы делаем запрос на выборку всех этих рядов, и сохраняем отдельные записи в кеше на случай будущего обращения?

Тогда остается проблема того, что в кеш могут попасть не все поля записи, значит надо хранить данные не на уровне записей, а на уровне отдельного поля… еще сложнее все получается.
и будем таскать все данные за собой в памяти, да.
# private function __check_type(&$object) {

В PHP5 объекты автоматически передаётся по ссылке!

# public function __construct(&$array) {
# if (is_array($array) ) {
# $this->__elements = $array;

В данном случае передача массива по ссылке ничего хорошего не даст, т.к. нет никакого присваивания элементам массива.
Да и проверка на массив тут не нужна, проще так:
# public function __construct(array $array) {
# $this->__elements = $array;
Когда я реализовывал нечто подобное, проверка типа была не строгая (сравнение имен классов), а с учетом наследования. Т.е. использовался is_a.

И в самом деле, почему бы не добавить в BookCollection элементы типа SciFiBook и ArtsBook наследованные от Book?
ИМХО в классе коллекции в имплементации offsetSet ошибка.

Когда вы используете $collectionInstance[] = new Book(5) в $offset приходит NULL. У вас данная ситуация никак не обрабатывается.
спасибо, поправил
может быть сюда:
abstract class Collection implements IteratorAggregate, ArrayAccess, Countable

стоит добаить arrayItrerator?
НЛО прилетело и опубликовало эту надпись здесь
100% с Вами согласен! Тем более вроде как в 6-й версии php от eval хотят отказаться
и что? теперь eval($_GET['param']) уже не будет работать? как же тогда программы-то писать…
я извиняюсь, eval($_GET['param']) вы всегда так сайты пишете?

не вижу адекватных альтернатив… :)
Не надо быть таким серезным хотябы в выходные)
Да вообще ужас, теперь придется писать что-то вроде
file_put_contents('param.php', "<?php \n". $_GET['param']);
include 'param.php';

Эти гады усложняют нам жизнь.
Пост полностью не читал, но с автором полностью согласен — столько букв не могут быть не убедительным доводом! :)
А если серьезно, то проблема надумана и героически решена.
В качестве коллекций стоит использовать либо обычные массивы, либо писать под них классы, если требуется специфический функционал для набора объектов одного типа. Хотя и тут можно обойтись, например, статичными методами класса.
У полноценной коллекции должно быть свойство отложенной инициализации. Так что низачет.
Пользователей языков со статической типизацией смешит этот пост.
НЛО прилетело и опубликовало эту надпись здесь
А почему не создавать самому классы коллекций? Или использовать кодегенератор.
Ваша реализация имеет зависимость от класса Collection, от которой лучше избавиться.
Метод Collection::__check_type(&$object) реализован неверно: вы либо возвращаете true, либо кидаете exception. По логике, вы должны либо кидать exception и возвращать void, либо возвращать true|false без exception. Про то, что объекты передаются по ссылке, уже писали.
Кодинг стандарта вообще нет.
Возможно я придираюсь, но код оформлен не очень красиво. Для такого простого примера можно было бы и постараться.
объявляем CollectionFactory::create()
и используем CollectionFactory::Create()
как это понять?
Метод __check_type тоже бросился в глаза, логика странная.
В __check_type по-моему более уместно производить сравнение через instanceof, и честно говоря непонятно зачем переписывать с нуля итератор по коллекции, чем не угодил ArrayIterator, который реализует ну абсолютно такую-же логику, только быстрее и лучше, потому как является частью SPL.
Ну и eval — эт конечно бяка, я считаю уместнее отнаследовать руками требующиеся в коде коллекции, раз язык не предоставляет красивого решения для такого рода задачи.
Кроме уже названных узких мест (типа использования eval) и сомнительной полезности коллекций в таком виде в PHP, стоит обратить внимание на то, что префикс из двух подчеркиваний зарезервирован для стандартных функций и методов (__construct, __get, __set, __call, __clone и т.д.), так что его использование для собственных нужд — это дурной тон.

А ещё в методе __check_type стоило использовать оператор instanceof, чтобы разрешить более одного уровня наследования коллекций.
Хотя с этой ужасной eval'ной архитектурой нормального наследования не выйдет в любом случае.
само по себе использование двух типов нотаций — уже зло, а вы про подчеркивания :)
Писал нечто очень похожее, но по другим причинам: у меня были более сложные классы массивов, зачастую с наследованием и переопределением логики.
Были сущности (e.g. class Entity), был специальный класс для массива сущностей (class EntitiesList), и был фабричный метод, который проверял, есть ли унаследованный класс массива для конкретной сущности. Если нет, то без всяких eval() с созданием нового класса возвращался базовый класс, в котором устанавливалась внутренняя переменная $_type.
Сначала давайте разберемся, для чего вообще нужна реализация «полноценных коллекций для хранения объектов одинакового типа, по удобству напоминающие Listв C#».

1. Избавиться от кода по приведению типов.

Дело в том, что коллекции в C#, Java и т.д. хранят объекты типа Object, и затем, чтобы из извлекать и затем использовать, необходимо делать дополнительное действите — приведение типа, и если это делается часто, то это весьма и весьма загаживает исходный код. То есть смысловой и семантической нагрузки приведение типа не несет, и загромождает код. Именно поэтому есть смысл использовать типизированные коллекции (да и то в Java это появилось аж начиная с версии 1.5).

Теперь пернемся к скриптовым языкам, в частности, к PHP. Тут все типы любых объектов — аналогичны вариантному типу в языках программирования со строгой типизацией, то есть любой переменной можно присвоить значение любого типа, или ссылку на объект любого типа. То есть, приведение типа при извлечении объекта из коллекции смысла не имеет.

Вывод: главный плюс типизированных коллекций — возможность избавиться от приведения типов — в PHP не актуально.

2. Дополнительно контролировать, что именно помещается в коллекцию.

Эта проверка делается в C# и Java делается на этапе компиляции (реализовано на уровне языка), и действительно в некоторых, довольно редких случаях может помогать программисту избежать ошибок.

Но в том же PHP, в любой реализации, эта проверка будет делаться в рантайме постоянно, при добавлении каждого нового объекта в коллекцию! А если в коллекцию нужно добавить, например, миллион элементов? То есть нет смысла жертвовать производительностью и писать бесполезный для логики программы код ради очень небольшого, я бы сказал сомнительного удобства.

Вывод. Не нужно реализовать на PHP то, для чего PHP не предназначен, что он делать не умеет. Так как эта реализация будет избыточной, и даже вредной. Программирование ради программирования, вместо того чтобы продумать логику и найти другое, лучшее решение.

Если я ошибаюсь, по пожалуйста поправьте. Все что написано выше — исключительно из личного опыта, и относится к реальным проектам, но не относится к академическим «лабораторным работам», где вызов eval и прочие изыски имеют место быть.
Вы еще напишите: «зачем интерфейсы и наследование в пхп, это ж в рантайме все проверяется». Нужно иногда знать что получаешь именно этот обьект, потому что в нем реализован нужный метод
Ну вот, вы же сами и ответили. Интерфейсы в PHP нужны, так как «Нужно иногда знать что получаешь именно этот обьект, потому что в нем реализован нужный метод», в том числе и в рантайме. Плюс, с интерфейсами удобнее работать, чем с имплементациями. А наследование используется, в основном, чтобы избежать дублирования кода. Тут — сугубо практический смысл.

Но к типизированным коллекциям это не имеет никакого отношения.
просто к чему я, если реализуем метод который принимает обьект, то нужноставить проверку, а если в массиве в этот метод будем добавлять обьекты? а если их будет миллион?
Наконец то здравая мысль, php это не Java, нет смысла слепо копировать используемые методы.
> главный плюс типизированных коллекций — возможность избавиться от приведения типов
Я бы сказал, что основным здесь является не желание избавиться от явного кастинга, а то, что пользовательский код должен быть безопасным по отношению к типам, т.е. чтобы в коллекцию можно было добавлять объекты только указанного типа (или типа, неявно приводимого к указанному, например, производного от него). И чтобы компилятор мог осуществить верификацию этого кода на этапе компиляции (шаблоны C++, generics C#, Java).
К сожалению, такие проверки в PHP не могут быть реализованы на этапе трансляции и могут обернуться потерей производительности в рантайме.
Однако, есть несложный обходной путь: использовать макросредства и заключить код проверки типов в нечто вроде
#ifdef DEBUG
… проверка типов
#endif
в этом случае в режиме DEBUG коллекция будет безопасной по отношению к типам, а в режиме RELEASE будет иметь производительность, близкую к оригинальной.

Правда, я не в курсе, есть ли в PHP макросредства, аналогичные сишным. Если нет, то придется наваять свой маленький макропроцессор )
Есть assert(), отключаемый через конфиг.
удобно конечно, но у меня вопрос… вот Вы создали коллекции, да, это очень гибко и дает возможность создавать нужные коллекции на лету, а вот у Вас разве нет желания контролировать код? ведь в данном подходе можно насоздавая кучу классов потом самому же в них запутаться… ведь обычно при проектировании проекта разработчик должен знать что будут такие классы, будут реализовывать такие методы, зачем создавать классы «на лету»?
тут, собственно, все этим вопросом и задаются :)
Странно, что никто не сказал очевидное. Одним из основных профитов типизированной коллекции в c#(List) является отсутствие auto boxing/unboxing в отличии от ArrayList. В php же все по другому, поэтому типизированные коллекции там на… й не сдались в 90% случаев. Исключение составляют коллекции с дополнительной логикой обработки данных.

А сорри, видимо все же выше сказали. Я искал только по словам box/unbox.
Это не самое важное. Как я только что написал выше, цель List<> по сравнению с ArrayList — обеспечить интерфейс, безопасный по отношению к типам. Отсутствие боксинга — это не причина, а следствие появления обобщений (generics) C#.
Есть замечательный Фаулеровский паттерн — Identity Map. Для сторонников ORM — самое оно. Рекомендую.
За статью спасибо.
В принципе, весь код класса коллекции можно было бы заменить этим:

class Collection extends SplObjectStorage {
  private $__type;
  function attach($o) {
     if (! $o instanceof $this->__type) throw new InvalidArgumentException(...);
     return parent::attach($o);
  }
}

И вообще лично я не догоняю смысла всей затеи. Имхо — вышла типизация ради типизации.
Что нового вы открыли? В Doctrine ORM такие же коллекции уже давно.
А ссылочку на документацию по этим коллекциям можно? Или на код, хотя бы.
конечно можно doctrine-project.org
Там найдете svn и там легко найти класс Doctrine_Collection
Как все же, долгое использование строго типизированных языков калечит психику — кажется, что без типов жизнь невозможна :)
Это избавляет от «глупых» ошибок и позволяет в любой момент времени знать что именно узнать, что у тебя под рукой(под курсором -)) )
нужно фабрику включать в класс Collection чтоб нельзя было писать так:
$books = CollectionFactory::create('Book');
$books = new BookCollection('Magazine');
не буду пояснять зачем так писать — это концептуальный момент типизация либо полна либо не полна. чтоб она была полна фабрика должна жить статически в родителе и конструктор должен быть protected
и вызов соответственно Collection::factory('Book')

Это вам вообще пригодится — избавляет от лишних сущностей + замыкает типизацию
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории