Главная » Хабрахабр » Кое-что о пространстве имён

Кое-что о пространстве имён

И немножко на JS. Я программирую на PHP. Попробовал на вкус python и dart. Когда-то я программировал на Java, ещё раньше — на LotusScript. Языки программирования меня интересуют с точки зрения создания компьютерных приложений. Basic, Fortran, Pascal, Prolog, VisualBasic, С++/С, perl — на всём этом я тоже изображал что-то исполняемое. Сложных web-приложений. Web-приложений. Точнее, лично незнакомые — они знают друг друга по подписям в коммитах в общий репозиторий и по nickname’ам в баг-трекерах. Таких, которые пишут незнакомые друг с другом люди. Я не слишком умён, чтобы программировать на С/С++ для различных ОС, и поэтому я программирую на PHP для Magento.

Так, вот, возвращаясь к теме статьи, могу сказать, что пространство имён — один из очень важных столпов, на которых базируется написание сложных web-приложений группой слабознакомых друг с другом разработчиков.

В данном тексте под пространством имён я подразумеваю namespace с точки зрения PHP, а не namespace с точки зрения python’а:

<?php
namespace Vendor\Project\Module\Component\Unit;

Впервые с пространством имён я столкнулся при изучении Java, когда пытался постичь тайну директивы "package":

package com.sun.source.util;

Рекомендация от авторов языка использовать в качестве части названия пакета зарегистрированного на тебя (на твою компанию) домена выглядело несколько экстравагантно. Было непонятно назначение этой директивы и что именно в ней указывать, если указывать можно было любую строку. Только впоследствии, когда я собирал приложения с помощью maven’а, я оценил прозорливость данной рекомендации. Это сейчас каждый-всякий-любой имеет свой собственный домен и такая рекомендация не сильно смущает, а 15-20 лет назад я очень сильно думал, какой домен взять в качестве названия для своего первого пакета и на что это может повлиять в дальнейшем.

Если твой код использует сторонний, который зависит от других пакетов, зависящих от третьих — в такой свалке очень трудно поддерживать порядок. Понять значение пространства имён мне помогли менеджеры зависимостей. Тем не менее, именно из-за обратно-доменного правила наименования пакетов в куче JAR’ов, сваленных в один каталог (например, в WEB-INF/lib), достаточно легко ориентироваться:

image

Сравните с npm (JavaScript):

image

В результате, в Java можно независимо создать большое количество бесконфликтных пакетов (модулей) без явного согласования их наименования независимыми группами разработчиков, а в JS для этого нужно явно использовать реестр npm. В Java разработчиками достаточно широко принято "обратно-доменное" наименование пакетов (как следствие — модулей), а в JS — нет. Да, в Java в разрешении конфликтов неявным образом задействован глобальный реестр доменов, но это же правило наименования может использовать любое сообщество, а не только Java-кодеры.

В PHP менеджер зависимостей composer создаёт двухуровневую структуру каталога: ./company/module:

image

что даёт некоторое преимущество в навигации по зависимостям перед одноуровневым размещением.

Вот статистика по центральным репозиториям пакетов для Java/JS/PHP:

https://mvnrepository.com/repos/central — 3 358 578 indexed jars
https://www.npmjs.com/ — 872 459 packages
https://packagist.org/statistics — 207 560 packages (1 472 944 versions)

Скорее всего для maven’а в статистике учитываются все версии модулей, в то время, как в npm и composer учитываются именно сами модули.

С этим успешно справляются "пространства имён" по версии python’а. Основной ответ — для предотвращения конфликтов различных элементов кода (константы, функции, классы, ...), имеющих одинаковые имена, но находящихся в различных модулях. по сути своей это ближе к области видимости (scope). Но я бы всё-таки взял здесь "пространство имён" в кавычки, т.к.

И вот это вот свойство пространства имён (логическая группировка) и даёт возможность создавать более сложные программные комплексы менее связанными друг с другом группами разработчиков. Пространство имён по версии Java (package) и PHP (namespace) прежде всего позволяет однозначно адресовать конкретный элемент кода в совокупной общности.

IDE способно без труда сформировать этот адрес. В PHP класс \Doctrine\DBAL\Schema\Column адресуется однозначно, каким бы образом не подключался исходный код к проекту. В PhpStorm это делается так (правой кнопкой по элементу кода):

image

Попробуем подобным образом сформировать адрес для ссылки на JS-функцию query: Тот же PhpStorm теряется, если применить подобный приём для JS-кода (где нет namespace’ов).

image

На выходе имеем module.query, что недостаточно информативно.

Для адресации функции query в документации (переписке, баг-трекере и т.п.) приходится ссылаться на конкретную строку кода в файле:

