美文网首页Android开发经验谈Android开发Android技术知识
实践GoF设计模式:Singleton Pattern 单例模式

实践GoF设计模式:Singleton Pattern 单例模式

作者: 程序老秃子 | 来源:发表于2022-04-18 15:12 被阅读0次

    前言

    QQ截图20220418145454.png

    我们在开发项目中,经常会遇到这种情况,为了节约所拥有的系统资源或数据的一致性时,需要保证系统中的某个类中有且只有唯一的一个实例,而且在其创建成功之后,我们将不能再次创建同类型的其他对象,也就是所有的操作都要以这个唯一的实例为中心,所以为了保证对象所具有的的唯一性,单例模式成为我们所具备的方法

    概念

    ● 设计模式中单例模式是最为简单的形式之一,而且是使用最广泛的设计模式之一,其目的是为了将类的一个对象成为系统中的唯一实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享

    ● 要想实现这一点,可以将客户端进行实例化,所以就可以将之作为只需要一种允许生成对象类的唯一实例机制,阻止其需要生成对象的所有访问,还可以队其使用工厂方式来对其限制实例化的过程,也被称之为静态方法,被称为静态方法是因为让其类的实例去生成另一个唯一实例而显得毫无意义

    ● 单例模式具有多种写法,例如饿汉式,懒汉式等等,对于多线程的场景也同样具有多种方法,例如双检锁头,atomic类等等

    优势

    ● 因为在系统内存中只能存在一个对象,所以可以节约系统资源,在当需要频繁创建和销毁的对象时,单例模式无疑可以提高系统的性能

    ● 单例模式具有一定的伸缩性,类能够自己来控制实例化进程,类本质上就是在改变实例化进程上有相应的伸缩性

    ● 在单例模式中,其活动的单例有且只有一个实例,因为对单例类的所有实例化得到的都是相同的一个实例,这样就能够防止其它对象对自己的实例化,确保所有的对象都访问一个实例

    ● 能提供对唯一实例的受控访问

    ● 能够允许其可变数目的实例

    ● 能够避免对共享资源的多重占用

    难点

    ● 因为单例类的职责过重,所以在一定程度上违背了单一职责原则

    ● 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难

    ● 不适用于变化的对象,要是同一类型的对象总是要在不同的用例场景发生变化,那么单例就会引起数据的错误,也因此不能保存彼此的状态

    ● 滥用单例一定会带来一些负面问题,例如为了节省资源将数据库连接池对象设计为的单例类,最终可能会导致共享连接池对象的程序过多而出现连接池溢出,再者实例化的对象长时间不被利用,那么系统会认为是垃圾而被回收,这也是导致对象状态的丢失

    QQ截图20220418145615.png

    核心

    ● 对于单例模式而言,其最核心的目的就是为了保证该类的实例对象是唯一的。为此一方面,需要将该类的构造函数设为private,另一方面,该类需要在内部完成实例的构造并对外提供访问接口

    ● 单例模式的好处显而易见,可以避免频繁创建、销毁实例所带来的性能开销;但其缺点也同样明显,此类不仅需要描述业务逻辑,同时还需要构造出该类的唯一对象并对外提供访问接口,其显然违背了单一职责原则

    要点

    ● 某个类只能有一个实例

    ● 它必须自行创建这个实例

    ● 它必须自行向整个系统提供这个实例

    注意事项

    ● 使用懒单例模式时注意线程安全问题

    ● 使用时不能用反射模式创建单例,否则会实例化一个新的对象

    ● 饿单例模式和懒单例模式构造方法由于都是私有的,因而是不能被继承的

    适用场景

    单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等,例如:

    ● 有状态的工具类对象

    ● 频繁访问数据库或文件的对象

    ● 需要频繁实例化然后销毁的对象

    ● 创建对象时耗时过多或者耗资源过多,但又经常用到的对象

    代码示例

    QQ截图20220418145834.png
    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n278" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Singleton
    {
    private:
     Singleton() {};
     ~Singleton() {};
    
     double m_fValue = 9.9;
    public:
     Singleton(const Singleton&) = delete;
     Singleton& operator=(const Singleton&)= delete;
    
     static Singleton* getInstance()
     {
     static Singleton instance;
     return &instance;
     }
    
     double getValue()
     {
     qDebug()<<"m_fValue:"<<m_fValue<<'\n';
     return m_fValue;
     }
    
    };
    
    int main(int argc, char *argv[])
    {
     Singleton* instance = Singleton::getInstance();
     instance->getValue();
    }</pre>
    

    看到这里,我们就能够想到如果将局部静态变量定义为类的静态成员变量会怎么样,这个也就是饿汉式的写法虽然这种方法也是线程安全的,但是会存在另一个问题就是 local static 对象的初始化发生在main函数执行之前,也即main函数之前的单线程启动阶段

    但是没有规定多个non-local static 对象的初始化顺序,尤其是来自多个编译单元的non-local static对象,他们的初始化顺序是随机的,所以如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例

    结语

    以上就是我今天向大家分享的内容

    更多Android进阶资料,学习笔记,底层源码解析

    资料获取方式:+V : ddz3090

    Android架构师之路还很漫长,与君共勉

    PS:有问题欢迎指正,在评论区留下你的建议和感受


    单例模式.png

    相关文章

      网友评论

        本文标题:实践GoF设计模式:Singleton Pattern 单例模式

        本文链接:https://www.haomeiwen.com/subject/bcydertx.html