首页 > 编程 > C# > 正文

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

2020-01-24 01:17:11
字体:
来源:转载
供稿:网友

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

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

import copydef Memento(obj, deep=False):  # 对你要做快照的对象做快照  state = (copy.copy if deep else copy.deepcopy)(obj.__dict__)  def Restore():    obj.__dict__ = state  return Restoreclass 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 wrappedMethodclass 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 Queueimport typesimport threadingfrom contextlib import contextmanagerclass 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 numclass echo_cls(object):  pass# 不用构造含有__enter__, __exit__的类就可以使用with,当然你可以直接把代码放到函数去用@contextmanagerdef 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 tif __name__ == '__main__':  threads = []  for i in range(200):    t = MyThread()    t.start()    threads.append(t)  for t in threads:    t.join(True)

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表