Observer Pattern nên dùng class thường hay static class?
Minh Khoa
Tác giả
Nhưng thật ra câu hỏi sâu hơn là:
Event này thuộc scope nào?
Nó sống trong bao lâu?
Ai sở hữu nó?
Ai chịu trách nhiệm cleanup?
Vì với Observer Pattern, phần khó không phải là phát event.
Phát event rất dễ:
SomethingChanged?.Invoke();
Phần khó là quản lý lifecycle.
Static class: tiện, nhưng dễ nguy hiểm
Một static event bus thường trông như thế này:
public static class GameEvents
{
public static event Action<int> CoinChanged;
public static void RaiseCoinChanged(int coin)
{
CoinChanged?.Invoke(coin);
}
}
Khi dùng:
GameEvents.RaiseCoinChanged(currentCoin);
Và nơi nghe:
private void OnEnable()
{
GameEvents.CoinChanged += UpdateCoinText;
}
private void OnDisable()
{
GameEvents.CoinChanged -= UpdateCoinText;
}
Điểm mạnh của static là rất rõ:
- Không cần tạo instance.
- Không cần truyền reference.
- Gọi được từ nhiều nơi.
- Setup nhanh.
- Hợp với event global.
Ví dụ event global:
GamePaused
GameResumed
LanguageChanged
InternetConnectionChanged
PurchaseCompleted
Những event này không thuộc riêng một scene hay một gameplay session nào. Chúng mang tính toàn app, nên static có thể hợp lý.
Nhưng static có một vấn đề lớn:
Static sống lâu hơn hầu hết object trong scene.
Nếu một object subscribe vào static event nhưng quên unsubscribe, static event vẫn giữ reference tới object đó.
Ví dụ thiếu OnDisable:
private void OnEnable()
{
GameEvents.CoinChanged += UpdateCoinText;
}
Khi đổi scene, UI cũ bị destroy, nhưng static event vẫn có thể giữ callback cũ.
Kết quả có thể là:
- Callback bị gọi nhiều lần.
- UI cũ vẫn nhận event.
- Sound/VFX chạy trùng.
- Gặp
MissingReferenceException. - Bug xuất hiện sau khi đổi scene hoặc play lại trong Editor.
Nên static không sai.
Nhưng static đòi hỏi kỷ luật:
Subscribe rõ.
Unsubscribe rõ.
Reset state khi cần.
Không biến GameEvents thành thùng rác global.