文 / 杨加康,CFUG 社区成员,《Flutter 开发之旅从南到北》作者,小米工程师
单例设计模式(Singleton Design Pattern)了解起来非常简单。
一个类只容许创立一个实例,那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
作为最简略的一种设计模式之一,对于单例自身的概念,大家一看就能明确,但在某些状况下也很容易应用不失当。相比其余语言,Dart 和 Flutter 中的单例模式也不尽相同,本篇文章咱们就一起探索看看它在 Dart 和 Flutter 中的利用。
Flutter(able) 的单例模式
一般来说,要在代码中应用单例模式,构造上会有上面这些约定俗成的要求:
- 单例类(Singleton)中蕴含一个援用本身类的动态属性实例(instance),且能自行创立这个实例。
- 该实例只能通过静态方法
getInstance()
拜访。 - 类构造函数通常没有参数,且被标记为公有,确保不能从类内部实例化该类。
遵循以上这些要求,咱们就不难能用 Dart 写出一个一般的单例模式:
class Singleton {
static Singleton _instance;
// 公有的命名构造函数
Singleton._internal();
static Singleton getInstance() {if (_instance == null) {_instance = Singleton._internal();
}
return _instance;
}
}
同时,在实现单例模式时,也须要思考如下几点,以防在应用过程中呈现问题:
- 是否须要懒加载,即类实例只在第一次须要时创立。
- 是否线程平安,在 Java、C++ 等多线程语言中须要思考到多线程的并发问题。因为 Dart 是单线程模型的语言,所有的代码通常都运行在同一个 isolate 中,因而不须要思考线程平安的问题。
- 在某些状况下,单例模式会被认为是一种 反模式,因为它违反了 SOLID 准则中的繁多责任准则,单例类本人管制了本人的创立和生命周期,且单例模式个别没有接口,扩大艰难。
- 单例模式的应用会影响到代码的可测试性。如果单例类依赖比拟重的内部资源,比方 DB,咱们在写单元测试的时候,心愿能通过 mock 的形式将它替换掉。而单例类这种硬编码式的应用形式,导致无奈实现 mock 替换。
在理论编码过程中,单例模式常见利用有:
- 全局日志的 Logger 类、利用全局的配置数据对象类,单业务管理类。
- 创立实例时占用资源较多,或实例化耗时较长的类。
- 等等 …
Dart 化
如上文所说的,Dart 语言作为单线程模型的语言,实现单例模式时,咱们自身曾经能够不必再去思考 线程平安 的问题了。Dart 的很多其余个性也仍然能够帮忙到咱们实现更加 Dart 化的单例。
应用 getter 操作符,能够突破单例模式中既定的,肯定要写一个 getInstance()
静态方法的规定,简化咱们必须要写的模版化代码,如下的 get instance
:
class Singleton {
static Singleton _instance;
static get instance {if (_instance == null) {_instance = Singleton._internal();
}
return _instance;
}
Singleton._internal();}
Dart 的 getter 的应用形式与一般办法大致相同,只是调用者不再须要应用括号,这样,咱们在应用时就能够间接应用如下形式拿到这个单例对象:
final singleton = Singleton.instance;
而 Dart 中特有的 工厂构造函数 (factory constructor)也原生具备了 不用每次都去创立新的类实例 的个性,将这个个性利用起来,咱们就能够写出更优雅的 Dart(able) 单例模式了,如下:
class Singleton {
static Singleton _instance;
Singleton._internal();
// 工厂构造函数
factory Singleton() {if (_instance == null) {_instance = Singleton._internal();
}
return _instance;
}
}
这里咱们不再应用 getter 操作符额定提供一个函数,而是将单例对象的生成交给工厂构造函数,此时,工厂构造函数仅在第一次须要时创立 _instance
,并之后每次返回雷同的实例。这时,咱们就能够像上面这样应用一般构造函数的形式获取到单例了:
final singleton = Singleton();
如果你还把握了 Dart 空平安及箭头函数等个性,那么还能够应用另一种形式进一步精简代码,写出像上面这样 Dart 风味十足的代码:
class Singleton {
static Singleton _instance;
Singleton._internal() {_instance = this;}
factory Singleton() => _instance ?? Singleton._internal();
}
这里,应用 ??
作为 _instance
实例的判空操作符,如果为空则调用构造函数实例化否则间接返回,也能够达到单例的成果。
以上,Dart 单例中懒加载的无不是应用判空来实现的(if (_instance == null)
或 ??
),然而在 Dart 空平安个性里还有一个十分重要的操作符 late
,它在语言层面就实现了实例的懒加载,如上面这个例子:
class Singleton {Singleton._internal();
factory Singleton() => _instance;
static late final Singleton _instance = Singleton._internal();}
被标记为 late
的变量 _instance
的初始化操作将会提早到字段首次被拜访时执行,而不是在类加载时就初始化。这样,Dart 语言特有的单例模式的实现形式就这么产生了。
Flutter 化
说到工厂构造函数 / 空平安操作符等 Dart 语法上的个性,Flutter 利用中的例子曾经不足为奇了,但光看单例模式的定义,咱们还必须联想到 Flutter 中另一个十分重要的 widget,那就是 InheritedWidget。
如果你曾经是一个 Flutter 小能手,或者曾经看过《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),
)
这个角度来看,InheritedWidget 齐全能够被咱们看作是最原生、最 Flutter 的单例利用了。
本文小结
本篇文章,咱们经验了从实现一般单例到利用 getter 操作符 的 Dart 单例,到应用 工厂构造函数 Dart 单例,再到应用了 工厂构造函数 + 空平安语法 + 箭头函数 的 Dart 单例,最初联合对 InheritedWidget 概念的了解,看到了 Flutter 中特有的单例模式,算是每一步都走了一遍。但学习设计模式的重点还是在于理论利用,心愿大家今后在理论工程中能将这些概念用起来,如果你想更进一步了解 Dart 中的单例模式,能够参阅「 拓展浏览」学习更多,心愿对你有帮忙。
拓展浏览
- 图书《Flutter 开发之旅从南到北》—— 第 2 章、第 9 章
- 单例模式
- Dart 空平安
- 提早初始化
对于本系列文章
Flutter / Dart 设计模式从南到北(简称 Flutter 设计模式)系列内容预计两周公布一篇,着重向开发者介绍 Flutter 利用开发中常见的设计模式以及开发方式,旨在推动 Flutter / Dart 语言个性的遍及,以及帮忙开发者更高效地开发出高质量、可保护的 Flutter 利用。