Python设计模式编程中的备忘录模式与对象池模式示例

 更新时间:2016年02月02日 16:34:41   作者:dongweiming  
这篇文章主要介绍了Python设计模式编程中的备忘录模式与对象池模式,文中分别举了表单和线程的相关示例,需要的朋友可以参考下

Memento备忘录模式
备忘录模式一个最好想象的例子:undo! 它对对象的一个状态进行了'快照', 在你需要的时候恢复原貌。做前端会有一个场景:你设计一个表单,当点击提交会对表单内容 验证,这个时候你就要对用户填写的数据复制下来,当用户填写的不正确或者格式不对等问题, 就可以使用快照数据恢复用户已经填好的,而不是让用户重新来一遍,不是嘛?

python的例子
这里实现了一个事务提交的例子

import copy

def Memento(obj, deep=False):

  # 对你要做快照的对象做快照
  state = (copy.copy if deep else copy.deepcopy)(obj.__dict__)
  def Restore():
    obj.__dict__ = state
  return Restore

class Transaction:

  deep = False
  def __init__(self, *targets):
    self.targets = targets
    self.Commit()
  # 模拟事务提交,其实就是初始化给每个对象往self.targets赋值
  def Commit(self):
    self.states = [Memento(target, self.deep) for target in self.targets]
  # 回滚其实就是调用Memento函数,执行其中的闭包,将__dict__恢复
  def Rollback(self):
    for state in self.states:
      state()

# 装饰器的方式给方法添加这个事务的功能
def transactional(method):
  # 这里的self其实就是要保存的那个对象,和类的实例无关
  def wrappedMethod(self, *args, **kwargs):
    state = Memento(self)
    try:
      return method(self, *args, **kwargs)
    except:
      # 和上面的回滚一样,异常就恢复
      state()
      raise
  return wrappedMethod

class NumObj(object):
  def __init__(self, value):
    self.value = value
  def __repr__(self):
    return '<%s: %r>' % (self.__class__.__name__, self.value)
  def Increment(self):
    self.value += 1

  @transactional
  def DoStuff(self):
    # 赋值成字符串,再自增长肯定会报错的
    self.value = '1111'
    self.Increment()

if __name__ == '__main__':

  n = NumObj(-1)
  print n
  t = Transaction(n)
  try:
    for i in range(3):
      n.Increment()
      print n
    # 这里事务提交会保存状态从第一次的-1到2
    t.Commit()
    print '-- commited'
    for i in range(3):
      n.Increment()
      print n
    n.value += 'x' # will fail
    print n
  except:
    # 回滚只会回顾到上一次comit成功的2 而不是-1
    t.Rollback()
    print '-- rolled back'
  print n
  print '-- now doing stuff ...'
  try:
    n.DoStuff()
  except:
    print '-> doing stuff failed!'
    import traceback
    traceback.print_exc(0)
    pass
  # 第二次的异常回滚n还是2, 整个过程都是修改NumObj的实例对象
  print n

注意
当你要保存的状态很大,可能会浪费大量内存


对象池模式
在开发中,我们总是用到一些和'池'相关的东西,比如 内存池,连接池,对象池,线程池.. 这里说的对象池其实也就是一定数量已经创建好的对象的集合。为什么要用对象池? 创建对象是要付出代价的(我暂时还没有研究过底层,只说我工作中体会的), 比如pymongo就自带线程池,这样用完就放回到池里再被重用,岂不是节省了创建的花费?

python的例子
我这里实现了个线程安全的简单的对象池

import Queue
import types
import threading
from contextlib import contextmanager

