注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

秋风扫落叶

 
 
 

日志

 
 
 
 

[经验之谈]Python隐藏实现细节 -pythoner.net  

2013-04-30 09:12:26|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

代理模式(Proxy)和状态模式(State)都提供一个代理类,用来隐藏真正的实现过程。在调用代理类时,他简单的将请求转发给实际类去处理,并返回其结果。这两种设计模式比较相似,代理模式可以看做是一种特殊的状态模式,他只有一个状态。一个观点是将这两者合并,称之为代理模式(Surrogate)。但Proxy这个词汇使用的太久了,概念被固化了。因此,这两个模式并没有被合并。

最开始的想法很简单,通过代理类,以实际类做支持,来完成实际的实现功能。

代理对象被建立后,他可以提供一个应用接口,将所有的调用转发给实际类。

在结构上,代理模式和状态模式很容易区分:代理模式只有一个应用,状态模式则有多个。《设计模式》一书的看法不同:代理模式用来控制对类的访问权限,状态模式这用来在运行过程中动态的改变类。

代理

我们来为上图的代理模式写些代码:

#: c04:ProxyDemo.py 
# Simple demonstration of the Proxy pattern.  

class Implementation: 
  def f(self): 
    print "Implementation.f()" 
  def g(self): 
    print "Implementation.g()" 
  def h(self): 
    print "Implementation.h()"  

class Proxy: 
  def __init__(self): 
    self.__implementation = Implementation() 
  # Pass method calls to the implementation: 
  def f(self): self.__implementation.f() 
  def g(self): self.__implementation.g() 
  def h(self): self.__implementation.h()  

p = Proxy() 
p.f(); p.g(); span>p.h() 
#:~

Proxy类的接口并不需要和Implementation类的接口保持完全一致,代理可以以任何方式来“代言”实际类。一般来讲,将代理类和实际类保持一致的接口会更为通用和方便,也能够保证实际类完全支持代理类的所有调用。

当然,在python中,我们可以使用内置的代理机制。简化后的代码像这样:

#: c04:ProxyDemo2.py 
# Simple demonstration of the Proxy pattern.  

class Implementation2: 
  def f(self): 
    print "Implementation.f()" 
  def g(self): 
    print "Implementation.g()" 
  def h(self): 
    print "Implementation.h()"  

class Proxy2: 
  def __init__(self): 
    self.__implementation = Implementation2() 
  def __getattr__(self, name): 
    return getattr(self.__implementation, name) 

p = Proxy2() 
p.f(); p.g(); p.h();
#:~

Proxy2使用__getattr__( )方法,这看起来更为优雅,他不依赖于任何的实施细节。

状态模式

和代理模式相比,状态模式中存在多个实现类,并提供了在实现类中动态切换的接口。

#: c04:StateDemo.py 
# Simple demonstration of the State pattern.  

class State_d: 
  def __init__(self, imp): 
    self.__implementation = imp 
  def changeImp(self, newImp): 
    self.__implementation = newImp 
  # Delegate calls to the implementation: 
  def __getattr__(self, span>name): 
    return getattr(self.__implementation, name) 

class Implementation1: 
  def f(self): 
    print "Fiddle de dum, Fiddle de dee," 
  def g(self): 
    print "Eric the half a bee." 
  def h(self): 
    print "Ho ho ho, tee hee hee,"  

class Implementation2: 
  def f(self): 
    print "We're Knights of the Round Table." 
  def g(self): 
    print "We dance whene'er we're able." 
  def h(self): 
    print "We do routines and chorus scenes"  

def run(b): 
  b.f() 
  b.g() 
  b.h() 
  b.g() 

b = State_d(Implementation1()) 
run(b) 
b.changeImp(Implementation2()) 
run(b) 
#:~

在程序中,我们先构造了一个实现类,并将其封装到State_d。运行一段时间后,程序又被切换给另一个实现类。

代理模式和状态模式有不同的使用场景。《设计模式》描述了代理模式的主要场景:

1. 远程代理
2. 虚拟代理,提供“惰性类”
3. 保护代理 用于限制客户端对被代理对象的完全访问。
4. 转发 建立代理时,增加一些额外的操作,用来对调用进行跟踪。

在pyhton引用中,使用了不少的保护代理,用来阻止对真实对象的访问。比如,你无法建立使用一个对NULL的引用。

状态机模式

