爲其它對象提供一種代理以控制對這個對象的訪問。git
sql
數據庫
網絡
學習
Subject:目標接口,定義代理和具體目標對象的接口,這樣就能夠在任何使用具體目標對象的地方使用代理對象測試
RealSubject:具體的目標對象,真正實現目標接口要求的功能。優化
訪問多條數據 spa
考慮這樣一個實際應用:要一次性訪問多條數據。代理
這個功能的背景是這樣的;在一個HR(人力資源)應用項目中客戶提出,當選擇一個部門或是分公司的時候,要把這個部門或者分公司下的全部員工都顯示出來,並且不要翻頁,好方便他們進行業務處理。在顯示所有員工的時候,只須要顯示名稱便可,可是也須要提供以下的功能:在必要的時候能夠選擇並查看某位員工的詳細信息。code
客戶方是一個集團公司,有些部門或者分公司可能有好幾百人,不讓翻頁,也就是要求一次性的獲取這多條數據並展現出來。該怎麼樣實現呢?
不用模式的解決方案
不就是要獲取某個部門或者某個分公司下的全部員工的信息嗎?直接使用sql語句從數據庫中查詢就能夠獲得,示意性的SQL大體以下:String sql = "select * from 用戶表,部門表 where 用戶表.depId = 部門表.depId and 部門表.depId like '"+用戶選擇查看的depId+"%'";
1:建表的語句以下:
CREATE TABLE TBL_DEP ( DEPID VARCHAR(20) PRIMARY KEY, NAME VARCHAR(20) ); CREATE TABLE TBL_USER ( USERID VARCHAR(20) PRIMARY KEY, NAME VARCHAR(20) , DEPID VARCHAR(20) , SEX VARCHAR(10) , CONSTRAINT TBL_USER_FK FOREIGN KEY(DEPID) REFERENCES TBL_DEP(DEPID) ); INSERT INTO TBL_DEP VALUES('01','總公司'); INSERT INTO TBL_DEP VALUES('0101','一分公司'); INSERT INTO TBL_DEP VALUES('0102','二分公司'); INSERT INTO TBL_DEP VALUES('010101','開發部'); INSERT INTO TBL_DEP VALUES('010102','測試部'); INSERT INTO TBL_DEP VALUES('010201','開發部'); INSERT INTO TBL_DEP VALUES('010202','客服部'); INSERT INTO TBL_USER VALUES('user0001','張三1','010101','男'); INSERT INTO TBL_USER VALUES('user0002','張三2','010101','男'); INSERT INTO TBL_USER VALUES('user0003','張三3','010102','男'); INSERT INTO TBL_USER VALUES('user0004','張三4','010201','男'); INSERT INTO TBL_USER VALUES('user0005','張三5','010201','男'); INSERT INTO TBL_USER VALUES('user0006','張三6','010202','男'); COMMIT;
並且從客戶使用角度來講,有很大的隨機性,客戶既可能訪問每一條數據,也可能一條都不訪問。也就是說,一次性訪問不少條數據,消耗了大量內存,可是極可能是浪費掉了,客戶根本就不會去訪問那麼多數據,對於每條數據,客戶只須要看看姓名而已。
那麼該怎麼實現,才能既把多條用戶數據的姓名顯示出來,而又能節省內存空間,固然還要實如今客戶想要看到更多數據的時候,能正確訪問到數據呢?
使用模式的解決方案
1:代理模式的功能
代理模式是經過建立一個代理對象,用這個代理對象去表明真實的對象,客戶端獲得這個代理對象事後,對客戶端沒有什麼影響,就跟獲得了真實對象同樣來使用。 當客戶端操做這個代理對象時,實際上功能最終仍是會由真實的對象來完成,只不過是經過代理操做的,也就是客戶端操做代理,代理操做真正的對象。 正是由於有代理對象夾在客戶端和被代理的真實對象中間,至關於一箇中轉,那麼在中轉的時候就有不少花招能夠玩,好比:判斷一下權限,若是沒有足夠的權限那就不給你中轉了,等等。
2:代理的分類
(1)虛代理:根據須要來建立開銷很大的對象,該對象只有在須要的時候纔會被真正建立
(2)遠程代理:用來在不一樣的地址空間上表明同一個對象,這個不一樣的地址空間能夠是在本機,也能夠在其它機器上,在Java裏面最典型的就是RMI技術
(3)copy-on-write代理:在客戶端操做的時候,只有對象確實改變了,纔會真的拷貝(或克隆)一個目標對象,算是虛代理的一個分支
(4)保護代理:控制對原始對象的訪問,若是有須要,能夠給不一樣的用戶提供不一樣的訪問權限,以控制他們對原始對象的訪問
(5)Cache代理:爲那些昂貴的操做的結果提供臨時的存儲空間,以便多個客戶端能夠共享這些結果
(6)防火牆代理:保護對象不被惡意用戶訪問和操做
(7)同步代理:使多個用戶可以同時訪問目標對象而沒有衝突
(8)智能指引:在訪問對象時執行一些附加操做,好比:對指向實際對象的引用計數、第一次引用一個持久對象時,將它裝入內存等 在這些代理類型中
最多見的是:虛代理、保護代理、遠程代理和智能指引這幾種。咱們主要來學習虛代理和保護代理,這是實際開發中使用頻率最高的。
3:虛代理的示例前面的例子就是一個典型的虛代理的實現。
4:copy-on-write 拷貝一個大的對象是很消耗資源的,若是這個被拷貝的對象從上次操做以來,根本就沒有被修改過,那麼再拷貝這個對象是沒有必要的,白白消耗資源而已。那麼就可使用代理來延遲拷貝的過程,能夠等到對象被修改的時候才真的對它進行拷貝。 copy-on-write能夠大大下降拷貝大對象的開銷,所以它算是一種優化方式,能夠根據須要來拷貝或者克隆對象。
5:具體目標和代理的關係 從代理模式的結構圖來看,好像是有一個具體目標類就有一個代理類,其實不是這樣的。若是代理類能徹底經過接口來操做它所代理的目標對象,那麼代理對象就不須要知道具體的目標對象,這樣就無須爲每個具體目標類都建立一個代理類了。 可是,若是代理類必需要實例化它代理的目標對象,那麼代理類就必須知道具體被代理的對象,這種狀況下,一個具體目標類一般會有一個代理類。這種狀況多出如今虛代理的實現裏面。
6:代理模式的調用順序示意圖
保護代理是一種控制對原始對象訪問的代理,多用於對象應該有不一樣的訪問權限的時候。保護代理會檢查調用者是否具備請求所必需的訪問權限,若是沒有相應的權限,那麼就不會調用目標對象,從而實現對目標對象的保護。
1:示例需求 如今有一個訂單系統,要求是:一旦訂單被建立,只有訂單的建立人才能夠修改訂單中的數據,其餘人不能修改。至關於如今若是有了一個訂單對象實例,那麼就須要控制外部對它的訪問,知足條件的能夠訪問,而不知足條件的就不能訪問了。
1:Java的靜態代理
一般把前面本身實現的代理模式,稱爲Java的靜態代理。這種實現方式有一個較大的缺點,就是若是Subject接口發生變化,那麼代理類和具體的目標實現都要變化,不是很靈活
2:Java的動態代理
一般把使用Java內建的對代理模式支持的功能來實現的代理稱爲Java的動態代理。動態代理跟靜態代理相比,明顯的變化是:靜態代理實現的時候,在Subject接口上定義不少的方法,代理類裏面天然也要實現不少方法;而動態代理實現的時候,雖然Subject接口上定義了不少方法,可是動態代理類始終只有一個invoke方法。這樣當Subject接口發生變化的時候,動態代理的接口就不須要跟着變化了。
代理模式在客戶和被客戶訪問的對象之間,引入了必定程度的間接性,客戶是直接使用代理,讓代理來與被訪問的對象進行交互。不一樣的代理類型,這種附加的間接性有不一樣的用途,也就是有不一樣的特色:
1:遠程代理:
隱藏了一個對象存在於不一樣的地址空間的事實,也便是客戶經過遠程代理去訪問一個對象,根本就不關心這個對象在哪裏,也不關心如何經過網絡去訪問到這個對象,從客戶的角度來說,它只是在使用代理對象而已。
2:虛代理:
能夠根據須要來建立「大」對象,只有到必須建立對象的時候,虛代理纔會建立對象,從而大大加快程序運行速度,並節省資源。經過虛代理能夠對系統進行優化。
3:保護代理:
能夠在訪問一個對象的先後,執行不少附加的操做,除了進行權限控制以外,還能夠進行不少跟業務相關的處理,而不須要修改被代理的對象。也就是說,能夠經過代理來給目標對象增長功能。
4:智能指引:
跟保護代理相似,也是容許在訪問一個對象的先後,執行不少附加的操做,這樣一來就能夠作不少額外的事情,好比:引用計數等
代理模式的本質是:控制對象訪問
1:須要爲一個對象在不一樣的地址空間提供局部表明的時候,可使用遠程代理
2:須要按照須要建立開銷很大的對象的時候,可使用虛代理
3:須要控制對原始對象的訪問的時候,可使用保護代理
4:須要在訪問對象的時候執行一些附加操做的時候,可使用智能指引代理
代碼地址: https://gitee.com/weixiaotao1992/DesignPatternsForJava