class ObjectPool(object):

  def __init__(self, fn_cls, *args, **kwargs):
    super(ObjectPool, self).__init__()
    self.fn_cls = fn_cls
    self._myinit(*args, **kwargs)

  def _myinit(self, *args, **kwargs):
    self.args = args
    self.maxSize = int(kwargs.get("maxSize",1))
    self.queue = Queue.Queue()
  def _get_obj(self):
    # 因为传进来的可能是函数,还可能是类
    if type(self.fn_cls) == types.FunctionType:
      return self.fn_cls(self.args)
    # 判断是经典或者新类
    elif type(self.fn_cls) == types.ClassType or type(self.fn_cls) == types.TypeType:
      return apply(self.fn_cls, self.args)
    else:
      raise "Wrong type"

  def borrow_obj(self):
    # 这个print 没用,只是在你执行的时候告诉你目前的队列数,让你发现对象池的作用
    print self.queue._qsize()
    # 要是对象池大小还没有超过设置的最大数,可以继续放进去新对象
    if self.queue.qsize()<self.maxSize and self.queue.empty():
      self.queue.put(self._get_obj())
    # 都会返回一个对象给相关去用
    return self.queue.get() 
  # 回收
  def recover_obj(self,obj):
    self.queue.put(obj)

# 测试用函数和类
def echo_func(num):
  return num

class echo_cls(object):
  pass

# 不用构造含有__enter__, __exit__的类就可以使用with,当然你可以直接把代码放到函数去用
@contextmanager
def poolobj(pool):
  obj = pool.borrow_obj()
  try:
    yield obj
  except Exception, e:
    yield None
  finally:
    pool.recover_obj(obj)

obj = ObjectPool(echo_func, 23, maxSize=4)
obj2 = ObjectPool(echo_cls, maxSize=4)

class MyThread(threading.Thread):

  def run(self):
    # 为了实现效果,我搞了个简单的多线程,2个with放在一个地方了,只为测试用
    with poolobj(obj) as t:
      print t
    with poolobj(obj2) as t:
      print t

if __name__ == '__main__':
  threads = []
  for i in range(200):
    t = MyThread()
    t.start()
    threads.append(t)
  for t in threads:
    t.join(True)

相关文章

  • C#中系统时间和UNIX时间戳互相转换

    C#中系统时间和UNIX时间戳互相转换

    本文主要介绍C#中系统时间和UNIX时间戳相互转换的方法,大家可以直接拿去用,希望有用。
    2016-05-05
  • C# BackgroundWorker使用教程

    C# BackgroundWorker使用教程

    这篇文章主要为大家详细介绍了C# BackgroundWorker使用教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • C#多线程之线程通讯(AutoResetEvent)

    C#多线程之线程通讯(AutoResetEvent)

    这篇文章介绍了C#多线程之线程通讯(AutoResetEvent)的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 服务器端C#实现的CSS解析器

    服务器端C#实现的CSS解析器

    服务器端C#实现的CSS解析器
    2008-09-09
  • 详解C#中普通缓存的使用

    详解C#中普通缓存的使用

    缓存的概念及优缺点在这里就不多做介绍,当然缓存包含多种有普通缓存、客户端缓存、DNS缓存、反向代理缓存以及分布式缓存等等。今天主要聊一聊C#通过编码来实现普通的缓存、话不多说直接上代码。
    2021-05-05
  • C#怎么给PDF添加背景图片

    C#怎么给PDF添加背景图片

    无论是办公还是日常生活中都经常会用到,很多时候,PDF文件的背景色都是白色,看多了难免觉得累,更换PDF的背景不仅可以让眼睛看起来更舒服,还可以让PDF文件看上去更美观。这篇文章我主要写的是如何使用C# 给PDF文件添加图片背景
    2016-02-02
  • C#命令模式用法实例

    C#命令模式用法实例

    这篇文章主要介绍了C#命令模式用法,以实例形式较为详细的分析了C#命令模式的功能、定义及使用技巧,需要的朋友可以参考下
    2015-07-07
  • c# WinForm 窗体之间传值的几种方式(小结)

    c# WinForm 窗体之间传值的几种方式(小结)

    这篇文章主要介绍了WinForm 窗体之间传值的几种方式(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • winform天气预报小工具(附源码下载)

    winform天气预报小工具(附源码下载)

    主要原理就是利用网上免费的webservice获取天气数据,需要的朋友可以参考下
    2012-03-03
  • 基于不要返回null之EmptyFactory的应用详解

    基于不要返回null之EmptyFactory的应用详解

    本篇文章对不要返回null之EmptyFactory进行了详细的分析介绍,需要的朋友参考下
    2013-05-05

最新评论