# 【Flutter】设计模式 - 单例模式
学习目标
- 什么是单例?
- 单例有什么好处?
- 单例有哪些应用场景?
- 怎么用单例?
- 既然称之为模式,那除了单例还有其它什么模式?它们有什么用?
什么是单例
单例设计模式, singleton design pattern。
一个类只允许创建一个实例,那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
单例的要求
- 单例类(Singleton)中包含一个引用自身类的静态属性实例(instance),且能自行创建这个实例。
class Singleton {
static Singleton _instance;
}
- 该实例只能通过静态方法 getInstance() 访问。
class Singleton {
static Singleton _instance;
static getInstance(){
if(_instance == null) { //只能有一个示例
_instance = Singleton();
}
return _instance;
}
}
- 类构造函数通常没有参数,且被标记为私有,确保不能从类外部实例化该类。
class Singleton {
static Singleton _instance;
static getInstance(){
if(_instance == null) { //只能有一个示例
_instance = Singleton();
}
return _instance;
}
//内部构造方法
Singleton._internal() {
}
}
单例模式的几种实例
普通单例
class Singleton {
static Singleton _instance; //静态属性实例
Singleton._internal(); //私有的命名构造函数
static getInstance (){ //外部访问实例的方法
if(_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
使用: Singleton.getInstance().XXX;
Dart化单例
为什么有Dart化?
使用单例时要考虑线程安全,在java、c++等多线程语言中需要考虑到多线程并发问题。 但是由于Dart是单线程模型的语言,所有的代码通常都运行在同一个isolate中,因此不需要考虑线程安全的问题。 这意味着我们可以利用dart的特性,简化单例。
使用getter操作符Dart化单例
class Singleton {
static Singleton _instance; //静态属性实例
Singleton._internal(); //私有的命名构造函数
static get instance { //外部访问实例的方法
if(_instance == null) { //懒加载
_instance = Singleton._internal();
}
return _instance;
}
}
使用: Singleton.instance.XXX;
使用工厂构造函数Dart化单例
工厂构造函数具备了不必每次都去创建新的类实例的特性。
class Singleton {
static Singleton _instance; //静态属性实例
Singleton._internal(); //私有的命名构造函数
factory Singleton (){ //外部访问实例的方法
if(_instance == null) {
_instance = Singleton._internal();
}
return _instance;
}
}
这里不需要再使用get操作符额外提供一个函数,而是将单例对象的生成交给工厂构造函数。此时,工厂构造函数仅在第一次需要时创建_instance,并之后每次返回相同的实例。
使用: final singleton = Singleton();
使用 ?? Dart化单例
class Singleton {
static Singleton _instance; //静态属性实例
Singleton._internal() { //私有的命名构造函数
_instance = this;
};
//外部访问实例的方法
factory Singleton () => _instance ?? Singleton._internal();
}
使用late Dart化单例
被标记为 late 的变量 _instance 的初始化操作将会延迟到字段首次被访问时执行,而不是在类加载时就初始化。
class Singleton {
static Singleton _instance; //静态属性实例
//私有的命名构造函数
static late final Singleton _instance = Singleton._internal();
//外部访问实例的方法
factory Singleton () => _instance
}
Flutter化单例
InheritedWidget 状态可遗传的特性可以帮助我们很方便的实现父子组件之间的数据传递,同时,它也可以作为状态管理中的 数据仓库,作为整个应用的数据状态统一保存的地方。
上面代码中,我们通过继承 InheritedWidget 就实现了自己的可遗传组件 _InheritedStateContainer
,其中的 data
变量表示全局状态数据,在这里就可以被认为是整个应用的一个单例对象。
_InheritedStateContainer
还接受 child
参数作为它的子组件,child
表示的所以子组件们就都能够以某种方式得到 data
这个单一的全局数据了。
约定俗成地,Flutter 源码经常会提供一些 of
方法(类比 getInstance()
)作为帮助我们拿到全局数据的辅助函数。
以 Flutter 中典型的 Theme 对象为例。我们通常会在应用的根组件 MaterialApp
中创建 ThemeData
对象作为应用统一的主题样式对象:
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
在其他任意的组件中,我们可以使用 Theme.of(context)
拿到该对象了,且这个对象全局唯一。如下所示,我们可以将该 ThemeData
对象中的 primaryColor
应用在 Text
中:
// 使用全局文本样式
Text(
'Flutter',
style: TextStyle(color: Theme.of(context).primaryColor),
)
单例有什么好处?
工程构造函数和懒加载特性(只创建一次实例):避免实例对象的重复创建,减少每次创建对象的时间开销,节约内存空间,避免操作多个实例导致的逻辑错误。
单例有哪些应用场景?
如果一个对象有可能贯穿整个应用程序的生命周期,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。
- 全局日志的 Logger 类、应用全局的配置数据对象类,网络请求方法类,单业务管理类。
- 创建实例时占用资源较多,或实例化耗时较长的类。
- 等等…
怎么用单例?
普通单例和Dart化的单例
共同点:静态属性实例
static Singleton _instance;
//静态属性实例
不同点:
更适合项目用的单例模式
class Singleton {
static Singleton _instance; //静态属性实例
//内部私有构造方法
static late final Singleton _shared() => Singleton._internal() ;
factory Singleton.shared() => _shared(); //供外部访问实例的方法
}
使用: Singleton.shared().XXX
既然称之为模式,那除了单例还有其它什么模式?它们有什么用?
什么是设计模式?
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。
共有23种设计模式,可另行了解。