1. 意圖
提供物件的替代品或其占位符,代理控制著對于原物件的訪問,允許在將請求提交給物件前后進行一些處理
2. 動機
在面向物件的系統中,有些物件由于某種原因(比如物件創建的開銷很大,或者某些操作需要安全控制,或者需要行程外的訪問等),直接訪問物件會給使用者或者系統結構帶來很多麻煩
3. 適用性
- 延遲初始化(虛擬代理),如果你有一個偶爾使用的重量級服務物件,一直保持該物件運行會消耗系統資源時,可使用代理模式
- 訪問控制(保護代理),如果只希望特定客戶端使用服務物件,這里的物件可以是作業系統中非常重要的部分,而客戶端則是各種已啟動的程式(包括惡意程式),此時可使用代理模式
- 本地執行遠程服務 (遠程代理),適用于服務物件位于遠程服務器上的情形,代理通過網路傳遞客戶端請求,負責處理所有與網路相關的復雜細節
- 記錄日志請求(日志記錄代理),適用于當你需要保存對于服務物件的請求歷史記錄時,代理可以在向服務傳遞請求前進行記錄
- 快取請求結果 (快取代理),適用于需要快取客戶請求結果并對快取生命周期進行管理時,特別是當回傳結果的體積非常大時,代理可對重復請求所需的相同結果進行快取,還可使用請求引數作為索引快取的鍵值
- 智能參考,可在沒有客戶端使用某個重量級物件時立即銷毀該物件,
代理會將所有獲取了指向服務物件或其結果的客戶端記錄在案,代理會時不時地遍歷各個客戶端,檢查它們是否仍在運行,如果相應的客戶端串列為空,代理就會銷毀該服務物件,釋放底層系統資源,代理還可以記錄客戶端是否修改了服務物件,其他客戶端還可以復用未修改的物件
4. 結構
5. 效果
1)可以在客戶端毫無察覺的情況下控制服務物件
2)客戶端對服務物件的生命周期沒有特殊要求, 你可以對生命周期進行管理
3)開閉原則, 你可以在不對服務或客戶端做出修改的情況下創建新代理
4)Proxy模式可以對用戶隱藏另一種稱為copy-on-write的優化方式,該優化與根據需要創建物件有關,拷貝一個龐大而復雜的物件是一種開銷很大的操作,如果這個拷貝并沒有被修改,那么開銷就沒有必要,可以用代理延遲這一拷貝程序,在實作copy-on-write時必須對物體進行參考計數,只有當用戶請求一個修改該物體的操作時,代理才會真正的拷貝它,此時,代理需要減少物體參考計數,當參考數目為零時,這個物體將被洗掉
5)服務回應可能會延遲
6. 代碼實作
some_cool_media_library/ThirdPartyYouTubeLib.java: 遠程服務介面
package proxy.some_cool_media_library; import java.util.HashMap; /** * @author GaoMing * @date 2021/7/20 - 19:25 */ public interface ThirdPartyYouTubeLib { HashMap<String, Video> popularVideos(); Video getVideo(String videoId); }
some_cool_media_library/ThirdPartyYouTubeClass.java: 遠程服務實作
package proxy.some_cool_media_library; import java.util.HashMap; /** * @author GaoMing * @date 2021/7/20 - 19:22 */ public class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib{ @Override public HashMap<String, Video> popularVideos() { connectToServer("http://www.youtube.com"); return getRandomVideos(); } @Override public Video getVideo(String videoId) { connectToServer("http://www.youtube.com/" + videoId); return getSomeVideo(videoId); } // ----------------------------------------------------------------------- // Fake methods to simulate network activity. They as slow as a real life. private int random(int min, int max) { return min + (int) (Math.random() * ((max - min) + 1)); } private void experienceNetworkLatency() { int randomLatency = random(5, 10); for (int i = 0; i < randomLatency; i++) { try { Thread.sleep(100); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private void connectToServer(String server) { System.out.print("Connecting to " + server + "... "); experienceNetworkLatency(); System.out.print("Connected!" + "\n"); } private HashMap<String, Video> getRandomVideos() { System.out.print("Downloading populars... "); experienceNetworkLatency(); HashMap<String, Video> hmap = new HashMap<String, Video>(); hmap.put("catzzzzzzzzz", new Video("sadgahasgdas", "Catzzzz.avi")); hmap.put("mkafksangasj", new Video("mkafksangasj", "Dog play with ball.mp4")); hmap.put("dancesvideoo", new Video("asdfas3ffasd", "Dancing video.mpq")); hmap.put("dlsdk5jfslaf", new Video("dlsdk5jfslaf", "Barcelona vs RealM.mov")); hmap.put("3sdfgsd1j333", new Video("3sdfgsd1j333", "Programing lesson#1.avi")); System.out.print("Done!" + "\n"); return hmap; } private Video getSomeVideo(String videoId) { System.out.print("Downloading video... "); experienceNetworkLatency(); Video video = new Video(videoId, "Some video title"); System.out.print("Done!" + "\n"); return video; } }
some_cool_media_library/Video.java: 視頻檔案
package proxy.some_cool_media_library; /** * @author GaoMing * @date 2021/7/20 - 19:22 */ public class Video { public String id; public String title; public String data; Video(String id, String title) { this.id = id; this.title = title; this.data = "https://www.cnblogs.com/muxianbai/p/Random video."; } }
proxy/YouTubeCacheProxy.java: 快取代理
package proxy.proxy; import proxy.some_cool_media_library.ThirdPartyYouTubeClass; import proxy.some_cool_media_library.ThirdPartyYouTubeLib; import proxy.some_cool_media_library.Video; import java.util.HashMap; /** * @author GaoMing * @date 2021/7/20 - 19:23 */ public class YouTubeCacheProxy implements ThirdPartyYouTubeLib{ private ThirdPartyYouTubeLib youtubeService; private HashMap<String, Video> cachePopular = new HashMap<String, Video>(); private HashMap<String, Video> cacheAll = new HashMap<String, Video>(); public YouTubeCacheProxy() { this.youtubeService = new ThirdPartyYouTubeClass(); } @Override public HashMap<String, Video> popularVideos() { if (cachePopular.isEmpty()) { cachePopular = youtubeService.popularVideos(); } else { System.out.println("Retrieved list from cache."); } return cachePopular; } @Override public Video getVideo(String videoId) { Video video = cacheAll.get(videoId); if (video == null) { video = youtubeService.getVideo(videoId); cacheAll.put(videoId, video); } else { System.out.println("Retrieved video '" + videoId + "' from cache."); } return video; } public void reset() { cachePopular.clear(); cacheAll.clear(); } }
downloader/YouTubeDownloader.java: 媒體下載應用
package proxy.downloader; import proxy.some_cool_media_library.ThirdPartyYouTubeLib; import proxy.some_cool_media_library.Video; import java.util.HashMap; /** * @author GaoMing * @date 2021/7/20 - 19:23 */ public class YouTubeDownloader { private ThirdPartyYouTubeLib api; public YouTubeDownloader(ThirdPartyYouTubeLib api) { this.api = api; } public void renderVideoPage(String videoId) { Video video = api.getVideo(videoId); System.out.println("\n-------------------------------"); System.out.println("Video page (imagine fancy HTML)"); System.out.println("ID: " + video.id); System.out.println("Title: " + video.title); System.out.println("Video: " + video.data); System.out.println("-------------------------------\n"); } public void renderPopularVideos() { HashMap<String, Video> list = api.popularVideos(); System.out.println("\n-------------------------------"); System.out.println("Most popular videos on YouTube (imagine fancy HTML)"); for (Video video : list.values()) { System.out.println("ID: " + video.id + " / Title: " + video.title); } System.out.println("-------------------------------\n"); } }
Demo.java: 初始化代碼
package proxy; import proxy.downloader.YouTubeDownloader; import proxy.proxy.YouTubeCacheProxy; import proxy.some_cool_media_library.ThirdPartyYouTubeClass; /** * @author GaoMing * @date 2021/7/20 - 19:21 */ public class Demo { public static void main(String[] args) { YouTubeDownloader naiveDownloader = new YouTubeDownloader(new ThirdPartyYouTubeClass()); YouTubeDownloader smartDownloader = new YouTubeDownloader(new YouTubeCacheProxy()); long naive = test(naiveDownloader); long smart = test(smartDownloader); System.out.print("Time saved by caching proxy: " + (naive - smart) + "ms"); } private static long test(YouTubeDownloader downloader) { long startTime = System.currentTimeMillis(); // User behavior in our app: downloader.renderPopularVideos(); downloader.renderVideoPage("catzzzzzzzzz"); downloader.renderPopularVideos(); downloader.renderVideoPage("dancesvideoo"); // Users might visit the same page quite often. downloader.renderVideoPage("catzzzzzzzzz"); downloader.renderVideoPage("someothervid"); long estimatedTime = System.currentTimeMillis() - startTime; System.out.print("Time elapsed: " + estimatedTime + "ms\n"); return estimatedTime; } }
執行結果
Connecting to http://www.youtube.com... Connected! Downloading populars... Done! ------------------------------- Most popular videos on YouTube (imagine fancy HTML) ID: sadgahasgdas / Title: Catzzzz.avi ID: asdfas3ffasd / Title: Dancing video.mpq ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi ID: mkafksangasj / Title: Dog play with ball.mp4 ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov ------------------------------- Connecting to http://www.youtube.com/catzzzzzzzzz... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: catzzzzzzzzz Title: Some video title Video: Random video. ------------------------------- Connecting to http://www.youtube.com... Connected! Downloading populars... Done! ------------------------------- Most popular videos on YouTube (imagine fancy HTML) ID: sadgahasgdas / Title: Catzzzz.avi ID: asdfas3ffasd / Title: Dancing video.mpq ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi ID: mkafksangasj / Title: Dog play with ball.mp4 ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov ------------------------------- Connecting to http://www.youtube.com/dancesvideoo... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: dancesvideoo Title: Some video title Video: Random video. ------------------------------- Connecting to http://www.youtube.com/catzzzzzzzzz... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: catzzzzzzzzz Title: Some video title Video: Random video. ------------------------------- Connecting to http://www.youtube.com/someothervid... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: someothervid Title: Some video title Video: Random video. ------------------------------- Time elapsed: 9354ms Connecting to http://www.youtube.com... Connected! Downloading populars... Done! ------------------------------- Most popular videos on YouTube (imagine fancy HTML) ID: sadgahasgdas / Title: Catzzzz.avi ID: asdfas3ffasd / Title: Dancing video.mpq ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi ID: mkafksangasj / Title: Dog play with ball.mp4 ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov ------------------------------- Connecting to http://www.youtube.com/catzzzzzzzzz... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: catzzzzzzzzz Title: Some video title Video: Random video. ------------------------------- Retrieved list from cache. ------------------------------- Most popular videos on YouTube (imagine fancy HTML) ID: sadgahasgdas / Title: Catzzzz.avi ID: asdfas3ffasd / Title: Dancing video.mpq ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi ID: mkafksangasj / Title: Dog play with ball.mp4 ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov ------------------------------- Connecting to http://www.youtube.com/dancesvideoo... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: dancesvideoo Title: Some video title Video: Random video. ------------------------------- Retrieved video 'catzzzzzzzzz' from cache. ------------------------------- Video page (imagine fancy HTML) ID: catzzzzzzzzz Title: Some video title Video: Random video. ------------------------------- Connecting to http://www.youtube.com/someothervid... Connected! Downloading video... Done! ------------------------------- Video page (imagine fancy HTML) ID: someothervid Title: Some video title Video: Random video. ------------------------------- Time elapsed: 5875ms Time saved by caching proxy: 3479msView Code
7. 與其他模式的關系
- 配接器模式能為被封裝物件提供不同的介面,代理模式能為物件提供相同的介面,裝飾模式則能為物件提供加強的介面
- 外觀模式與代理的相似之處在于它們都快取了一個復雜物體并自行對其進行初始化,但是,代理與其服務物件遵循同一介面,其與服務物件可以互換
- 裝飾和代理有著相似的結構,但是其意圖卻非常不同,這兩個模式的構建都基于組合原則,也就是說一個物件應該將部分作業委派給另一個物件,兩者之間的不同之處在于代理通常自行管理其服務物件的生命周期,而裝飾的生成則總是由客戶端進行控制
8. 已知應用
java.lang.reflect.Proxy
識別方法:代理模式會將所有實際
作業委派給一些其他物件,除非代理是某個服務的子類,否則每個代理方法最后都應該參考一個服務物件
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/308445.html
標籤:設計模式
上一篇:領域驅動設計簡介
下一篇:職責鏈模式(學習筆記)
