代理模式 - Flutter中的智能替身,掌控对象访问的每一道关卡!

发布于:2025-07-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

痛点场景:直接加载高清大图

假设你的应用需要显示用户相册:

NetworkImage('https://example.com/high-res-photo.jpg')

面临的问题:

  • 📶 网络差时长时间白屏
  • 💾 重复下载相同图片浪费流量
  • 🔒 敏感图片无权限验证
  • 📱 内存占用过高导致崩溃

代理模式解决方案

核心思想: 为其他对象提供一种代理以控制对这个对象的访问。

三种常见代理类型:

  1. 虚拟代理: 延迟加载大资源(如占位图→高清图)
  2. 保护代理: 控制访问权限
  3. 缓存代理: 存储请求结果避免重复计算/下载

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();
  }
)


代理模式最佳实践

  1. 何时使用代理模式:

    • 需要延迟加载大资源时(虚拟代理)
    • 需要控制原始对象访问权限时(保护代理)
    • 需要缓存请求结果时(缓存代理)
    • 需要记录日志或监控对象访问时
  2. Flutter特化技巧:

    // 组合多种代理
    ImageLoader loader = LoggingImageLoader(
      CacheImageLoader(
        AuthImageLoader(
          RealImageLoader()
        )
      )
    );
    
    // 使用Futures组合
    Future<ImageProvider> load(String url) async {
      final lowRes = await _loadLowRes(url);
      final highRes = _loadHighRes(url); // 不await
      return ProxyImage(lowRes, highRes);
    }
    
    
  3. 性能优化:

    // 预加载代理
    class PrefetchImageLoader implements ImageLoader {
      final List<String> _prefetchUrls;
      
      void prefetchAll() {
        for (final url in _prefetchUrls) {
          _realLoader.load(url); // 不await,后台加载
        }
      }
    }
    
    
  4. 测试策略:

    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调用、权限管理、性能优化

🕶️ 设计启示: 当你需要在访问对象时加入"中间层"控制时,代理模式就是你的"隐形眼镜",既能看清需求,又能保护眼睛!


网站公告

今日签到

点亮在社区的每一天
去签到