Python設(shè)計(jì)模式編程中的備忘錄模式與對(duì)象池模式示例
Memento備忘錄模式
備忘錄模式一個(gè)最好想象的例子:undo! 它對(duì)對(duì)象的一個(gè)狀態(tài)進(jìn)行了'快照', 在你需要的時(shí)候恢復(fù)原貌。做前端會(huì)有一個(gè)場(chǎng)景:你設(shè)計(jì)一個(gè)表單,當(dāng)點(diǎn)擊提交會(huì)對(duì)表單內(nèi)容 驗(yàn)證,這個(gè)時(shí)候你就要對(duì)用戶填寫(xiě)的數(shù)據(jù)復(fù)制下來(lái),當(dāng)用戶填寫(xiě)的不正確或者格式不對(duì)等問(wèn)題, 就可以使用快照數(shù)據(jù)恢復(fù)用戶已經(jīng)填好的,而不是讓用戶重新來(lái)一遍,不是嘛?
python的例子
這里實(shí)現(xiàn)了一個(gè)事務(wù)提交的例子
import copy def Memento(obj, deep=False): # 對(duì)你要做快照的對(duì)象做快照 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() # 模擬事務(wù)提交,其實(shí)就是初始化給每個(gè)對(duì)象往self.targets賦值 def Commit(self): self.states = [Memento(target, self.deep) for target in self.targets] # 回滾其實(shí)就是調(diào)用Memento函數(shù),執(zhí)行其中的閉包,將__dict__恢復(fù) def Rollback(self): for state in self.states: state() # 裝飾器的方式給方法添加這個(gè)事務(wù)的功能 def transactional(method): # 這里的self其實(shí)就是要保存的那個(gè)對(duì)象,和類(lèi)的實(shí)例無(wú)關(guān) def wrappedMethod(self, *args, **kwargs): state = Memento(self) try: return method(self, *args, **kwargs) except: # 和上面的回滾一樣,異常就恢復(fù) 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): # 賦值成字符串,再自增長(zhǎng)肯定會(huì)報(bào)錯(cuò)的 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 # 這里事務(wù)提交會(huì)保存狀態(tài)從第一次的-1到2 t.Commit() print '-- commited' for i in range(3): n.Increment() print n n.value += 'x' # will fail print n except: # 回滾只會(huì)回顧到上一次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 # 第二次的異?;貪Ln還是2, 整個(gè)過(guò)程都是修改NumObj的實(shí)例對(duì)象 print n
注意
當(dāng)你要保存的狀態(tài)很大,可能會(huì)浪費(fèi)大量?jī)?nèi)存
對(duì)象池模式
在開(kāi)發(fā)中,我們總是用到一些和'池'相關(guān)的東西,比如 內(nèi)存池,連接池,對(duì)象池,線程池.. 這里說(shuō)的對(duì)象池其實(shí)也就是一定數(shù)量已經(jīng)創(chuàng)建好的對(duì)象的集合。為什么要用對(duì)象池? 創(chuàng)建對(duì)象是要付出代價(jià)的(我暫時(shí)還沒(méi)有研究過(guò)底層,只說(shuō)我工作中體會(huì)的), 比如pymongo就自帶線程池,這樣用完就放回到池里再被重用,豈不是節(jié)省了創(chuàng)建的花費(fèi)?
python的例子
我這里實(shí)現(xiàn)了個(gè)線程安全的簡(jiǎn)單的對(duì)象池
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): # 因?yàn)閭鬟M(jìn)來(lái)的可能是函數(shù),還可能是類(lèi) if type(self.fn_cls) == types.FunctionType: return self.fn_cls(self.args) # 判斷是經(jīng)典或者新類(lèi) 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): # 這個(gè)print 沒(méi)用,只是在你執(zhí)行的時(shí)候告訴你目前的隊(duì)列數(shù),讓你發(fā)現(xiàn)對(duì)象池的作用 print self.queue._qsize() # 要是對(duì)象池大小還沒(méi)有超過(guò)設(shè)置的最大數(shù),可以繼續(xù)放進(jìn)去新對(duì)象 if self.queue.qsize()<self.maxSize and self.queue.empty(): self.queue.put(self._get_obj()) # 都會(huì)返回一個(gè)對(duì)象給相關(guān)去用 return self.queue.get() # 回收 def recover_obj(self,obj): self.queue.put(obj) # 測(cè)試用函數(shù)和類(lèi) def echo_func(num): return num class echo_cls(object): pass # 不用構(gòu)造含有__enter__, __exit__的類(lèi)就可以使用with,當(dāng)然你可以直接把代碼放到函數(shù)去用 @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): # 為了實(shí)現(xiàn)效果,我搞了個(gè)簡(jiǎn)單的多線程,2個(gè)with放在一個(gè)地方了,只為測(cè)試用 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)
相關(guān)文章
C#實(shí)現(xiàn)Excel動(dòng)態(tài)生成PivotTable
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)Excel動(dòng)態(tài)生成PivotTable的相關(guān)方法,感興趣的小伙伴們可以參考一下2016-04-04實(shí)例詳解C#實(shí)現(xiàn)http不同方法的請(qǐng)求
本篇文章給大家分享了C#實(shí)現(xiàn)http不同方法的請(qǐng)求的相關(guān)知識(shí)點(diǎn)以及實(shí)例代碼,有需要的朋友參考下。2018-07-07Unity3D實(shí)現(xiàn)待機(jī)狀態(tài)圖片循環(huán)淡入淡出
這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)待機(jī)狀態(tài)圖片循環(huán)淡入淡出,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04WPF實(shí)現(xiàn)動(dòng)畫(huà)效果(四)之緩動(dòng)函數(shù)
這篇文章介紹了WPF實(shí)現(xiàn)動(dòng)畫(huà)效果之緩動(dòng)函數(shù),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06VS2010寫(xiě)的程序在自己電腦可以運(yùn)行、其他電腦上不能運(yùn)行的解決方案
自己用Visual Studio 2010 旗艦版寫(xiě)了一個(gè)軟件,在自己電腦上運(yùn)行完全沒(méi)有問(wèn)題,但是拷貝到其他人電腦上之后不管雙擊還是以管理身份運(yùn)行,均沒(méi)有反應(yīng),進(jìn)程管理器中相關(guān)進(jìn)程也只是一閃而過(guò)2013-04-04C#實(shí)現(xiàn)封裝常用Redis工具類(lèi)的示例代碼
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)封裝常用Redis工具類(lèi)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03