Refactoring
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Service d'optimisation des performances pour l'application UnionFlow
|
||||
///
|
||||
/// Fournit des utilitaires pour :
|
||||
/// - Optimisation des widgets
|
||||
/// - Gestion de la mémoire
|
||||
/// - Mise en cache intelligente
|
||||
/// - Monitoring des performances
|
||||
class PerformanceOptimizer {
|
||||
static const String _tag = 'PerformanceOptimizer';
|
||||
|
||||
/// Singleton instance
|
||||
static final PerformanceOptimizer _instance = PerformanceOptimizer._internal();
|
||||
factory PerformanceOptimizer() => _instance;
|
||||
PerformanceOptimizer._internal();
|
||||
|
||||
/// Cache pour les widgets optimisés
|
||||
final Map<String, Widget> _widgetCache = {};
|
||||
|
||||
/// Cache pour les images
|
||||
final Map<String, ImageProvider> _imageCache = {};
|
||||
|
||||
/// Compteurs de performance
|
||||
final Map<String, int> _performanceCounters = {};
|
||||
|
||||
/// Temps de début pour les mesures
|
||||
final Map<String, DateTime> _performanceTimers = {};
|
||||
|
||||
// ========================================
|
||||
// OPTIMISATION DES WIDGETS
|
||||
// ========================================
|
||||
|
||||
/// Optimise un widget avec RepaintBoundary si nécessaire
|
||||
static Widget optimizeWidget(Widget child, {
|
||||
String? key,
|
||||
bool forceRepaintBoundary = false,
|
||||
bool addSemantics = true,
|
||||
}) {
|
||||
Widget optimized = child;
|
||||
|
||||
// Ajouter RepaintBoundary pour les widgets complexes
|
||||
if (forceRepaintBoundary || _shouldAddRepaintBoundary(child)) {
|
||||
optimized = RepaintBoundary(
|
||||
key: key != null ? Key('repaint_$key') : null,
|
||||
child: optimized,
|
||||
);
|
||||
}
|
||||
|
||||
// Ajouter Semantics pour l'accessibilité
|
||||
if (addSemantics && _shouldAddSemantics(child)) {
|
||||
optimized = Semantics(
|
||||
key: key != null ? Key('semantics_$key') : null,
|
||||
child: optimized,
|
||||
);
|
||||
}
|
||||
|
||||
return optimized;
|
||||
}
|
||||
|
||||
/// Détermine si un RepaintBoundary est nécessaire
|
||||
static bool _shouldAddRepaintBoundary(Widget widget) {
|
||||
// Ajouter RepaintBoundary pour les widgets qui changent fréquemment
|
||||
return widget is AnimatedWidget ||
|
||||
widget is CustomPaint ||
|
||||
widget is Image ||
|
||||
widget.runtimeType.toString().contains('Chart') ||
|
||||
widget.runtimeType.toString().contains('Graph');
|
||||
}
|
||||
|
||||
/// Détermine si Semantics est nécessaire
|
||||
static bool _shouldAddSemantics(Widget widget) {
|
||||
return widget is GestureDetector ||
|
||||
widget is InkWell ||
|
||||
widget is ElevatedButton ||
|
||||
widget is TextButton ||
|
||||
widget is IconButton;
|
||||
}
|
||||
|
||||
/// Crée un widget avec mise en cache
|
||||
Widget cachedWidget(String key, Widget Function() builder) {
|
||||
if (_widgetCache.containsKey(key)) {
|
||||
return _widgetCache[key]!;
|
||||
}
|
||||
|
||||
final widget = builder();
|
||||
_widgetCache[key] = widget;
|
||||
return widget;
|
||||
}
|
||||
|
||||
/// Nettoie le cache des widgets
|
||||
void clearWidgetCache() {
|
||||
_widgetCache.clear();
|
||||
debugPrint('$_tag: Widget cache cleared');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// OPTIMISATION DES IMAGES
|
||||
// ========================================
|
||||
|
||||
/// Optimise le chargement d'une image
|
||||
static ImageProvider optimizeImage(String path, {
|
||||
double? width,
|
||||
double? height,
|
||||
BoxFit fit = BoxFit.cover,
|
||||
}) {
|
||||
// Utiliser ResizeImage pour optimiser la mémoire
|
||||
if (width != null || height != null) {
|
||||
return ResizeImage(
|
||||
AssetImage(path),
|
||||
width: width?.round(),
|
||||
height: height?.round(),
|
||||
);
|
||||
}
|
||||
|
||||
return AssetImage(path);
|
||||
}
|
||||
|
||||
/// Met en cache une image
|
||||
ImageProvider cachedImage(String key, String path) {
|
||||
if (_imageCache.containsKey(key)) {
|
||||
return _imageCache[key]!;
|
||||
}
|
||||
|
||||
final image = AssetImage(path);
|
||||
_imageCache[key] = image;
|
||||
return image;
|
||||
}
|
||||
|
||||
/// Précharge les images critiques
|
||||
static Future<void> preloadCriticalImages(BuildContext context, List<String> imagePaths) async {
|
||||
final futures = imagePaths.map((path) =>
|
||||
precacheImage(AssetImage(path), context)
|
||||
).toList();
|
||||
|
||||
await Future.wait(futures);
|
||||
debugPrint('$_tag: ${imagePaths.length} critical images preloaded');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// MONITORING DES PERFORMANCES
|
||||
// ========================================
|
||||
|
||||
/// Démarre un timer de performance
|
||||
void startTimer(String operation) {
|
||||
_performanceTimers[operation] = DateTime.now();
|
||||
}
|
||||
|
||||
/// Arrête un timer et log le résultat
|
||||
void stopTimer(String operation) {
|
||||
final startTime = _performanceTimers[operation];
|
||||
if (startTime != null) {
|
||||
final duration = DateTime.now().difference(startTime);
|
||||
debugPrint('$_tag: $operation took ${duration.inMilliseconds}ms');
|
||||
_performanceTimers.remove(operation);
|
||||
|
||||
// Incrémenter le compteur
|
||||
_performanceCounters[operation] = (_performanceCounters[operation] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Incrémente un compteur de performance
|
||||
void incrementCounter(String metric) {
|
||||
_performanceCounters[metric] = (_performanceCounters[metric] ?? 0) + 1;
|
||||
}
|
||||
|
||||
/// Obtient les statistiques de performance
|
||||
Map<String, int> getPerformanceStats() {
|
||||
return Map.from(_performanceCounters);
|
||||
}
|
||||
|
||||
/// Réinitialise les statistiques
|
||||
void resetStats() {
|
||||
_performanceCounters.clear();
|
||||
_performanceTimers.clear();
|
||||
debugPrint('$_tag: Performance stats reset');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// OPTIMISATION MÉMOIRE
|
||||
// ========================================
|
||||
|
||||
/// Force le garbage collection (debug uniquement)
|
||||
static void forceGarbageCollection() {
|
||||
if (kDebugMode) {
|
||||
// Forcer le GC en créant et supprimant des objets
|
||||
final temp = List.generate(1000, (i) => Object());
|
||||
temp.clear();
|
||||
debugPrint('PerformanceOptimizer: Forced garbage collection');
|
||||
}
|
||||
}
|
||||
|
||||
/// Nettoie tous les caches
|
||||
void clearAllCaches() {
|
||||
clearWidgetCache();
|
||||
_imageCache.clear();
|
||||
debugPrint('$_tag: All caches cleared');
|
||||
}
|
||||
|
||||
/// Obtient la taille des caches
|
||||
Map<String, int> getCacheSizes() {
|
||||
return {
|
||||
'widgets': _widgetCache.length,
|
||||
'images': _imageCache.length,
|
||||
};
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// OPTIMISATION DES ANIMATIONS
|
||||
// ========================================
|
||||
|
||||
/// Crée un AnimationController optimisé
|
||||
static AnimationController createOptimizedController({
|
||||
required Duration duration,
|
||||
required TickerProvider vsync,
|
||||
double? value,
|
||||
Duration? reverseDuration,
|
||||
String? debugLabel,
|
||||
}) {
|
||||
return AnimationController(
|
||||
duration: duration,
|
||||
reverseDuration: reverseDuration,
|
||||
vsync: vsync,
|
||||
value: value,
|
||||
debugLabel: debugLabel ?? 'OptimizedController',
|
||||
);
|
||||
}
|
||||
|
||||
/// Dispose proprement une liste d'AnimationControllers
|
||||
static void disposeControllers(List<AnimationController> controllers) {
|
||||
for (final controller in controllers) {
|
||||
try {
|
||||
controller.dispose();
|
||||
} catch (e) {
|
||||
// Controller déjà disposé, ignorer l'erreur
|
||||
debugPrint('$_tag: Controller already disposed: $e');
|
||||
}
|
||||
}
|
||||
controllers.clear();
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// UTILITAIRES DE PERFORMANCE
|
||||
// ========================================
|
||||
|
||||
/// Vérifie si l'appareil est performant
|
||||
static bool isHighPerformanceDevice() {
|
||||
// Logique basée sur les capacités de l'appareil
|
||||
// Pour l'instant, retourne true par défaut
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Obtient le niveau de performance recommandé
|
||||
static PerformanceLevel getRecommendedPerformanceLevel() {
|
||||
if (isHighPerformanceDevice()) {
|
||||
return PerformanceLevel.high;
|
||||
} else {
|
||||
return PerformanceLevel.medium;
|
||||
}
|
||||
}
|
||||
|
||||
/// Applique les optimisations selon le niveau de performance
|
||||
static void applyPerformanceLevel(PerformanceLevel level) {
|
||||
switch (level) {
|
||||
case PerformanceLevel.high:
|
||||
// Toutes les animations et effets activés
|
||||
debugPrint('$_tag: High performance mode enabled');
|
||||
break;
|
||||
case PerformanceLevel.medium:
|
||||
// Animations réduites
|
||||
debugPrint('$_tag: Medium performance mode enabled');
|
||||
break;
|
||||
case PerformanceLevel.low:
|
||||
// Animations désactivées
|
||||
debugPrint('$_tag: Low performance mode enabled');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// MONITORING EN TEMPS RÉEL
|
||||
// ========================================
|
||||
|
||||
/// Démarre le monitoring des performances
|
||||
void startPerformanceMonitoring() {
|
||||
// Monitoring du frame rate
|
||||
WidgetsBinding.instance.addPersistentFrameCallback((timeStamp) {
|
||||
_monitorFrameRate();
|
||||
});
|
||||
|
||||
// Monitoring de la mémoire (toutes les 30 secondes)
|
||||
Timer.periodic(const Duration(seconds: 30), (_) {
|
||||
_monitorMemoryUsage();
|
||||
});
|
||||
|
||||
debugPrint('$_tag: Performance monitoring started');
|
||||
}
|
||||
|
||||
void _monitorFrameRate() {
|
||||
// Logique de monitoring du frame rate
|
||||
// Pour l'instant, juste incrémenter un compteur
|
||||
incrementCounter('frames_rendered');
|
||||
}
|
||||
|
||||
void _monitorMemoryUsage() {
|
||||
// Logique de monitoring de la mémoire
|
||||
if (kDebugMode) {
|
||||
final cacheSize = getCacheSizes();
|
||||
debugPrint('$_tag: Cache sizes - Widgets: ${cacheSize['widgets']}, Images: ${cacheSize['images']}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Niveaux de performance
|
||||
enum PerformanceLevel {
|
||||
low,
|
||||
medium,
|
||||
high,
|
||||
}
|
||||
|
||||
/// Extension pour optimiser les widgets
|
||||
extension WidgetOptimization on Widget {
|
||||
/// Optimise ce widget
|
||||
Widget optimized({
|
||||
String? key,
|
||||
bool forceRepaintBoundary = false,
|
||||
bool addSemantics = true,
|
||||
}) {
|
||||
return PerformanceOptimizer.optimizeWidget(
|
||||
this,
|
||||
key: key,
|
||||
forceRepaintBoundary: forceRepaintBoundary,
|
||||
addSemantics: addSemantics,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
/// Service de mise en cache intelligent pour optimiser les performances
|
||||
///
|
||||
/// Fonctionnalités :
|
||||
/// - Cache multi-niveaux (mémoire + stockage)
|
||||
/// - Expiration automatique des données
|
||||
/// - Invalidation intelligente
|
||||
/// - Compression des données
|
||||
/// - Statistiques de cache
|
||||
@singleton
|
||||
class SmartCacheService {
|
||||
static const String _tag = 'SmartCacheService';
|
||||
|
||||
/// Cache en mémoire (niveau 1)
|
||||
final Map<String, CacheEntry> _memoryCache = {};
|
||||
|
||||
/// Instance SharedPreferences pour le cache persistant
|
||||
SharedPreferences? _prefs;
|
||||
|
||||
/// Statistiques du cache
|
||||
final CacheStats _stats = CacheStats();
|
||||
|
||||
/// Taille maximale du cache mémoire (nombre d'entrées)
|
||||
static const int _maxMemoryCacheSize = 100;
|
||||
|
||||
/// Durée par défaut de validité du cache
|
||||
static const Duration _defaultCacheDuration = Duration(minutes: 15);
|
||||
|
||||
/// Initialise le service de cache
|
||||
Future<void> initialize() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
await _cleanExpiredEntries();
|
||||
debugPrint('$_tag: Service initialized');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// OPÉRATIONS DE CACHE PRINCIPALES
|
||||
// ========================================
|
||||
|
||||
/// Met en cache une valeur avec une clé
|
||||
Future<void> put<T>(
|
||||
String key,
|
||||
T value, {
|
||||
Duration? duration,
|
||||
CacheLevel level = CacheLevel.both,
|
||||
bool compress = false,
|
||||
}) async {
|
||||
final entry = CacheEntry(
|
||||
key: key,
|
||||
value: value,
|
||||
timestamp: DateTime.now(),
|
||||
duration: duration ?? _defaultCacheDuration,
|
||||
compressed: compress,
|
||||
);
|
||||
|
||||
// Cache mémoire
|
||||
if (level == CacheLevel.memory || level == CacheLevel.both) {
|
||||
_putInMemory(key, entry);
|
||||
}
|
||||
|
||||
// Cache persistant
|
||||
if (level == CacheLevel.storage || level == CacheLevel.both) {
|
||||
await _putInStorage(key, entry);
|
||||
}
|
||||
|
||||
_stats.incrementWrites();
|
||||
debugPrint('$_tag: Cached $key (level: $level)');
|
||||
}
|
||||
|
||||
/// Récupère une valeur du cache
|
||||
Future<T?> get<T>(String key, {CacheLevel level = CacheLevel.both}) async {
|
||||
CacheEntry? entry;
|
||||
|
||||
// Essayer d'abord le cache mémoire (plus rapide)
|
||||
if (level == CacheLevel.memory || level == CacheLevel.both) {
|
||||
entry = _getFromMemory(key);
|
||||
if (entry != null && !entry.isExpired) {
|
||||
_stats.incrementHits();
|
||||
debugPrint('$_tag: Memory cache hit for $key');
|
||||
return entry.value as T?;
|
||||
}
|
||||
}
|
||||
|
||||
// Essayer le cache persistant
|
||||
if (level == CacheLevel.storage || level == CacheLevel.both) {
|
||||
entry = await _getFromStorage(key);
|
||||
if (entry != null && !entry.isExpired) {
|
||||
// Remettre en cache mémoire pour les prochains accès
|
||||
_putInMemory(key, entry);
|
||||
_stats.incrementHits();
|
||||
debugPrint('$_tag: Storage cache hit for $key');
|
||||
return entry.value as T?;
|
||||
}
|
||||
}
|
||||
|
||||
_stats.incrementMisses();
|
||||
debugPrint('$_tag: Cache miss for $key');
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Vérifie si une clé existe dans le cache
|
||||
Future<bool> contains(String key, {CacheLevel level = CacheLevel.both}) async {
|
||||
if (level == CacheLevel.memory || level == CacheLevel.both) {
|
||||
final entry = _getFromMemory(key);
|
||||
if (entry != null && !entry.isExpired) return true;
|
||||
}
|
||||
|
||||
if (level == CacheLevel.storage || level == CacheLevel.both) {
|
||||
final entry = await _getFromStorage(key);
|
||||
if (entry != null && !entry.isExpired) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Supprime une entrée du cache
|
||||
Future<void> remove(String key, {CacheLevel level = CacheLevel.both}) async {
|
||||
if (level == CacheLevel.memory || level == CacheLevel.both) {
|
||||
_memoryCache.remove(key);
|
||||
}
|
||||
|
||||
if (level == CacheLevel.storage || level == CacheLevel.both) {
|
||||
await _prefs?.remove(_getStorageKey(key));
|
||||
}
|
||||
|
||||
debugPrint('$_tag: Removed $key from cache');
|
||||
}
|
||||
|
||||
/// Vide complètement le cache
|
||||
Future<void> clear({CacheLevel level = CacheLevel.both}) async {
|
||||
if (level == CacheLevel.memory || level == CacheLevel.both) {
|
||||
_memoryCache.clear();
|
||||
}
|
||||
|
||||
if (level == CacheLevel.storage || level == CacheLevel.both) {
|
||||
final keys = _prefs?.getKeys().where((k) => k.startsWith('cache_')).toList() ?? [];
|
||||
for (final key in keys) {
|
||||
await _prefs?.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
_stats.reset();
|
||||
debugPrint('$_tag: Cache cleared (level: $level)');
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// CACHE MÉMOIRE
|
||||
// ========================================
|
||||
|
||||
void _putInMemory(String key, CacheEntry entry) {
|
||||
// Vérifier la taille du cache et nettoyer si nécessaire
|
||||
if (_memoryCache.length >= _maxMemoryCacheSize) {
|
||||
_evictOldestMemoryEntry();
|
||||
}
|
||||
|
||||
_memoryCache[key] = entry;
|
||||
}
|
||||
|
||||
CacheEntry? _getFromMemory(String key) {
|
||||
return _memoryCache[key];
|
||||
}
|
||||
|
||||
void _evictOldestMemoryEntry() {
|
||||
if (_memoryCache.isEmpty) return;
|
||||
|
||||
String? oldestKey;
|
||||
DateTime? oldestTime;
|
||||
|
||||
for (final entry in _memoryCache.entries) {
|
||||
if (oldestTime == null || entry.value.timestamp.isBefore(oldestTime)) {
|
||||
oldestTime = entry.value.timestamp;
|
||||
oldestKey = entry.key;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldestKey != null) {
|
||||
_memoryCache.remove(oldestKey);
|
||||
debugPrint('$_tag: Evicted oldest memory entry: $oldestKey');
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// CACHE PERSISTANT
|
||||
// ========================================
|
||||
|
||||
Future<void> _putInStorage(String key, CacheEntry entry) async {
|
||||
final storageKey = _getStorageKey(key);
|
||||
final jsonData = entry.toJson();
|
||||
await _prefs?.setString(storageKey, jsonEncode(jsonData));
|
||||
}
|
||||
|
||||
Future<CacheEntry?> _getFromStorage(String key) async {
|
||||
final storageKey = _getStorageKey(key);
|
||||
final jsonString = _prefs?.getString(storageKey);
|
||||
|
||||
if (jsonString == null) return null;
|
||||
|
||||
try {
|
||||
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
|
||||
return CacheEntry.fromJson(jsonData);
|
||||
} catch (e) {
|
||||
debugPrint('$_tag: Error deserializing cache entry $key: $e');
|
||||
await _prefs?.remove(storageKey);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String _getStorageKey(String key) => 'cache_$key';
|
||||
|
||||
// ========================================
|
||||
// NETTOYAGE ET MAINTENANCE
|
||||
// ========================================
|
||||
|
||||
/// Nettoie les entrées expirées
|
||||
Future<void> _cleanExpiredEntries() async {
|
||||
// Nettoyer le cache mémoire
|
||||
final expiredMemoryKeys = _memoryCache.entries
|
||||
.where((entry) => entry.value.isExpired)
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
|
||||
for (final key in expiredMemoryKeys) {
|
||||
_memoryCache.remove(key);
|
||||
}
|
||||
|
||||
// Nettoyer le cache persistant
|
||||
final allKeys = _prefs?.getKeys().where((k) => k.startsWith('cache_')).toList() ?? [];
|
||||
int cleanedCount = 0;
|
||||
|
||||
for (final storageKey in allKeys) {
|
||||
final key = storageKey.substring(6); // Enlever 'cache_'
|
||||
final entry = await _getFromStorage(key);
|
||||
if (entry?.isExpired == true) {
|
||||
await _prefs?.remove(storageKey);
|
||||
cleanedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$_tag: Cleaned ${expiredMemoryKeys.length} memory entries and $cleanedCount storage entries');
|
||||
}
|
||||
|
||||
/// Nettoie périodiquement le cache
|
||||
void startPeriodicCleanup() {
|
||||
Timer.periodic(const Duration(minutes: 30), (_) {
|
||||
_cleanExpiredEntries();
|
||||
});
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// STATISTIQUES
|
||||
// ========================================
|
||||
|
||||
/// Obtient les statistiques du cache
|
||||
CacheStats getStats() => _stats;
|
||||
|
||||
/// Obtient des informations détaillées sur le cache
|
||||
Future<CacheInfo> getCacheInfo() async {
|
||||
final memorySize = _memoryCache.length;
|
||||
final storageKeys = _prefs?.getKeys().where((k) => k.startsWith('cache_')).length ?? 0;
|
||||
|
||||
return CacheInfo(
|
||||
memoryEntries: memorySize,
|
||||
storageEntries: storageKeys,
|
||||
stats: _stats,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Niveaux de cache
|
||||
enum CacheLevel {
|
||||
memory, // Cache en mémoire uniquement
|
||||
storage, // Cache persistant uniquement
|
||||
both, // Les deux niveaux
|
||||
}
|
||||
|
||||
/// Entrée de cache
|
||||
class CacheEntry {
|
||||
final String key;
|
||||
final dynamic value;
|
||||
final DateTime timestamp;
|
||||
final Duration duration;
|
||||
final bool compressed;
|
||||
|
||||
CacheEntry({
|
||||
required this.key,
|
||||
required this.value,
|
||||
required this.timestamp,
|
||||
required this.duration,
|
||||
this.compressed = false,
|
||||
});
|
||||
|
||||
bool get isExpired => DateTime.now().difference(timestamp) > duration;
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'key': key,
|
||||
'value': value,
|
||||
'timestamp': timestamp.millisecondsSinceEpoch,
|
||||
'duration': duration.inMilliseconds,
|
||||
'compressed': compressed,
|
||||
};
|
||||
|
||||
factory CacheEntry.fromJson(Map<String, dynamic> json) => CacheEntry(
|
||||
key: json['key'],
|
||||
value: json['value'],
|
||||
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp']),
|
||||
duration: Duration(milliseconds: json['duration']),
|
||||
compressed: json['compressed'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Statistiques du cache
|
||||
class CacheStats {
|
||||
int _hits = 0;
|
||||
int _misses = 0;
|
||||
int _writes = 0;
|
||||
|
||||
int get hits => _hits;
|
||||
int get misses => _misses;
|
||||
int get writes => _writes;
|
||||
|
||||
double get hitRate => (_hits + _misses) > 0 ? _hits / (_hits + _misses) : 0.0;
|
||||
|
||||
void incrementHits() => _hits++;
|
||||
void incrementMisses() => _misses++;
|
||||
void incrementWrites() => _writes++;
|
||||
|
||||
void reset() {
|
||||
_hits = 0;
|
||||
_misses = 0;
|
||||
_writes = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'CacheStats(hits: $_hits, misses: $_misses, writes: $_writes, hitRate: ${(hitRate * 100).toStringAsFixed(1)}%)';
|
||||
}
|
||||
|
||||
/// Informations sur le cache
|
||||
class CacheInfo {
|
||||
final int memoryEntries;
|
||||
final int storageEntries;
|
||||
final CacheStats stats;
|
||||
|
||||
CacheInfo({
|
||||
required this.memoryEntries,
|
||||
required this.storageEntries,
|
||||
required this.stats,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => 'CacheInfo(memory: $memoryEntries, storage: $storageEntries, $stats)';
|
||||
}
|
||||
Reference in New Issue
Block a user