代码操作中经常使用到设计模式之单例模式

作者: 良知犹存

前言:

应一位小朋友之邀继续分享一下设计模式,关于设计模式的基本介绍在上一篇文章已经有所描述,这篇就不多做赘述。今天给大家介绍一下设计模式的里面的入门篇——单例模式。
学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。

作者:良知犹存

转载授权以及围观:欢迎关注微信公众号:羽林君

或者添加作者个人微信:become_me


单例模式概念定义 {单例模式概念定义}

单例模式是们用到最简单的设计模式之一,这种类型的设计模式属于创建者类型模式。指定一个类只有一个实例,且该类能自行创建这个实例。

例如,们在一些应用编程时候会定义一些独一无二的驱动类,这个类只会有一个实例产生,通过使用单例模式,们就可以避免因为不需要的多次实例化这个类减少内存的浪费,或者造成数据的不一致。

结构定义: {结构定义:}

单例类:包含一个实例且能自行创建这个实例的类。

访问类:使用单例的类。

结构图如下 {结构图如下}


单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过"new 构造函数()“来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

下面介绍一些常用的单例模式使用的代码例子:

第 1 种:懒汉式单例 {第-1-种:懒汉式单例}

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。

范例如下: {范例如下:}

C++实现 {c实现}

include <iostream>
using namespace std;
class Signleton {
private:
 Signleton() { cout << "init singleton" << endl; 
 }

 static Signleton* _instance;
public:
    static Signleton* getInstance() {
        if(_instance == nullptr) {
            _instance = new Signleton;
        }
        return _instance;
    }
};

Signleton * Signleton:: _instance = nullptr;

int main() {
//  Signleton obj1; //这样定义会报错,调用用下面方式

  Signleton::getInstance();
  Signleton::getInstance();
  Signleton::getInstance();

  return 0;
}

python实现 {python实现}

!/usr/bin/python3

class Singleton(object):

    def __init__(self):
        print("init singleton")

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance
if __name__ == "__main__":
    obj1 = Singleton.instance()
    obj2 = Singleton.instance()
    obj3 = Singleton.instance()

第 2 种:饿汉式单例 {第-2-种:饿汉式单例}

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

范例如下: {范例如下:-1}

C++实现 {c实现-1}

include <iostream>
using namespace std;
class Signleton {
private:
 Signleton() { cout << "init singleton" << endl; 
 }

 static Signleton _instance;
public:
        static Signleton getInstance() {

        return _instance;
    }
};

Signleton  Signleton:: _instance ;

int main() {
  Signleton::getInstance();
  Signleton::getInstance();
  Signleton::getInstance();

  return 0;
}

python实现 {python实现-1}

!/usr/bin/python3

class Singleton(object):
    _instance = None

    def __init__(self):
        if not Singleton._instance:
            print("init singleton")
    @classmethod
    def instance(cls):
        if not cls._instance:
            cls._instance = Singleton()
        return cls._instance
if __name__ == "__main__":
    obj1 = Singleton.instance()
    obj2 = Singleton.instance()
    obj3 = Singleton.instance()
    print(Singleton.instance())
    print(Singleton.instance())
    print(Singleton.instance())

编译之后可以看到,们调用了三次,但是实际上构造函数只执行了一次。

两种创建单例对象的方式对比: {两种创建单例对象的方式对比:}

懒汉式 :只有这个类在被调用的时候才会创建,若是多线程同时使用,就会考虑线程安全的为题。这个代码只适合在单线程下,当多线程时,是不安全的,多线程加锁。考虑两个线程同时首次调用instance方法且同时检测到p是nullptr,则两个线程会同时构造一个实例给p,这将违反了单例的准则。

饿汉式:当成类被加载的时候,这个类就会被实例化,虽然没有被使用但是会占用内存,由于是一开始就会被实例化了,所以这个线程安全的。

第 3 种:优化版本(经常使用的版本) {第-3-种:优化版本(经常使用的版本)}

范例如下: {范例如下:-2}

C++实现 {c实现-2}

include <iostream>
using namespace std;
class Signleton {
private:
 Signleton() {
   cout << "init singleton" << endl; 
 }
public:
        static Signleton getInstance() {
        static  Signleton m_instance;
        return m_instance;
    }
};

Signleton  Signleton:: _instance ;

int main() {
  Signleton::getInstance();
  Signleton::getInstance();
  Signleton::getInstance();

  return 0;
}

这个在工作中经常使用,使用了一个局部static变量,代码也比较简洁,优先推荐这个给大家使用。

补充:C++中static对象的初始化 {补充:c中static对象的初始化}

non-local static对象(函数外)

C++规定,non-local static 对象的初始化发生在main函数执行之前,也即main函数之前的单线程启动阶段,所以不存在线程安全问题。但C++没有规定多个non-local static 对象的初始化顺序,尤其是来自多个编译单元的non-local static对象,他们的初始化顺序是随机的。

local static 对象(函数内)

对于local static 对象,其初始化发生在控制流第一次执行到该对象的初始化语句时。多个线程的控制流可能同时到达其初始化语句。

