- PVSM.RU - https://www.pvsm.ru -
Я программирую на PHP
. И немножко на JS
. Когда-то я программировал на Java
, ещё раньше — на LotusScript
. Попробовал на вкус python
и dart
. Basic
, Fortran
, Pascal
, Prolog
, VisualBasic
, С++
/С
, perl
— на всём этом я тоже изображал что-то исполняемое. Языки программирования меня интересуют с точки зрения создания компьютерных приложений. Web-приложений. Сложных web-приложений. Таких, которые пишут незнакомые друг с другом люди. Точнее, лично незнакомые — они знают друг друга по подписям в коммитах в общий репозиторий и по nickname’ам в баг-трекерах. Я не слишком умён, чтобы программировать на С
/С++
для различных ОС, и поэтому я программирую на PHP
для Magento [1].
Так, вот, возвращаясь к теме статьи, могу сказать, что пространство имён — один из очень важных столпов, на которых базируется написание сложных web-приложений группой слабознакомых друг с другом разработчиков.
В данном тексте под пространством имён я подразумеваю namespace [2] с точки зрения PHP
, а не namespace [3] с точки зрения python
’а:
<?php
namespace VendorProjectModuleComponentUnit;
Впервые с пространством имён я столкнулся при изучении Java
, когда пытался постичь тайну директивы "package [4]":
package com.sun.source.util;
Было непонятно назначение этой директивы и что именно в ней указывать, если указывать можно было любую строку. Рекомендация от авторов языка использовать в качестве части названия пакета зарегистрированного на тебя (на твою компанию) домена выглядело несколько экстравагантно. Это сейчас каждый-всякий-любой имеет свой собственный домен и такая рекомендация не сильно смущает, а 15-20 лет назад я очень сильно думал, какой домен взять в качестве названия для своего первого пакета и на что это может повлиять в дальнейшем. Только впоследствии, когда я собирал приложения с помощью maven
’а, я оценил прозорливость данной рекомендации.
Понять значение пространства имён мне помогли менеджеры зависимостей. Если твой код использует сторонний, который зависит от других пакетов, зависящих от третьих — в такой свалке очень трудно поддерживать порядок. Тем не менее, именно из-за обратно-доменного правила наименования пакетов в куче JAR’ов, сваленных в один каталог (например, в WEB-INF/lib
), достаточно легко ориентироваться:
Сравните с npm
(JavaScript
):
В Java
разработчиками достаточно широко принято "обратно-доменное" наименование пакетов (как следствие — модулей), а в JS
— нет. В результате, в Java
можно независимо создать большое количество бесконфликтных пакетов (модулей) без явного согласования их наименования независимыми группами разработчиков, а в JS
для этого нужно явно использовать реестр npm [5]. Да, в Java
в разрешении конфликтов неявным образом задействован глобальный реестр доменов [6], но это же правило наименования может использовать любое сообщество, а не только Java
-кодеры.
В PHP
менеджер зависимостей composer
создаёт двухуровневую структуру каталога: ./company/module
:
что даёт некоторое преимущество в навигации по зависимостям перед одноуровневым размещением.
Вот статистика по центральным репозиториям пакетов для Java
/JS
/PHP
:
https://mvnrepository.com/repos/central [7] — 3 358 578 indexed jars
https://www.npmjs.com/ [5] — 872 459 packages
https://packagist.org/statistics [8] — 207 560 packages (1 472 944 versions)
Скорее всего для maven
’а в статистике учитываются все версии модулей, в то время, как в npm
и composer
учитываются именно сами модули.
Основной ответ — для предотвращения конфликтов различных элементов кода (константы, функции, классы, ...), имеющих одинаковые имена, но находящихся в различных модулях. С этим успешно справляются "пространства имён" по версии python’а [3]. Но я бы всё-таки взял здесь "пространство имён" в кавычки, т.к. по сути своей это ближе к области видимости (scope [9]).
Пространство имён по версии Java
(package
) и PHP
(namespace
) прежде всего позволяет однозначно адресовать конкретный элемент кода в совокупной общности. И вот это вот свойство пространства имён (логическая группировка) и даёт возможность создавать более сложные программные комплексы менее связанными друг с другом группами разработчиков.
В PHP
класс DoctrineDBALSchemaColumn
адресуется однозначно, каким бы образом не подключался исходный код к проекту. IDE способно без труда сформировать этот адрес. В PhpStorm это делается так (правой кнопкой по элементу кода):
Тот же PhpStorm теряется, если применить подобный приём для JS
-кода (где нет namespace’ов). Попробуем подобным образом сформировать адрес для ссылки на JS
-функцию query
:
На выходе имеем module.query
, что недостаточно информативно.
Для адресации функции query
в документации (переписке, баг-трекере и т.п.) приходится ссылаться на конкретную строку кода в файле:
Результат: ./node_modules/express/lib/middleware/query.js:25
Разумеется, при изменении кол-ва строк в файле или перемещении/переименовании файла мы будем иметь в документации устаревший адрес интересующего нас программного элемента.
Таким образом, использование пространства имён позволяет ссылкам на различные элементы кода проекта оставаться актуальными гораздо дольше, чем ссылки на строку в файле.
Современные сложные приложения не могут разрабатываться без менеджеров зависимостей (maven
, composer
, npm
, ...). При этом наши зависимости тянут свои зависимости, которые тянут свои и т.д., что в результате может приводить к конфликтам по версиям для одного и того же пакета, подтянутого через различные зависимости (jar hell [10]).
В JS
подобного не возникает в силу отсутствия namespace’ов. Я сам сталкивался с ситуацией, когда при установке в Magento
дополнительных модулей количество подгружаемых ими различных версий библиотеки jQuery
переваливало за 5-6. С одной стороны, подобное поведение даёт бОльшую свободу самим разработчикам, с другой — бОльшая свобода предъявляет и бОльшие требования к квалификации. Ну а поиск ошибок в такой разноверсионной лапше зависимостей — квалификации на порядок-два выше, чем квалификации для создания этих самых ошибок.
Использование namespace’ов в PHP
позволяет легко обнаруживать подобные конфликты на уровне IDE (для примера я сделал второй файл с дубликатом класса внутри):
Таким образом, задача по обнаружению дубликатов элементов кода в проекте становится достаточно легко выполнимой.
Функция spl_autoload_register
в PHP
позволяет разработчику не заморачиваться тем, где именно находятся файлы с исходниками его классов. В любом проекте можно переопределить эту функцию и реализовать собственный алгоритм загрузки скриптов по имени класса. Без применения пространства имён приходилось выписывать довольно кучерявые имена для классов, чтобы обеспечить их уникальность в пределах сложного проекта (особенно с учётом сторонних библиотек). В Zend1
абстрактный адаптер для работы с БД определялся таким образом:
abstract class Zend_Db_Adapter_Abstract {}
Для обеспечения уникальности приходилось, по сути, добавлять namespace в имя класса. Само собой, при использовании таких имён классов в коде приходится шире водить глазами по строкам.
В Zend2
, где уже используются namespaces, аналогичное определение класса выглядит так:
namespace ZendDbAdapter;
class Adapter implements ... {}
Код в итоге становится более читаемым, но самым значимым результатом применения пространства имён становится возможность унификации функционала загрузчика классов с привязкой логической иерархии классов к файловой структуре. Вот выдержка из файла ./vendor/composer/autoload_namespaces.php
, который создаёт composer
в PHP
для работы загрузчика ./vendor/autoload.php
:
<?php
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Zend_' => array($vendorDir . '/magento/zendframework1/library'),
'Yandex' => array($vendorDir . '/allure-framework/allure-codeception/src', $vendorDir . '/allure-framework/allure-php-api/src', $vendorDir . '/allure-framework/allure-php-api/test'),
'Prophecy\' => array($vendorDir . '/phpspec/prophecy/src'),
'PhpOption\' => array($vendorDir . '/phpoption/phpoption/src'),
'PhpCollection' => array($vendorDir . '/phpcollection/phpcollection/src'),
'PHPMD\' => array($vendorDir . '/phpmd/phpmd/src/main/php'),
'OAuth\Unit' => array($vendorDir . '/lusitanian/oauth/tests'),
'OAuth' => array($vendorDir . '/lusitanian/oauth/src'),
...
Видно, что исходники в разных библиотеках могут располагаться по различным путям (различные внтуримодульные структуры), а composer
при формировании проекта создаёт карту наложения логической иерархии классов на файловую систему. И пространства имён играют в этом наложении значимую роль.
Для оценки этой роли достаточно попробовать разбить какой-нибудь npm
-модуль на несколько модулей поменьше и перестроить свой проект на использование двух новых модулей вместо одного большого. Кстати, наличие классов в ES6
и отсутствие пространства имён в смысле логической группировки кода вероятно приведёт к появлению в больших ES6
-проектах имён, аналогичных именам в Zend1
(Module_Path_To_Class
).
Идентификатором объектов в IoC-контейнерах является строка (по крайней мере, в PHP
). В простых примерах вполне допустимо использовать идентификаторы типа dbAdapter
, serviceA
, serviceB
и т.д. Но чем крупнее проект, тем сложнее ориентироваться, в каком месте происходит создание объекта с идентификатором, например, searchFilterList
и где он используется. Логичным выходом является использование в качестве идентификаторов объектов имён классов. В таком случае логика создания объектов контейнером становится предсказуемой, а исходный код и места использования элементарно определяются IDE. Пространство имён позволяет организовать все классы проекта в одной логической структуре и использовать соответствующие пути при создании объектов контейнером.
В свете вышесказанного я считаю, что языки программирования, нативно использующие пространства имён для структурирования исходного кода при помощи логической группировки его элементов позволяют с меньшими затратами строить более сложные приложения, чем языки, подобной логической группировки не имеющие. Соответственно, максимальная сложность приложений, которые можно создать на Java
/PHP
/C++
/..., не может быть достигнута разработчиками с аналогичной квалификацией на JavaScript
/Python
/C
/....
Автор: Alex Gusev
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/304101
Ссылки в тексте:
[1] Magento: https://magento.com/
[2] namespace: http://php.net/manual/ru/language.namespaces.definition.php
[3] namespace: https://www.programiz.com/python-programming/namespace
[4] package: https://ru.wikipedia.org/wiki/Package_(Java)
[5] реестр npm: https://www.npmjs.com/
[6] глобальный реестр доменов: https://ru.wikipedia.org/wiki/DNS
[7] https://mvnrepository.com/repos/central: https://mvnrepository.com/repos/central
[8] https://packagist.org/statistics: https://packagist.org/statistics
[9] scope: https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C_%D0%B2%D0%B8%D0%B4%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8
[10] jar hell: https://tech-read.com/2009/01/13/what-is-jar-hell/
[11] Источник: https://habr.com/post/434968/?utm_source=habrahabr&utm_medium=rss&utm_campaign=434968
Нажмите здесь для печати.