object_pool 1.0.0
object_pool: ^1.0.0 copied to clipboard
A high-performance generic object pool for Dart and Flutter that reduces garbage collection pressure by reusing pre-allocated objects, ideal for games, animations, particle systems, and performance-cr [...]
// ignore_for_file: avoid_print
/// Comprehensive usage examples for the `object_pool` package.
///
/// Run with: `dart run example/main.dart`
library;
import 'package:object_pool/object_pool.dart';
// ─── Example 1: Basic Usage ───────────────────────────────────────────────────
class Bullet implements Poolable {
double x = 0;
double y = 0;
double speed = 0;
int damage = 0;
bool active = false;
void fire({
required double x,
required double y,
required double speed,
required int damage,
}) {
this.x = x;
this.y = y;
this.speed = speed;
this.damage = damage;
}
@override
void onAcquire() => active = true;
@override
void onRelease() {
x = 0;
y = 0;
speed = 0;
damage = 0;
active = false;
}
@override
bool get isValid => true;
@override
String toString() =>
'Bullet(x: ${x.toStringAsFixed(1)}, y: ${y.toStringAsFixed(1)}, '
'speed: $speed, damage: $damage, active: $active)';
}
void example1BasicUsage() {
print('\n════════════════════════════════════════');
print(' Example 1 — Basic Acquire / Release');
print('════════════════════════════════════════');
// Create a pool of 10 Bullets, max 50
final bulletPool = ObjectPool<Bullet>(
factory: Bullet.new,
initialSize: 10,
maxSize: 50,
);
print('Pool created: $bulletPool');
// Acquire a bullet
final b1 = bulletPool.acquire()!;
b1.fire(x: 100, y: 200, speed: 15.0, damage: 25);
print('Acquired → $b1');
print('Pool state: ${bulletPool.activeCount} active, '
'${bulletPool.availableCount} available');
// Acquire a second
final b2 = bulletPool.acquire()!;
b2.fire(x: 300, y: 150, speed: 20.0, damage: 50);
print('Acquired → $b2');
// Release the first — it returns to pool and gets cleaned up
bulletPool.release(b1);
print('Released → b1 is now: $b1');
print('Pool state: ${bulletPool.activeCount} active, '
'${bulletPool.availableCount} available');
// Reuse the same object
final b3 = bulletPool.acquire()!;
b3.fire(x: 50, y: 50, speed: 10.0, damage: 5);
print('Reacquired → same object as b1? ${identical(b1, b3)}');
print('b3 = $b3');
bulletPool.release(b2);
bulletPool.release(b3);
bulletPool.dispose();
print('Pool disposed.');
}
// ─── Example 2: Scoped use() — RAII Pattern ───────────────────────────────────
class Matrix4x4 {
final List<double> data = List.filled(16, 0.0);
void setIdentity() {
for (var i = 0; i < 16; i++) {
data[i] = (i % 5 == 0) ? 1.0 : 0.0;
}
}
double get determinant {
// Simplified — just for demo
return data[0] * data[5] * data[10] * data[15];
}
}
void example2ScopedUse() {
print('\n════════════════════════════════════════');
print(' Example 2 — Scoped use() / useAsync()');
print('════════════════════════════════════════');
final matrixPool = ObjectPool<Matrix4x4>(
factory: Matrix4x4.new,
initialSize: 20,
);
// Synchronous scoped use — guaranteed release
final det = matrixPool.use((m) {
m.setIdentity();
return m.determinant;
});
print('Matrix determinant: $det');
print('Active after use(): ${matrixPool.activeCount}');
// use() when pool is full with DiscardStrategy
final discardPool = ObjectPool<Matrix4x4>(
factory: Matrix4x4.new,
initialSize: 1,
strategy: const DiscardStrategy(),
);
discardPool.acquire(); // exhaust the pool
final result = discardPool.use((m) => m.determinant);
print('use() on exhausted pool returned: $result'); // null
matrixPool.dispose();
discardPool.dispose();
}
// ─── Example 3: Overflow Strategies ──────────────────────────────────────────
class AudioChannel {
int id;
String? currentTrack;
bool playing = false;
DateTime? startedAt;
AudioChannel(this.id);
void play(String trackName) {
currentTrack = trackName;
playing = true;
startedAt = DateTime.now();
print(' Channel $id → playing "$trackName"');
}
void stop() {
currentTrack = null;
playing = false;
startedAt = null;
}
}
void example3OverflowStrategies() {
print('\n════════════════════════════════════════');
print(' Example 3 — Overflow Strategies');
print('════════════════════════════════════════');
var channelId = 0;
// ── DiscardStrategy
print('\n[DiscardStrategy] — 4 channels max:');
final discardPool = ObjectPool<AudioChannel>(
factory: () => AudioChannel(++channelId),
initialSize: 4,
maxSize: 4,
strategy: const DiscardStrategy(),
);
final tracks = ['epic_theme', 'battle_music', 'victory', 'ambient'];
final channels = tracks.map((t) {
final ch = discardPool.acquire();
if (ch != null) {
ch.play(t);
} else {
print(' ✗ Dropped: "$t" (all channels busy)');
}
return ch;
}).toList();
final overflow = discardPool.acquire();
print(' 5th request: ${overflow == null ? "null (dropped)" : "got channel"}');
print(' Exhaustions: ${discardPool.metrics.exhaustionCount}');
for (final ch in channels) {
if (ch != null) discardPool.release(ch);
}
discardPool.dispose();
// ── ExpandStrategy
channelId = 0;
print('\n[ExpandStrategy] — starts with 2, grows to 10:');
final expandPool = ObjectPool<AudioChannel>(
factory: () => AudioChannel(++channelId),
initialSize: 2,
strategy: ExpandStrategy(maxSize: 10, growthIncrement: 2),
);
final expandedChannels = <AudioChannel>[];
for (var i = 0; i < 6; i++) {
final ch = expandPool.acquire();
if (ch != null) {
ch.play('track_$i');
expandedChannels.add(ch);
}
}
print(' Pool size after 6 acquires: ${expandPool.size}');
print(' Expansions: ${expandPool.metrics.expansionCount}');
for (final ch in expandedChannels) expandPool.release(ch);
expandPool.dispose();
}
// ─── Example 4: prewarm() for Performance-Critical Moments ───────────────────
class GameEntity implements Poolable {
int id = 0;
double x = 0, y = 0;
String type = '';
bool alive = false;
@override
void onAcquire() => alive = true;
@override
void onRelease() {
id = 0;
x = 0;
y = 0;
type = '';
alive = false;
}
@override
bool get isValid => true;
}
void example4Prewarm() {
print('\n════════════════════════════════════════');
print(' Example 4 — prewarm() / drain()');
print('════════════════════════════════════════');
final entityPool = ObjectPool<GameEntity>(
factory: GameEntity.new,
initialSize: 10,
maxSize: 1000,
);
print('Initial size: ${entityPool.size}');
// Simulate "level loading" — prewarm to avoid first-frame allocations
print('Loading level... prewarm(500)...');
entityPool.prewarm(500);
print('Pool ready: ${entityPool.size} entities pre-allocated');
// Simulate level play — spawn many entities
final activeEntities = <GameEntity>[];
for (var i = 0; i < 200; i++) {
final e = entityPool.acquire()!;
e.id = i;
e.x = (i * 7.3) % 1920;
e.y = (i * 4.1) % 1080;
e.type = i % 3 == 0 ? 'enemy' : (i % 3 == 1 ? 'projectile' : 'pickup');
activeEntities.add(e);
}
print('Level playing: ${entityPool.activeCount} entities active');
print('Pool metrics: ${entityPool.metrics.utilizationRate.toStringAsFixed(2)} utilization');
// Level ends — release all entities
for (final e in activeEntities) entityPool.release(e);
print('Level ended: ${entityPool.activeCount} active, ${entityPool.availableCount} available');
// Drain idle entities to free memory between levels
entityPool.drain();
print('After drain: ${entityPool.size} total, ${entityPool.availableCount} available');
entityPool.dispose();
}
// ─── Example 5: Performance Metrics Dashboard ────────────────────────────────
void example5MetricsDashboard() {
print('\n════════════════════════════════════════');
print(' Example 5 — Metrics Dashboard');
print('════════════════════════════════════════');
final pool = ObjectPool<Bullet>(
factory: Bullet.new,
initialSize: 50,
maxSize: 200,
);
// Simulate 1000 acquire/release cycles
for (var i = 0; i < 1000; i++) {
pool.use((b) {
b.fire(
x: (i * 3.7) % 1920,
y: (i * 2.1) % 1080,
speed: 15.0,
damage: 10,
);
});
}
// Simulate some exhaustion
final held = <Bullet>[];
for (var i = 0; i < 50; i++) {
held.add(pool.acquire()!);
}
for (var i = 0; i < 5; i++) {
pool.acquire(); // hits expand strategy
}
for (final b in held) pool.release(b);
print(pool.metrics);
pool.dispose();
}
// ─── Main ─────────────────────────────────────────────────────────────────────
void main() {
print('╔══════════════════════════════════════╗');
print('║ object_pool — Usage Examples ║');
print('╚══════════════════════════════════════╝');
example1BasicUsage();
example2ScopedUse();
example3OverflowStrategies();
example4Prewarm();
example5MetricsDashboard();
print('\n✅ All examples completed successfully.\n');
}