Nhảy tới nội dung

Object Pool (Bể Đối Tượng)

Mẫu thiết kế Object Pool (Bể Đối Tượng) là một mẫu thiết kế khởi tạo phần mềm sử dụng một tập hợp các đối tượng đã được khởi tạo sẵn – gọi là một "pool" (bể) – thay vì liên tục cấp phát (allocate) và hủy (destroy) chúng theo yêu cầu. Khi cần sử dụng, client sẽ yêu cầu một đối tượng từ pool và thực hiện các thao tác trên đối tượng đó. Sau khi hoàn thành, client trả đối tượng về pool thay vì hủy nó. Việc này mang lại sự cải thiện hiệu năng đáng kể trong các tình huống mà chi phí khởi tạo và hủy đối tượng lớn, đồng thời giúp ngăn ngừa hiện tượng phân mảnh bộ nhớ.

Phân mảnh bộ nhớ (Memory fragmentation)

Phân mảnh bộ nhớ là tình trạng bộ nhớ bị chia nhỏ thành các khối nhỏ rời rạc, không liên tục. Điều này xảy ra khi việc cấp phát và thu hồi bộ nhớ diễn ra theo thứ tự ngẫu nhiên. Dẫn đến tình trạng bộ nhớ không được sử dụng hiệu quả: bộ nhớ bị phân mảnh thành nhiều khối nhỏ, nhưng chương trình lại cần một khối bộ nhớ lớn liên tục. Tình trạng này có thể khiến chương trình báo hết bộ nhớ ngay cả khi tổng lượng bộ nhớ còn trống vẫn đủ.

Mẫu Thiết Kế Object Pool

Phân mảnh bộ nhớ cũng như chi phí cấp phát / thu hồi bộ nhớ có thể rất tốn kém. Một giải pháp đơn giản là xin cấp phát một khối bộ nhớ lớn ngay khi trò chơi bắt đầu, và giữ nguyên khối bộ nhớ đó cho đến khi trò chơi kết thúc.

Dưới đây là một ví dụ đơn giản về một Object Pool quản lý danh sách đạn (Bullet). Pool được khởi tạo với một lớp đại diện cho đối tượng cần lưu trữ. Pool cung cấp phương thức để lấy đối tượng ra (get) và phương thức để trả đối tượng về pool (release).

class ObjectPool {
constructor(ObjectClass) {
this.ObjectClass = ObjectClass;
this._pool = [];
}

get() {
if (this._pool.length > 0) {
return this._pool.pop();
}
return new this.ObjectClass();
}

release(obj) {
this._pool.push(obj);
}

clear() {
this._pool = [];
}
}

class GameObject {
constructor() {
this.x = 0;
this.y = 0;
this.active = false;
}
}

class Bullet extends GameObject {
constructor() {
super();
this.speed = 5;
}

update(dt) {
this.x += this.speed * dt;
}
}

const bulletPool = new ObjectPool(Bullet);
const bullets = [];
let timeCounter = 0;

function update(dt) {
timeCounter += dt;

// sinh ra đạn mỗi giây
if (timeCounter >= 1) {
spawnBullets();
timeCounter = 0;
}

// cập nhật vị trí đạn và thu hồi đạn khi bay khỏi màn hình
for (let i = 0; i < bullets.length; i++) {
const bullet = bullets[i];
bullet.update(dt);
if (bullet.x > 800) {
bullet.active = false;
bulletPool.release(bullet);
bullets.splice(i, 1);
i--;
}
}
}

function spawnBullets() {
// sinh ngẫu nhiên từ 1 đến 10 viên đạn
let numBullets = Math.random() * 10;
for (let i = 0; i < numBullets; i++) {
const bullet = bulletPool.get();
bullet.active = true;
bullet.y = Math.random() * 800;
bullets.push(bullet);
}
}