天天看點

設計模式之代理模式(Proxy)

在一個HR(人力資源)應用項目中客戶提出,當選擇一個部門或是分公司的時候,要把這個部門或者分公司下的所有員工都顯示出來,而且不使用分頁,友善他們進行業務處理。在顯示全部員工的時候, 隻需要顯示姓名即可,但是也需要提供如下功能:在必要的時候可以選擇并檢視某位員工的詳細資訊(user表中的所有字段)。

實作起來也非常簡單,隻需要查詢對應deptid下的user表就可以了(這樣進行的查詢是全表查詢,也就是會查詢表中的所有字段)。但是實作看似簡單,功能也正确,但是蘊涵了一個比較大的問題。那就是,一次性通路的資料條數過多,而且每條描述的資料量又很大,這樣操作将會消耗較多的記憶體。而從使用者的角度來說,有很大的随機性。客戶有可能通路每一條用戶端詳細資訊,也有可能一條都不通路。也就是說,一次性通路很多條資料,消耗了大量記憶體,但是消耗的記憶體很有可能是浪費掉的。客戶有可能根本就不會去通路那麼多資料,對于每條資料,客戶隻需要檢視姓名而已。

那麼該怎麼實作,才能既把使用者資料的姓名顯示出來,而又能節省記憶體空間?當然還要實作在客戶想要看到更多資料的時候,能夠正确通路到資料呢?這就是我們接下來要講的代理模式。

為其他對象提供一種代理以控制對這個對象的通路。

設計模式之代理模式(Proxy)

Proxy:代理對象,通常具有如下功能。實作與具體的目标對象一樣的接口,這樣就可以使用代理來代替具體的目标對象。儲存一個指向具體目标對象的引用(代理對象中有具體對象的成員變量),可以在需要的時候調用具體的目标對象。可以控制對具體對象的通路,并可以負責建立和删除它。

Subject:具體接口,定義代理和具體對象的接口,這樣就可以在任何使用具體目标對象的地方使用代理對象。

RealSubject:具體的目标對象,真正實作目标接口要求的功能。

在運作時一種可能的代理結構的對象圖如下圖所示

設計模式之代理模式(Proxy)

也就是在客戶首先見到的是Subject接口。接口調用代理Proxy,然後代理再調用具體對象RealSubject。

(1)使用者資料對象接口,就是對使用者資料對象屬性進行操作的getter/setter方法。

UserModelApi.java

設計模式之代理模式(Proxy)

View Code

(2)定義了接口,需要讓UserModel來實作它。

UserModel.java

設計模式之代理模式(Proxy)

(3)接下來定義代理對象

Proxy.java

設計模式之代理模式(Proxy)

(4)定義資料庫連接配接對象

DBConnection.java

設計模式之代理模式(Proxy)

(5)在UserManager對象中,不再對表進行全表查詢,而隻查詢userid和username字段。

UserManager.java

設計模式之代理模式(Proxy)

(6)測試用戶端

Client.java

設計模式之代理模式(Proxy)

(7)運作結果:

使用者編号:=user0001,使用者姓名:=張三1

使用者編号:=user0002,使用者姓名:=張三2

使用者編号:=user0003,使用者姓名:=張三3

重新查詢資料庫擷取完整的使用者資料,userId==user0001

使用者編号:=user0001,使用者姓名:=張三1,所屬部門:=010101

重新查詢資料庫擷取完整的使用者資料,userId==user0002

使用者編号:=user0002,使用者姓名:=張三2,所屬部門:=010101

重新查詢資料庫擷取完整的使用者資料,userId==user0003

使用者編号:=user0003,使用者姓名:=張三3,所屬部門:=010102

(8)總結說明:

如果隻是通路使用者編号和使用者信命的資料,是不需要重新查詢資料庫的。隻有當通路到這兩個資料以外的資料的時候,才需要重新查詢資料庫以獲得完整的資料。

(9)1+N次查詢

上述執行個體存在一個潛在的問題,那就是如果客戶對每條使用者資料都要求檢視詳細資料的話,那麼總的查詢資料庫的次數會是1+N次。

第一次查詢,獲得了N條資料的使用者編号與姓名,然後展示給客戶看。如果這個時候,客戶對每條資料都點選檢視詳細資訊的話,那麼每一條資料都需要重新查詢資料庫,那麼最後總的查詢資料庫的次數就是1+N次了。

本文執行個體中使用的是mysql資料庫,其中所用到的兩張表的結構與内容如下:

(1)tbl_user

設計模式之代理模式(Proxy)

(2)tbl_dept

設計模式之代理模式(Proxy)

代碼執行個體的類圖結構如下圖所示:

設計模式之代理模式(Proxy)