⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch12_07.htm

📁 编程珍珠,里面很多好用的代码,大家可以参考学习呵呵,
💻 HTM
📖 第 1 页 / 共 3 页
字号:
you won't have to wait until run time to learn about this.  Thecompiler knows what type of object <tt class="literal">$self</tt> issupposed to refer to (because you told it), so it can check that thecode accesses only those fields that <tt class="literal">Person</tt> objectsactually have.  If you have horses on the brain and try to access anonexistent field (such as <tt class="literal">$self-&gt;{mane}</tt>), thecompiler can flag this error right away and will never turn theerroneous program over to the interpreter to run.</p><p>There's still a bit of repetition in declaring methods to get atinstance variables, so you still might like to automate the creationof simple accessor methods using one of the techniques below.However, because all these techniques use some sort of indirection, ifyou use them, you will lose the compile-time benefits of typo-checkinglexically typed hash accesses.  You'll still keep the (small) time andspace advantages, though.</p><p><a name="INDEX-2537"></a><a name="INDEX-2538"></a><a name="INDEX-2539"></a>If you do elect to use a pseudohash to implement your class, any classthat inherits from this one must be aware of that underlyingpseudohash implementation.  If an object is implemented as apseudohash, all participants in the inheritance hierarchy shouldemploy the <tt class="literal">use base</tt> and <tt class="literal">usefields</tt> declarations.  For example,<blockquote><pre class="programlisting">package Wizard;use base "Person";use fields qw(staff color sphere);</pre></blockquote>This makes the <tt class="literal">Wizard</tt> module a subclass of class<tt class="literal">Person</tt>, and loads the<em class="emphasis">Person.pm</em> file.  It also registers three newfields in this class to go along with those from<tt class="literal">Person</tt>.That way when you write:<blockquote><pre class="programlisting">my Wizard $mage = fields::new("Wizard");</pre></blockquote>you'll get a pseudohash object with access to both classes' fields:<blockquote><pre class="programlisting">$mage-&gt;name("Gandalf");$mage-&gt;color("Grey");</pre></blockquote></p><p>Since all subclasses must know that they are using a pseudohashimplementation, they should use the direct pseudohash notation for both efficiency and type safety:<blockquote><pre class="programlisting">$mage-&gt;{name} = "Gandalf";$mage-&gt;{color} = "Grey";</pre></blockquote>If you want to keep your implementations interchangeable, however,outside users of your class must use the accessor methods.</p><p>Although <tt class="literal">use base</tt> supports only single inheritance,this is seldom a severe restriction.  See the descriptions of<tt class="literal">use base</tt> and <tt class="literal">use fields</tt> in <a href="ch31_01.htm">Chapter 31, "Pragmatic Modules"</a>.</p><a name="INDEX-2540"></a><a name="INDEX-2541"></a><a name="INDEX-2542"></a><h3 class="sect2">12.7.2. Generating Classes with Class::Struct</h3><a name="INDEX-2543"></a><a name="INDEX-2544"></a><a name="INDEX-2545"></a><a name="INDEX-2546"></a><a name="INDEX-2547"></a><p>The standard <tt class="literal">Class::Struct</tt> module exports a function named<tt class="literal">struct</tt>.  This creates all the trapping you'll need to get startedon an entire class.  It generates a constructor named <tt class="literal">new</tt>, plusaccessor methods for each of the data fields (instance variables)named in that structure.</p><p>For example, if you put the class in a <em class="emphasis">Person.pm</em> file:<blockquote><pre class="programlisting">package Person;use Class::Struct;struct Person =&gt; {    # create a definition for a "Person"    name    =&gt; '$',   #    name field is a scalar    race    =&gt; '$',   #    race field is also a scalar    aliases =&gt; '@',   #    but aliases field is an array ref};1;</pre></blockquote>Then you could use that module this way:<blockquote><pre class="programlisting">use Person;my $mage = Person-&gt;new();$mage-&gt;name("Gandalf");$mage-&gt;race("Istar");$mage-&gt;aliases( ["Mithrandir", "Olorin", "Incanus"] );</pre></blockquote>The <tt class="literal">Class::Struct</tt> module created all four of thosemethods.  Because it follows the subclass-safe policy of alwaysprefixing the field name with the class name, it also permits aninherited class to have its own separate field of the same name as abase class field without conflict.  That means in this case that"<tt class="literal">Person::name</tt>" rather than just"<tt class="literal">name</tt>" is used for the hash key for that particularinstance variable.</p><p>Fields in a <tt class="literal">struct</tt> declaration don't have to be basic Perl types.They can also specify other classes, but classes created with<tt class="literal">struct</tt> work best because the function makes assumptions about howthe classes behave that aren't generally true of all classes.  Forexample, the <tt class="literal">new</tt> method for the appropriate class is invoked toinitialize the field, but many classes have constructors withother names.</p><p>See the description of <tt class="literal">Class::Struct</tt> in <a href="ch32_01.htm">Chapter 32, "Standard Modules"</a>, and its online documentationfor more information.  Many standard modules use<tt class="literal">Class::Struct</tt> to implement their classes, including<tt class="literal">User::pwent</tt> and <tt class="literal">Net::hostent</tt>.Reading their code can prove instructive.</p><h3 class="sect2">12.7.3. Generating Accessors with Autoloading</h3><p><a name="INDEX-2548"></a><a name="INDEX-2549"></a><a name="INDEX-2550"></a><a name="INDEX-2551"></a>As we mentioned earlier, when you invoke a nonexistent method, Perlhas two different ways to look for an <tt class="literal">AUTOLOAD</tt>method, depending on whether you declared a stub method.  You can usethis property to provide access to the object's instance data withoutwriting a separate function for each instance.  Inside the<tt class="literal">AUTOLOAD</tt> routine, the name of the method actuallyinvoked can be retrieved from the <tt class="literal">$AUTOLOAD</tt>variable.  Consider the following code:<blockquote><pre class="programlisting">use Person;$him = Person-&gt;new;$him-&gt;name("Aragorn");$him-&gt;race("Man");$him-&gt;aliases( ["Strider", "Estel", "Elessar"] );printf "%s is of the race of %s.\n", $him-&gt;name, $him-&gt;race;print "His aliases are: ", join(", ", @{$him-&gt;aliases}), ".\n";</pre></blockquote>As before, this version of the <tt class="literal">Person</tt> classimplements a data structure with three fields:<tt class="literal">name</tt>, <tt class="literal">race</tt>, and<tt class="literal">aliases</tt>:<blockquote><pre class="programlisting">package Person;use Carp;my %Fields = (    "Person::name"  =&gt; "unnamed",    "Person::race"   =&gt; "unknown",    "Person::aliases"  =&gt; [],);# The next declaration guarantees we get our own autoloader.use subs qw(name race aliases);sub new {    my $invocant = shift;    my $class = ref($invocant) || $invocant;    my $self  = { %Fields, @_ };    # clone like Class::Struct    bless $self, $class;    return $self;}sub AUTOLOAD {    my $self = shift;    # only handle instance methods, not class methods    croak "$self not an object" unless ref($invocant);    my $name = our $AUTOLOAD;    return if $name =~ /::DESTROY$/;    unless (exists $self-&gt;{$name}) {        croak "Can't access `$name' field in $self";    }    if (@_) { return $self-&gt;{$name} = shift }    else    { return $self-&gt;{$name} }}</pre></blockquote>As you see, there are no methods named <tt class="literal">name</tt>,<tt class="literal">race</tt>, or <tt class="literal">aliases</tt> anywhere to befound.  The <tt class="literal">AUTOLOAD</tt> routine takes care of allthat.  When someone uses<tt class="literal">$him-&gt;name("Aragorn")</tt>, the<tt class="literal">AUTOLOAD</tt> subroutine is called with<tt class="literal">$AUTOLOAD</tt> set to "<tt class="literal">Person::name</tt>".Conveniently, by leaving it fully qualified, it's in exactly the rightform for accessing fields of the object hash.  That way if you usethis class as part of a larger class hierarchy, you don't conflictwith uses of the same name in other classes.</p><h3 class="sect2">12.7.4. Generating Accessors with Closures</h3><p><a name="INDEX-2552"></a><a name="INDEX-2553"></a><a name="INDEX-2554"></a><a name="INDEX-2555"></a>Most accessor methods do essentially the same thing: they simply fetchor store a value from that instance variable.  In Perl, the most natural way tocreate a family of near-duplicate functions is looping arounda closure.  But closures are anonymous functions lacking names, andmethods need to be named subroutines in the class's package symboltable so that they can be called by name.  This is no problem--justassign the closure reference to a typeglob of the appropriate name.<blockquote><pre class="programlisting">package Person;sub new {    my $invocant = shift;    my $self = bless({}, ref $invocant || $invocant);    $self-&gt;init();    return $self;}sub init {    my $self = shift;    $self-&gt;name("unnamed");    $self-&gt;race("unknown");    $self-&gt;aliases([]);}for my $field (qw(name race aliases)) {    my $slot = __PACKAGE__ . "::$field";    no strict "refs";          # So symbolic ref to typeglob works.    *$field = sub {        my $self = shift;        $self-&gt;{$slot} = shift if @_;        return $self-&gt;{$slot};    };}</pre></blockquote>Closures are the cleanest hand-rolled way to create a multitude ofaccessor methods for your instance data.  It's efficient for both thecomputer and you.  Not only do all the accessors share the same bit ofcode (they only need their own lexical pads), but later if you decideto add another attribute, the changes required are minimal:  just addone more word to the <tt class="literal">for</tt> loop's list, and perhaps something to the<tt class="literal">init</tt> method.</p><a name="ch12-sect-po"></a><h3 class="sect2">12.7.5. Using Closures for Private Objects</h3><p><a name="INDEX-2556"></a><a name="INDEX-2557"></a><a name="INDEX-2558"></a><a name="INDEX-2559"></a>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -