網(wǎng)站首頁 編程語言 正文
關(guān)于代理模式、裝飾模式
設(shè)計(jì)模式中經(jīng)常提到的代理模式、裝飾模式,這兩種叫法實(shí)際上是說的同一件事,只是側(cè)重點(diǎn)有所不同而已。
這兩者都是通過在原有對象的基礎(chǔ)上封裝一層對象,通過調(diào)用封裝后的對象而不是原來的對象來實(shí)現(xiàn)代理/裝飾的目的。
例如:(以Java為例)
public class CountProxy implements Count { private CountImpl countImpl; public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事務(wù)處理之前"); // 調(diào)用委托類的方法; countImpl.queryCount(); System.out.println("事務(wù)處理之后"); } @Override public void updateCount() { System.out.println("事務(wù)處理之前"); // 調(diào)用委托類的方法; countImpl.updateCount(); System.out.println("事務(wù)處理之后"); } }
在這個(gè)例子中CountProxy
是對CountImpl
的封裝。
使用者通過CountProxy.queryCount
方法來調(diào)用CountImpl.queryCount
方法,這被稱為代理,即CountProxy
是代理類,CountImpl
是被代理類。
在CountProxy.queryCount
方法中,可以在CountImpl.queryCount
方法調(diào)用之前和之后添加一些額外的操作,被稱為裝飾,即CountProxy
是裝飾類,CountImpl
是被裝飾類。
如果強(qiáng)調(diào)通過CountProxy
?對CountImpl
進(jìn)行代理的作用,則稱為代理模式;
如果強(qiáng)調(diào)通過CountProxy
?對CountImpl
增加額外的操作,則稱為裝飾模式;
不論是哪種稱呼,其本質(zhì)都在于對原有對象的封裝。
其封裝的目的在于增強(qiáng)所封裝對象的功能或管理所封裝的對象。
從上面的例子也可以發(fā)現(xiàn),代理/封裝所圍繞的核心是可調(diào)用對象(比如函數(shù))。
Python中的代理/裝飾
Python中的可調(diào)用對象包括函數(shù)、方法、實(shí)現(xiàn)了__call__方法的類。
Python中的函數(shù)也是對象,可以作為高階函數(shù)的參數(shù)傳入或返回值返回。
因此,當(dāng)代理/裝飾的對象是函數(shù)時(shí),可以使用高階函數(shù)來對某個(gè)函數(shù)進(jìn)行封裝。
例如:
def query_count_proxy(fun, name, age): print('do something before') rv = fun(name, age) print('do something after') return rv def query_count(name, age): print('name is %s, age is %d' % (name, age)) query_count_proxy(query_count, 'Lee', 20)
但是,這個(gè)例子中,query_count
函數(shù)作為參數(shù)傳入query_count_proxy
函數(shù)中,并在query_count_proxy
函數(shù)中被調(diào)用,其結(jié)果作為返回值返回。這就完成了代理的功能,同時(shí),在調(diào)用query_count
函數(shù)的前后,我們還增加了裝飾代碼。
但是,query_count_proxy
的函數(shù)參數(shù)與query_count
不一樣了,理想的代理應(yīng)該保持接口一致才對。
為了保持一致,我們可以利用高階函數(shù)可以返回函數(shù)的特點(diǎn)來完成:
def query_count_proxy(fun): def wrapper(name, age): print('do something before') rv = fun(name, age) print('do something after') return rv return wrapper def query_count(name, age): print('name is %s, age is %d' % (name, age)) query_count_proxy(query_count)('Lee', 20)
修改后的例子,query_count_proxy
僅負(fù)責(zé)接受被代理的函數(shù)query_count
作為參數(shù),同時(shí),返回一個(gè)函數(shù)對象wrapper
作為返回值,真正的封裝動作在wrapper
這個(gè)函數(shù)中完成。
此時(shí),如果調(diào)用query_count_proxy(query_count)
就得到了wrapper
函數(shù)對象,則,執(zhí)行query_count_proxy(query_count)('Lee', 20)
就相當(dāng)于執(zhí)行了wrapper('Lee', 20)
。
但是可以看到,query_count_proxy(query_count)('Lee', 20)
這種使用方法,仍然不能保證一致。
為了保持一致,我們需要利用Python中對象與其名稱可以動態(tài)綁定的特點(diǎn)。不使用query_count_proxy(quer_count)('Lee', 20)
來調(diào)用代理函數(shù),而是使用下面兩句:
query_count = query_count_proxy(query_count) query_count('Lee', 20)
執(zhí)行query_count_proxy(query_count)
生成wrapper
函數(shù)對象,將這個(gè)對象通過query_count = query_count_proxy(query_count)
綁定到query_count
這個(gè)名字上來,這樣執(zhí)行query_count('Lee', 20)
時(shí),其實(shí)執(zhí)行的是wrapper('Lee', 20)
。
這么做的結(jié)果就是:使用代理時(shí)調(diào)用query_count('Lee', 20)
與不使用代理時(shí)調(diào)用query_count('Lee', 20)
對使用者而言保持不變,不用改變代碼,但是在真正執(zhí)行時(shí),使用的是代理/裝飾后的函數(shù)。
這里,基本利用Python的高階函數(shù)及名稱綁定完成了代理/裝飾的功能。
還有什么不理想的地方呢?
對,就是query_count = query_count_proxy(query_count)
,因?yàn)檫@句既不簡潔,又屬于重復(fù)工作。
Python為我們提供了語法糖來完成這類的tedious work。
方法就是:
@query_count_proxy def query_count(name, age): return 'name is %s, age is %d' % (name, age)
query_count = query_count_proxy(query_count)
就等同于在定義query_count
函數(shù)的時(shí)候,在其前面加上@query_count_proxy
。
Python看到這樣的語法,就會自動的執(zhí)行query_count = query_count_proxy(query_count)
進(jìn)行name rebinding
補(bǔ)充
以上就是Python實(shí)現(xiàn)可調(diào)用對象裝飾的核心。
可調(diào)用對象包括函數(shù)、方法、實(shí)現(xiàn)了__call__方法的類,上述內(nèi)容只是針對函數(shù)來解釋,對于方法、實(shí)現(xiàn)了__call__方法的類,其基本原理相同,具體實(shí)現(xiàn)略有差別。
原文鏈接:https://segmentfault.com/a/1190000003719779
相關(guān)推薦
- 2022-11-13 Framework源碼面試之a(chǎn)ctivity啟動流程_Android
- 2022-11-23 Qt采用線程以隊(duì)列方式實(shí)現(xiàn)下發(fā)數(shù)據(jù)_C 語言
- 2022-01-21 Flink中window 窗口和時(shí)間以及watermark水印
- 2022-09-05 Go語言接口的用法詳解_Golang
- 2022-11-30 Golang底層原理解析String使用實(shí)例_Golang
- 2022-08-04 GoFrame?gredis配置文件及配置方法對比_Golang
- 2022-01-22 記一次線上sql優(yōu)化從9s到400ms的過程
- 2022-05-11 Nginx代理Redis哨兵主從配置
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支