状态模式允许客户端程序在运行过程中修改其实现过程,我们可以在实现过程中,将一个实施自动的转变为另一个实施,这样的实现,我们称之为状态机模式。当前的运行状态代表系统当前的状态,而状态可以由系统自动的切换。最基本的状态机模式是对象状态。

切换系统状态一般可以使用上述模板方法中所介绍的代码。

每一个状态都使用run()接口实现其具体行为,或者你也可以传入一个input参数,以便告诉系统,你应该切换的下一个状态是什么。这两种设计的主要不同点是由谁来决定下一个状态是什么。我们可以传入一个input参数,标示下一个状态,或者,在状态类中定义一个表(或者定一个全局表),定义当前状态的下一个状态是什么。

#: c04:statemachine:State.py 
# A State has an operation, and can be moved 
# into the next State given an Input: 
class State: 
	def run(self): 
		assert 1, "run not implemented" 
	def next(self, input): 
		assert 1, "next not implemented" 
#:~

这个类并不是很完整,但他足以说明状态类的代码结构,如果其子类没有重构其所有方法时,他也可以给出一个简短的错误提示。更简单,我们可以直接定义为:

class State: pass

因为如果run或next没有被实现,系统同样也会抛出异常。

状态机保存了状态的轨迹,这些轨迹一般定义在状态机的构造函数中。runAll方法将对所有的input对象实施状态操作及变更。run方法则只执行其特定的对象的状态操作及变更。

#: c04:statemachine:StateMachine.py 
# Takes a list of Inputs to move from State to 
# State using a template method. 
class StateMachine: 
	def __init__(self, initialState): 
		self.currentState = initialState 
		self.currentState.run() 
	# Template method: 
	def runAll(self, inputs): 
		for i in inputs: 
			print i 
			self.currentState = self.currentState.next(i) 
			self.currentState.run() 
#:~

我将runAll方法也写作成了模板方法同样的形式,尽管这并不是必须的。

目前,状态机模式的基本框架已经完成了。下面我们将使用捕鼠器做为示例来惊醒一个演示(放心,没有哪只老鼠会收到伤害)。我们可以操作捕鼠器的各个状态,老鼠类及其星系将放到mouse这个包中,主要包括老鼠的移动行为,他将作为状态机的输入参数。

#: c04:mouse:MouseAction.py 
class MouseAction: 
	def __init__(self, action): 
		self.action = action 
	def __str__(self): return self.action 

	def __cmp__(self, other): 
		return cmp(self.action, other.action) 
	# Necessary when __cmp__ or __eq__ is defined 
	# in order to make this class usable as a 
	# dictionary key: 
	def __hash__(self): 
		return hash(self.action) 

# Static fields; an enumeration of instances: 
MouseAction.appears = MouseAction("mouse appears") 
MouseAction.runsAway = MouseAction("mouse runs away") 
MouseAction.enters = MouseAction("mouse enters trap") 
MouseAction.escapes = MouseAction("mouse escapes") 
MouseAction.trapped = MouseAction("mouse trapped") 
MouseAction.removed = MouseAction("mouse removed") 
#:~

我们注意到__cmp__( )被重构了,用来比较两个action对象的值。每一种移动行为都被定义在MouseAction对象中,成为该对象的一个属性。

为此,我们再建立一个测试用例,创建一个老鼠的行为队列:

#:! c04:mouse:MouseMoves.txt 
mouse appears 
mouse runs away 
mouse appears 
mouse enters trap 
mouse escapes 
mouse appears 
mouse enters trap 
mouse trapped 
mouse removed 
mouse appears 
mouse runs away 
mouse appears 
mouse enters trap 
mouse trapped 
mouse removed 
#:~

有了以上的这些基础,我们现在可以开始编写老鼠夹的代码了。每一个状态子类都定义了run的方法。也有if语句用来选择下一个状态是什么。

#: c04:mousetrap1:MouseTrapTest.py 
# State Machine pattern using 'if' statements 
# to determine the next state. 
import string, sys 
sys.path += ['../statemachine', '../mouse'] 
from State import State 
from StateMachine import StateMachine 
from MouseAction import MouseAction 
# A different subclass for each state:  

class Waiting(State): 
  def run(self): 
    print "Waiting: Broadcasting cheese smell" 

  def next(self, input): 
    if input == MouseAction.appears: 
      return MouseTrap.luring 
    return MouseTrap.waiting 

