提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

        以前一直是复制粘贴,如今有点时间了,开始回头思考一些理所当然的代码,说实话,单例用了很久了,但是依然不知道为啥这么用,今天好好总结下,好摆脱只会复制粘贴的小白。感谢可可鸭,他的终结通俗易懂,浅显直白,本文很大一部分引用子他的博文,特此表示感谢。

       工厂方法构造函数:如果一个构造函数不总是返回一个新对象,则用foctory来定义这个构造函数。例如一个工厂构造函数,可能从缓存中获取一个实例并返回,或者返回一个子类型的实例。使用单例本身目的在于创建一个唯一的对象,避免该对象可能会出现多头使用,造成数据冲突,还一个好处-----减少内存消耗。

一般来说,要在代码中使用单例模式,结构上会有下面这些约定俗成的要求:

单例类(Singleton)中包含一个引用自身类的静态属性实例(instance),且能自行创建这个实例。该实例只能通过静态方法 getInstance() 访问。类构造函数通常没有参数,且被标记为私有,确保不能从类外部实例化该类,遵循以上这些要求,我们就不难能用 Dart 写出一个普通的单例模式如例一;


一、使用步骤

在网上一共找到了3中单例模式:

第一种:例一

普通实现

class Singleton {
    ///静态实例
  static Singleton? _instance;
  
  /// 私有的命名构造函数,在此处初始化
  Singleton._internal();
  
    ///定义getInstance方法获取实例
  static Singleton getInstance() {
      ///如果定义的实例为空,将构造函数赋值给实例
    if (_instance == null) {
      _instance = Singleton._internal();
    }
    
      ///返回实例
    return _instance!;
  }
}

 第二种:

利用getter实现

Dart 语言作为单线程模型的语言,实现单例模式时,我们本身已经可以不用再去考虑 线程安全 的问题了。Dart 的很多其他特性也依然可以帮助到我们实现更加 Dart 化的单例。使用 getter 操作符,可以打破单例模式中既定的,一定要写一个 getInstance()静态方法的规则,简化我们必须要

写的模版化代码,如下的 get instance:


class Singleton {

  static Singleton? _instance;

  Singleton._internal();

    ///用get直接获取实例
  static get instance {
  
    if (_instance == null) {
      _instance = Singleton._internal();
    }

    return _instance;
  }
}

Dart 的 getter 的使用方式与普通方法大致相同,只是调用者不再需要使用括号,这样,我们在使用时就可以直接使用如下方式拿到这个单例对象:

final singleton = Singleton.instance;

第三种:

利用工厂构造函数factory实现

有这么一句话:
当你需要构造函数不是每次都创建一个新的对象时,使用factory关键字。

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 语言特有的单例模式的实现方式就这么产生了。

第四种:

利用判空符??和延迟初始化符号late

class Singleton {
  static Singleton? _instance;

  Singleton._internal() {
    _instance = this;
  }
	///判空符??
  factory Singleton() => _instance ?? Singleton._internal();
}


class Singleton {
  Singleton._internal();
  
  factory Singleton() => _instance;
  
  static late final Singleton _instance = Singleton._internal();
}

1.步骤

代码如下(示例):

构造函数并不是每次创建一个对象,使用factory关键字
class 类名{
    //静态私有成员,没有初始化
    static 类名 _单例名
    //单列公开访问点
    factory 类名() =>静态获取单例方法
    //私有构造函数
    类名._构造函数名称();{若要初始化}
    //对外暴露单例
    static 类名 _静态获取单例的方法(){
    if(_单例名==null){
        _单例名 = 类名._构造函数名称();
    }
    return _单例名;
    }
} 

2.读入数据

代码如下(示例):

class B{
  /*
  1.使用单例本身目的:该工具类使用比较频繁,全局公用一个就可以减少内存的消耗,想要这个类的对象唯一就要对它的构造方法进行修改
  2.步骤:*/
  //首先用静态实例化一个类对象,这个类对象是私有成员变量。
  static B _instance = B._internal();
  //然后修改构造方法,提供了一个工厂方法获取该类的实例,将实例对象对应的方法返回出去
  factory B(){
    return _instance;
  }
  //通过私有方法隐藏构造方法,防止被误创建
  B._internal(){
  //此处进行初始化操作
  } 
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