Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







Recipe 6.14. Implementing the State Design Pattern


Credit: Elmar Bschorer


Problem


An object
in your program can switch among several
"states", and the
object's behavior must change along with the
object's state.


Solution


The key idea of the State Design Pattern is to objectify the
"state" (with its several
behaviors) into a class instance (with its several methods). In
Python, you don't have to build an abstract class to
represent the interface that is common to the various states: just
write the classes for the "state"s
themselves. For example:

class TraceNormal(object):
' state for normal level of verbosity '
def startMessage(self):
self.nstr = self.characters = 0
def emitString(self, s):
self.nstr += 1
self.characters += len(s)
def endMessage(self):
print '%d characters in %d strings' % (self.characters, self.nstr)
class TraceChatty(object):
' state for high level of verbosity '
def startMessage(self):
self.msg = [ ]
def emitString(self, s):
self.msg.append(repr(s))
def endMessage(self):
print 'Message: ', ', '.join(self.msg)
class TraceQuiet(object):
' state for zero level of verbosity '
def startMessage(self): pass
def emitString(self, s): pass
def endMessage(self): pass
class Tracer(object):
def _ _init_ _(self, state): self.state = state
def setState(self, state): self.state = state
def emitStrings(self, strings):
self.state.startMessage( )
for s in strings: self.state.emitString(s)
self.state.endMessage( )
if _ _name_ _ == '_ _main_ _':
t = Tracer(TraceNormal( ))
t.emitStrings('some example strings here'.split( ))
# emits: 21 characters in 4 strings
t.setState(TraceQuiet( ))
t.emitStrings('some example strings here'.split( ))
# emits nothing
t.setState(TraceChatty( ))
t.emitStrings('some example strings here'.split( ))
# emits: Message: 'some', 'example', 'strings', 'here'


Discussion


With the State Design Pattern, you can "factor
out" a number of related behaviors of an object (and
possibly some data connected with these behaviors) into an auxiliary
state object, to which the main object delegates these behaviors as
needed, through calls to methods of the
"state" object. In Python terms,
this design pattern is related to the idioms of rebinding an
object's whole _ _class_ _, as
shown in Recipe 6.11, and
rebinding just certain methods (shown in Recipe 2.14). This design pattern, in a
sense, lies in between those Python idioms: you group a set of
related behaviors, rather than switching either all behavior, by
changing the object's whole _ _class_
_
, or each method on its own, without grouping. With
relation to the classic design pattern terminology, this recipe
presents a pattern that falls somewhere between the classic State
Design Pattern and the classic Strategy Design
Pattern.

This State Design Pattern has some extra oomph, compared to the
related Pythonic idioms, because an appropriate amount of data can
live together with the behaviors you're
delegatingexactly as much, or as little, as needed to support
each specific behavior. In the examples given in this
recipe's Solution, for example, the different state
objects differ greatly in the kind and amount of data they need: none
at all for class TraceQuiet, just a couple of
numbers for TraceNormal, a whole list of strings for
TraceChatty. These responsibilities are usefully
delegated from the main object to each specific
"state object".

In some cases, although not in the specific examples shown in this
recipe, state objects may need to cooperate more closely with the
main object, by calling main object methods or accessing main object
attributes in certain circumstances. To allow this, the main object
can pass as an argument either self or some bound
method of self to methods of the
"state" objects. For example,
suppose that the functionality in this recipe's
Solution needs to be extended, in that the main object must keep
track of how many lines have been emitted by messages it has sent.
Tracer._ _init_ _ will have to add one per-instance
initialization self.lines = 0, and the signature
of the "state"
object's endMessage methods will
have to be extended to def
endMessage(self, tracer):. The implementation of
endMessage in class TraceQuiet will
just ignore the tracer argument, since it
doesn't actually emit any lines; the implementations
in the other two classes will each add a statement
tracer.lines += 1, since each
of them emits one line per message.

As you see, the kind of closer coupling implied by this kind of extra
functionality need not be particularly problematic. In particular,
the key feature of the classic State Design Pattern, that state
objects are the ones that handle state switching (while, in the
Strategy Design Pattern, the switching comes from the outside), is
just not enough of a big deal in Python to warrant considering the
two design patterns as separate.


See Also


See http://exciton.cs.rice.edu/JavaResources/DesignPatterns/
for good coverage of the classic design patterns, albeit in a Java
context.


/ 394