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)
您可能感兴趣的文章
- 01-10C#编程实现自定义热键的方法
- 01-10C#编程获取资源文件中图片的方法
- 01-10深入浅出23种设计模式
- 01-10C#编程自学之数据类型和变量二
- 01-10C#编程自学之开篇介绍
- 01-10C#编程自学之数据类型和变量三
- 01-10C#编程自学之运算符和表达式
- 01-10C#编程自学之类和对象
- 01-10C#编程和Visual Studio使用技巧(下)
- 01-10C#编程自学之数据类型和变量一
阅读排行
本栏相关
- 01-10C#通过反射获取当前工程中所有窗体并
- 01-10关于ASP网页无法打开的解决方案
- 01-10WinForm限制窗体不能移到屏幕外的方法
- 01-10WinForm绘制圆角的方法
- 01-10C#实现txt定位指定行完整实例
- 01-10WinForm实现仿视频 器左下角滚动新
- 01-10C#停止线程的方法
- 01-10C#实现清空回收站的方法
- 01-10C#通过重写Panel改变边框颜色与宽度的
- 01-10C#实现读取注册表监控当前操作系统已
随机阅读
- 01-10delphi制作wav文件的方法
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-11ajax实现页面的局部加载
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-10SublimeText编译C开发环境设置
- 01-10C#中split用法实例总结
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 04-02jquery与jsp,用jquery