2010-05-27 7 views
7

Ich frage mich, was die beste Praxis in Perl ist zu bekommen - oder, was noch wichtiger ist, setzen - eine globale Variable eines Moduls durch direkten Zugriff auf $Module::varName für den Fall, dass das Modul Getter/Setter-Methode dafür nicht zur Verfügung gestellt.Ist es richtig, die globalen Variablen des Perl-Moduls direkt zu bekommen und speziell zu setzen?

Der Grund, dass es mir schlecht riecht, ist die Tatsache, dass es die Kapselung irgendwie umgeht. Nur weil ich kann tun es in Perl, ich bin nicht ganz sicher, ich sollte (vorausgesetzt, es gibt tatsächlich eine Alternative wie Hinzufügen eines Getter/Setter zum Modul).

+0

Oh, welches Modul? –

+2

Gehen Sie einfach dafür. Wenn sie es ablehnen, was dann? Schlimmeres passiert im Leben. –

+1

Also * kapseln * es. Und Sie können dies als Referenz verwenden: http://stackoverflow.com/questions/1540539/how-do-you-localize-a-number-of-legacy-globals-witound-eval – Axeman

Antwort

8

Die Kapselung wird nicht verletzt, wenn die Variable Teil der öffentlichen API ist. (Wenn es nicht das ist eine andere Sache.)

Ich denke, einen direkten Zugang ist vorzuziehen, da es Sie die Vorteile von dynamischen Scoping nehmen können:

local $Module::varName = 42; 

Dies macht Konflikte mit anderen Code Module weniger wahrscheinlich.

+2

Einverstanden, ich mache Dinge wie 'local $ Data :: Dumper :: Maxlevels = 1;' die ganze Zeit. – Ether

+0

+1 Verstärkung der "wenn Teil der öffentlichen API"; Wenn dies nicht der Fall ist, könnten andere Benutzer dieses verschämt benannten Moduls unerwartetes Verhalten bekommen und Sie mit ekligen Zähnen anstellen. – msw

+0

Was genau würden Sie als "öffentliche API" bezeichnen? Was ist in POD? Wenn ja, nein, die Variable ist NICHT in POD. – DVK

2

Globale Modulvariablen waren in der Vergangenheit in Mode, wurden aber in Modern Perl als "schlechte Form" angesehen. Es ist wichtig zu erkennen, dass Perl jetzt 22-23 Jahre alt ist und Stile und Praktiken sich geändert haben. :) Beachten Sie jedoch, dass es Zeiten gibt, in denen es immer noch angemessen ist, weil es einige sehr nette Features gibt, die mit Paketvariablen einhergehen. Es ist wie immer eine Frage der Erfahrung und Praxis zu sehen, was eine gute Lösung sein könnte.

Um die beste Verwendung für Paketvariablen zu verstehen, müssen Sie wirklich verstehen, wie local funktioniert. Schauen Sie sich local's perldoc help an. Mit Local können Sie eine Paketvariable wie (beispielsweise) $My::Variable im Paket My erstellen und eine dynamically scoped -Version davon erstellen. Normalerweise, wenn Sie $My::Variable an Ort und Stelle ändern, wird es sich auf Ihr gesamtes Programm auswirken und wird bestehen bleiben. Für kleine Programme ist das vielleicht keine große Sache. Für große kann dies katastrophale Nebenwirkungen haben. local können Sie eine temporäre Änderung an dieser Variable vornehmen, die auf Ihren aktuellen Bereich beschränkt ist.

Hier ist, wie es funktioniert:

use 5.012; 
use warnings; 

package My; 

our $Variable = 5; 

package main; 

say $My::Variable; # prints 5 
$My::Variable = 7; 
say $My::Variable; # prints 7 
{ # create a new lexical scope 
    local $My::Variable = 10; # create a new dynamic scope for $My::Variable 
           # that will persist to the end of the lexical scope 
    say $My::Variable; # prints 10 
} 
say $My::Variable; # end of the lexical scope for the localized 
        # $My::Variable, so prints 7 again 

Effektiv können Sie damit Paketvariablen in einer sicheren Weise. Leider weiß nicht jeder über local, so dass sie oft die globale Variable überlisten. Eine gute Dokumentation zu dokumentieren (zB local) hilft immer.

Ein Getter/Setter mit korrekter Objektkapselung verhindert viel, aber nicht immer. Damit es so funktioniert wie eine lokale Variable, müssten Sie viel zusätzliche Arbeit leisten. Das Schönste daran, eine Paketvariable lokalisieren zu können, ist, dass Sie temporäre Änderungen sehr einfach durchführen können, z. B. für eine Debug-Variable. Normalerweise müssen Sie wie ein Muster tun:

