Python設(shè)計(jì)模式編程中的備忘錄模式與對象池模式示例
Memento備忘錄模式
備忘錄模式一個(gè)最好想象的例子:undo! 它對對象的一個(gè)狀態(tài)進(jìn)行了'快照', 在你需要的時(shí)候恢復(fù)原貌。做前端會(huì)有一個(gè)場景:你設(shè)計(jì)一個(gè)表單,當(dāng)點(diǎn)擊提交會(huì)對表單內(nèi)容 驗(yàn)證,這個(gè)時(shí)候你就要對用戶填寫的數(shù)據(jù)復(fù)制下來,當(dāng)用戶填寫的不正確或者格式不對等問題, 就可以使用快照數(shù)據(jù)恢復(fù)用戶已經(jīng)填好的,而不是讓用戶重新來一遍,不是嘛?
python的例子
這里實(shí)現(xiàn)了一個(gè)事務(wù)提交的例子
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()
# 模擬事務(wù)提交,其實(shí)就是初始化給每個(gè)對象往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è)對象,和類的實(shí)例無關(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):
# 賦值成字符串,再自增長肯定會(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è)過程都是修改NumObj的實(shí)例對象
print n
注意
當(dāng)你要保存的狀態(tài)很大,可能會(huì)浪費(fèi)大量內(nèi)存
對象池模式
在開發(fā)中,我們總是用到一些和'池'相關(guān)的東西,比如 內(nèi)存池,連接池,對象池,線程池.. 這里說的對象池其實(shí)也就是一定數(shù)量已經(jīng)創(chuàng)建好的對象的集合。為什么要用對象池? 創(chuàng)建對象是要付出代價(jià)的(我暫時(shí)還沒有研究過底層,只說我工作中體會(huì)的), 比如pymongo就自帶線程池,這樣用完就放回到池里再被重用,豈不是節(jié)省了創(chuàng)建的花費(fèi)?
python的例子
我這里實(shí)現(xiàn)了個(gè)線程安全的簡單的對象池
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)來的可能是函數(shù),還可能是類
if type(self.fn_cls) == types.FunctionType:
return self.fn_cls(self.args)
# 判斷是經(jīng)典或者新類
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 沒用,只是在你執(zhí)行的時(shí)候告訴你目前的隊(duì)列數(shù),讓你發(fā)現(xiàn)對象池的作用
print self.queue._qsize()
# 要是對象池大小還沒有超過設(shè)置的最大數(shù),可以繼續(xù)放進(jìn)去新對象
if self.queue.qsize()<self.maxSize and self.queue.empty():
self.queue.put(self._get_obj())
# 都會(huì)返回一個(gè)對象給相關(guān)去用
return self.queue.get()
# 回收
def recover_obj(self,obj):
self.queue.put(obj)
# 測試用函數(shù)和類
def echo_func(num):
return num
class echo_cls(object):
pass
# 不用構(gòu)造含有__enter__, __exit__的類就可以使用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è)簡單的多線程,2個(gè)with放在一個(gè)地方了,只為測試用
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#使用FileInfo和DirectoryInfo類來執(zhí)行文件和文件夾操作
System.IO.FileInfo?和?System.IO.DirectoryInfo?是C#中用于操作文件和文件夾的類,它們提供了許多有用的方法和屬性來管理文件和文件夾,這篇文章主要介紹了C#使用FileInfo和DirectoryInfo類來執(zhí)行文件和文件夾操作,需要的朋友可以參考下2023-08-08
基于C# wpf 實(shí)現(xiàn)Grid內(nèi)控件拖動(dòng)詳情
這篇文章主要介紹了基于C# wpf 實(shí)現(xiàn)Grid內(nèi)控件拖動(dòng),有一些業(yè)務(wù)場景中我們需要拖動(dòng)控件,在Grid中就可以實(shí)現(xiàn)控件拖動(dòng),通過設(shè)置Margin屬性即可,下面文章我們來看看具體的實(shí)現(xiàn)內(nèi)容2021-11-11
在C#中使用二叉樹實(shí)時(shí)計(jì)算海量用戶積分排名的實(shí)現(xiàn)詳解
這篇文章主要介紹了在C#中使用二叉樹實(shí)時(shí)計(jì)算海量用戶積分排名的實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
C#使用Aspose.Cells創(chuàng)建和讀取Excel文件
這篇文章主要為大家詳細(xì)介紹了C#使用Aspose.Cells創(chuàng)建和讀取Excel文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10

