?作者簡介:大家好,我是 Meteors., 向往著更加簡潔高效的代碼寫法與編程方式,持續分享Java技術內容。
🍎個人主頁:Meteors.的博客
💞當前專欄:設計模式
?特色專欄:知識分享
🥭本文內容:23種設計模式——代理模式(Proxy Pattern)
📚 ** ps **? :閱讀文章如果有問題或者疑惑,歡迎在評論區提問或指出。
目錄
一. 背景
二. 介紹
三. 核心概念
四. 實際應用示例
1. 圖片加載代理
2. 權限控制代理
3. 網絡請求代理
4. 緩存代理
五.?代理模式的類型
六. 代理模式的優勢
七. 注意事項
八. 總結
一. 背景
也行此時你還感覺不到代理模式是多么的好用,我慢慢幫你剖析吧!
不知道讀者對于Java的Spring AOP(方法粒度的面向切面編程)是否了解?其實代理模式可以類比成一種對象粒度的AOP。在我們在項目中創建一個對象的時候,如果還要添加類似于日志、判斷前置條件(如android中的用戶是否允許某種權限)、或者進行緩存判斷(類似Redis)的操作,如果這個時候如果都把代碼寫成一團,功能肯定也可以實現,但是代碼會顯得非常冗余,這個時候代理模式的優勢就體現出來了,讓我們一起來揭開它的面紗!
二. 介紹
代理模式(Proxy Pattern)是面向對象設計模式中的一種結構型模式。它為其他對象提供一種代理以控制對這個對象的訪問。代理模式在客戶端和目標對象之間起到中介作用,可以在不改變目標對象的情況下,增加額外的功能操作(知道是結構型和可以添加功能就夠了~)。
三. 核心概念
在代理模式中,通常涉及三個角色:
抽象主題(Subject):聲明了真實主題和代理主題的共同接口(方法功能提到接口)
真實主題(RealSubject):定義了代理主題所代表的真實對象(原本要New的對象)
代理主題(Proxy):保存一個引用使得代理可以訪問實體,并提供一個與Subject的接口相同的接口(完成延遲加載和添加日志等功能,后面使用對象的時候只要創建這個對象就夠了)
四. 實際應用示例
1. 圖片加載代理
在Android應用中,我們經常需要加載網絡圖片。為了提高性能,可以使用代理模式來實現圖片的延遲加載:
// 抽象接口 public interface Image {void display(); }// 真實主題 - 真實圖片 public class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadFromDisk();}private void loadFromDisk() {System.out.println("從磁盤加載圖片: " + filename);// 模擬耗時操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void display() {System.out.println("顯示圖片: " + filename);} }// 代理主題 - 圖片代理 public class ImageProxy implements Image {private RealImage realImage;private String filename;public ImageProxy(String filename) {this.filename = filename;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(filename);}realImage.display();} }// 使用示例 public class MainActivity {public void onCreate() {// 創建代理對象,不會立即加載圖片Image image = new ImageProxy("photo.jpg");// 只有在需要顯示時才真正加載圖片image.display(); // 此時才加載并顯示圖片} }
這種方式可以有效減少內存使用,只有在真正需要顯示圖片時才加載,提高了應用性能。
2. 權限控制代理
在Android應用中,某些功能需要特定權限才能訪問,可以使用代理模式進行權限檢查:
// 抽象接口 public interface Camera {void takePhoto();void recordVideo(); }// 真實主題 - 相機功能實現 public class RealCamera implements Camera {@Overridepublic void takePhoto() {System.out.println("拍照成功");}@Overridepublic void recordVideo() {System.out.println("開始錄像");} }// 代理主題 - 帶權限檢查的相機代理 public class CameraPermissionProxy implements Camera {private RealCamera realCamera;private Context context;public CameraPermissionProxy(Context context) {this.context = context;}@Overridepublic void takePhoto() {if (checkCameraPermission()) {if (realCamera == null) {realCamera = new RealCamera();}realCamera.takePhoto();} else {System.out.println("沒有相機權限,無法拍照");}}@Overridepublic void recordVideo() {if (checkCameraPermission()) {if (realCamera == null) {realCamera = new RealCamera();}realCamera.recordVideo();} else {System.out.println("沒有相機權限,無法錄像");}}private boolean checkCameraPermission() {// 檢查相機權限return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;} }// 使用示例 public class CameraActivity {private Camera camera;public void onCreate() {camera = new CameraPermissionProxy(this);}public void onTakePhotoClick() {camera.takePhoto(); // 自動檢查權限} }
3. 網絡請求代理
在網絡請求中,可以使用代理模式添加日志、緩存等功能:
// 抽象接口 public interface HttpClient {String get(String url);String post(String url, String data); }// 真實主題 - 真實的HTTP客戶端 public class RealHttpClient implements HttpClient {@Overridepublic String get(String url) {// 模擬網絡請求System.out.println("發送GET請求到: " + url);return "Response from " + url;}@Overridepublic String post(String url, String data) {System.out.println("發送POST請求到: " + url + " 數據: " + data);return "Response from " + url;} }// 代理主題 - 帶日志功能的HTTP客戶端代理 public class LoggingHttpClientProxy implements HttpClient {private RealHttpClient realHttpClient;private boolean enableLogging;public LoggingHttpClientProxy(boolean enableLogging) {this.enableLogging = enableLogging;}@Overridepublic String get(String url) {long startTime = System.currentTimeMillis();if (realHttpClient == null) {realHttpClient = new RealHttpClient();}if (enableLogging) {System.out.println("開始GET請求: " + url);}String result = realHttpClient.get(url);if (enableLogging) {long endTime = System.currentTimeMillis();System.out.println("GET請求完成,耗時: " + (endTime - startTime) + "ms");}return result;}@Overridepublic String post(String url, String data) {long startTime = System.currentTimeMillis();if (realHttpClient == null) {realHttpClient = new RealHttpClient();}if (enableLogging) {System.out.println("開始POST請求: " + url);}String result = realHttpClient.post(url, data);if (enableLogging) {long endTime = System.currentTimeMillis();System.out.println("POST請求完成,耗時: " + (endTime - startTime) + "ms");}return result;} }// 使用示例 public class NetworkManager {private HttpClient httpClient;public NetworkManager() {// 開發環境啟用日志boolean isDebug = true;httpClient = new LoggingHttpClientProxy(isDebug);}public void fetchData() {String result = httpClient.get("https://api.example.com/data");System.out.println("獲取到數據: " + result);} }
4. 緩存代理
在數據訪問中,代理模式可以用來實現緩存機制:
// 抽象接口 public interface DataService {String getData(String key); }// 真實主題 - 真實的數據服務 public class RealDataService implements DataService {@Overridepublic String getData(String key) {// 模擬從數據庫或網絡獲取數據System.out.println("從數據庫獲取數據,鍵: " + key);try {Thread.sleep(1000); // 模擬耗時操作} catch (InterruptedException e) {e.printStackTrace();}return "Data for " + key;} }// 代理主題 - 帶緩存功能的數據服務代理 public class CachedDataServiceProxy implements DataService {private RealDataService realDataService;private Map<String, String> cache = new HashMap<>();@Overridepublic String getData(String key) {// 先檢查緩存if (cache.containsKey(key)) {System.out.println("從緩存獲取數據,鍵: " + key);return cache.get(key);}// 緩存未命中,從真實服務獲取if (realDataService == null) {realDataService = new RealDataService();}String data = realDataService.getData(key);cache.put(key, data); // 存入緩存System.out.println("數據已緩存,鍵: " + key);return data;}public void clearCache() {cache.clear();System.out.println("緩存已清空");} }// 使用示例 public class DataRepository {private DataService dataService = new CachedDataServiceProxy();public void displayData(String key) {// 第一次調用String data1 = dataService.getData(key);System.out.println("第一次獲取: " + data1);// 第二次調用(從緩存獲取)String data2 = dataService.getData(key);System.out.println("第二次獲取: " + data2);} }
五.?代理模式的類型
代理模式還可以根據作用,分為下面幾種類型:
遠程代理(Remote Proxy):為一個位于不同地址空間的對象提供本地代表。
虛擬代理(Virtual Proxy):根據需要創建開銷很大的對象。
保護代理(Protection Proxy):控制對原始對象的訪問,常用于對象應該有不同的訪問權限時。
智能引用(Smart Reference):取代簡單的指針,在訪問對象時執行一些附加操作。
六. 代理模式的優勢
延遲加載:虛擬代理可以實現對象的延遲初始化,節省系統資源
權限控制:保護代理可以在訪問真實對象前進行權限檢查
功能增強:可以在不修改原始類的情況下添加額外功能(如日志、緩存等)
遠程訪問:遠程代理可以隱藏網絡通信的復雜性
對象訪問控制:可以控制對昂貴資源的訪問
七. 注意事項
性能開銷:代理模式會增加系統的復雜性和可能的性能開銷
代碼復雜性:引入代理會增加類的數量,使系統更加復雜
調試困難:代理模式可能使調試變得更加困難
合理使用:不是所有場景都需要代理,應根據實際需求選擇
八. 總結
代理模式是開發中非常實用的設計模式,它可以在不改變原有代碼結構的情況下,為對象提供額外的功能。通過合理使用代理模式,我們可以實現延遲加載、權限控制、日志記錄、緩存等功能,提高應用的性能和用戶體驗。
在實際開發中,我們可以根據具體需求選擇合適的代理類型,如虛擬代理用于延遲加載,保護代理用于權限控制,智能引用代理用于添加額外功能等。正確使用代理模式可以讓我們的代碼更加優雅、高效和易于維護!
最后,
? ? ? ? 其它設計模式會陸續更新,希望文章對你有所幫助!