Статьи

Признания преобразованного PHP-разработчика: пространство имен Superhero!

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

Пространства имен! Это то, что ты хочешь.

Пространства имен — это способ инкапсуляции кода в определенную библиотеку или область. Это легче всего рассматривать как способ создания классов, которые вряд ли конфликтуют с другими классами. Если у вас был класс с именем foo, а затем вы начали использовать библиотеку классов, в которую также был включен класс с именем foo, у вас возникли проблемы, так как теперь у вас есть два класса с одинаковым именем. В PHP вы получите ужасную ошибку, которая на самом деле весьма хороша, поскольку вы можете отследить ее. В Ruby вы бы просто получили Франкенштейн из двух классов в одном — что, безусловно, нехорошо.

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

Пространство имен является основной частью Ruby с момента его создания — просто официально под этим именем — тогда как в PHP это относительно новое дополнение. В результате во многих библиотеках PHP существует соглашение о полу-фактическом именовании, которое отображает классы в папки как форму пространства имен. Хотя это работает для создания уникальных классов, оно также делает код несколько многословным и неприглядным.

Отсутствие в PHP пространства имен

PHP только ввел правильное пространство имен в версии 5.3, выпущенной в конце июня 2009 года, что сильно фурорировало и, по моему опыту, было очень медленным в освоении. Я бы связал это с одним главным фактом, и это обратная совместимость. К сожалению, хостинговое сообщество усваивает новые версии PHP очень медленно, и поэтому доступность PHP 5.3 для значительной части пользовательской базы отсутствует. На PHP 5.3 приходится менее 9% веб-сайтов, которые используют PHP . Чтобы разработчик мог использовать пространства имен в своей библиотеке, он фактически отсекает большую пользовательскую базу, и хотя это не оправдывает удушение использования функции — вы можете понять, почему некоторые разработчики не хотят этого делать.

Пространство имен Psuedo от PHP

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

Примером библиотеки, которая использует этот метод, является библиотека Facade .

Листинг фасадных файлов

Файловая структура фасада

Мы видим, что в Request.php Facade/S3 есть файл Request.php . Следуя пространству имен psuedo, мы получаем класс с именем Facade_S3_Request . Это также позволяет автозагрузчикам найти файл для загрузки. Если вы запрашиваете Facade_S3_Request то легко найти Facade/S3/Request.php .

Это работает хорошо, за исключением того, что вы застряли в необходимости использовать подробные имена классов как в коде библиотек, так и в коде, который должен его использовать. Это может стать немного уродливым, когда вы начнете использовать большие кодовые базы — вот имя класса в библиотеке, которую я использую: HTMLPurifier_AttrDef_URI_Email_SimpleCheck .

Официальное пространство имен PHP

Пространства имен определяются с помощью ключевого слова namespace в верхней части вашего PHP-файла (оно должно быть там), например:

 <?PHP namespace Foo; // or alternatively namespace Foo{ # code here } 

Весь код после namespace Foo; Команда будет в пространстве имен Foo.

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

 <?PHP namespace FooBar; class MyClass { public function hello() { return "Hello World"; } } 

Теперь вы можете использовать этот код одним из двух способов, во-первых:

 <?PHP include 'my_class.php'; // Let's assume our previous code is in this file echo FooBarMyClass::hello(); 

Это использует полностью определенное пространство имен для ссылки на класс или, во-вторых:

 <?PHP use FooBar; include 'my_class.php'; // Remember our assumption from before... echo BarMyClass::hello(); 

Мы говорим PHP, что хотим использовать пространство имен FooBar , а затем вызываем BarMyClass . Возможно, вы подумали, что мы сможем вызвать MyClass без части Bar , и хотя это имеет смысл — это просто не тот случай.

Это действительно имеет смысл, только если вы посмотрите на псевдоним пространств имен PHP. use FooBar — это то же самое, что сказать use FooBar as Bar — это означает, что Bar FooBar с FooBar . Вы можете сказать, use FooBar as Potato а затем вызвать echo PotatoMyClass::Hello() . Смешение? Да Полезно? В некоторой степени .

