2010-05-29 27 views
8

Nel mio attuale lavoro sto costruendo una suite di script Perl che dipendono fortemente dagli oggetti. (usando Perl's bless() su un hash per avvicinarsi il più possibile a OO)Simulazione degli aspetti della tipizzazione statica in una lingua con dattilografia

Ora, per mancanza di un modo migliore di farlo, la maggior parte dei programmatori della mia azienda non sono molto intelligenti. Peggio, a loro non piace leggere la documentazione e sembrano avere problemi a capire il codice di altre persone. La codifica dei cowboy è il gioco qui. Ogni volta che incontrano un problema e cercano di risolverlo, escogitano una soluzione orrenda che in realtà non risolve nulla e di solito lo rende peggiore.

Questo risultato in me, francamente, non mi fido di loro con il codice scritto in lingua dattiloscritta. Ad esempio, vedo troppi problemi con loro che non ottengono un errore esplicito per l'uso scorretto degli oggetti. Ad esempio, se il tipo A ha il membro foo e fanno qualcosa del tipo, instance->goo, non vedranno immediatamente il problema. Restituirà un valore nullo/indefinito e probabilmente perderanno un'ora a trovare la causa. Quindi finisce per cambiare qualcos'altro perché non hanno identificato correttamente il problema originale.

Quindi sono un brainstorming per un modo per mantenere il mio linguaggio di scripting (il suo rapido sviluppo è un vantaggio) ma fornire un messaggio di errore esplicito quando un oggetto non viene utilizzato correttamente. Mi rendo conto che dal momento che non esiste una fase di compilazione o una digitazione statica, l'errore dovrà essere in fase di esecuzione. Sto bene con questo, fintanto che l'utente ottiene un avviso molto esplicito dicendo "questo oggetto non ha X"

Come parte della mia soluzione, non voglio che sia richiesto che controllino se esiste un metodo/variabile prima di provare ad usarlo.

Anche se il mio lavoro è in Perl, penso che questo possa essere indipendente dal linguaggio.

+0

Dato che sei in questo ambiente, scrivendo in Perl mentre ammetti che i tuoi colleghi non lo capiscono ... Sembra che tu sia parte del problema! – Stephen

+0

non sto usando Perl per scelta. era o questo o sh secondo la direzione. – Mike

+0

sono in una posizione di leadership, ma i burocrati prendono decisioni tecniche. Sto lavorando per cambiare questo, ma non ho ancora l'influenza di cui ho bisogno. – Mike

risposta

15

Se si ha qualche possibilità di aggiungere moduli da utilizzare, provare Moose. Fornisce praticamente tutte le funzionalità che desideri in un moderno ambiente di programmazione e altro ancora. Fa tipo di controllo, ereditarietà eccellente, ha capacità di introspezione e con MooseX::Declare, una delle interfacce più belle per le classi Perl disponibili. Date un'occhiata:

use MooseX::Declare; 

class BankAccount { 
    has 'balance' => (isa => 'Num', is => 'rw', default => 0); 

    method deposit (Num $amount) { 
     $self->balance($self->balance + $amount); 
    } 

    method withdraw (Num $amount) { 
     my $current_balance = $self->balance(); 
     ($current_balance >= $amount) 
      || confess "Account overdrawn"; 
     $self->balance($current_balance - $amount); 
    } 
} 

class CheckingAccount extends BankAccount { 
    has 'overdraft_account' => (isa => 'BankAccount', is => 'rw'); 

    before withdraw (Num $amount) { 
     my $overdraft_amount = $amount - $self->balance(); 
     if ($self->overdraft_account && $overdraft_amount > 0) { 
      $self->overdraft_account->withdraw($overdraft_amount); 
      $self->deposit($overdraft_amount); 
     } 
    } 
} 

Penso che sia abbastanza bello, me stesso. :) È uno strato sul sistema di oggetti di Perl, quindi funziona con cose che hai già (in pratica)

Con Moose, puoi creare sottotipi molto facilmente, così puoi assicurarti che il tuo input sia valido. I programmatori pigri sono d'accordo: con così poco che deve essere fatto per far funzionare i sottotipi in Moose, è più facile da fare che non farlo! (Da Cookbook 4)

subtype 'USState' 
    => as Str 
    => where { 
      ( exists $STATES->{code2state}{ uc($_) } 
      || exists $STATES->{state2code}{ uc($_) }); 
     }; 

