Dart单例模式深度解析
class Logger {
static final Logger _instance = Logger._internal();
// 私有命名构造函数
Logger._internal();
// 工厂构造函数,总是返回唯一的实例
factory Logger() {
return _instance;
}
}
1. 解读 static final Logger _instance = Logger._internal();
首先,我们来分析这行代码的每一个关键字:
-
static: 这个关键字意味着_instance变量不属于任何一个Logger对象,而是属于Logger这个类本身。它在内存中只有一份。程序第一次使用Logger类时,这个静态变量就会被初始化,且只初始化这一次。 -
final: 这表示_instance变量在被赋值一次后,就不能再被修改了。它的引用地址是固定的,确保了我们始终指向同一个Logger实例。 -
= Logger._internal(): 这是最关键的部分。这里不是“复制构造函数”,而是 “调用”Logger._internal()这个私有命名构造函数来创建一个真实的Logger实例,然后把这个新创建的实例赋值给_instance变量。
问:Logger 类内部可以使用自己的 Logger 吗? 答:可以,而且必须可以! 一个类当然可以在自己的定义内部创建自己的实例。正是因为
_instance的声明在Logger类的内部,所以它有权限调用私有的Logger._internal()构造函数。如果是在类的外部,这个调用就会因为私有性而报错。
2. 谁先执行?静态变量初始化优先!
你的问题是“工厂构造函数优先执行吗”,答案是:不优先。执行顺序是这样的:
-
类加载时(第一次使用
Logger类时):-
Dart 运行时发现有一个静态变量
_instance需要初始化。 -
它立刻执行
= Logger._internal()这部分代码,调用私有构造函数创建了一个Logger对象。 -
这个对象被赋值给
_instance,并存储在内存中。 -
此时,单例对象已经创建好了,并且静静地等待被使用。
-
-
代码调用时(例如
var myLogger = Logger();):-
这时,
factory Logger()这个工厂构造函数才会被调用。 -
工厂构造函数内部的逻辑非常简单:
return _instance;。 -
它不去创建新对象,而是直接把你在第 1 步中已经创建好的那个
_instance对象返回给你。
-
所以,无论你调用多少次 Logger(),工厂构造函数每次都只是把你早已创建好的那个唯一的实例交给你。
3. 三者关系:分工明确的团队合作
你可以把这三个部分想象成一个分工合作的团队:
-
Logger._internal()(私有命名构造函数)-
角色:工人
-
职责:它的唯一任务就是实际地、真正地去创建一个
Logger对象。它被设为私有 (_),就是为了防止外部任何人随意雇佣这个“工人”去创建新的、多余的对象,保证了对象创建的唯一入口。
-
-
static final Logger _instance(静态实例)-
角色:仓库
-
职责:它是一个存放唯一产品的仓库。这个仓库在程序开始时,就让“工人”生产了一个产品并存了进去 (
= Logger._internal())。因为是static,所以全局只有一个仓库;因为是final,所以仓库里的产品不能被替换。
-
-
factory Logger()(工厂构造函数)-
角色:门店/客服
-
职责:这是对外服务的窗口。当你的代码(客户)说“我想要一个
Logger” (Logger()) 时,这个“客服”并不会去重新生产一个,而是直接去“仓库”里,把那个唯一的产品 (_instance) 拿出来交给你。
-
总结
-
执行顺序:
static变量的初始化先于工厂构造函数的调用。实例在第一次使用类时就已经被创建。 -
关系:
-
私有命名构造函数是真正的创建者。
-
静态
final变量是持有者,它持有那个被唯一创建的实例。 -
工厂构造函数是提供者,它不创造,只负责把持有的实例返回给调用方。
-
它们三者完美配合,共同实现了 Dart 中既安全又高效的单例模式。希望这个解释能让你豁然开朗!
更多推荐

所有评论(0)