Design Pattern trong Dart: Singleton

Introduction to the Singleton Pattern

The Singleton Pattern is one of the most widely used Creational Design Patterns. Its main goal is to ensure that a class has only one instance and provides a global point of access to it.

When to Use Singleton:

  • When exactly one instance of a class is needed (e.g., Logger, Database connection, AppConfig).
  • When object creation is expensive and needs to be reused.
  • When managing shared resources or global states (e.g., caching, configuration).

Implementing Singleton in Dart

Dart provides a simple and elegant way to implement the Singleton pattern. Below are two common approaches:


Approach 1: Using factory constructor (Most Common)

class Logger {
static final Logger _instance = Logger._internal();

factory Logger() {
return _instance;
}

Logger._internal(); // private named constructor

void log(String message) {
print("[LOG]: $message");
}
}

Explanation:

  • Logger._internal() is a private constructor used only once to initialize the instance.
  • factory Logger() always returns the same _instance.
  • This pattern ensures only one instance exists and is reused.

Real-World Example: Logging with Singleton

void main() {
var logger1 = Logger();
var logger2 = Logger();

logger1.log("App is running...");
logger2.log("Processing data...");

print(identical(logger1, logger2)); // true
}

Output:

[LOG]: App is running...
[LOG]: Processing data...
true

Approach 2: Using a static getter (Flutter-style)

class AppConfig {
static final AppConfig _instance = AppConfig._();

AppConfig._(); // private constructor

static AppConfig get instance => _instance;

String apiUrl = "https://api.example.com";
}

Usage:

void main() {
var config = AppConfig.instance;
print(config.apiUrl);

config.apiUrl = "https://dev.api.example.com";

print(AppConfig.instance.apiUrl); // updated value persists
}

Benefits of Singleton

  • Guarantees a single instance across the app.
  • Centralized control of shared resources.
  • Reduces memory and initialization overhead.

Drawbacks

  • Can introduce global state, which may hurt testability.
  • Violates Single Responsibility Principle if misused.
  • Not ideal for apps requiring multiple isolates unless explicitly managed.

Tips & Best Practices

  • Use Singleton only when it makes sense (logging, configuration, etc.).
  • Be careful when mixing Singletons with state management libraries like Provider, Riverpod, or GetX.
  • Prefer immutability for Singleton state whenever possible.

Summary Table

AttributeSingleton Pattern
PurposeEnsure a class has only one instance
Easy in Dart?Yes (factory + private constructor)
Common Use CasesLogger, Config, DatabaseService
Thread-safe?Yes in Dart (single-threaded by default)

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Lên đầu trang