Spiffy — платформа интерфейса Spiffy Perl для вас
package Keen; use Spiffy -Base; field 'mirth'; const mood => ':-)'; sub happy { if ($self->mood eq ':-(') { $self->mirth(-1); print "Cheer up!"; } super; }
«Spiffy» — это фреймворк и методология объектно-ориентированного (ОО) программирования на Perl. Spiffy объединяет лучшие части Exporter.pm, base.pm, mixin.pm и SUPER.pm в один волшебный базовый класс. Он пытается исправить все недостатки традиционного объектно-ориентированного программирования Perl чистым, простым и (возможно, когда-нибудь) стандартным способом.
Spiffy заимствует идеи из других объектно-ориентированных языков, таких как Python, Ruby, Java и Perl 6. Он также добавляет несколько собственных приемов.
Если вы посмотрите на CPAN, то увидите массу модулей, связанных с ОО. Начиная новый проект, вам нужно выбрать набор модулей, который имеет наибольший смысл, а затем использовать эти модули в каждом из ваших классов. С другой стороны, Spiffy содержит все, что вам, возможно, понадобится в одном модуле, и вам нужно использовать его только один раз в одном из ваших классов. Если вы сделаете Spiffy.pm базовым классом базового класса вашего проекта, Spiffy автоматически передаст всю свою магию всем вашим подклассам. Со временем вы можете забыть, что вообще им пользуетесь!
Самое поразительное различие между Spiffy и другими объектно-ориентированными базовыми классами Perl заключается в том, что он имеет возможность экспорта. Если вы создадите подкласс Spiffy, все, что экспортирует Spiffy, будет автоматически экспортировано вашим подклассом в дополнение к всем вещам, которые вы хотите экспортировать. И если кто-то создаст подкласс вашего подкласса, все это будет экспортировано автоматически и так далее. Думайте об этом как о «наследованном экспорте», и он использует знакомый синтаксис спецификации Exporter.pm.
Чтобы использовать Spiffy или любой подкласс Spiffy в качестве базового класса вашего класса, вы указываете аргумент -base
для команды use
.
use MySpiffyBaseModule -base;
Вы также можете использовать традиционную use base 'MySpiffyBaseModule';
синтаксис, и все будет работать точно так же. Единственное предостережение: Spiffy.pm уже должен быть загружен. Это потому, что Spiffy на лету переконфигурирует base.pm, чтобы выполнять всю магию Spiffy.
Spiffy поддерживает Ruby-подобные миксины с ролями Perl6. Как и в случае base
вы можете использовать любой из следующих вызовов:
use mixin 'MySpiffyBaseModule'; use MySpiffyBaseModule -mixin;
Вторая версия будет работать только в том случае, если смешиваемый класс является подклассом Spiffy. Первая версия будет работать во всех случаях, если Spiffy уже загружен.
Чтобы ограничить количество смешанных методов, используйте роли. (Подсказка: они работают так же, как список экспортеров):
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
В объектно-ориентированном Perl почти каждая подпрограмма является методом. Каждый метод получает переданный ему объект в качестве первого аргумента. Это означает, что практически каждая подпрограмма начинается со строки:
my $self = shift;
Spiffy предоставляет простой дополнительный механизм фильтрации для вставки этой строки, что приводит к более чистому коду. Если вы считаете, что средний метод имеет 10 строк кода, это 10% вашего кода! Чтобы включить эту опцию, вы просто используете опцию - Base
вместо опции -base
или добавляете опцию -selfless
. Если фильтрация источников вызывает у вас тошноту, не используйте эту функцию. Лично я нахожу это захватывающим в своем стремлении писать безупречно чистый и поддерживаемый код.
Полезной особенностью Spiffy является то, что он экспортирует две функции: field
и const
, которые можно использовать для объявления атрибутов вашего класса и автоматически генерировать для них методы доступа. Единственная разница между этими двумя функциями заключается в том, что атрибуты const
не могут быть изменены; таким образом, метод доступа работает намного быстрее.
Один интересный аспект объектно-ориентированного программирования — это когда метод вызывает тот же метод из родительского класса. Обычно это называется вызовом суперметода. Средства Perl для этого уродливы:
sub cleanup { my $self = shift; $self->scrub; $self->SUPER::cleanup(@_); }
Spiffy позволяет очень легко вызывать суперметоды. Вы просто используете super
. Вам не нужно передавать ему какие-либо аргументы, поскольку он автоматически передает их вам. Вот та же функция со Spiffy:
sub cleanup { $self->scrub; super; }
У Spiffy есть специальный метод для анализа аргументов, называемый parse_arguments
, который он также использует для анализа своих собственных аргументов. Вы объявляете, какие аргументы являются логическими (одиночными), а какие парными, с помощью двух специальных методов, называемых boolean_arguments
paired_arguments
. Анализ аргументов извлекает логические значения и пары и возвращает их в виде анонимного хеша, за которым следует список несовпадающих аргументов.
Наконец, Spiffy может экспортировать несколько функций отладки WWW
, XXX
, YYY
и ZZZ
. Каждый из них создает дамп YAML своих аргументов. WWW предупреждает вывод, XXX умирает вместе с выводом, YYY печатает вывод, а ZZZ подтверждает вывод. Если YAML не соответствует вашим потребностям, вы можете переключить все дампы в формат Data::Dumper с помощью опции -dumper
.
Это Спиффи!
Spiffy реализует в Perl совершенно новую идею. Модули, которые действуют как объектно-ориентированные классы, а также экспортируют функции. Но это продвигает концепцию Exporter.pm на шаг дальше; он проходит весь путь @ISA
класса и учитывает спецификации экспорта каждого модуля. Поскольку Spiffy для этого вызывает модуль Exporter, вы можете использовать все причудливые функции интерфейса, которые есть в Exporter, включая теги и отрицание.
Spiffy учитывает все аргументы, которые не начинаются с тире, в качестве спецификации экспорта.
package Vehicle; use Spiffy -base; our $SERIAL_NUMBER = 0; our @EXPORT = qw($SERIAL_NUMBER); our @EXPORT_BASE = qw(tire horn); package Bicycle; use Vehicle -base, '!field'; $self->inflate(tire);
В этом случае Bicycle->isa('Vehicle')
, а также все, что экспортируют Vehicle
и Spiffy
, перейдут в Bicycle
, за исключением field
.
Экспорт может быть очень полезен, если вы разработали систему с сотнями классов и хотите, чтобы все они имели доступ к некоторым функциям или константам.
or variables. Just export them in your main base class and every subclass
получат необходимые им функции.
Вы можете делать почти все, что делает Exporter, потому что Spiffy делегирует работу Exporter (после добавления некоторой магии Spiffy). Spiffy предлагает переменную @EXPORT_BASE
, которая похожа на @EXPORT
, но только для случаев, когда используется -base
.
Если вы много занимались объектно-ориентированным программированием на Perl, вы, вероятно, использовали множественное наследование (MI), а если вы много работали с MI, вы, вероятно, сталкивались со странными проблемами и головными болями. Некоторые языки, такие как Ruby, пытаются решить проблемы MI, используя технику, называемую миксинами. По сути, все классы Ruby используют только одиночное наследование (SI), а затем при необходимости примешивают функциональность других модулей.
На упрощенном уровне миксины можно рассматривать как импорт методов другого класса в ваш подкласс. Но с точки зрения реализации это не лучший способ сделать это. Спиффи делает то же, что и Руби. Он создает пустой анонимный класс, импортирует все в этот класс, а затем связывает новый класс с вашим путем SI ISA. Другими словами, если вы говорите:
package AAA; use BBB -base; use CCC -mixin; use DDD -mixin;
В итоге вы получите одну цепочку наследования классов, подобную этой:
AAA << AAA-DDD << AAA-CCC << BBB;
AAA-DDD
и AAA-CCC
— это фактические имена пакетов сгенерированных классов. Преимущество этого стиля в том, что микширование в CCC не засоряет никакие методы в AAA, а DDD также не конфликтует с AAA или CCC. Если вы смешали метод в CCC, который был также в AAA, вы все равно можете получить к нему доступ с помощью super
.
Когда Spiffy смешивает CCC, он включает все методы CCC, которые не начинаются с подчеркивания. На самом деле дело идет еще дальше. Если CCC является подклассом, он будет использовать каждый метод, который CCC can
использовать посредством наследования. Это очень мощно, может быть, даже слишком мощно.
Чтобы ограничить то, что вы смешиваете, Spiffy заимствует концепцию ролей из Perl6. Однако в Spiffy термин «роль» используется более широко. Это очень похоже на список импорта, который использует модуль Exporter, и вы можете использовать группы (теги) и отрицание. Если первый элемент вашего списка использует отрицание, Spiffy начнет со всех методов, которые может выполнять ваш класс примеси.
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
В этом примере walk
и run
— это методы, которые может выполнять EEE, а tools
и sharp_tools
— это роли класса EEE. Как класс EEE определяет эти роли? Он очень просто определяет методы _role_tools
и _role_sharp_tools
, которые возвращают списки дополнительных методов. (И, возможно, другие роли!) Самое интересное здесь то, что, поскольку роли — это всего лишь методы, они тоже могут наследоваться. Возьмите этот Perl6!
Используя флаг -Base
вместо -base
вам никогда не придется писать строку:
my $self = shift;
Этот оператор добавляется в каждую подпрограмму вашего класса с помощью фильтра исходного кода. Магия проста и быстра, поэтому создание чистого кода наравне с Ruby и Python приводит к небольшому снижению производительности.
package Example; use Spiffy -Base; sub crazy { $self->nuts; } sub wacky { } sub new() { bless [], shift; }
точно так же, как:
package Example; use Spiffy -base; use strict;use warnings; sub crazy {my $self = shift; $self->nuts; } sub wacky {my $self = shift; } sub new { bless [], shift; } ;1;
Обратите внимание, что пустые круглые скобки после подпрограммы new
предотвращают добавление $self. Также обратите внимание, что к существующим строкам добавляется дополнительный код, чтобы гарантировать, что номера строк не будут изменены.
-Base
также включает прагмы строгие и предупреждения и добавляет раздражающую '1;' линия к вашему модулю.
Spiffy теперь поддерживает частные методы при использовании механизма фильтрации «-Base». Вы просто объявляете субтитры с помощью ключевого слова my
и называете их с помощью знака '$'
впереди. Так:
package Keen; use SomethingSpiffy -Base; # normal public method sub swell { $self->$stinky; } # private lexical method. uncallable from outside this file. my sub stinky { ... }
Функция XXX очень удобна для отладки, поскольку ее можно вставить практически куда угодно, и она выгрузит ваши данные в красивом чистом формате YAML. Возьмите следующее утверждение:
my @stuff = grep { /keen/ } $self->find($a, $b);
Если у вас возникли проблемы с этим оператором, вы можете отладить его любым из следующих способов:
XXX my @stuff = grep { /keen/ } $self->find($a, $b); my @stuff = XXX grep { /keen/ } $self->find($a, $b); my @stuff = grep { /keen/ } XXX $self->find($a, $b); my @stuff = grep { /keen/ } $self->find(XXX $a, $b);
XXX легко вставлять и удалять. Также существует традиция отмечать неопределенные области кода XXX. Это облегчит обнаружение дамперов отладки, если вы забудете их удалить.
WWW и YYY хороши тем, что они сбрасывают свои аргументы, а затем возвращают их. Таким образом, вы можете вставлять их во многие места, и при этом код будет работать, как и раньше. Используйте ZZZ, когда вам нужно умереть как с дампом YAML, так и с полной трассировкой стека.
Функции отладки экспортируются по умолчанию, если вы используете опцию -base
, но только если вы ранее использовали опцию -XXX
. Чтобы экспортировать все 4 функции, используйте тег экспорта:
use SomeSpiffyModule ':XXX';
Чтобы заставить функции отладки использовать Data::Dumper вместо YAML:
use SomeSpiffyModule -dumper;
В этом разделе описаны функции, которые экспортирует Spiffy. Функции field
, const
, stub
и super
экспортируются только при использовании опций -base
или -Base
.
поле
Определяет методы доступа для поля вашего класса:
package Example; use Spiffy -Base; field 'foo'; field bar => []; sub lalala { $self->foo(42); push @{$self->{bar}}, $self->foo; }
Первый параметр, передаваемый в field
— это имя определяемого атрибута. Аксессорам может быть присвоено необязательное значение по умолчанию. Это значение будет возвращено, если в объекте не установлено значение поля.
константа
const bar => 42;
Функция const
аналогична <field>, за исключением того, что она неизменяема. Он также не хранит данные в объекте. Вероятно, вы всегда захотите присвоить const
значение по умолчанию, иначе сгенерированный метод будет бесполезен.
заглушка
stub 'cigar';
Функция- stub
генерирует метод, который завершается с соответствующим сообщением. Идея состоит в том, что подклассы должны реализовывать эти методы, чтобы не вызывались методы-заглушки.
супер
Если эта функция вызывается без каких-либо аргументов, она вызовет тот же метод, в котором она находится, выше в дереве ISA, передав ему все те же аргументы. Если он вызывается с аргументами, он будет использовать эти аргументы с $self
впереди. Другими словами, это работает так, как и следовало ожидать.
sub foo { super; # Same as $self->SUPER::foo(@_); super('hello'); # Same as $self->SUPER::foo('hello'); $self->bar(42); } sub new() { my $self = super; $self->init; return $self; }
super
просто ничего не сделает, если нет суперметода. Наконец, super
делает правильные вещи в подпрограммах АВТОЗАГРУЗКИ.
В этом разделе перечислены все методы, которые автоматически наследуются любым подклассом Spiffy.
миксин
Метод смешивания классов во время выполнения. Принимает те же аргументы, что и use mixin ...
. Делает целевой класс миксином вызывающего объекта.
$self->mixin('SomeClass'); $object->mixin('SomeOtherClass' => 'some_method');
parse_arguments
Этот метод принимает список аргументов и группирует их в пары. Он допускает логические аргументы, которые могут иметь или не иметь значение (по умолчанию 1). Метод возвращает ссылку на хэш всех пар в виде ключей и значений в хеше. Любые аргументы, которые не могут быть объединены в пару, возвращаются в виде списка. Вот пример:
sub boolean_arguments { qw(-has_spots -is_yummy) } sub paired_arguments { qw(-name -size) } my ($pairs, @others) = $self->parse_arguments( 'red', 'white', -name => 'Ingy', -has_spots => -size => 'large', 'black', -is_yummy => 0, );
После этого вызова $pairs
будет содержать:
{ -name => 'Ingy', -has_spots => 1, -size => 'large', -is_yummy => 0, }
а @others
будет содержать «красный», «белый» и «черный».
логические_аргументы
Возвращает список аргументов, которые распознаются как логические. Переопределите этот метод, чтобы определить свой собственный список.
парные_аргументы
Возвращает список аргументов, которые распознаются как парные. Переопределите этот метод, чтобы определить свой собственный список.
Когда вы use
модуль Spiffy или его подкласс, вы можете передать ему список аргументов. Эти аргументы анализируются с использованием метода parse_arguments
описанного выше. Специальный аргумент -base
используется для того, чтобы сделать текущий пакет подклассом используемого модуля Spiffy.
Любые непарные параметры действуют как обычный список импорта; точно так же, как те, которые используются с модулем Exporter.
Правильный способ использовать модуль Spiffy в качестве базового класса — использовать параметр -base
в инструкции use
. Это отличается от типичных модулей, в которых вы хотели бы use base
.
package Something; use Spiffy::Module -base; use base 'NonSpiffy::Module';
Теперь может быть трудно отследить, что Spiffy, а что нет. Поэтому Spiffy фактически создан для работы с base.pm. Вы можете сказать:
package Something; use base 'Spiffy::Module'; use base 'NonSpiffy::Module';
use base
также очень полезно, когда ваш класс не является реальным модулем (отдельным файлом), а просто пакетом в каком-то уже загруженном файле. base
будет работать независимо от того, является ли класс модулем или нет, тогда как синтаксис -base
не может работать таким образом, поскольку use
всегда пытается загрузить модуль.
Чтобы заставить Spiffy работать с base.pm, был сделан подвох. Spiffy заменяет base::import
своей собственной версией. Если базовые модули не являются Spiffy, Spiffy вызывает исходный base::import. Если базовые модули — Spiffy, то Spiffy делает свое дело.
Есть два предостережения.
Сначала необходимо загрузить Spiffy.
Если Spiffy не загружен и в модуле Spiffy вызывается use base
, Spiffy завершится с полезным сообщением, предлагающим автору прочитать эту документацию. Это потому, что Спиффи нужно было заранее произвести импортную замену.
Если вы получили эту ошибку, просто поместите такой оператор в свой код:
use Spiffy ();
Без смешивания
base.pm
может принимать несколько аргументов. И это работает со Spiffy, пока все базовые классы являются Spiffy или не являются Spiffy. Если их смешать, Спиффи умрет. В этом случае просто используйте отдельные use base
.
Spiffy — замечательный способ объектно-ориентированного программирования на Perl, но работа над ним все еще находится в стадии разработки. Будут добавлены новые вещи, а то, что не работает, может быть удалено.
Ingy döt Net <[email protected]>
Авторские права 2004-2014. Инги Дёт Нет.
Эта программа является бесплатным программным обеспечением; вы можете распространять его и/или изменять на тех же условиях, что и сам Perl.
См. http://www.perl.com/perl/misc/Artistic.html.