痛點場景:直接加載高清大圖
假設你的應用需要顯示用戶相冊:
NetworkImage('https://example.com/high-res-photo.jpg')
面臨的問題:
- 📶 網絡差時長時間白屏
- 💾 重復下載相同圖片浪費流量
- 🔒 敏感圖片無權限驗證
- 📱 內存占用過高導致崩潰
代理模式解決方案
核心思想: 為其他對象提供一種代理以控制對這個對象的訪問。
三種常見代理類型:
- 虛擬代理: 延遲加載大資源(如占位圖→高清圖)
- 保護代理: 控制訪問權限
- 緩存代理: 存儲請求結果避免重復計算/下載
Flutter圖片加載代理實現
1. 定義圖片加載接口
abstract class ImageLoader {Future<ImageProvider> load(String url);
}
2. 實現真實圖片加載器
class RealImageLoader implements ImageLoader { Future<ImageProvider> load(String url) async {print('真實加載器:從網絡加載高清圖片');return NetworkImage(url);}
}
3. 創建智能代理
class SmartImageLoader implements ImageLoader {final RealImageLoader _realLoader = RealImageLoader();final Map<String, ImageProvider> _cache = {}; Future<ImageProvider> load(String url) async {// 1. 檢查緩存if (_cache.containsKey(url)) {print('代理:從緩存返回圖片');return _cache[url]!;}// 2. 顯示占位圖print('代理:先返回低分辨率占位圖');final placeholder = ResizeImage(NetworkImage('$url?thumb=true'),width: 100,height: 100,);// 3. 后臺加載高清圖_realLoader.load(url).then((highResImage) {print('代理:高清圖加載完成,更新緩存');_cache[url] = highResImage;// 通知UI更新(可通過ChangeNotifier或Stream)});return placeholder;}
}
4. 在Flutter中使用
class PhotoViewer extends StatelessWidget {final ImageLoader loader = SmartImageLoader(); // 使用代理 Widget build(BuildContext context) {return FutureBuilder<ImageProvider>(future: loader.load('https://example.com/photo1.jpg'),builder: (ctx, snapshot) {if (snapshot.hasData) {return Image(image: snapshot.data!);}return CircularProgressIndicator();},);}
}
Flutter中的實際應用場景
場景1:權限控制代理
class AuthImageLoader implements ImageLoader {final UserService _userService;final RealImageLoader _realLoader; Future<ImageProvider> load(String url) async {if (!await _userService.checkPhotoPermission(url)) {return AssetImage('assets/locked.png');}return _realLoader.load(url);}
}// 使用
ImageLoader loader = AuthImageLoader(userService, realLoader);
場景2:API請求緩存代理
class CachedApiClient implements ApiClient {final ApiClient _realClient;final Map<String, dynamic> _cache = {}; Future<dynamic> fetchData(String endpoint) async {if (_cache.containsKey(endpoint)) {return _cache[endpoint];}final data = await _realClient.fetchData(endpoint);_cache[endpoint] = data;return data;}
}// 使用
final apiClient = CachedApiClient(RealApiClient());
final product = await apiClient.fetchData('/products/123');
場景3:敏感操作延遲代理
class ExpenseReportGeneratorProxy implements ReportGenerator {RealReportGenerator? _realGenerator; Future<Report> generate() async {if (!await _checkPermission()) throw UnauthorizedException();_realGenerator ??= RealReportGenerator(); // 按需創建return _realGenerator!.generate();}
}
代理模式與Flutter狀態管理結合
將圖片代理與Provider結合:
class ImageLoaderProvider extends ChangeNotifier {final ImageLoader _loader;ImageProvider? _currentImage;ImageLoaderProvider(this._loader);Future<void> loadImage(String url) async {_currentImage = await _loader.load(url);notifyListeners();}
}// 使用
context.read<ImageLoaderProvider>().loadImage('photo.jpg');Consumer<ImageLoaderProvider>(builder: (context, provider, child) {return provider.currentImage != null? Image(image: provider.currentImage!): Placeholder();}
)
代理模式最佳實踐
-
何時使用代理模式:
- 需要延遲加載大資源時(虛擬代理)
- 需要控制原始對象訪問權限時(保護代理)
- 需要緩存請求結果時(緩存代理)
- 需要記錄日志或監控對象訪問時
-
Flutter特化技巧:
// 組合多種代理 ImageLoader loader = LoggingImageLoader(CacheImageLoader(AuthImageLoader(RealImageLoader())) );// 使用Futures組合 Future<ImageProvider> load(String url) async {final lowRes = await _loadLowRes(url);final highRes = _loadHighRes(url); // 不awaitreturn ProxyImage(lowRes, highRes); }
-
性能優化:
// 預加載代理 class PrefetchImageLoader implements ImageLoader {final List<String> _prefetchUrls;void prefetchAll() {for (final url in _prefetchUrls) {_realLoader.load(url); // 不await,后臺加載}} }
-
測試策略:
test('緩存代理測試', () async {final mockLoader = MockImageLoader();final proxy = CachedImageLoader(mockLoader);when(mockLoader.load('test.jpg')).thenAnswer((_) async => FakeImage());// 第一次調用真實對象await proxy.load('test.jpg');// 第二次應從緩存返回await proxy.load('test.jpg');verify(mockLoader.load('test.jpg')).called(1); // 僅調用一次 });
代理模式 vs 裝飾器模式
特性 | 代理模式 | 裝飾器模式 |
---|---|---|
目的 | 控制訪問 | 增強功能 |
關系 | 代理與真實對象關系固定 | 裝飾器可遞歸嵌套 |
創建時機 | 通常提前知道真實對象 | 運行時動態組合 |
典型應用 | 懶加載、權限控制、緩存 | 流式處理、動態擴展 |
代理模式的高級變體
1. 遠程代理(跨進程通信)
// 本地代理
class LocalImageProxy implements ImageLoader { Future<ImageProvider> load(String url) async {// 通過平臺通道調用原生代碼final result = await MethodChannel('image_loader').invokeMethod('loadImage', {'url': url});return MemoryImage(base64Decode(result));}
}
2. 智能引用代理
class ImageRefProxy implements ImageLoader {int _refCount = 0;ImageProvider? _cachedImage; Future<ImageProvider> load(String url) async {_refCount++;if (_cachedImage == null) {_cachedImage = await _realLoader.load(url);}return _cachedImage!;}void release() {_refCount--;if (_refCount == 0) {_cachedImage?.evict(); // 釋放內存_cachedImage = null;}}
}
3. 日志監控代理
class AnalyticsImageLoader implements ImageLoader {final AnalyticsService _analytics;final ImageLoader _realLoader; Future<ImageProvider> load(String url) async {_analytics.logEvent('image_load_start', {'url': url});final stopwatch = Stopwatch()..start();try {final image = await _realLoader.load(url);_analytics.logEvent('image_load_success', {'url': url,'duration': stopwatch.elapsedMilliseconds,});return image;} catch (e) {_analytics.logError('image_load_failed', {'url': url});rethrow;}}
}
總結:代理模式是你的訪問管家
- 核心價值: 在訪問真實對象前后插入額外邏輯
- Flutter優勢:
- 實現圖片懶加載和緩存
- 控制敏感資源訪問
- 減少網絡請求和計算開銷
- 添加監控和日志記錄
- 適用場景: 圖片加載、API調用、權限管理、性能優化
🕶? 設計啟示: 當你需要在訪問對象時加入"中間層"控制時,代理模式就是你的"隱形眼鏡",既能看清需求,又能保護眼睛!