image

Результат: ./node_modules/express/lib/middleware/query.js:25

Разумеется, при изменении кол-ва строк в файле или перемещении/переименовании файла мы будем иметь в документации устаревший адрес интересующего нас программного элемента.

Таким образом, использование пространства имён позволяет ссылкам на различные элементы кода проекта оставаться актуальными гораздо дольше, чем ссылки на строку в файле.

При этом наши зависимости тянут свои зависимости, которые тянут свои и т.д., что в результате может приводить к конфликтам по версиям для одного и того же пакета, подтянутого через различные зависимости (jar hell). Современные сложные приложения не могут разрабатываться без менеджеров зависимостей (maven, composer, npm, ...).

Я сам сталкивался с ситуацией, когда при установке в Magento дополнительных модулей количество подгружаемых ими различных версий библиотеки jQuery переваливало за 5-6. В JS подобного не возникает в силу отсутствия namespace’ов. Ну а поиск ошибок в такой разноверсионной лапше зависимостей — квалификации на порядок-два выше, чем квалификации для создания этих самых ошибок. С одной стороны, подобное поведение даёт бОльшую свободу самим разработчикам, с другой — бОльшая свобода предъявляет и бОльшие требования к квалификации.

Использование namespace’ов в PHP позволяет легко обнаруживать подобные конфликты на уровне IDE (для примера я сделал второй файл с дубликатом класса внутри):

image

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

В любом проекте можно переопределить эту функцию и реализовать собственный алгоритм загрузки скриптов по имени класса. Функция spl_autoload_register в PHP позволяет разработчику не заморачиваться тем, где именно находятся файлы с исходниками его классов. В Zend1 абстрактный адаптер для работы с БД определялся таким образом: Без применения пространства имён приходилось выписывать довольно кучерявые имена для классов, чтобы обеспечить их уникальность в пределах сложного проекта (особенно с учётом сторонних библиотек).

abstract class Zend_Db_Adapter_Abstract

Само собой, при использовании таких имён классов в коде приходится шире водить глазами по строкам. Для обеспечения уникальности приходилось, по сути, добавлять namespace в имя класса.

В Zend2, где уже используются namespaces, аналогичное определение класса выглядит так:

namespace Zend\Db\Adapter;
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 при формировании проекта создаёт карту наложения логической иерархии классов на файловую систему.

Кстати, наличие классов в ES6 и отсутствие пространства имён в смысле логической группировки кода вероятно приведёт к появлению в больших ES6-проектах имён, аналогичных именам в Zend1 (Module_Path_To_Class). Для оценки этой роли достаточно попробовать разбить какой-нибудь npm-модуль на несколько модулей поменьше и перестроить свой проект на использование двух новых модулей вместо одного большого.

В простых примерах вполне допустимо использовать идентификаторы типа dbAdapter, serviceA, serviceB и т.д. Идентификатором объектов в IoC-контейнерах является строка (по крайней мере, в PHP). Логичным выходом является использование в качестве идентификаторов объектов имён классов. Но чем крупнее проект, тем сложнее ориентироваться, в каком месте происходит создание объекта с идентификатором, например, searchFilterList и где он используется. Пространство имён позволяет организовать все классы проекта в одной логической структуре и использовать соответствующие пути при создании объектов контейнером. В таком случае логика создания объектов контейнером становится предсказуемой, а исходный код и места использования элементарно определяются IDE.

Соответственно, максимальная сложность приложений, которые можно создать на Java/PHP/C++/..., не может быть достигнута разработчиками с аналогичной квалификацией на JavaScript/Python/C/.... В свете вышесказанного я считаю, что языки программирования, нативно использующие пространства имён для структурирования исходного кода при помощи логической группировки его элементов позволяют с меньшими затратами строить более сложные приложения, чем языки, подобной логической группировки не имеющие.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

WebAssembly в продакшне и «минное поле» Smart TV: интервью с Андреем Нагих

Разработка приложений под Smart TV — тоже «нетипичный JavaScript», когда все слышали о чём-то, но немногие лично пробовали. Интерес к WebAssembly велик, но пока что нечасто встретишь людей, использующих эту технологию в рабочем проекте. TV, а в последние месяцы так ...

[Перевод] Ethereum планирует стать на 99% экономичней

Криптовалюта скоро сядет на энергетическую диету, чтобы конкурировать с более эффективными блокчейнами На фоне ажиотажа вокруг Биткоина его «младший брат» Ethereum отошел в тень. Но проект с рыночной капитализацией около 10 млрд долларов вряд ли можно считать незаметным. И объемы ...