E Tada, l'USState ora è un tipo che si può usare! Nessun problema, niente muss e solo una piccola quantità di codice. Se non è giusto, genera un errore e tutti i consumatori della classe devono passare uno scalare con quella stringa. Se va bene (che dovrebbe essere ... giusto? :)) Lo usano come al solito, e la tua classe è protetta dalla spazzatura. Che bello!

Alce ha tonnellate di cose fantastiche come questa.

Fidati di me. Controlla. :)

+2

+1 per Moose :) – DVK

+0

Moose ha un modo di trasformare $ oggetto -> {membro} in un errore? Penso che sia ciò che l'OP vuole. – Schwern

+2

Cerca MooseX :: InsideOut. Dovrebbe rendere $ object -> {member} non valido, pur essendo ancora in grado di usare tutta la qualità antlered descritta sopra. –

4

In Perl,

  • rendono necessario che use strict e use warnings sono in in 100% del codice

  • Si può provare a fare un variabili membro quasi privata con la creazione di closures. Un ottimo esempio è la sezione "Variabili membro privato, ordinamento" in http://www.usenix.org/publications/login/1998-10/perl.html. Non sono private al 100% ma abbastanza ovvi come accedere a meno che tu non sappia veramente cosa stai facendo (e chiedi loro di leggere il tuo codice e fare ricerche per scoprire come).

  • Se non si desidera utilizzare le chiusure, il seguente approccio funziona un po 'così:

    fare tutte le variabili membro dell'oggetto (alias oggetto chiavi di hash in Perl) avvolto in funzioni di accesso. Ci sono modi per farlo in modo efficiente dagli standard di codifica POV. Uno dei meno sicuri è Class :: Accessor :: Fast. Sono sicuro che Moose ha modi migliori ma non mi è familiare con Moose.

    Assicurati di "nascondere" le variabili membro effettive nei nomi di convenzioni private, ad es. $object->{'__private__var1'} sarebbe la variabile membro e $object->var1() sarebbe un accessore getter/setter.

    NOTA: per l'ultimo, Class :: Accessor :: Fast non funziona poiché le sue variabili membro condividono i nomi con gli accessor. Ma puoi avere builder molto semplici che funzionano come Class :: Accessor :: Fast e creare valori chiave come $ obj -> {'__ private__foo'} per "foo".

    Questo non impedirà loro di spararsi nel piede, ma renderà molto più difficile farlo.

    Nel tuo caso, se usano $obj->goo o $obj->goo(), VERRANNO SUGGERITI errori di runtime, almeno in Perl.

    Ovviamente potrebbero fare di tutto per fare $obj->{'__private__goo'}, ma se fanno la merda del cowboy gonzo a causa della pigrizia pura, quest'ultimo è molto più lavoro di fare il corretto $obj->foo().

    È anche possibile eseguire una scansione del codice che rileva le stringhe di tipo $object->{"_, anche se dalla descrizione che potrebbe non funzionare come deterrente.

+3

@jrockway - abbastanza giusto - parolacce da parte mia. Toglierò l'exaggregatrion. Ma come ho detto nell'opzione "privato per nome", il nome del gioco è "Make doing the Bad Thing più difficile di Good Thing". – DVK

4

È possibile utilizzare Class::InsideOut o Object::InsideOut che forniscono la vera privacy dei dati. Invece di memorizzare i dati in un riferimento di hash benedetto, un riferimento scalare benedetto viene utilizzato come chiave per gli hash dei dati lessicali. Per farla breve, se i tuoi colleghi provano il numero $obj->{member}, riceveranno un errore di runtime. Non c'è niente in $obj da cui aggrapparsi e non è un modo semplice per ottenere i dati se non attraverso gli accessor.

Ecco a discussion of the inside-out technique and various implementations.

+0

Ricordo che alcuni anni fa si parlava molto di oggetti dentro e fuori e sembravano essere i nuovi hotness, ma in questi giorni sono praticamente caduti dal radar. Qualche idea sul perché sono caduti in disgrazia? Ci sono problemi reali con loro o sono stati semplicemente eclissati da Moose? –

+0

AFAIK la maggior parte dei problemi che li ha resi problematici sono stati risolti, era una caratteristica importante di 5.10 (che è stata ripristinata a 5.8) per aggiungere gli hash di campo (vedi Hash :: Util :: FieldHash) per supportarli correttamente. Forse ora che i problemi di implementazione sono stati risolti non vale più la pena parlarne? – Schwern