perlboot.html
来自「perl教程」· HTML 代码 · 共 851 行 · 第 1/5 页
HTML
851 行
<span class="operator">}</span>
</pre>
<p>There! Now we have the animals all talking, and safely at that,
without the use of symbolic coderefs.</p>
<p>But look at all that common code. Each of the <code>speak</code> routines has a
similar structure: a <a href="../../lib/Pod/perlfunc.html#item_print"><code>print</code></a> operator and a string that contains
common text, except for two of the words. It'd be nice if we could
factor out the commonality, in case we decide later to change it all
to <code>says</code> instead of <code>goes</code>.</p>
<p>And we actually have a way of doing that without much fuss, but we
have to hear a bit more about what the method invocation arrow is
actually doing for us.</p>
<p>
</p>
<h2><a name="the_extra_parameter_of_method_invocation">The extra parameter of method invocation</a></h2>
<p>The invocation of:</p>
<pre>
Class->method(@args)</pre>
<p>attempts to invoke subroutine <code>Class::method</code> as:</p>
<pre>
<span class="variable">Class::method</span><span class="operator">(</span><span class="string">"Class"</span><span class="operator">,</span> <span class="variable">@args</span><span class="operator">);</span>
</pre>
<p>(If the subroutine can't be found, "inheritance" kicks in, but we'll
get to that later.) This means that we get the class name as the
first parameter (the only parameter, if no arguments are given). So
we can rewrite the <code>Sheep</code> speaking subroutine as:</p>
<pre>
<span class="keyword">sub</span><span class="variable"> Sheep::speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes baaaah!\n"</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>And the other two animals come out similarly:</p>
<pre>
<span class="keyword">sub</span><span class="variable"> Cow::speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes moooo!\n"</span><span class="operator">;</span>
<span class="operator">}</span>
<span class="keyword">sub</span><span class="variable"> Horse::speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes neigh!\n"</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>In each case, <code>$class</code> will get the value appropriate for that
subroutine. But once again, we have a lot of similar structure. Can
we factor that out even further? Yes, by calling another method in
the same class.</p>
<p>
</p>
<h2><a name="calling_a_second_method_to_simplify_things">Calling a second method to simplify things</a></h2>
<p>Let's call out from <code>speak</code> to a helper method called <code>sound</code>.
This method provides the constant text for the sound itself.</p>
<pre>
<span class="operator">{</span> <span class="keyword">package</span> <span class="variable">Cow</span><span class="operator">;</span>
<span class="keyword">sub</span><span class="variable"> sound </span><span class="operator">{</span> <span class="string">"moooo"</span> <span class="operator">}</span>
<span class="keyword">sub</span><span class="variable"> speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes "</span><span class="operator">,</span> <span class="variable">$class</span><span class="operator">-></span><span class="variable">sound</span><span class="operator">,</span> <span class="string">"!\n"</span>
<span class="operator">}</span>
<span class="operator">}</span>
</pre>
<p>Now, when we call <code>Cow->speak</code>, we get a <code>$class</code> of <code>Cow</code> in
<code>speak</code>. This in turn selects the <code>Cow->sound</code> method, which
returns <code>moooo</code>. But how different would this be for the <code>Horse</code>?</p>
<pre>
<span class="operator">{</span> <span class="keyword">package</span> <span class="variable">Horse</span><span class="operator">;</span>
<span class="keyword">sub</span><span class="variable"> sound </span><span class="operator">{</span> <span class="string">"neigh"</span> <span class="operator">}</span>
<span class="keyword">sub</span><span class="variable"> speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes "</span><span class="operator">,</span> <span class="variable">$class</span><span class="operator">-></span><span class="variable">sound</span><span class="operator">,</span> <span class="string">"!\n"</span>
<span class="operator">}</span>
<span class="operator">}</span>
</pre>
<p>Only the name of the package and the specific sound change. So can we
somehow share the definition for <code>speak</code> between the Cow and the
Horse? Yes, with inheritance!</p>
<p>
</p>
<h2><a name="inheriting_the_windpipes">Inheriting the windpipes</a></h2>
<p>We'll define a common subroutine package called <code>Animal</code>, with the
definition for <code>speak</code>:</p>
<pre>
<span class="operator">{</span> <span class="keyword">package</span> <span class="variable">Animal</span><span class="operator">;</span>
<span class="keyword">sub</span><span class="variable"> speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes "</span><span class="operator">,</span> <span class="variable">$class</span><span class="operator">-></span><span class="variable">sound</span><span class="operator">,</span> <span class="string">"!\n"</span>
<span class="operator">}</span>
<span class="operator">}</span>
</pre>
<p>Then, for each animal, we say it "inherits" from <code>Animal</code>, along
with the animal-specific sound:</p>
<pre>
<span class="operator">{</span> <span class="keyword">package</span> <span class="variable">Cow</span><span class="operator">;</span>
<span class="variable">@ISA</span> <span class="operator">=</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
<span class="keyword">sub</span><span class="variable"> sound </span><span class="operator">{</span> <span class="string">"moooo"</span> <span class="operator">}</span>
<span class="operator">}</span>
</pre>
<p>Note the added <code>@ISA</code> array. We'll get to that in a minute.</p>
<p>But what happens when we invoke <code>Cow->speak</code> now?</p>
<p>First, Perl constructs the argument list. In this case, it's just
<code>Cow</code>. Then Perl looks for <code>Cow::speak</code>. But that's not there, so
Perl checks for the inheritance array <code>@Cow::ISA</code>. It's there,
and contains the single name <code>Animal</code>.</p>
<p>Perl next checks for <code>speak</code> inside <code>Animal</code> instead, as in
<code>Animal::speak</code>. And that's found, so Perl invokes that subroutine
with the already frozen argument list.</p>
<p>Inside the <code>Animal::speak</code> subroutine, <code>$class</code> becomes <code>Cow</code> (the
first argument). So when we get to the step of invoking
<code>$class->sound</code>, it'll be looking for <code>Cow->sound</code>, which
gets it on the first try without looking at <code>@ISA</code>. Success!</p>
<p>
</p>
<h2><a name="a_few_notes_about__isa">A few notes about @ISA</a></h2>
<p>This magical <code>@ISA</code> variable (pronounced "is a" not "ice-uh"), has
declared that <code>Cow</code> "is a" <code>Animal</code>. Note that it's an array,
not a simple single value, because on rare occasions, it makes sense
to have more than one parent class searched for the missing methods.</p>
<p>If <code>Animal</code> also had an <code>@ISA</code>, then we'd check there too. The
search is recursive, depth-first, left-to-right in each <code>@ISA</code>.
Typically, each <code>@ISA</code> has only one element (multiple elements means
multiple inheritance and multiple headaches), so we get a nice tree of
inheritance.</p>
<p>When we turn on <code>use strict</code>, we'll get complaints on <code>@ISA</code>, since
it's not a variable containing an explicit package name, nor is it a
lexical ("my") variable. We can't make it a lexical variable though
(it has to belong to the package to be found by the inheritance mechanism),
so there's a couple of straightforward ways to handle that.</p>
<p>The easiest is to just spell the package name out:</p>
<pre>
<span class="variable">@Cow::ISA</span> <span class="operator">=</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
</pre>
<p>Or allow it as an implicitly named package variable:</p>
<pre>
<span class="keyword">package</span> <span class="variable">Cow</span><span class="operator">;</span>
<span class="keyword">use</span> <span class="variable">vars</span> <span class="string">qw(@ISA)</span><span class="operator">;</span>
<span class="variable">@ISA</span> <span class="operator">=</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
</pre>
<p>If you're bringing in the class from outside, via an object-oriented
module, you change:</p>
<pre>
<span class="keyword">package</span> <span class="variable">Cow</span><span class="operator">;</span>
<span class="keyword">use</span> <span class="variable">Animal</span><span class="operator">;</span>
<span class="keyword">use</span> <span class="variable">vars</span> <span class="string">qw(@ISA)</span><span class="operator">;</span>
<span class="variable">@ISA</span> <span class="operator">=</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
</pre>
<p>into just:</p>
<pre>
<span class="keyword">package</span> <span class="variable">Cow</span><span class="operator">;</span>
<span class="keyword">use</span> <span class="variable">base</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
</pre>
<p>And that's pretty darn compact.</p>
<p>
</p>
<h2><a name="overriding_the_methods">Overriding the methods</a></h2>
<p>Let's add a mouse, which can barely be heard:</p>
<pre>
<span class="comment"># Animal package from before</span>
<span class="operator">{</span> <span class="keyword">package</span> <span class="variable">Mouse</span><span class="operator">;</span>
<span class="variable">@ISA</span> <span class="operator">=</span> <span class="string">qw(Animal)</span><span class="operator">;</span>
<span class="keyword">sub</span><span class="variable"> sound </span><span class="operator">{</span> <span class="string">"squeak"</span> <span class="operator">}</span>
<span class="keyword">sub</span><span class="variable"> speak </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="variable">$class</span> <span class="operator">=</span> <span class="keyword">shift</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"a $class goes "</span><span class="operator">,</span> <span class="variable">$class</span><span class="operator">-></span><span class="variable">sound</span><span class="operator">,</span> <span class="string">"!\n"</span><span class="operator">;</span>
<span class="keyword">print</span> <span class="string">"[but you can barely hear it!]\n"</span><span class="operator">;</span>
<span class="operator">}</span>
<span class="operator">}</span>
</pre>
<pre>
<span class="variable">Mouse</span><span class="operator">-></span><span class="variable">speak</span><span class="operator">;</span>
</pre>
<p>which results in:</p>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?