📄 sect06.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIPython.rtf
Application Directory:c:\tools\rtf2html\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:12/31/2001
Translation Time:08:24:12
Translation Platform:Win32
Number of Output files:18
This File:Sect06.htm
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>X: Decorators: dynamic type selection</TITLE>
<script language="JavaScript">
</script>
</head>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
<!-- [ <a href="README.txt">Viewing Hints</a> ]
[ <a href="RevisionHistory.htm">Revision History</a> ] -->
[ <a href="http://www.mindview.net/Books/TIPython/">Book Home Page</a> ]
[ <a href="http://www.mindview.net/Etc/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/Seminars">Seminars</a> ]
[ <a href="http://www.mindview.net/CDs">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/Services">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
Thinking in Python<br>
<small>Revision 0.1.2 (12/31/01) -- Incomplete and Unfinished</small></FONT></H2>
<H3><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
by Bruce Eckel ©2002 MindView, Inc.</FONT></H3>
<FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
[ <a href="Sect05.htm">Previous Chapter</a> ]
[ <a href="javascript:window.location.href = 'Index.htm';">Table of Contents</a> ]
[ <a href="DocIdx.htm">Index</a> ]
[ <a href="Sect07.htm">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="_Toc476705902"></A><A NAME="_Toc534420097"></A><A NAME="Heading48"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H1 ALIGN="LEFT">
X: Decorators: dynamic type selection</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia" SIZE=4>The use of layered objects to
dynamically and transparently add responsibilities to individual objects is
referred to as the <I>decorator</I> pattern.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_202">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Used when subclassing creates too many
(& inflexible) classes
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_203">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All decorators that wrap around the
original object must have the same basic interface
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_204">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Dynamic proxy/surrogate?
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_205">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This accounts for the odd inheritance
structure
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_206">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Tradeoff: coding is more complicated when
using decorators
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_207">Add Comment</A></FONT><A NAME="_Toc534420098"></A><BR></P></DIV>
<A NAME="Heading49"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Basic decorator structure</H2></FONT>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIPyth02.gif"></FONT><A NAME="_Toc534420099"></A><BR></P></DIV>
<A NAME="Heading50"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
A coffee example</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider going down to the local coffee
shop, <I>BeanMeUp</I>, for a coffee. There are typically many different drinks
on offer -- espressos, lattes, teas, iced coffees, hot chocolate to name a few,
as well as a number of extras (which cost extra too) such as whipped cream or an
extra shot of espresso. You can also make certain changes to your drink at no
extra cost, such as asking for decaf coffee instead of regular coffee.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_208">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Quite clearly if we are going to model
all these drinks and combinations, there will be sizeable class diagrams. So for
clarity we will only consider a subset of the coffees: Espresso, Espresso Con
Panna, Café Late, Cappuccino and Café Mocha. We'll include 2
extras - whipped cream ("whipped") and an extra shot of espresso; and three
changes - decaf, steamed milk ("wet") and foamed milk ("dry").
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_209">Add Comment</A></FONT><A NAME="_Toc534420100"></A><BR></P></DIV>
<A NAME="Heading51"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Class for each combination </H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One solution is to create an individual
class for every combination. Each class describes the drink and is responsible
for the cost etc. The resulting menu is huge, and a part of the class diagram
would look something like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_210">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia">
<IMG SRC="TIPyth03.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here is one of the combinations, a simple
implementation of a Cappuccino:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_211">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> Cappuccino:
<font color=#0000ff>def</font> __init__(self):
self.cost = 1
self.description = <font color=#004488>"Cappucino"</font>
<font color=#0000ff>def</font> getCost(self):
<font color=#0000ff>return</font> self.cost
<font color=#0000ff>def</font> getDescription(self):
<font color=#0000ff>return</font> self.description
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The key to using this method is to
find the particular combination you want. So, once you've found the drink you
would like, here is how you would use it, as shown in the CoffeeShop class in
the following code:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_212">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#: cX:decorator:nodecorators:CoffeeShop.py
# Coffee example with no decorators
<font color=#0000ff>class</font> Espresso: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> DoubleEspresso: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> EspressoConPanna: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Cappuccino:
<font color=#0000ff>def</font> __init__(self):
self.cost = 1
self.description = <font color=#004488>"Cappucino"</font>
<font color=#0000ff>def</font> getCost(self):
<font color=#0000ff>return</font> self.cost
<font color=#0000ff>def</font> getDescription(self):
<font color=#0000ff>return</font> self.description
<font color=#0000ff>class</font> CappuccinoDecaf: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoDecafWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoDry: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoDryWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoExtraEspresso: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoExtraEspressoWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CappuccinoWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMocha: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaDecaf: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaDecafWhipped:
<font color=#0000ff>def</font> __init__(self):
self.cost = 1.25
self.description = \
<font color=#004488>"Cafe Mocha decaf whipped cream"</font>
<font color=#0000ff>def</font> getCost(self):
<font color=#0000ff>return</font> self.cost
<font color=#0000ff>def</font> getDescription(self):
<font color=#0000ff>return</font> self.description
<font color=#0000ff>class</font> CafeMochaExtraEspresso: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaExtraEspressoWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaWet: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaWetWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeMochaWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatte: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteDecaf: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteDecafWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteExtraEspresso: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteExtraEspressoWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteWet: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteWetWhipped: <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> CafeLatteWhipped: <font color=#0000ff>pass</font>
cappuccino = Cappuccino()
<font color=#0000ff>print</font> (cappuccino.getDescription() + <font color=#004488>": $"</font> +
´cappuccino.getCost()´)
cafeMocha = CafeMochaDecafWhipped()
<font color=#0000ff>print</font> (cafeMocha.getDescription()
+ <font color=#004488>": $"</font> + ´cafeMocha.getCost()´)
#:~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">And here is the corresponding
output:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_213">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Cappucino: $1.0Cafe Mocha decaf whipped cream: $1.25</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You
can see that creating the particular combination you want is easy, since you are
just creating an instance of a class. However, there are a number of problems
with this approach. Firstly, the combinations are fixed statically so that any
combination a customer may wish to order needs to be created up front. Secondly,
the resulting menu is so huge that finding your particular combination is
difficult and time consuming.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_214">Add Comment</A></FONT><A NAME="_Toc534420101"></A><BR></P></DIV>
<A NAME="Heading52"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
The decorator approach</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Another approach would be to break the
drinks down into the various components such as espresso and foamed milk, and
then let the customer combine the components to describe a particular coffee.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_215">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In order to do this programmatically, we
use the Decorator pattern. A Decorator adds responsibility to a component by
wrapping it, but the Decorator conforms to the interface of the component it
encloses, so the wrapping is transparent. Decorators can also be nested without
the loss of this transparency.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_216">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIPyth04.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Methods invoked on the Decorator can in
turn invoke methods in the component, and can of course perform processing
before or after the invocation.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_217">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">So if we added <B>getTotalCost()</B> and
<B>getDescription()</B> methods to the <B>DrinkComponent</B> interface, an
Espresso looks like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_218">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> Espresso(Decorator):
cost = 0.75f
description = <font color=#004488>" espresso"</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -