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)
(0)

相关推荐

  • 举例讲解Python设计模式编程的代理模式与抽象工厂模式

    代理模式 Proxy模式是一种常用的设计模式,它主要用来通过一个对象(比如B)给一个对象(比如A) 提供'代理'的方式方式访问.比如一个对象不方便直接引用,代理就在这个对象和访问者之间做了中介 你先设想:一个对象提供rgb三种颜色值,我想获得一个对象的rgb三种颜色,但是我不想让你获得蓝色属性,怎么办? class Proxy(object): def __init__(self, subject): self.__subject = subject # 代理其实本质上就是属性的委托 def _

  • 举例讲解Python设计模式编程中的访问者与观察者模式

    访问者模式 我觉得Visitor模式是在补修改已有程序结构前提下,通过添加额外的访问者完成对代码功能的拓展 为什么这样用?当你的类层次较多,在某层结构中增加新的方法,要是在基类上面添加或者变更,可能破坏原来的设计, 有兼容问题,所以只在需要的类上面动态添加. python的例子 这里是个构建车的例子,每个部件都有一个accept的方法接受我上面说的所谓'访问者',而这个访问者 以参数的方式传进来,但是其实他是一个含有一些功能的类的实例,它拥有很多个visit开头的方法对应不同的部件. 这样就不需

  • 实例讲解Python设计模式编程之工厂方法模式的使用

    工厂方法模式是简单工厂模式的进一步抽象和推广,它不仅保持了简单工厂模式能够向客户隐藏类的实例化过程这一优点,而且还通过多态性克服了工厂类过于复杂且不易于扩展的缺点.在工厂方法模式中,处于核心地位的工厂类不再负责所有产品的创建,而是将具体的创建工作交由子类去完成.工厂方法模式中的核心工厂类经过功能抽象之后,成为了一个抽象的工厂角色,仅负责给出具体工厂子类必须实现的接口,而不涉及哪种产品类应当被实例化这一细节.工厂方法模式的一般性结构如下图所示,图中为了简化只给出了一个产品类和一个工厂类,但在实际系

  • 使用简单工厂模式来进行Python的设计模式编程

    计模式的目的是让代码易维护.易扩展,不能为了模式而模式,因此一个简单的工具脚本是不需要用到任何模式的. 简单工厂模式又叫静态工厂方法模式,工厂模式家族中最简单的一种模式.这个模式的基本工作方式: 通过一个工厂来决定创建哪种具体的产品实例. 下面是一个简单的工厂实例: def create_animal(name): if name == 'dog': return Dog() elif name == 'cat': return Cat() animal = create_animal('dog

  • 举例讲解Python设计模式编程中对抽象工厂模式的运用

    抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 优点:易于交换"产品系列",只要更改相应的工厂即可. 缺点:建立产品的时候很繁琐,需要增加和修改很多东西. 优化1:为了避免客户端有过多的逻辑判断,可以封装出一个简单工厂类来生成产品类. 优化2:为了减少简单工厂类里面的逻辑判断,可以采用"反射"机制,直接根据外部的配置文件读取出需要使用产品类的信息. #encoding=utf-8 # #by panda #抽象工厂模式 def p

  • 深入解析Python设计模式编程中建造者模式的使用

    建造者模式:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示. 基本思想 某类产品的构建由很多复杂组件组成: 这些组件中的某些细节不同,构建出的产品表象会略有不同: 通过一个指挥者按照产品的创建步骤来一步步执行产品的创建: 当需要创建不同的产品时,只需要派生一个具体的建造者,重写相应的组件构建方法即可. 代码结构 class Builder(object): """基类""" def Part1(self): # 不同类型

  • 简介Python设计模式中的代理模式与模板方法模式编程

    代理模式 Proxy模式是一种常用的设计模式,它主要用来通过一个对象(比如B)给一个对象(比如A) 提供'代理'的方式方式访问.比如一个对象不方便直接引用,代理就在这个对象和访问者之间做了中介 python的例子 你先设想:一个对象提供rgb三种颜色值,我想获得一个对象的rgb三种颜色,但是我不想让你获得蓝色属性,怎么办? class Proxy(object): def __init__(self, subject): self.__subject = subject # 代理其实本质上就是属

  • 实例解析Python设计模式编程之桥接模式的运用

    我们先来看一个例子: #encoding=utf-8 # #by panda #桥接模式 def printInfo(info): print unicode(info, 'utf-8').encode('gbk') #抽象类:手机品牌 class HandsetBrand(): soft = None def SetHandsetSoft(self, soft): self.soft = soft def Run(self): pass #具体抽象类:手机品牌1 class HandsetBr

  • Python的设计模式编程入门指南

    有没有想过设计模式到底是什么?通过本文可以看到设计模式为什么这么重要,通过几个Python的示例展示为什么需要设计模式,以及如何使用. 设计模式是什么? 设计模式是经过总结.优化的,对我们经常会碰到的一些编程问题的可重用解决方案.一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码.反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板.设计模式不会绑定具体的编程语言.一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性).最为重要的是,设计模

  • 设计模式中的原型模式在Python程序中的应用示例

    原型模式: 原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式本质就是克隆对象,所以在对象初始化操作比较复杂的情况下,很实用,能大大降低耗时,提高性能,因为"不用重新初始化对象,而是动态地获得对象运行时的状态". 应用特性: 需要大量的基于某个基础原型进行微量修改而得到新原型时使用. 结构特性: 对象的复制机制,即浅复制和深复制. 例1: #!/usr/bin/env python #encoding: utf-8 from copy import copy, d

  • 详解设计模式中的工厂方法模式在Python程序中的运用

    工厂方法(Factory Method)模式又称为虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,属于类的创建型模式.在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实体化哪一个类. 在简单工厂模式中,一个工厂类处于对产品类进行实例化的中心位置上,它知道每一个产品类的细节,并决定何时哪一个产品类应当被实例化.简单工厂模式的优点是能

  • Python设计模式编程中Adapter适配器模式的使用实例

    将一个类的接口转换成客户希望的另外一个接口.使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 应用场景:希望复用一些现存的类,但是接口又与复用环境要求不一致. 模式特点:将一个类的接口转换成为客户希望的另外一个接口. 分类:类适配器(通过多重继承).对象适配器. 来通过例子说明,下面是用户通过适配器使用一个类的方法 class Target: def Request(): print "common request." class Adaptee(Target): def S

随机推荐