Существует несколько правил, касающихся приоритета, и еще несколько функций, но я достаточно подробно рассказал о PHP. Вы здесь для Ruby, поэтому давайте посмотрим, как Ruby реализует пространства имен. Вы можете прочитать больше о пространствах имен PHP в этой серии статей SitePoint .

Модули Ruby, э … пространства имен!

Мы уже видели модули Ruby в действии, когда говорили о миксинах , но они также встроены в Ruby для создания пространств имен.

Основное пространство имен

 module Good class Person def self.Welcome puts "Well, Hello World!" end end end module Evil class Person def self.Welcome puts "I will kill ye' all." end end end 

Теперь у нас есть два модуля с отдельными классами Person , каждый из которых имеет собственный метод Welcome . Мы можем получить к ним доступ, используя стандартный оператор модуля.

 Good::Person.Welcome > "Well, Hello World!" Evil::Person.Welcome > "I will kill ye' all." 

В качестве альтернативы мы можем включить это в другой класс и удалить имя модуля.

 class HelloWorld include Good def self.WelcomeMessage Person.Welcome end end HelloWorld.WelcomeMessage > "Well, Hello World!" 

Итак, что мы там сделали, это создали новый класс, который включает в себя модуль Good . Внутри WelcomeMessage класса WelcomeMessage мы просто вызываем Person.Welcome и поскольку мы включили модуль Good , Person ссылается на Good::Person .

Применение нескольких «пространств имен»

Этот метод пространства имен интересен, так как он позволяет применять несколько модулей с помощью оператора include , и методы просто перезаписывают друг друга, но оставляют методы, которые недоступны в более позднем модуле. Мы можем увидеть это, сделав две вещи. Во-первых, мы добавим новый класс в модуль Good и вызовем его в нашем методе WelcomeMessage внутри класса HelloWorld , а во-вторых, мы скажем классу HelloWorld включить Evil — который перезапишет все классы из Good которые также есть в Evil ,

 module Good class Animal def self.Welcome puts "Meow." end end end class HelloWorld include Evil def self.WelcomeMessage Person.Welcome Animal.Welcome end end HelloWorld.WelcomeMessage > "I will kill ye' all." > "Meow." 

Таким образом, результат, который мы видим здесь, является смесью как Good и Evil . Сначала мы включили Good , который дал нам классы Person и Animal , но когда мы включили Evil он переписал класс Person — но не класс Animal , так как в Evil не было определено ни одного. Boom!

Вложенные модули

Модули в Ruby довольно универсальны, вы можете вкладывать их друг в друга, чтобы создавать многоуровневые пространства имен (технически вы можете вкладывать их и в классы, так как Class является подклассом Module , но это не так?).

 module SuperHeroes module JusticeLeague class Batman end class Superman end end module Avengers class IronMan end class CaptainAmerica end end end 

Здесь мы создали модуль SuperHeroes в качестве супер-пространства имен, а затем создали два JusticeLeague и Avengers качестве суб-пространств имен. В каждом из этих подпространств имен есть пара супергероев, определенных в качестве классов.

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

 class MyHeroes include SuperHeroes::Avengers end MyHeroes.constants > [:IronMan, :CaptainAmerica] 

Я включил Мстителей в свой класс MyHeroes , а затем перечислил доступные константы (в данном случае, классы, которые были включены). Это дает нам IronMan и CaptainAmerica .

Модульные / классные сокращения

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

 module SuperHeroes::SHIELD class NickFury end end 

И это прекрасно работает, но только потому, что мы уже определили модуль SuperHeroes . Если я попытаюсь сделать это без предопределенного модуля, я получу ошибку.

 module IHave::NoFriends end > NameError: uninitialized constant Object::IHave 

Это все люди

Сегодня у нас был вводный взгляд на способы пространства имен вашего кода в Ruby, и насколько он сильно отличается от PHP. Я только поцарапал поверхность, когда дело доходит до модулей в Ruby, они действительно универсальный и сложный зверь!

Если у вас есть какие-либо советы по работе с модулями, сообщите нам об этом в комментариях!