• База знаний
  • /
  • Блог
  • /
  • Wiki
  • /
+380 (44) 364 05 71

Правила работы с динамическими массивами и пользовательскими классами коллекций

Массивы PHP это мощный и многогранный инструмент. С его помощью можно создавать самые разные структуры данных. Чтоб код был понятным и читаемым нужно придерживаться стандартов. Вот несколько правил, которые я использую для работы с динамическими массивами. Это в большей степени «Руководство по стилю проектирования массивов».

 

Оглавление

 

Использование списков в качестве массивов

Все элементы должны быть одного типа

При использовании массива в качестве списка (набора значений с определенным порядком) каждое значение должно быть одного типа:

Элементы массива

Общепринятый стиль аннотирования типа списка @var array<TypeOfElement> . Убедитесь, что вы не добавили тип индекса (он всегда должен быть int).

Игнорируйте индекс каждого элемента

PHP автоматически создает новые индексы для каждого элемента в списке (0, 1, 2 и т. Д.). Однако вы не должны полагаться на эти индексы или использовать их напрямую. Единственное свойство списка, на которое должны полагаться клиенты, - это то, что он является повторяемым и счетным.

Поэтому не бойтесь использовать foreach и count (), но не используйте for для циклической работы с элементами в списке:

Использование foreach и count

(В PHP цикл for может даже не работать, потому что в списке могут отсутствовать индексы, или же количество индексов больше, чем количество элементов в списке.)

Вместо удаления элементов используйте фильтр

Вы можете удалить элементы из списка по их индексу (unset ()), но целесообразней использовать array_filter () вместо удаления элементов для создания нового списка без нежелательных элементов.

Опять же, не полагайтесь на индекс элементов, поэтому при использовании array_filter () вы не должны использовать параметр flag для фильтрации элементов на основе индекса или даже на основе как элемента, так и индекса.

Использование array_filter ()

Использование массивов в качестве ассоциативных массивов

Когда ключи актуальны и не являются индексами (0, 1, 2 и так далее) не бойтесь использовать массив как ассоциативный массив (коллекцию, из которой вы можете извлекать значения по их уникальному ключу).

Все ключи должны быть одного типа

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

Использование ключей одного типа

Все значения должны быть одного типа

То же самое происходит и со значениями: они должны быть одного типа.

Использование значений одного типа

Общепринятый стиль аннотирования типа:

@var array<TypeOfKey, TypeOfValue>

Ассоциативные массивы должны оставаться приватными

Списки можно безопасно передавать от объекта к объекту из-за их простых характеристик. Любой клиент может использовать его для циклического перебора или подсчёта элементов, даже если список пуст. С ассоциативными массивами работать сложнее, потому как клиенты могут полагаться на ключи, которые не имеют соответствующего значения. Это значит, что в целом они должны оставаться закрытыми для объекта, который ими управляет. Вместо того, чтобы позволять клиентам напрямую обращаться к внутренним ассоциативным массивам, предлагайте геттеры (и, возможно, сеттеры) для получения значений. Убирайте исключения, если значение для запрошенного ключа не существует. Однако, если вы можете полностью сохранить карту и ее значения, сделайте это.

Передача данных в ассоциативных массивах

Используйте объекты для ассоциативных массивов с несколькими типами значений

Если вы хотите использовать ассоциативные массивы, и при этом хранить в них различные типы значений, используйте объекты. Определите класс и добавьте к нему общедоступные типизированные свойства или же конструктор и геттеры. Примеры таких объектов - объекты конфигурации или объекты команд:

Объекты в ассоциативных массивах

Исключения из этих правил

Иногда библиотеки или фреймворки требуют более динамичного использования массивов. В таких случаях невозможно (и нежелательно) следовать предыдущим правилам. Примерами являются массив данных, которые будут храниться в таблице базы данных, или конфигурация формы Symfony.

Пользовательские классы коллекций

Пользовательские классы коллекций могут быть очень крутым способом наконец поработать с Iterator, ArrayAccess и их «друзьями», но я считаю, что большая часть полученного кода, запутанная. Тем, кто впервые смотрит на код, придется поискать подробности в руководстве по PHP, даже если они опытные разработчики. Кроме того, вам нужно написать больше кода, который вы должны поддерживать (тестировать, отлаживать и т. д.). Поэтому в большинстве случаев я считаю, что простого массива с некоторыми соответствующими аннотациями типов вполне достаточно.

Как понять, что вам все-таки нужно превратить ваш массив в пользовательский объект коллекции?

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

Используйте собственный класс коллекции для предотвращения дублирования логики

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

Использование классов коллекций

Преимущество использования метода преобразования в коллекции состоит в том, что преобразование получает имя. Это позволяет вам добавить короткую и содержательную метку к довольно сложному на вид array_filter ().

Используйте пользовательский класс коллекции для разделения клиентов

Если клиент, который работает с определенным массивом, перебирает его, извлекает часть данных из выбранных элементов и что-то делает с этими данными, этот клиент становится тесно связанным со всеми задействованными типами: самим массивом, типом элементов, которые находятся в массиве, типом значений, которые он извлекает из выбранных элементов, типом метода селектора и т. д. Проблема с такой глубокой связью состоит в том, что становится действительно сложно изменить что-либо в задействованных типах, не нарушая клиента, который от них зависит. Таким образом, в этом случае вы также можете обернуть массив в настраиваемый класс коллекции и давать правильный ответ за один раз, выполнив необходимые вычисления внутри и ослабив связь клиента с коллекцией.

Применение классов коллекций для разделения клиентов

Некоторые правила для пользовательских классов коллекций

Давайте посмотрим на некоторые правила, которые я применяю при работе с пользовательскими классами коллекций.

Сделайте их неизменными

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

Не изменяемые ссылки на экземпляр коллекции

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

Предлагайте только то поведение, которое нужно реальным клиентам и которое они используют

Вместо того, чтобы расширять общий класс библиотеки коллекции или самостоятельно реализовывать универсальный метод filter, map и reduce для каждого настраиваемого класса коллекции, реализуйте только то, что действительно необходимо. Если в какой-то момент метод перестает использоваться, удалите его.

Используйте IteratorAggregate и ArrayIterator для поддержки итерирования

Если вы работаете с PHP, вместо того, чтобы самостоятельно реализовывать все методы интерфейса Iterator (и сохранять внутренний указатель и т. д.), просто реализуйте интерфейс IteratorAggregate и позвольте ему вернуть экземпляр ArrayIterator на основе внутреннего массива:

Применение IteratorAggregate

Рассмотрим компромисс

Написание большего количества кода для вашего пользовательского класса коллекции должно облегчить клиентам работу с этой коллекцией (а не только с массивом). Если клиентский код становится проще для понимания, а коллекция обеспечивает полезное поведение, это оправдывает дополнительные затраты на поддержку настраиваемого класса коллекции.

Однако, поскольку с динамическими массивами так легко работать, в основном потому, что для элементов массива пока нету поддержки типов использовать их нужно аккуратно.

Дата: 12.01.2021
Автор: Евгений
Голосование

Авторам статьи важно Ваше мнение. Будем рады его обсудить с Вами:

comments powered by Disqus
Спасибо, что выбираете FREEhost.UA