Flutter单例模式实现插件singleton的使用

Flutter单例模式实现插件singleton的使用

Singleton 是一个非常有用的模式,在Flutter应用中可以帮助我们优化对象的实例化。它可以减少内存浪费,提高应用加载速度,并且可以在多个地方复用同一个对象。

为什么创建这个库

Singleton 模式可以用于以下场景:

  • 延迟实例化对象以加速应用加载。
  • 按需实例化对象,减少未使用的对象占用的内存。
  • 对象创建复杂或依赖于某些资源,但在整个应用中广泛使用。
  • 对象依赖异步资源,但需要同步方式使用。

但是有时候使用 Singleton 模式会带来一些麻烦,比如:

  • 每次使用时都需要手动实现延迟模式,这很繁琐。
  • Singleton 不利于单元测试,因为它们在整个测试过程中存在,可能会导致意外的测试失败。
  • 依赖异步资源的 Singleton 管理起来比较复杂。

Singleton 库旨在简化这些场景,使开发者能够更优雅地在 Dart 中使用 Singleton

这个库能做什么

该库主要支持三种类型的 Singleton 使用:

  • 惰性单例:类型按需延迟创建。它类似于 Kotlin 的 lazy 委托属性。
  • 急切单例:类型创建复杂或依赖于某些仅在特定情况下可用的资源,因此提前创建实例是个好主意。
  • 未来单例:类型依赖于异步资源进行实例化,但需要在强制同步的环境中使用。
  • 允许为测试重置所有注册的 Singleton,从而减轻跨测试污染。

该库设计时考虑了 Flutter,但它不依赖任何 Flutter 特定的代码,因此可以在任何 Dart 工作的地方使用。

惰性单例

手动实现惰性单例

你可能会多次编写类似的代码:

class MyLazyService {
  static MyLazyService _instance;
  static MyLazyService get instance {
    if (_instance == null) {
      _instance = MyLazyService._();
    }

    return _instance;
  }

  /// 私有构造函数
  MyLazyService._() {
  }

  /// 执行某些操作
  void doSomething() {

  }
}

MyLazyService.instance.doSomething();

这种方法虽然可行,但写起来很枯燥,而且会影响测试。

使用 Singleton 实现惰性单例

class MyLazyService {
  /// 工厂方法自动复用相同的实例
  factory MyLazyService() => Singleton.lazy(() => MyLazyService._()).instance;

  /// 私有构造函数
  MyLazyService._() {}

  /// 执行某些操作
  void doSomething() {}
}

MyLazyService().doSomething(); // 看起来像新的实例,但实际上是一个单例。

急切单例

class MyEagerService {
  /// 工厂方法自动复用相同的实例
  factory MyEagerService() => Singleton<MyEagerService>().instance;

  final MyApi api;

  /// 构造函数创建并注册新实例
  MyEagerService.initialize(this.api) {
    // 注册当前实例
    Singleton.register(this);
  }

  /// 执行某些操作
  void doSomething() {}
}

void main() {
  final appSettings = getAppSettings();
  final httpClient = createHttpClient(appSetting);
  final api = createApi(httpClient);

  MyEagerService.initialize(api) // 创建并注册单例
                .doSomething();  // 使用实例
}

MyEagerService().doSomething(); // 使用单例实例

未来单例

处理依赖异步资源的单例可能有些棘手。不幸的是,在 Flutter/Dart 中,异步资源无处不在。

Singleton 库一起处理这种情况

class AppSettings {
  static Future<AppSettings> loadAppSettings() {
    // 从某个地方异步加载应用设置
  }
}

class HttpClient {
  final AppSettings appSettings;

  HttpClient(this.appSettings);
}

使用 Singleton 库定义类型:

class MyFutureService {
  /// 工厂方法自动复用相同的实例
  factory MyFutureService() => Singleton<MyFutureService>().instance;

  static Future<MyFutureService> createInstance() async {
    final appSettings = await Singleton<AppSettings>().ensuredInstance();

    final httpClient = HttpClient(appSettings);

    return MyFutureService._(httpClient);
  }

  final HttpClient httpClient;

  MyFutureService._(this.httpClient);

