2015-07-28 9 views
11

Come si scrivono metodi di accesso personalizzati in Perl6?Come si scrivono metodi di accesso personalizzati in Perl6?

Se ho questa classe:

class Wizard { 
    has Int $.mana is rw; 
} 

posso fare questo:

my Wizard $gandalf .= new; 
$gandalf.mana = 150; 

Diciamo che voglio aggiungere un po 'di controllo per un setter nella mia classe Perl6 senza rinunciando alla notazione $gandalf.mana = 150; (in altre parole, non voglio scrivere questo: $gandalf.setMana(150);). Il programma dovrebbe morire se prova a impostare un mana negativo. Come faccio a fare questo? La documentazione di Perl6 menziona semplicemente che è possibile scrivere accessors personalizzati, ma non dice come.

+2

Se si utilizza '' ne serve solo una volta. Se vuoi cambiarlo per blocco di codice usa '' proprio prima di esso. –

risposta

9

È possibile ottenere la stessa interfaccia di accesso che dire $.mana fornisce dichiarando un metodo is rw.Poi si può avvolgere un proxy intorno l'attributo di fondo in questo modo:

#!/usr/bin/env perl6 
use v6; 

use Test; 
plan 2; 

class Wizard { 
    has Int $!mana; 

    method mana() is rw { 
     return Proxy.new: 
      FETCH => sub ($) { return $!mana }, 
      STORE => sub ($, $mana) { 
       die "It's over 9000!" if ($mana // 0) > 9000; 
       $!mana = $mana; 
      } 
    } 
} 

my Wizard $gandalf .= new; 
$gandalf.mana = 150; 
ok $gandalf.mana == 150, 'Updating mana works'; 
throws_like sub { 
    $gandalf.mana = 9001; 
}, X::AdHoc, 'Too much mana is too much'; 

Proxy è fondamentalmente un modo per intercettare leggere e scrivere le chiamate verso lo stoccaggio e fare qualcosa di diverso il comportamento predefinito. Come suggeriscono le lettere maiuscole, FETCH e STORE vengono chiamati automaticamente da Perl per risolvere espressioni come $gandalf.mana = $gandalf.mana + 5.

C'è una discussione più completa, incluso se si dovrebbe anche tentare di farlo, allo PerlMonks. Vorrei raccomandare contro gli attributi sopra e public in generale rw. È più una visualizzazione di ciò che è possibile esprimere nella lingua piuttosto che uno strumento utile.

+0

Grazie - questa struttura FETCH & STORE era ciò che stavo cercando. Un po 'brutto - un sacco di piatti. Mi aspettavo qualcosa nello stile di blocchi C# get/set. Senza metodi di accesso personalizzati facilmente scritti, le proprietà degenerano in attributi pubblici. –

+0

@raiph Penso che modificherò il testo della domanda. Stavo davvero cercando uno strumento di potere generale (il titolo della domanda lo suggerisce, a differenza del testo della domanda). Sono d'accordo con te sul fatto che nel 90% dei casi, 'where' è lo strumento giusto. Non volevo risolvere il problema presentato, volevo conoscere lo strumento e l'esempio utilizzato era solo un esempio. –

+0

@raiph Ci sono alcuni casi in cui si desidera fare alcune cose sporche, ad esempio, registrare ogni utilizzo del getter durante il debugging, ignorando gli usi del setter. È bello sapere come. –

14

Con versioni più recenti di Rakudo esiste un sottoinsieme denominato UInt che lo limita ai valori positivi.

class Wizard { 
    has UInt $.mana is rw; 
} 

In modo che non sei bloccato in un asso se avete bisogno di qualcosa di simile; Ecco come definito:
(si può lasciare fuori il my, ma ho voluto mostrare la actual line dalla fonte Rakudo)

my subset UInt of Int where * >= 0; 

si potrebbe anche fare questo:

class Wizard { 
    has Int $.mana is rw where * >= 0; 
} 

Vorrei sottolineare che il vincolo è solo un modo breve per creare un Callable.

Si potrebbe avere uno dei seguenti come where vincolo:

... where &subroutine # a subroutine that returns a true value for positive values 
... where { $_ >= 0 } 
... where -> $a { $a >= 0 } 
... where { $^a >= 0 } 
... where $_ >= 0 # statements also work (「$_」 is set to the value it's testing) 

(Se si voleva che non solo essere pari a zero si potrebbe anche usare ... where &prefix:<?> che probabilmente è meglio scritto come ... where ?* o ... where * !== 0)


Se hai voglia di essere fastidioso per le persone che usano il tuo codice, potresti anche farlo.

class Wizard { 
    has UInt $.mana is rw where Bool.pick; # accepts changes randomly 
} 

Se si vuole fare in modo il valore "ha senso" quando si esaminano tutti i valori nella classe in forma aggregata, si dovrà andare a lavorare molto di più.
(Si può richiedere molta più conoscenza di attuazione, nonché)

class Wizard { 
    has Int $.mana; # use . instead of ! for better `.perl` representation 

    # overwrite the method the attribute declaration added 
    method mana() is rw { 
    Proxy.new(
     FETCH => -> $ { $!mana }, 
     STORE => -> $, Int $new { 
     die 'invalid mana' unless $new >= 0; # placeholder for a better error 
     $!mana = $new 
     } 
    ) 
    } 
} 
+0

Grazie per una risposta estenuante. Ho accettato l'altro, perché è più breve e potrebbe risparmiare un po 'di tempo per le persone che google. Fondamentalmente, volevo l'ultima parte della tua risposta: la struttura FETCH/STORE. Scusa se non era chiaro dalla mia domanda. –

+0

@ AdamLibuša Spesso con Perl 6 persone assumono molto e fanno la domanda sbagliata, quindi ho scritto una risposta per la domanda che qualcuno avrebbe dovuto chiedere. Una volta che ho visto l'altra risposta, ho pensato che sarebbe diventato accettabile, il che va bene. –

Problemi correlati