class Luring(State): 
  def run(self): 
    print "Luring: Presenting Cheese, door open" 

  def next(self, input): 
    if input == MouseAction.runsAway: 
      return MouseTrap.waiting 
    if input == MouseAction.enters: 
      return MouseTrap.trapping 
    return MouseTrap.luring 

class Trapping(State): 
  def run(self): 
    print "Trapping: Closing door" 

  def next(self, input): 
    if input == MouseAction.escapes: 
      return MouseTrap.waiting 
    if input == MouseAction.trapped: 
      return MouseTrap.holding 
    return MouseTrap.trapping 

class Holding(State): 
  def run(self): 
    print "Holding: Mouse caught" 
  def next(self, input): 
    if input == MouseAction.removed: 
      return MouseTrap.waiting 
    return MouseTrap.holding 

class MouseTrap(StateMachine): 
  def __init__(self): 
    # Initial state 
    StateMachine.__init__(self, MouseTrap.waiting) 

# Static variable initialization: 
MouseTrap.waiting = Waiting() 
MouseTrap.luring = Luring() 
MouseTrap.trapping = Trapping() 
MouseTrap.holding = Holding() 

moves = map(string.strip, 
  open("../mouse/MouseMoves.txt").readlines()) 
MouseTrap().runAll(map(MouseAction, moves)) 
#:~

状态机类的定义了所有可能的状态类型,并建立了初始状态。单元测试程序建立了一个老鼠夹,并运行了我们刚刚建立的老鼠行为队列。

在next()方法中使用if方法是可行的,但是在状态比较多的时候,使用if则会变得很难维护。一个更好的办法是在每个状态内部建立一个表,并基于传入参数进行自动判断。

这乍看起来很容易,我们可以在每一个子状态中建立一个静态表,定义其下一个状态元素。但是,这将造成各个状态类之间的循环依赖关系。为了解决这个问题,我们只能在调用next方法时,才惰性得再加载这个状态表。因为这个原因,next的写法看起来会有些怪。

我们重新定义了上述的State类,并将其命名围StateT,他增加了一个Map并将其初始化为一个二位数组。next方法定义了一个基本实现逻辑,StateT子类的next也必须调用该方法。

#: c04:mousetrap2:MouseTrap2Test.py 
# A better mousetrap using tables 
import string, sys 
sys.path += ['../statemachine', '../mouse'] 
from State import State 
from StateMachine import StateMachine 
from MouseAction import MouseAction 

class StateT(State): 
  def __init__(self): 
    self.transitions = None 
  def next(self, input): 
    if self.transitions.has_key(input): 
      return self.transitions[input] 
    else: 
      raise "Input not supported for current state" 

class Waiting(StateT): 
  def run(self): 
    print "Waiting: Broadcasting cheese smell" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.appears : MouseTrap.luring 
      } 
    return StateT.next(self, input) 

class Luring(StateT): 
  def run(self): 
    print "Luring: Presenting Cheese, door open" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.enters : MouseTrap.trapping, 
        MouseAction.runsAway : MouseTrap.waiting 
      } 
    return StateT.next(self, input) 

class Trapping(StateT): 
  def run(self):     print "Trapping: Closing door" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.escapes : MouseTrap.waiting, 
        MouseAction.trapped : MouseTrap.holding 
      } 
    return StateT.next(self, input) 

class Holding(StateT): 
  def run(self): 
    print "Holding: Mouse caught" 
  def next(self, input): 
    # Lazy initialization: 
    if not self.transitions: 
      self.transitions = { 
        MouseAction.removed : MouseTrap.waiting 
      } 
    return StateT.next(self, input) 

class MouseTrap(StateMachine): 
  def __init__(self): 
    # Initial state 
    StateMachine.__init__(self, MouseTrap.waiting) 

# Static variable initialization: 
MouseTrap.waiting = Waiting() 
MouseTrap.luring = Luring() 
MouseTrap.trapping = Trapping() 
MouseTrap.holding = Holding() 

moves = map(string.strip, 
  open("../mouse/MouseMoves.txt").readlines()) 
mouseMoves = map(MouseAction, moves) 
MouseTrap().runAll(mouseMoves) 
#:~

其他的代码也是一样的,不同的只在于StateT的类名和next方法的具体实现上。

当你不得不建立或维护大量的状态时,使用这样的方式无疑是必要的。通过查看状态表,将很容易阅读并理解这些状态。

  评论这张
 
阅读(1235)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017