  /// 执行某些操作
  void doSomething() {}
}

注册未来单例。Singleton.register 了解 Future,它解析 Future 并将 Future 的值注册为单例。

void main() {
  // 将 AppSettings 设置为未来单例
  Singleton.register(AppSettings.loadAppSettings());

  // 创建并注册 MyService 作为单例
  Singleton.register(MyFutureService.createInstance());

  runApp();
}

使用未来单例

未来单例可以像其他类型的单例一样使用,

MyFutureService().doSomething();

但如果单例是在未来解析之前被使用,将会抛出一个 StateError 说 “Singleton is being used before being resolved”。可以通过确保实例在检查点处执行实例创建来避免此错误:

await Singleton.ensureInstanceFor(MyFutureService);

可以同时检查多个类型:

await Singleton.ensureInstanceFor([MyFutureService, AppSettings]);

错误处理

如果未来的错误被抛出,该错误不会丢失,它会在调用 Singleton.ensureInstanceForSingleton.instance 时重新抛出。

支持单元测试

由于 Singleton 跨越测试边界,它们可能会导致意外的测试失败。通过 Singleton 库创建的 Singleton 可以通过仅在测试中可见的 API 清除。

setUp(() {
  // 在设置环境之前重置单例注册表,以避免潜在的污染
  Singleton.resetAllForTest();

  Singleton.register(...);
});

tearDown(() {
  Singleton.resetAllForTest(); // 重置单例注册表,避免单例污染
});

查看已注册的单例

有时你可能希望检查单例的状态以进行诊断。你可以通过以下方式实现:

Singleton.debugPrintAll();

或者只关心某些类型:

Singleton.debugPrintAll(MySingleton);

Singleton.debugPrintAll(Singleton<MySingleton>());

Singleton.debugPrintAll([MySingleton, AnotherSingleton]);

Singleton.debugPrintAll([Singleton<MySingleton>(), Singleton<AnotherSingleton>()]);

Singleton.debugPrintAll([Singleton<MySingleton>(), AnotherSingleton]);

删除单例

这应该是罕见的情况,但在某些极端情况下,如果你想摆脱你的单例。这是可能的:

Singleton<MySingleton>().deregister();

更多关于Flutter单例模式实现插件singleton的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter单例模式实现插件singleton的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。使用单例模式可以避免重复创建对象,节省资源,并且可以在整个应用程序中共享状态。

在Flutter中实现单例模式通常有两种方式:

  1. 使用工厂构造函数
  2. 使用Singleton插件

1. 使用工厂构造函数实现单例模式

class Singleton {
  // 私有静态实例变量
  static final Singleton _instance = Singleton._internal();

  // 私有构造函数
  Singleton._internal();

  // 工厂构造函数,返回唯一的实例
  factory Singleton() {
    return _instance;
  }

  // 示例方法
  void doSomething() {
    print("Doing something...");
  }
}

void main() {
  // 获取单例实例
  Singleton instance1 = Singleton();
  Singleton instance2 = Singleton();

  // 检查是否为同一个实例
  print(identical(instance1, instance2)); // 输出: true

  instance1.doSomething();
}

2. 使用Singleton插件实现单例模式

Singleton插件是一个第三方库,可以帮助你更简单地实现单例模式。你可以通过以下步骤使用它:

步骤1:添加依赖

pubspec.yaml文件中添加singleton插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  singleton: ^2.0.0

然后运行flutter pub get来安装依赖。

步骤2:使用Singleton插件

import 'package:singleton/singleton.dart';

class MyService {
  // 使用Singleton注解
  [@Singleton](/user/Singleton)(
    deprecated: false,
    lazy: true,
  )
  static final MyService _instance = MyService._internal();

  // 私有构造函数
  MyService._internal();

  // 获取单例实例
  static MyService get instance => _instance;

  // 示例方法
  void doSomething() {
    print("Doing something...");
  }
}

void main() {
  // 获取单例实例
  MyService instance1 = MyService.instance;
  MyService instance2 = MyService.instance;

  // 检查是否为同一个实例
  print(identical(instance1, instance2)); // 输出: true

  instance1.doSomething();
}
回到顶部