{ 
    my $current_variable My::get_variable(); 
    $My::set_variable($new_value); 

    # Do code work 

    $My::set_variable($current_variable); 
} 

mit lokalen, dies zu:

{ 
    local $My::Variable = $new_value; 

    # do code work 
} 

(Übrigens, ich wünsche Ihnen auch dies zu lexikalischen Variablen tun könnte, aus dem gleichen Grund .. .aber Sie können nicht.) Also, für einige Dinge, können Paketvariablen Sinn machen. Es hängt davon ab, wie Sie es verwenden möchten.Dinge wie

  • Debugging Variablen
  • globale Konfiguration, die nicht/nicht oft

jedoch geändert werden, der Fall ist, wenn es etwas, das in regelmäßigen Abständen geändert werden muss, wie

  • verwendet regelmäßig Variablen (siehe die schreckliche Schnittstelle für File::Find)
  • Temporäre Konfiguration
  • „Objekt“ Variablen

Im Grunde alles, was mehr als einmal oder in seltenen Fällen geändert werden muss oder sollte auf andere Weise in ein erzeugten Objekt eingekapselt wird, dann würde ich das Paket vermeiden Variable.

+0

Das Sagen von Perl ist 22 Jahre alt und macht * mich * alt. :( – Ether

+0

Wird in etwa 6 Monaten 23 sein!: D –

+1

'local' liefert * dynamic * scoping, nicht * lexical * scoping. Wenn Sie eine Subroutine innerhalb des Blocks aufrufen, würde $' My :: Variable' als 10 angezeigt 7. Im Nachhinein hätte "local" den Namen "temp" erhalten sollen, da es einen temporären Wert für eine globale Variable liefert. –

-2

Gibt es keine Eingabe-Hygiene/Fehlerbehandlung, worum es bei der Kapselung geht?

Wenn es keine Notwendigkeit für solche gibt, dann könnte man gegen die Implementierung von Getter und Setter argumentieren, die dann etwas albern wie sub set_variable { $variable = shift; } aussehen würde.

Wohlgemerkt, es ist einfacher für das Auge zu verwenden set_variable(42) als was Michael suggeriert!

+4

Die Kapselung verdeckt die Details der Implementierung, sodass sie sich ohne ändern kann der Anrufer weiß das. Es ist eine Bestätigung, dass wir die Zukunft vorhersagen.Wenn beispielsweise die Eingabesanierung und die Fehlerbehandlung nicht wichtig sind *, * kann es später sein. Verwenden Sie eine globale und es gibt keine einfache Möglichkeit, um es zu shim (Sie müssen tie verwenden, jetzt ist Ihr Code langsam und kompliziert). Oder vielleicht sind die fraglichen Daten jetzt global, aber später wollen Sie es pro Objekt. Oder möchten Sie vielleicht bei jedem Einsatz eine Aktion durchführen? Wenn Sie ein Global exponieren, wird auf Ihre Vorhersage gesetzt, wie sich Ihr Code in Zukunft entwickeln wird. – Schwern

+0

@Swwern: Es gibt ganze Java-Shops, die so etwas wie "Kein Verhalten in Value-Objekten" als "Standard" haben. Dies zeigt, wie allgemein es für Getter und Setter ist, der Verkapselung absolut nichts hinzuzufügen. – Axeman

+0

@Axeman Ob sie im Allgemeinen eine gute Idee sind oder nicht, es gibt einen Wert in Accessoren auf einem reinen VO. Manchmal ist es sinnvoll, die interne Struktur einer VO zu ändern. vielleicht ist es teuer oder verschwenderisch, alle Daten zur Bauzeit zu berechnen, und Sie wollen es nur auf Nachfrage tun? Vielleicht möchten Sie, dass eine VO ein Proxy- oder Fliegengewichtsobjekt wird? Vielleicht ist ein Teil der Daten veraltet und Sie möchten den Benutzer warnen oder seinen Zugriff aufzeichnen? Dies ist bei öffentlichen Datenmitgliedern nicht möglich. Eine gute Sprache vermeidet dieses Problem, indem sie keine Syntax zwischen einem öffentlichen Datenelement und einem Accessor unterscheidet. – Schwern

1

Wenn das Modul keinen Accessor bietet, erstellen Sie einen, verwenden Sie ihn und senden Sie den Patch.

Verwandte Themen