Spiffy - Estrutura de interface Spiffy Perl para você
package Keen; use Spiffy -Base; field 'mirth'; const mood => ':-)'; sub happy { if ($self->mood eq ':-(') { $self->mirth(-1); print "Cheer up!"; } super; }
"Spiffy" é uma estrutura e metodologia para fazer programação orientada a objetos (OO) em Perl. Spiffy combina as melhores partes de Exporter.pm, base.pm, mixin.pm e SUPER.pm em uma classe mágica de base. Ele tenta consertar todos os problemas do Perl OO tradicional, de uma forma limpa, direta e (talvez um dia) padrão.
Spiffy empresta ideias de outras linguagens OO como Python, Ruby, Java e Perl 6. Ele também adiciona alguns truques próprios.
Se você der uma olhada no CPAN, há vários módulos relacionados a OO. Ao iniciar um novo projeto, você precisa escolher o conjunto de módulos que faz mais sentido e, então, usar esses módulos em cada uma de suas aulas. O Spiffy, por outro lado, tem tudo que você provavelmente precisa em um módulo, e você só precisa usá-lo uma vez em uma de suas aulas. Se você fizer do Spiffy.pm a classe base da classe mais básica do seu projeto, o Spiffy passará automaticamente toda a sua magia para todas as suas subclasses. Você pode eventualmente esquecer que está usando!
A diferença mais marcante entre o Spiffy e outras classes base orientadas a objetos Perl é que ele tem a capacidade de exportar coisas. Se você criar uma subclasse do Spiffy, todas as coisas que o Spiffy exportar serão automaticamente exportadas pela sua subclasse, além de mais coisas que você deseja exportar. E se alguém criar uma subclasse da sua subclasse, todas essas coisas serão exportadas automaticamente e assim por diante. Pense nisso como "Exportação Herdada" e usa a familiar sintaxe de especificação Exporter.pm.
Para usar o Spiffy ou qualquer subclasse do Spiffy como classe base de sua classe, você especifica o argumento -base
para o comando use
.
use MySpiffyBaseModule -base;
Você também pode usar a use base 'MySpiffyBaseModule';
sintaxe e tudo funcionará exatamente da mesma forma. A única ressalva é que o Spiffy.pm já deve estar carregado. Isso ocorre porque o Spiffy reconecta o base.pm rapidamente para fazer todas as mágicas do Spiffy.
Spiffy tem suporte para mixins do tipo Ruby com funções do tipo Perl6. Assim como base
você pode usar qualquer uma das seguintes invocações:
use mixin 'MySpiffyBaseModule'; use MySpiffyBaseModule -mixin;
A segunda versão só funcionará se a classe misturada for uma subclasse do Spiffy. A primeira versão funcionará em todos os casos, desde que o Spiffy já esteja carregado.
Para limitar os métodos misturados, use funções. (Dica: funcionam como uma lista de exportadores):
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
No Perl orientado a objetos, quase toda sub-rotina é um método. Cada método recebe o objeto passado como seu primeiro argumento. Isso significa que praticamente todas as sub-rotinas começam com a linha:
my $self = shift;
Spiffy fornece um mecanismo de filtro simples e opcional para inserir essa linha para você, resultando em um código mais limpo. Se você descobrir que um método médio tem 10 linhas de código, isso representa 10% do seu código! Para ativar esta opção, basta usar a opção - Base
em vez da opção -base
ou adicionar a opção -selfless
. Se a filtragem de origem deixa você enjoado, não use o recurso. Pessoalmente, acho isso viciante em minha busca por escrever um código totalmente limpo e sustentável.
Um recurso útil do Spiffy é que ele exporta duas funções: field
e const
que podem ser usadas para declarar os atributos da sua classe e gerar automaticamente métodos acessadores para eles. A única diferença entre as duas funções é que os atributos const
não podem ser modificados; portanto, o acessador é muito mais rápido.
Um aspecto interessante da programação OO é quando um método chama o mesmo método de uma classe pai. Isso geralmente é conhecido como chamar um supermétodo. A facilidade do Perl para fazer isso é feia:
sub cleanup { my $self = shift; $self->scrub; $self->SUPER::cleanup(@_); }
O Spiffy torna super fácil chamar supermétodos. Você acabou de usar a super
função. Você não precisa passar nenhum argumento porque ele os transmite automaticamente para você. Aqui está a mesma função com Spiffy:
sub cleanup { $self->scrub; super; }
Spiffy tem um método especial para analisar argumentos chamado parse_arguments
, que também usa para analisar seus próprios argumentos. Você declara quais argumentos são booleanos (singletons) e quais são emparelhados, com dois métodos especiais chamados boolean_arguments
e paired_arguments
. Os argumentos de análise extraem os booleanos e pares e os retorna em um hash anônimo, seguido por uma lista de argumentos sem correspondência.
Finalmente, o Spiffy pode exportar algumas funções de depuração WWW
, XXX
, YYY
e ZZZ
. Cada um deles produz um dump YAML de seus argumentos. WWW avisa a saída, XXX morre com a saída, YYY imprime a saída e ZZZ confessa a saída. Se o YAML não atender às suas necessidades, você pode mudar todos os dumps para o formato Data::Dumper com a opção -dumper
.
Isso é bacana!
Spiffy implementa uma ideia completamente nova em Perl. Módulos que atuam tanto como classes orientadas a objetos quanto que também exportam funções. Mas leva o conceito de Exporter.pm um passo adiante; ele percorre todo o caminho @ISA
de uma classe e respeita as especificações de exportação de cada módulo. Como o Spiffy chama o módulo Exportador para fazer isso, você pode usar todos os recursos sofisticados da interface que o Exportador possui, incluindo tags e negação.
Spiffy considera todos os argumentos que não começam com um travessão para compor a especificação de exportação.
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);
Neste caso, Bicycle->isa('Vehicle')
e também todas as coisas que Vehicle
e Spiffy
exportam, irão para Bicycle
, exceto field
.
A exportação pode ser muito útil quando você projeta um sistema com centenas de classes e deseja que todas elas tenham acesso a algumas funções ou constantes
or variables. Just export them in your main base class and every subclass
obterão as funções de que precisam.
Você pode fazer quase tudo o que o Exportador faz porque o Spiffy delega o trabalho ao Exportador (depois de adicionar um pouco da mágica do Spiffy). Spiffy oferece uma variável @EXPORT_BASE
que é semelhante a @EXPORT
, mas apenas para usos que usam -base
.
Se você fez muita programação OO em Perl, provavelmente já usou Herança Múltipla (MI), e se fez muita MI, provavelmente se deparou com problemas estranhos e dores de cabeça. Algumas linguagens como Ruby tentam resolver problemas de MI usando uma técnica chamada mixins. Basicamente, todas as classes Ruby usam apenas herança única (SI) e, em seguida, misturam funcionalidades de outros módulos, se necessário.
Os mixins podem ser considerados em um nível simplista como a importação de métodos de outra classe para sua subclasse. Mas do ponto de vista da implementação, essa não é a melhor maneira de fazer isso. Spiffy faz o que Ruby faz. Ele cria uma classe anônima vazia, importa tudo para essa classe e, em seguida, encadeia a nova classe em seu caminho SI ISA. Em outras palavras, se você disser:
package AAA; use BBB -base; use CCC -mixin; use DDD -mixin;
Você acaba com uma única cadeia de herança de classes como esta:
AAA << AAA-DDD << AAA-CCC << BBB;
AAA-DDD
e AAA-CCC
são os nomes reais dos pacotes das classes geradas. O bom desse estilo é que mixar em CCC não atrapalha nenhum método em AAA, e DDD também não entra em conflito com AAA ou CCC. Se você misturou um método no CCC que também estava no AAA, ainda poderá acessá-lo usando super
.
Quando o Spiffy faz mixagem no CCC, ele extrai todos os métodos do CCC que não começam com um sublinhado. Na verdade, vai além disso. Se CCC for uma subclasse, ele extrairá todos os métodos que o CCC can
executar por meio de herança. Isso é muito poderoso, talvez poderoso demais.
Para limitar o que você mixa, o Spiffy pega emprestado o conceito de Roles do Perl6. O termo papel é usado de forma mais livre no Spiffy. É muito parecido com uma lista de importação que o módulo Exportador usa, e você pode usar grupos (tags) e negação. Se o primeiro elemento da sua lista usar negação, o Spiffy começará com todos os métodos que sua classe mixin pode fazer.
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
Neste exemplo, walk
e run
são métodos que EEE podem executar, e tools
e sharp_tools
são funções da classe EEE. Como a classe EEE define essas funções? Ele define de forma muito simples métodos chamados _role_tools
e _role_sharp_tools
que retornam listas de mais métodos. (E possivelmente outros papéis!) O interessante aqui é que, como os papéis são apenas métodos, eles também podem ser herdados. Pegue esse Perl6!
Ao usar o sinalizador -Base
em vez de -base
você nunca precisará escrever a linha:
my $self = shift;
Esta instrução é adicionada a cada sub-rotina da sua classe usando um filtro de origem. A mágica é simples e rápida, então há uma pequena penalidade de desempenho na criação de código limpo no mesmo nível de Ruby e Python.
package Example; use Spiffy -Base; sub crazy { $self->nuts; } sub wacky { } sub new() { bless [], shift; }
é exatamente igual a:
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;
Observe que os parênteses vazios após a sub-rotina new
evitam que um $self seja adicionado. Observe também que o código extra é adicionado às linhas existentes para garantir que os números das linhas não sejam alterados.
-Base
também ativa os pragmas estritos e de avisos e adiciona aquele irritante '1;' linha para o seu módulo.
Spiffy agora tem suporte para métodos privados quando você usa o mecanismo de filtro '-Base'. Você apenas declara os subs com a palavra-chave my
e os chama com um '$'
na frente. Assim:
package Keen; use SomethingSpiffy -Base; # normal public method sub swell { $self->$stinky; } # private lexical method. uncallable from outside this file. my sub stinky { ... }
A função XXX é muito útil para depuração porque você pode inseri-la em quase qualquer lugar e despejará seus dados em um YAML bem limpo. Tomemos a seguinte afirmação:
my @stuff = grep { /keen/ } $self->find($a, $b);
Se você tiver algum problema com esta instrução, poderá depurá-la de uma das seguintes maneiras:
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 é fácil de inserir e remover. Também é tradição marcar áreas incertas do código com XXX. Isso tornará os dumpers de depuração fáceis de detectar caso você se esqueça de retirá-los.
WWW e YYY são legais porque descartam seus argumentos e depois os retornam. Dessa forma, você pode inseri-los em vários lugares e ainda executar o código como antes. Use ZZZ quando precisar morrer com um dump YAML e um rastreamento de pilha completo.
As funções de depuração serão exportadas por padrão se você usar a opção -base
, mas somente se você tiver usado a opção -XXX
anteriormente. Para exportar todas as 4 funções use a tag de exportação:
use SomeSpiffyModule ':XXX';
Para forçar as funções de depuração a usar Data::Dumper em vez de YAML:
use SomeSpiffyModule -dumper;
Esta seção descreve as funções que o Spiffy exporta. As funções field
, const
, stub
e super
são exportadas apenas quando você usa as opções -base
ou -Base
.
campo
Define métodos acessadores para um campo da sua classe:
package Example; use Spiffy -Base; field 'foo'; field bar => []; sub lalala { $self->foo(42); push @{$self->{bar}}, $self->foo; }
O primeiro parâmetro passado para field
é o nome do atributo que está sendo definido. Os acessadores podem receber um valor padrão opcional. Este valor será retornado se nenhum valor para o campo tiver sido definido no objeto.
const
const bar => 42;
A função const
é semelhante a <field> exceto que é imutável. Também não armazena dados no objeto. Você provavelmente sempre deseja fornecer um valor padrão a const
, caso contrário, o método gerado será um tanto inútil.
esboço
stub 'cigar';
A função stub
gera um método que morrerá com uma mensagem apropriada. A ideia é que as subclasses implementem esses métodos para que os métodos stub não sejam chamados.
super
Se esta função for chamada sem nenhum argumento, ela chamará o mesmo método em que está, na parte superior da árvore ISA, passando todos os mesmos argumentos. Se for chamado com argumentos, usará esses argumentos com $self
na frente. Em outras palavras, funciona como você esperaria.
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
simplesmente não fará nada se não houver um método super. Finalmente, super
faz a coisa certa nas sub-rotinas AUTOLOAD.
Esta seção lista todos os métodos que qualquer subclasse do Spiffy herda automaticamente.
mixando
Um método para mixar uma classe em tempo de execução. Toma os mesmos argumentos que use mixin ...
. Torna a classe alvo um mixin do chamador.
$self->mixin('SomeClass'); $object->mixin('SomeOtherClass' => 'some_method');
parse_argumentos
Este método pega uma lista de argumentos e os agrupa em pares. Permite argumentos booleanos que podem ou não ter um valor (padrão 1). O método retorna uma referência hash de todos os pares como chaves e valores no hash. Quaisquer argumentos que não possam ser emparelhados serão retornados como uma lista. Aqui está um exemplo:
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, );
Após esta chamada, $pairs
conterá:
{ -name => 'Ingy', -has_spots => 1, -size => 'large', -is_yummy => 0, }
e @others
conterão 'vermelho', 'branco' e 'preto'.
argumentos_booleanos
Retorna a lista de argumentos reconhecidos como booleanos. Substitua este método para definir sua própria lista.
argumentos_pareados
Retorna a lista de argumentos que são reconhecidos como emparelhados. Substitua este método para definir sua própria lista.
Ao use
o módulo Spiffy ou uma subclasse dele, você pode passar uma lista de argumentos. Esses argumentos são analisados usando o método parse_arguments
descrito acima. O argumento especial -base
é usado para tornar o pacote atual uma subclasse do módulo Spiffy que está sendo usado.
Quaisquer parâmetros não emparelhados atuam como uma lista de importação normal; assim como aqueles usados com o módulo Exportador.
A maneira correta de usar um módulo Spiffy como classe base é com o parâmetro -base
na instrução use
. Isso difere dos módulos típicos onde você gostaria de use base
.
package Something; use Spiffy::Module -base; use base 'NonSpiffy::Module';
Agora pode ser difícil acompanhar o que é Spiffy e o que não é. Portanto, o Spiffy foi feito para funcionar com base.pm. Você pode dizer:
package Something; use base 'Spiffy::Module'; use base 'NonSpiffy::Module';
use base
também é muito útil quando sua classe não é um módulo real (um arquivo separado), mas apenas um pacote em algum arquivo que já foi carregado. base
funcionará independentemente de a classe ser um módulo ou não, enquanto a sintaxe -base
não pode funcionar dessa maneira, pois use
sempre tenta carregar um módulo.
Para fazer o Spiffy funcionar com base.pm, um truque sujo foi feito. Spiffy troca base::import
por sua própria versão. Se os módulos base não forem Spiffy, o Spiffy chama o base::import original. Se os módulos básicos forem Spiffy, então o Spiffy fará seu próprio trabalho.
Existem duas advertências.
O Spiffy deve ser carregado primeiro.
Se o Spiffy não estiver carregado e use base
for invocado em um módulo do Spiffy, o Spiffy morrerá com uma mensagem útil informando ao autor para ler esta documentação. Isso porque o Spiffy precisava fazer a troca de importação com antecedência.
Se você receber esse erro, simplesmente coloque uma instrução como esta no seu código:
use Spiffy ();
Sem mistura
base.pm
pode receber vários argumentos. E isso funciona com o Spiffy, desde que todas as classes base sejam Spiffy ou todas não-Spiffy. Se eles estiverem misturados, Spiffy morrerá. Neste caso, basta usar instruções use base
separadas.
Spiffy é uma ótima maneira de fazer programação OO em Perl, mas ainda é um trabalho em andamento. Novas coisas serão adicionadas e coisas que não funcionam bem poderão ser removidas.
Ingy döt Net <[email protected]>
Direitos autorais 2004-2014. Ingy döt Net.
Este programa é um software livre; você pode redistribuí-lo e/ou modificá-lo sob os mesmos termos do próprio Perl.
Consulte http://www.perl.com/perl/misc/Artistic.html