Spiffy – Spiffy Perl Interface Framework für Sie
package Keen; use Spiffy -Base; field 'mirth'; const mood => ':-)'; sub happy { if ($self->mood eq ':-(') { $self->mirth(-1); print "Cheer up!"; } super; }
„Spiffy“ ist ein Framework und eine Methodik für die objektorientierte Programmierung (OO) in Perl. Spiffy kombiniert die besten Teile von Exporter.pm, base.pm, mixin.pm und SUPER.pm in einer magischen Foundation-Klasse. Es versucht, alle Fehler und Schwächen des traditionellen Perl OO auf saubere, unkomplizierte und (vielleicht eines Tages) standardisierte Weise zu beheben.
Spiffy übernimmt Ideen aus anderen OO-Sprachen wie Python, Ruby, Java und Perl 6. Es fügt auch einige eigene Tricks hinzu.
Wenn Sie einen Blick auf CPAN werfen, gibt es eine Menge OO-bezogener Module. Wenn Sie ein neues Projekt starten, müssen Sie die Module auswählen, die am sinnvollsten sind, und diese Module dann in jedem Ihrer Kurse verwenden. Spiffy hingegen hat alles, was Sie wahrscheinlich brauchen, in einem Modul und Sie müssen es nur einmal in einem Ihrer Kurse verwenden. Wenn Sie Spiffy.pm zur Basisklasse der Basisklasse in Ihrem Projekt machen, gibt Spiffy seine gesamte Magie automatisch an alle Ihre Unterklassen weiter. Möglicherweise vergessen Sie irgendwann, dass Sie es überhaupt verwenden!
Der auffälligste Unterschied zwischen Spiffy und anderen objektorientierten Perl-Basisklassen besteht darin, dass es die Möglichkeit hat, Dinge zu exportieren. Wenn Sie eine Unterklasse von Spiffy erstellen, werden alle Dinge, die Spiffy exportiert, automatisch von Ihrer Unterklasse exportiert, zusätzlich zu allen weiteren Dingen, die Sie exportieren möchten. Und wenn jemand eine Unterklasse Ihrer Unterklasse erstellt, werden alle diese Dinge automatisch exportiert und so weiter. Betrachten Sie es als „Inherited Exportation“ und es verwendet die bekannte Exporter.pm-Spezifikationssyntax.
Um Spiffy oder eine Unterklasse von Spiffy als Basisklasse Ihrer Klasse zu verwenden, geben Sie das Argument -base
für den Befehl use
an.
use MySpiffyBaseModule -base;
Sie können auch die herkömmliche use base 'MySpiffyBaseModule';
Syntax und alles wird genauso funktionieren. Die einzige Einschränkung besteht darin, dass Spiffy.pm bereits geladen sein muss. Das liegt daran, dass Spiffy base.pm im Handumdrehen neu verkabelt, um alle Spiffy-Magie auszuführen.
Spiffy unterstützt Ruby-ähnliche Mixins mit Perl6-ähnlichen Rollen. Genau wie base
können Sie einen der folgenden Aufrufe verwenden:
use mixin 'MySpiffyBaseModule'; use MySpiffyBaseModule -mixin;
Die zweite Version funktioniert nur, wenn die eingemischte Klasse eine Unterklasse von Spiffy ist. Die erste Version funktioniert in jedem Fall, sofern Spiffy bereits geladen wurde.
Um die Einmischung von Methoden einzuschränken, verwenden Sie Rollen. (Hinweis: Sie funktionieren genau wie eine Exporterliste):
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
Im objektorientierten Perl ist fast jede Unterroutine eine Methode. Jede Methode erhält das Objekt, das ihr als erstes Argument übergeben wird. Das bedeutet, dass praktisch jedes Unterprogramm mit der Zeile beginnt:
my $self = shift;
Spiffy bietet einen einfachen, optionalen Filtermechanismus, um diese Zeile für Sie einzufügen, was zu saubererem Code führt. Wenn Sie davon ausgehen, dass eine durchschnittliche Methode 10 Codezeilen hat, sind das 10 % Ihres Codes! Um diese Option zu aktivieren, verwenden Sie einfach die Option - Base
anstelle der Option -base
oder fügen Sie die Option -selfless
hinzu. Wenn Ihnen die Quellenfilterung unangenehm ist, verwenden Sie die Funktion nicht. Ich persönlich finde es süchtig machend in meinem Bestreben, blitzsauberen, wartbaren Code zu schreiben.
Eine nützliche Funktion von Spiffy besteht darin, dass es zwei Funktionen exportiert: field
und const
, mit denen Sie die Attribute Ihrer Klasse deklarieren und automatisch Zugriffsmethoden für sie generieren können. Der einzige Unterschied zwischen den beiden Funktionen besteht darin, dass const
-Attribute nicht geändert werden können; daher ist der Zugriff viel schneller.
Ein interessanter Aspekt der OO-Programmierung besteht darin, dass eine Methode dieselbe Methode von einer übergeordneten Klasse aufruft. Dies wird allgemein als Aufruf einer Supermethode bezeichnet. Perls Möglichkeiten, dies zu tun, sind absolut hässlich:
sub cleanup { my $self = shift; $self->scrub; $self->SUPER::cleanup(@_); }
Spiffy macht es, äh, supereinfach, Supermethoden aufzurufen. Sie nutzen einfach die super
. Sie müssen ihm keine Argumente übergeben, da diese automatisch für Sie weitergegeben werden. Hier ist die gleiche Funktion mit Spiffy:
sub cleanup { $self->scrub; super; }
Spiffy verfügt über eine spezielle Methode zum Parsen von Argumenten namens parse_arguments
, die es auch zum Parsen seiner eigenen Argumente verwendet. Sie deklarieren, welche Argumente boolesch (Singletons) sind und welche gepaart sind, mit zwei speziellen Methoden namens boolean_arguments
paired_arguments
. Parse arguments ruft die booleschen Werte und Paare ab und gibt sie in einem anonymen Hash zurück, gefolgt von einer Liste der nicht übereinstimmenden Argumente.
Schließlich kann Spiffy einige Debugging-Funktionen WWW
, XXX
, YYY
und ZZZ
exportieren. Jeder von ihnen erzeugt einen YAML-Dump seiner Argumente. WWW warnt die Ausgabe, XXX stirbt mit der Ausgabe, YYY druckt die Ausgabe und ZZZ bestätigt die Ausgabe. Wenn YAML nicht Ihren Anforderungen entspricht, können Sie alle Dumps mit der Option -dumper
in das Data::Dumper-Format umstellen.
Das ist Spiffy!
Spiffy setzt eine völlig neue Idee in Perl um. Module, die sowohl als objektorientierte Klassen fungieren als auch Funktionen exportieren. Aber es führt das Konzept von Exporter.pm noch einen Schritt weiter; Es durchläuft den gesamten @ISA
-Pfad einer Klasse und berücksichtigt die Exportspezifikationen jedes Moduls. Da Spiffy dazu das Exporter-Modul aufruft, können Sie alle schicken Schnittstellenfunktionen von Exporter nutzen, einschließlich Tags und Negation.
Spiffy berücksichtigt alle Argumente, die nicht mit einem Bindestrich beginnen, als Teil der Exportspezifikation.
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);
In diesem Fall werden Bicycle->isa('Vehicle')
und auch alle Dinge, die Vehicle
und Spiffy
exportieren, in Bicycle
abgelegt, mit Ausnahme von field
.
Das Exportieren kann sehr hilfreich sein, wenn Sie ein System mit Hunderten von Klassen entworfen haben und Sie möchten, dass alle auf bestimmte Funktionen oder Konstanten zugreifen können
or variables. Just export them in your main base class and every subclass
erhalten die Funktionen, die sie benötigen.
Sie können fast alles tun, was Exporter tut, weil Spiffy die Aufgabe an Exporter delegiert (nachdem er etwas Spiffy-Magie hinzugefügt hat). Spiffy bietet eine Variable @EXPORT_BASE
an, die @EXPORT
ähnelt, jedoch nur für Verwendungen, die -base
verwenden.
Wenn Sie viel OO-Programmierung in Perl gemacht haben, haben Sie wahrscheinlich Multiple Inheritance (MI) verwendet, und wenn Sie viel MI gemacht haben, sind Sie wahrscheinlich auf seltsame Probleme und Kopfschmerzen gestoßen. Einige Sprachen wie Ruby versuchen, MI-Probleme mithilfe einer Technik namens Mixins zu lösen. Grundsätzlich verwenden alle Ruby-Klassen nur Single Inheritance (SI) und mischen dann bei Bedarf Funktionen aus anderen Modulen ein.
Vereinfacht kann man sich Mixins als Importieren der Methoden einer anderen Klasse in Ihre Unterklasse vorstellen. Aber vom Standpunkt der Implementierung aus ist das nicht der beste Weg, dies zu tun. Spiffy macht das, was Ruby macht. Es erstellt eine leere anonyme Klasse, importiert alles in diese Klasse und verkettet dann die neue Klasse in Ihrem SI ISA-Pfad. Mit anderen Worten, wenn Sie sagen:
package AAA; use BBB -base; use CCC -mixin; use DDD -mixin;
Am Ende erhalten Sie eine einzelne Vererbungskette von Klassen wie diese:
AAA << AAA-DDD << AAA-CCC << BBB;
AAA-DDD
und AAA-CCC
sind die tatsächlichen Paketnamen der generierten Klassen. Das Schöne an diesem Stil ist, dass das Mischen in CCC keine Methoden in AAA beeinträchtigt und DDD auch nicht mit AAA oder CCC in Konflikt steht. Wenn Sie eine Methode in CCC eingemischt haben, die auch in AAA war, können Sie immer noch darauf zugreifen, indem Sie super
verwenden.
Wenn Spiffy CCC einmischt, zieht es alle Methoden in CCC ein, die nicht mit einem Unterstrich beginnen. Eigentlich geht es noch weiter. Wenn CCC eine Unterklasse ist, wird jede Methode übernommen, die CCC durch Vererbung ausführen can
. Das ist sehr mächtig, vielleicht zu mächtig.
Um das, was Sie mischen, einzuschränken, übernimmt Spiffy das Konzept der Rollen von Perl6. Der Begriff Rolle wird in Spiffy jedoch lockerer verwendet. Es ähnelt einer Importliste, die das Exporter-Modul verwendet, und Sie können Gruppen (Tags) und Negationen verwenden. Wenn das erste Element Ihrer Liste Negation verwendet, beginnt Spiffy mit allen Methoden, die Ihre Mixin-Klasse ausführen kann.
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
In diesem Beispiel sind walk
und run
Methoden, die EEE ausführen kann, und tools
und sharp_tools
sind Rollen der Klasse EEE. Wie definiert die Klasse EEE diese Rollen? Es definiert ganz einfach Methoden namens _role_tools
und _role_sharp_tools
, die Listen weiterer Methoden zurückgeben. (Und möglicherweise auch andere Rollen!) Das Tolle daran ist, dass Rollen, da sie nur Methoden sind, auch vererbt werden können. Nimm das Perl6!
Durch die Verwendung des Flags -Base
anstelle von -base
müssen Sie nie die Zeile schreiben:
my $self = shift;
Diese Anweisung wird mithilfe eines Quellfilters zu jeder Unterroutine Ihrer Klasse hinzugefügt. Die Magie ist einfach und schnell, daher gibt es kaum Leistungseinbußen bei der Erstellung von sauberem Code auf Augenhöhe mit Ruby und Python.
package Example; use Spiffy -Base; sub crazy { $self->nuts; } sub wacky { } sub new() { bless [], shift; }
ist genau das gleiche wie:
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;
Beachten Sie, dass die leeren Klammern nach der Unterroutine new
verhindern, dass ein $self hinzugefügt wird. Beachten Sie außerdem, dass der zusätzliche Code zu vorhandenen Zeilen hinzugefügt wird, um sicherzustellen, dass die Zeilennummern nicht geändert werden.
-Base
aktiviert außerdem die Pragmas strict und warnings und fügt das lästige „1;“ hinzu. Linie zu Ihrem Modul.
Spiffy unterstützt jetzt private Methoden, wenn Sie den Filtermechanismus „-Base“ verwenden. Sie deklarieren die Subs einfach mit dem Schlüsselwort my
und rufen sie mit einem '$'
davor auf. So was:
package Keen; use SomethingSpiffy -Base; # normal public method sub swell { $self->$stinky; } # private lexical method. uncallable from outside this file. my sub stinky { ... }
Die XXX-Funktion ist zum Debuggen sehr praktisch, da Sie sie fast überall einfügen können und Ihre Daten in einem schönen, sauberen YAML-Format abgelegt werden. Nehmen Sie die folgende Aussage:
my @stuff = grep { /keen/ } $self->find($a, $b);
Wenn Sie ein Problem mit dieser Anweisung haben, können Sie sie auf eine der folgenden Arten debuggen:
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 lässt sich leicht einsetzen und entfernen. Es ist auch Tradition, unsichere Codebereiche mit XXX zu kennzeichnen. Dadurch sind die Debug-Dumper leicht zu erkennen, wenn Sie vergessen, sie herauszunehmen.
WWW und YYY sind nett, weil sie ihre Argumente ausgeben und sie dann zurückgeben. Auf diese Weise können Sie sie an vielen Stellen einfügen und den Code trotzdem wie zuvor ausführen lassen. Verwenden Sie ZZZ, wenn Sie sowohl mit einem YAML-Dump als auch mit einem vollständigen Stack-Trace sterben müssen.
Die Debugging-Funktionen werden standardmäßig exportiert, wenn Sie die Option -base
verwenden, jedoch nur, wenn Sie zuvor die Option -XXX
verwendet haben. Um alle 4 Funktionen zu exportieren, verwenden Sie das Export-Tag:
use SomeSpiffyModule ':XXX';
So erzwingen Sie, dass die Debugfunktionen Data::Dumper anstelle von YAML verwenden:
use SomeSpiffyModule -dumper;
In diesem Abschnitt werden die Funktionen beschrieben, die Spiffy exportiert. Die Funktionen field
, const
, stub
und super
werden nur exportiert, wenn Sie die Optionen -base
oder -Base
verwenden.
Feld
Definiert Zugriffsmethoden für ein Feld Ihrer Klasse:
package Example; use Spiffy -Base; field 'foo'; field bar => []; sub lalala { $self->foo(42); push @{$self->{bar}}, $self->foo; }
Der erste an field
übergebene Parameter ist der Name des zu definierenden Attributs. Accessoren kann ein optionaler Standardwert zugewiesen werden. Dieser Wert wird zurückgegeben, wenn im Objekt kein Wert für das Feld festgelegt wurde.
const
const bar => 42;
Die const
Funktion ähnelt <field>, außer dass sie unveränderlich ist. Außerdem werden keine Daten im Objekt gespeichert. Wahrscheinlich möchten Sie einer const
immer einen Standardwert zuweisen, da die generierte Methode sonst einigermaßen nutzlos wird.
Stummel
stub 'cigar';
Die stub
-Funktion generiert eine Methode, die mit einer entsprechenden Nachricht beendet wird. Die Idee ist, dass Unterklassen diese Methoden implementieren müssen, damit die Stub-Methoden nicht aufgerufen werden.
super
Wenn diese Funktion ohne Argumente aufgerufen wird, ruft sie dieselbe Methode weiter oben im ISA-Baum auf, in der sie sich befindet, und übergibt ihr alle gleichen Argumente. Wenn es mit Argumenten aufgerufen wird, werden diese Argumente mit $self
am Anfang verwendet. Mit anderen Worten: Es funktioniert genau so, wie Sie es erwarten.
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
wird einfach nichts bewirken, wenn es keine super-Methode gibt. Schließlich macht super
in AUTOLOAD-Unterroutinen das Richtige.
In diesem Abschnitt werden alle Methoden aufgelistet, die jede Unterklasse von Spiffy automatisch erbt.
mixin
Eine Methode zum Mischen einer Klasse zur Laufzeit. Akzeptiert die gleichen Argumente wie use mixin ...
. Macht die Zielklasse zu einem Mixin des Aufrufers.
$self->mixin('SomeClass'); $object->mixin('SomeOtherClass' => 'some_method');
parse_arguments
Diese Methode nimmt eine Liste von Argumenten und gruppiert sie in Paaren. Es ermöglicht boolesche Argumente, die einen Wert haben können oder auch nicht (standardmäßig 1). Die Methode gibt eine Hash-Referenz aller Paare als Schlüssel und Werte im Hash zurück. Alle Argumente, die nicht gepaart werden können, werden als Liste zurückgegeben. Hier ist ein Beispiel:
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, );
Nach diesem Aufruf enthält $pairs
:
{ -name => 'Ingy', -has_spots => 1, -size => 'large', -is_yummy => 0, }
und @others
enthält „rot“, „weiß“ und „schwarz“.
boolesche_Argumente
Gibt die Liste der Argumente zurück, die als boolesch erkannt werden. Überschreiben Sie diese Methode, um Ihre eigene Liste zu definieren.
gepaarte_Argumente
Gibt die Liste der Argumente zurück, die als gepaart erkannt werden. Überschreiben Sie diese Methode, um Ihre eigene Liste zu definieren.
Wenn Sie das Spiffy-Modul oder eine Unterklasse davon use
, können Sie ihm eine Liste von Argumenten übergeben. Diese Argumente werden mit der oben beschriebenen Methode parse_arguments
analysiert. Das spezielle Argument -base
wird verwendet, um das aktuelle Paket zu einer Unterklasse des verwendeten Spiffy-Moduls zu machen.
Alle nicht gepaarten Parameter verhalten sich wie eine normale Importliste; genau wie diejenigen, die mit dem Exporter-Modul verwendet werden.
Der richtige Weg, ein Spiffy-Modul als Basisklasse zu verwenden, ist die Verwendung des Parameters -base
in der use
-Anweisung. Dies unterscheidet sich von typischen Modulen, bei denen Sie use base
möchten.
package Something; use Spiffy::Module -base; use base 'NonSpiffy::Module';
Jetzt kann es schwierig sein, den Überblick darüber zu behalten, was Spiffy ist und was nicht. Aus diesem Grund wurde Spiffy für die Zusammenarbeit mit base.pm entwickelt. Sie können sagen:
package Something; use base 'Spiffy::Module'; use base 'NonSpiffy::Module';
use base
ist auch sehr nützlich, wenn Ihre Klasse kein tatsächliches Modul (eine separate Datei) ist, sondern nur ein Paket in einer bereits geladenen Datei. base
funktioniert unabhängig davon, ob die Klasse ein Modul ist oder nicht, während die Syntax -base
auf diese Weise nicht funktionieren kann, da use
immer versucht, ein Modul zu laden.
Damit Spiffy mit base.pm funktioniert, wurde ein schmutziger Trick gespielt. Spiffy tauscht base::import
gegen eine eigene Version aus. Wenn die Basismodule nicht Spiffy sind, ruft Spiffy den ursprünglichen base::import auf. Wenn die Basismodule Spiffy sind, dann macht Spiffy sein eigenes Ding.
Es gibt zwei Vorbehalte.
Spiffy muss zuerst geladen werden.
Wenn Spiffy nicht geladen ist und use base
auf einem Spiffy-Modul aufgerufen wird, bricht Spiffy mit einer nützlichen Meldung ab, die den Autor auffordert, diese Dokumentation zu lesen. Das liegt daran, dass Spiffy den Import-Swap vorher durchführen musste.
Wenn Sie diesen Fehler erhalten, fügen Sie einfach eine Anweisung wie diese vorne in Ihren Code ein:
use Spiffy ();
Kein Mischen
base.pm
kann mehrere Argumente annehmen. Und das funktioniert mit Spiffy, solange alle Basisklassen Spiffy sind oder alle Nicht-Spiffy. Wenn sie gemischt werden, wird Spiffy sterben. In diesem Fall verwenden Sie einfach separate use base
-Anweisungen.
Spiffy ist eine wunderbare Möglichkeit, OO-Programmierung in Perl durchzuführen, aber es ist noch in Arbeit. Neue Dinge werden hinzugefügt und Dinge, die nicht gut funktionieren, werden möglicherweise entfernt.
Ingy im Internet <[email protected]>
Urheberrecht 2004-2014. Ingy ist im Netz.
Dieses Programm ist freie Software; Sie können es unter den gleichen Bedingungen wie Perl selbst weiterverbreiten und/oder ändern.
Siehe http://www.perl.com/perl/misc/Artistic.html