文档章节

单例模式(Singleton Pattern)

自我修炼
 自我修炼
发布于 2017/02/09 20:56
字数 1614
阅读 4
收藏 0

单例模式

1. 啥时候使用单例模式
保证系统中某一服务有一个统一的入口,如:一个系统中可以存在多个打印服务,但只能有一个正在工作的任务;一个系统中只能有一个计时工具或序号生成器。

如何保证一个类只有一个实例并且这个实例易于被访问?定义一个全局变量可以保证对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没与其它实例被创建,并且它可以提供一个访问该实例的方法。

2. 单例模式的定义

单例模式(Singleton Pattern):确保某一个类有且仅有一个实例,并且自行实例化向整个系统提供这个实例。

分析:某个类仅有一个实例,并且必须是自身创建这个实例,还必须自身向整个系统提供这个实例。

3. 该模式中包含的角色及其职责

1)、Singleton:单实例
对整个系统提供有且仅有一个实例,并且是自身创建这个实例。

这里写图片描述

不废话了,看下面代码!

4. 撸代码

PHP的单例模式相对于其他语言是比较简单的,因为它不需要考虑多线程问题,下面我们给出两种代码实例(PHP和C#)。

先看下在PHP下的单例模式:


/** * 单实例角色:Singleton * 防止类被继承 */
final class Singleton
{
    /** * 定义一个私有的静态全局成员变量来保存该类的唯一实例 * @var Singleton */
    private static $instance;

    /** * 通过静态方法来构造对象实例 * */
    public static function getInstance()
    {
        //检查类是否已被实例化
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    /** * 定义私有的构造函数 * 防止外部通过new关键字实例化对象 * 只允许自身实例化 */
    private function __construct()
    {
    }

    /** * 防止被复制或克隆 */
    private function __clone()
    {
    }

    /** * 防止反序列化创建该实例 */
    private function __wakeup()
    {
    }
}
/** * 测试单例模式 * */
class TestSingleton
{
    public function test(){
        //获取并创建单例对象
        $singleton = Singleton::getInstance();
    }
}

通过上例代码看出,在PHP中创建单例模式,必须遵循一下几点:
1)、有一个私有的类对象成员变量
2)、构造函数必须是私有的,防止通过new关键字被实例化
3)、__clone()和__wakeup()必须为私有的,防止复制、克隆和反序列化
4)、必须提供一个可供外界访问的静态方法,并通过此静态方法实现自身实例化对象。

在PHP中实现单例模式相对简单,因为它不需要考虑多线程问题,但C#中就必须要靠线程安全,如果C#在多线程中访问单例模式就有可能被创建多个对象,所以不得不考虑线程安全问题,要确保在多线程下访问单例模式依然是被创建一个实例对象。

C#代码如下:


    /// <summary>
    /// 单实例角色:Singleton
    /// </summary>
    public sealed class Singleton
    {
        /// <summary>
        /// 定义一个私有的静态全局成员变量来保存该类的唯一实例
        /// </summary>
        private static Singleton instance = null;
        /// <summary>
        /// 定义一个只读静态对象,且这个对象是在程序运行时创建
        /// 必须为引用类型,确保访问同一地址
        /// </summary>
        private static readonly object syncObject = new object();
        /// <summary>
        /// 定义私有的构造函数
        /// 防止外部通过new关键字实例化对象
        /// 只允许自身实例化
        /// </summary>
        private Singleton() { }
        /// <summary>
        /// 通过静态方法来构造对象实例
        /// </summary>
        /// <returns></returns>
        public static Singleton GetInstance()
        {
            //第一次判断:主要判断单例对象是否已被实例化
            if (instance == null)
            {
                //锁住引用类型对象,进行同步操作
                //必须为引用类型,因为引用类型的变量地址是同一内存地址
                lock (syncObject)
                {
                    //第二次判断:主要是防止可能延迟加载或缓存,造成创建多个实例
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }

            return instance;
        }
    }

通过上例代码看出,在C#或含有线程操作的面向对象语言中,必须遵循一下几点:
1)、有一个私有的类对象成员变量
2)、有一个私有的静态只读引用类型的对象变量,并且该对象在程序运行时被创建。
3)、构造函数必须是私有的,防止通过new关键字被实例化
4)、必须提供一个可供外界访问的静态方法,并通过此静态方法实现自身实例化对象。
并且在此方法内必须进行两次检查对象实例是否被创建,防止可能延迟加载或缓存,造成创建多个实例。

5. 单例模式的优点

实例控制

单例模式会防止其他对象实例化其自身的单例对象的副本,从而确保所有对象都访问唯一实例。

灵活性

因为类控制了实例化过程,所以类可以灵活更改实例化过程。

6. 单例模式的缺点

开销

虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。

对象生存期

不能解决删除单个对象的问题。在提供内存管理的语言中(c#),只有单例类能够导致实例被取消分配,因为它包含该实例的私有引用。

滥用单例带来的一些负面问题

如果为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

7. 使用场景

系统只需要一个实例对象,如系统要求提供一个唯一的入口,或者考虑资源消耗太大而只允许创建一个实例。

© 著作权归作者所有

共有 人打赏支持
自我修炼
粉丝 0
博文 9
码字总数 11400
作品 0
昆明
项目经理
私信 提问

暂无文章

【58沈剑 架构师之路】1分钟了解MyISAM与InnoDB的索引差异

《数据库索引,到底是什么做的?》介绍了B+树,它是一种非常适合用来做数据库索引的数据结构: (1)很适合磁盘存储,能够充分利用局部性原理,磁盘预读; (2)很低的树高度,能够存储大量数据;...

张锦飞
15分钟前
1
0
代码优化----使用builder模式构造对象

看《effective java》的时候,创建对象章节提到使用builder模式来创建对象。觉得非常好用,做一个记录。以后应该就会这么写啦~~~~~~ 对于一个有很多属性的类,在为属性赋值时,通常会用到两种...

wuyiyi
16分钟前
2
0
一文带你看懂cookie,面试前端不用愁

本文由云+社区发表 在前端面试中,有一个必问的问题:请你谈谈cookie和localStorage有什么区别啊? localStorage是H5中的一种浏览器本地存储方式,而实际上,cookie本身并不是用来做服务器存...

腾讯云加社区
17分钟前
1
0
随行付微服务测试之接口测试和契约测试

背景 日常开发过程中,项目的接口通常由服务提供方约定和提供,微服务模式下接口被多个消费者调用更是常态,那么提供方接口的变更如何快速、高效、无遗漏的通知给消费者呢?另外,当一个ser...

马力-随行付
18分钟前
1
0
为什么Python是2019最值得学的编程语言?

对于那些从来没有学习编程小伙伴,Python 是最好的选择之一, Python 是一种清晰的语言,用缩进来表示程序的嵌套关系可谓是一种创举,把过去软性的编程风格升级为硬性的语法规定。再不需要在...

糖宝lsh
18分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部