在C++11之前,在多线程环境下local static对象的初始化并不是线程安全的。具体表现就是:如果一个线程正在执行local static对象的初始化语句但还没有完成初始化,此时若其它线程也执行到该语句,那么这个线程会认为自己是第一次执行该语句并进入该local static对象的构造函数中。这会造成这个local static对象的重复构造,进而产生内存泄露问题。所以,local static对象在多线程环境下的重复构造问题是需要解决的。

而C++11则在语言规范中解决了这个问题。C++11规定,在一个线程开始local static 对象的初始化后到完成初始化前,其他线程执行到这个local static对象的初始化语句就会等待,直到该local static 对象初始化完成。



单例模式的优点和缺点 {单例模式的优点和缺点}

单例模式的优点:

单例模式可以保证内存里只有一个实例,减少了内存的开销。

可以避免对资源的多重占用。

单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。

在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。

单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

结语

这就是分享的设计模式中的单例模式,如果大家有更好的想法和需求,也欢迎大家加好友交流分享哈。


作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注,与一起同行。

                                                ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推荐阅读

【1】C++的智能指针你了解吗?

【2】嵌入式底层开发的软件框架简述

【3】CPU中的程序是怎么运行起来的 必读

【4】cartographer环境建立以及建图测试

【5】设计模式之简单工厂模式、工厂模式、抽象工厂模式的对比

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。

原文作者:良知犹存

原文链接:https://www.cnblogs.com/conscience-remain/p/14882432.html

更多推荐

更多
  • .NET人工智能教程-四、使用自然语言理解 什么是 NLU?,自然语言理解的历史,为什么机器很难理解自然语言,语言理解智能服务(LUIS),为 LUIS 获取 Azure 订阅,演示:定义应用,概述,自然语言的复杂性,统计模型作为解决方案是不够的,充满希望的未来,基于 LUIS
    Apache CN

  • .NET人工智能教程-十、人工智能的未来 AI 为什么这么受欢迎?,改进的计算能力,人工智能算法的发明,数据是新的货币,云计算的出现,服务 vs 解决方案?,认知类别,NLU 的挑战和未来,演讲的挑战和未来,搜索的挑战和未来,挑战和建议的未来,AI 优先,智能边缘,将被淘汰的是
    Apache CN

  • .NET人工智能教程-七、与语音 API 交互 与语音互动的方式,入门指南,首先获取 JSON Web 令牌,消费者语音 API,语音合成,定制语音服务,说话人识别,摘要,认知搜索 API,语音识别,语音识别内部,定制声学模型,自定义语言模型,发音数据,自定义语音转文本端点,说话人验
    Apache CN

  • .NET人工智能教程-五、探索认知语言模式 iamfeanggoodgermanyvsargentinafootballliveepic fail,Bing 拼写检查 API,文本分析 API,Web 语言模型(WebLM) API,语言分析 API,概述,这是什么?
    Apache CN

  • .NET人工智能教程-一、人工智能基础入门 真实与虚构,历史和演变,微软和人工智能,基本概念,微软的认知服务,概述,当前的事态,人工智能的商品化,机器学习,语言,演讲,计算机视觉,视力,演讲,语言,知识,搜索, 想象一下,创建一个如此智能的软件,它不仅能理解人类语言,还能理解俚语
    Apache CN

  • .NET人工智能教程-三、使用微软技术构建对话式用户界面 什么是对话式用户界面?,简史,设计原则,微软机器人框架,使用 Bot 框架创建 CUI 应用,概述,一开始:命令行界面(CLI),然后是图形用户界面,UI 又一次进化了:对话式用户界面,艾在《崔》中的角色,崔的陷阱,混合用户界面(CUI
    Apache CN

  • .NET人工智能教程-二、在 Visual Studio 中创建基于人工智能的应用 使用认知服务的先决条件,设置开发环境,获取认知服务的 Azure 订阅密钥,测试 API,创建你的第一个基于人工智能的应用,让你的应用更有趣,概述,步骤 1:设置 Azure 帐户,步骤 2:创建一个新的认知服务帐户,步骤 3:获取订阅
    Apache CN

  • .NET人工智能教程-八、应用搜索产品 搜索无处不在,普及、预测、主动(搜索的三个 p),冰的历史,必应有什么独特之处?,搜索 API,Bing 图像搜索 API,Bing 新闻搜索 API,Bing 视频搜索 API,如何使用 Bing 视频搜索 API,Bing 网络搜索
    Apache CN

  • .NET人工智能教程-九、使用建议 了解基础知识,经常汇集(FBT)的建议,逐项,基于过去历史的建议,这些建议是如何起作用的?,模型和类型,建议构建,经常聚集在一起(FBT)建设,排名,SAR(智能自适应)构建,在构建中设置规则,离线评估,用户界面,摘要, 机器学习无处不
    Apache CN

  • .NET人工智能教程-六、消费和应用 LUIS 规划您的应用,创建 LUIS 应用,添加意图,添加/标记话语,发布您的应用,添加实体,添加短语列表,建议的后续步骤,LUIS 与 Bot 框架的集成,将您的机器人添加到 Skype,概述,机器人应该能做什么?,机器人需要用户提供什么信息
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多