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

📄 ch12_07.htm

📁 编程珍珠,里面很多好用的代码,大家可以参考学习呵呵,
💻 HTM
📖 第 1 页 / 共 3 页
字号:
So far, these techniques for managing instance data have offered nomechanism for "protection" from external access.  Anyone outside theclass can open up the object's black box and poke about inside--if theydon't mind voiding the warranty.  Enforced privacy tends to get in theway of people trying to get their jobs done.  Perl's philosophy is thatit's better to encapsulate one's data with a sign that says:<blockquote><pre class="programlisting">IN CASE OF FIRE  BREAK GLASS</pre></blockquote>You should respect such encapsulation when possible, but still haveeasy access to the contents in an emergency situation, like fordebugging.</p><p>But if you do want to enforce privacy, Perl isn't about to get in yourway.  Perl offers low-level building blocks that you can use to surroundyour class and its objects with an impenetrable privacy shield--onestronger, in fact, than that found in many popular object-orientedlanguages.  Lexical scopes and the lexical variables inside them arethe key components here, and closures play a pivotal role.</p><p><a name="INDEX-2560"></a>In the section <a href="ch12_05.htm#ch12-sect-pm">Section 12.5.5, "Private Methods"</a> we saw how a class can useclosures to implement methods that are invisible outside the modulefile.  Later we'll look at accessor methods that regulate classdata so private that not even the rest of the class has unrestrictedaccess.  Those are still fairly traditional uses of closures.  Thetruly interesting approach is to use a closure as the very objectitself.  The object's instance variables are locked up inside ascope to which the object alone--that is, the closure--has freeaccess.  This is a very strong form of encapsulation; not only isit proof against external tampering, even other methods in the sameclass must use the proper access methods to get at the object'sinstance data.</p><p>Here's an example of how this might work.  We'll use closuresboth for the objects themselves and for the generated accessors:<blockquote><pre class="programlisting">package Person;sub new {    my $invocant  = shift;    my $class = ref($invocant) || $invocant;    my $data = {       NAME     =&gt; "unnamed",       RACE     =&gt; "unknown",       ALIASES  =&gt; [],    };    my $self = sub {       my $field = shift;       #############################       ### ACCESS CHECKS GO HERE ###       #############################       if (@_) { $data-&gt;{$field} = shift }       return    $data-&gt;{$field};    };    bless($self, $class);    return $self;}# generate method namesfor my $field (qw(name race aliases)) {    no strict "refs";  # for access to the symbol table    *$field = sub {        my $self = shift;        return $self-&gt;(uc $field, @_);    };}</pre></blockquote>The object created and returned by the <tt class="literal">new</tt> methodis no longer a hash, as it was in other constructors we've looked at.It's a closure with unique access to the attribute data stored in thehash referred to by <tt class="literal">$data</tt>.  Once the constructorcall is finished, the only access to <tt class="literal">$data</tt> (andhence to the attributes) is via the closure.</p><p>In a call like <tt class="literal">$him-&gt;name("Bombadil")</tt>,the invoking object stored in <tt class="literal">$self</tt> is the closurethat was blessed and returned by the constructor.  There's not a lotone can do with a closure beyond calling it, so we do just that with<tt class="literal">$self-&gt;(uc $field, @_)</tt>.  Don't be fooled by thearrow; this is just a regular indirect function call, not a methodinvocation.  The initial argument is the string"<tt class="literal">name</tt>", and any remaining arguments are whateverelse was passed in.<a href="#FOOTNOTE-7">[7]</a> Once we're executing inside theclosure, the hash reference inside <tt class="literal">$data</tt> is againaccessible.  The closure is then free to permit or deny access towhatever it pleases.</p><blockquote class="footnote"><a name="FOOTNOTE-7"></a><p>[7] Sure, the double-function call isslow, but if you wanted fast, would you really be using objects in thefirst place?</p></blockquote><p><a name="INDEX-2561"></a><a name="INDEX-2562"></a>No one outside the closure object has unmediated access to this veryprivate instance data, not even other methods in the class.  They couldtry to call the closure the way the methods generated by the <tt class="literal">for</tt>loop do, perhaps setting an instance variable the class never heardof.  But this approach is easily blocked by inserting various bits of code in the constructor where you see the comment about access checks.  First, weneed a common preamble:<blockquote><pre class="programlisting">use Carp;local $Carp::CarpLevel = 1;  # Keeps croak messages shortmy ($cpack, $cfile) = caller();</pre></blockquote>Now for each of the checks.  The first one makes sure the specifiedattribute name exists:<blockquote><pre class="programlisting">croak "No valid field '$field' in object"    unless exists $data-&gt;{$field};</pre></blockquote>This one allows access only by callers from the same file:<blockquote><pre class="programlisting">carp "Unmediated access denied to foreign file"    unless $cfile eq __FILE__;</pre></blockquote>This one allows access only by callers from the same package:<blockquote><pre class="programlisting">carp "Unmediated access denied to foreign package ${cpack}::"    unless $cpack eq __PACKAGE__;</pre></blockquote>And this one allows access only by callers whose classes inherit ours:<blockquote><pre class="programlisting">carp "Unmediated access denied to unfriendly class ${cpack}::"    unless $cpack-&gt;isa(__PACKAGE__);</pre></blockquote>All these checks block unmediated access only.  Users of the class whopolitely use the class's designated methods are under no suchrestriction.  Perl gives you the tools to be just as persnickety as youwant to be.  Fortunately, not many people want to be.</p><p><a name="INDEX-2563"></a><a name="INDEX-2564"></a><a name="INDEX-2565"></a><a name="INDEX-2566"></a>But some people ought to be.  Persnickety is good when you're writingflight control software.  If you either want or ought to be one ofthose people, and you prefer using working code over reinventingeverything on your own, check out Damian Conway's<tt class="literal">Tie::SecureHash</tt> module on CPAN.  It implementsrestricted hashes with support for public, protected, and privatepersnicketations.  It also copes with the inheritance issues thatwe've ignored in the previous example.  Damian has also written aneven more ambitious module, <tt class="literal">Class::Contract</tt>, thatimposes a formal software engineering regimen over Perl's flexibleobject system.  This module's feature list reads like a checklist froma computer science professor's software engineeringtextbook,<a href="#FOOTNOTE-8">[8]</a>including enforced encapsulation, staticinheritance, anddesign-by-contract condition checking for object-oriented Perl, alongwith a declarative syntax for attribute, method, constructor, anddestructor definitions at both the object and class level, andpreconditions, postconditions, and class invariants.  Whew!</p><blockquote class="footnote"><a name="FOOTNOTE-8"></a><p>[8] Can you guess what Damian's job is?  By theway, we highly recommend his book, <em class="emphasis">Object OrientedPerl</em> (Manning Publications,1999).</p></blockquote><a name="INDEX-2567"></a><a name="INDEX-2568"></a><a name="INDEX-2569"></a><a name="INDEX-2570"></a><h3 class="sect2">12.7.6. New Tricks</h3><p><a name="INDEX-2571"></a><a name="INDEX-2572"></a><a name="INDEX-2573"></a><a name="INDEX-2574"></a><a name="INDEX-2575"></a><a name="INDEX-2576"></a><a name="INDEX-2577"></a>As of release 5.6 of Perl, you can also declare a method to indicatethat it returns an lvalue.  This is done with the lvalue subroutine attribute (not to be confused with object attributes).This experimental feature allows you to treat the method as somethingthat would appear on the lefthand side of an equal sign:<blockquote><pre class="programlisting">package Critter;sub new {    my $class = shift;    my $self = { pups =&gt; 0, @_ };    # Override default.    bless $self, $class;}sub pups : lvalue {                  # We'll assign to pups() later.    my $self = shift;    $self-&gt;{pups};}package main;$varmint = Critter-&gt;new(pups =&gt; 4);$varmint-&gt;pups *= 2;                 # Assign to $varmint-&gt;pups!$varmint-&gt;pups =~ s/(.)/$1$1/;       # Modify $varmint-&gt;pups in place!print $varmint-&gt;pups;                # Now we have 88 pups.</pre></blockquote>This lets you pretend <tt class="literal">$varmint-&gt;pups</tt> is a variable whilestill obeying encapsulation.  See the section <a href="ch06_05.htm#ch06-sect-lvalue">Section 12.5.2, "The lvalue Attribute"</a> in<a href="ch06_01.htm">Chapter 6, "Subroutines"</a>.</p><p><a name="INDEX-2578"></a><a name="INDEX-2579"></a><a name="INDEX-2580"></a><a name="INDEX-2581"></a>If you're running a threaded version of Perl and want to ensure that only onethread can call a particular method on an object, you can use the<tt class="literal">locked</tt> and <tt class="literal">method</tt> attributes to do that:<blockquote><pre class="programlisting">sub pups : locked method {    ...}</pre></blockquote>When any thread invokes the <tt class="literal">pups</tt> method on anobject, Perl locks the object before execution, preventing otherthreads from doing the same.  See the section <a href="ch06_05.htm#ch06-sect-locked">Section 12.5.1, "The locked and method Attributes"</a> in <a href="ch06_01.htm">Chapter 6, "Subroutines"</a>.</p><a name="INDEX-2582"></a><a name="INDEX-2583"></a><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch12_06.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch12_08.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">12.6. Instance Destructors</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">12.8. Managing Class Data</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright &copy; 2001</a> O'Reilly &amp; Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>

⌨️ 快捷键说明

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